/*
* client.cc
* Copyright (C) 2000 Frank Hale
* frankhale@yahoo.com
* http://sapphire.sourceforge.net/
*
* Updated: 15 Jan 2002
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "aewm.hh"
using namespace std;
Client::Client(Display *d, Window new_client, list<Client *> *l)
{
initialize(d);
l->push_back(this);
make_new_client(new_client);
}
Client::~Client()
{
remove_client();
}
void Client::initialize(Display *d)
{
dpy = d;
name = NULL;
//icon_name = NULL;
window = None;
frame = None;
title = None;
trans = None;
window_menu = NULL;
x = 1;
y = 1;
width = 1;
height = 1;
ignore_unmap = 0;
pointer_x = 0;
pointer_y = 0;
old_cx = 0;
old_cy = 0;
wire_move = wm->getWireMove();
#ifdef SHAPE
has_been_shaped = false;
#endif
has_title = true;
has_border = true;
has_focus = false;
is_iconified = false;
// Extra Window States
is_shaded = false;
is_maximized = false;
is_maximized_vertical = false;
is_maximized_horizontal = false;
is_sticky = false;
is_always_on_top = false;
client_strut = new Strut;
client_strut->east = 0;
client_strut->west = 0;
client_strut->north = 0;
client_strut->south = 0;
has_strut=false;
has_extended_net_name = false;
skip_taskbar = false;
skip_pager = false;
last_button1_time = 0;
old_x = 0;
old_y = 0;
old_width = 1;
old_height = 1;
direction = 0;
ascent = 0;
descent = 0;
text_width = 0;
text_justify = 0;
justify_style = wm->getTextJustify();
screen = DefaultScreen(dpy);
root = wm->getRootWindow();
xres = wm->getXRes();
yres = wm->getYRes();
}
void Client::getXClientName()
{
//if (name) XFree(name);
name = new char[256];
wm->getExtendedWMHintString(window, wm->atom_extended_net_wm_name, &name);
// _NET_WM_NAME isn't set fallback to XA_WM_NAME
if(name==NULL)
{
XFetchName(dpy, window, &name);
if(name==NULL)
{
XStoreName(dpy, window, "no name");
XFetchName(dpy, window, &name);
}
}
else
has_extended_net_name=true;
if(name!=NULL)
{
XTextExtents(wm->getFont(), name , strlen(name),
&direction, &ascent,
&descent, &overall);
text_width = overall.width;
}
}
/*
// We aren't using Icon name yet so its commented out!
void Client::getXIconName()
{
if(icon_name) XFree(icon_name);
XGetIconName(dpy, w, &icon_name);
if(icon_name==NULL)
{
XSetIconName(dpy, w, "no name");
XGetIconName(dpy, w, &icon_name);
}
}
*/
// Set up a client structure for the new (not-yet-mapped) window. The
// confusing bit is that we have to ignore 2 unmap events if the
// client was already mapped but has IconicState set (for instance,
// when we are the second window manager in a session). That's
// because there's one for the reparent (which happens on all viewable
// windows) and then another for the unmapping itself.
//
void Client::make_new_client(Window w)
{
XWindowAttributes attr;
XWMHints *hints;
MwmHints *mhints;
long dummy;
XGrabServer(dpy);
window = w;
getXClientName();
//getXIconName();
XGetTransientForHint(dpy, window, &trans);
XGetWindowAttributes(dpy, window, &attr);
x = attr.x;
y = attr.y;
width = attr.width;
height = attr.height;
cmap = attr.colormap;
size = XAllocSizeHints();
XGetWMNormalHints(dpy, window, size, &dummy);
old_x = x;
old_y = y;
old_width = width;
old_height = height;
if ((mhints = get_mwm_hints(window)))
{
if (mhints->flags & MwmHintsDecorations && !(mhints->decorations & MwmDecorAll))
{
has_title = mhints->decorations & MwmDecorTitle;
has_border = mhints->decorations & MwmDecorBorder;
}
XFree(mhints);
}
if (attr.map_state == IsViewable) ignore_unmap++;
init_position();
if ((hints = XGetWMHints(dpy, w)))
{
if (hints->flags & StateHint)
set_wm_state(hints->initial_state);
else
set_wm_state(NormalState);
XFree(hints);
}
window_menu = wm->getWindowMenu();
gravitate(APPLY_GRAVITY);
reparent();
NetWMStates *win_states;
win_states = wm->getExtendedNetWMStates(window);
if(win_states->sticky) is_sticky=true;
if(win_states->shaded) shade();
if(win_states->max_vert && win_states->max_horz) maximize();
if(win_states->skip_taskbar) skip_taskbar=true;
if(win_states->skip_pager) skip_pager=true;
delete win_states;
if(!trans) wm->updateClientList();
belongsToDesktop = wm->findGnomeDesktopHint(window);
if(belongsToDesktop == -1)
{
belongsToDesktop = wm->findExtendedDesktopHint(window);
}
if(belongsToDesktop == -1)
{
belongsToDesktop = wm->getCurrentDesktop();
}
wm->setGnomeHint(window, wm->atom_gnome_win_workspace, belongsToDesktop);
wm->setExtendedWMHint(window, wm->atom_extended_net_wm_desktop, belongsToDesktop);
if(wm->getHint(window, wm->atom_gnome_win_state)&WIN_STATE_STICKY) is_sticky=true;
if(wm->getHint(window, wm->atom_gnome_win_hints)&WIN_HINTS_DO_NOT_COVER) is_always_on_top=true;
// Do we want a strut?
int num=0;
CARD32 *temp_strut = NULL;
temp_strut = (CARD32*) wm->getExtendedNetPropertyData(window, wm->atom_extended_net_wm_strut, XA_CARDINAL, &num);
if(temp_strut)
{
client_strut->west = temp_strut[0];
client_strut->east = temp_strut[1];
client_strut->north = temp_strut[2];
client_strut->south = temp_strut[3];
wm->addStrut(client_strut);
has_strut=true;
}
// Actually updates both gnome and net client lists.
update_net_wm_states();
if (get_wm_state() == NormalState || get_wm_state() == WithdrawnState)
{
XMapWindow(dpy, window);
XMapWindow(dpy, title);
XMapWindow(dpy, frame);
wm->restackOnTopWindows();
is_iconified=false;
}
else if (get_wm_state() == IconicState) iconify();
XSync(dpy, False);
XUngrabServer(dpy);
}
void Client::remove_client()
{
XGrabServer(dpy);
XUngrabButton(dpy, AnyButton, AnyModifier, frame);
gravitate(REMOVE_GRAVITY);
XReparentWindow(dpy, window, root, x, y);
XRemoveFromSaveSet(dpy,window);
XDestroyWindow(dpy, title);
XDestroyWindow(dpy, frame);
if (name) XFree(name);
//if (icon_name) XFree(icon_name);
if (size) XFree(size);
XSync(dpy, False);
XUngrabServer(dpy);
if(has_strut)
wm->removeStrut(client_strut);
delete client_strut;
window_menu->hide();
wm->getClientList()->remove(this);
wm->updateClientList();
}
/* For a regular window, trans is None (false), and we include
* enough space to draw the title. For a transient window we just make
* a tiny strip. */
int Client::theight()
{
if (!has_title) return 0;
return (trans ? 0 : wm->getFont()->ascent + wm->getFont()->descent) + 2*SPACE + BW;
}
/* Attempt to follow the ICCCM by explicity specifying 32 bits for
* this property. Does this goof up on 64 bit systems? */
void Client::set_wm_state(int state)
{
CARD32 data[2];
data[0] = state;
data[1] = None; /* Icon? We don't need no steenking icon. */
XChangeProperty(dpy, window, wm->atom_wm_state, wm->atom_wm_state,
32, PropModeReplace, (unsigned char *)data, 2);
}
/* If we can't find a wm->wm_state we're going to have to assume
* Withdrawn. This is not exactly optimal, since we can't really
* distinguish between the case where no WM has run yet and when the
* state was explicitly removed (Clients are allowed to either set the
* atom to Withdrawn or just remove it... yuck.) */
long Client::get_wm_state()
{
Atom real_type; int real_format;
unsigned long items_read, items_left;
long *data, state = WithdrawnState;
if (XGetWindowProperty(dpy, window, wm->atom_wm_state, 0L, 2L, False,
wm->atom_wm_state, &real_type, &real_format, &items_read, &items_left,
(unsigned char **) &data) == Success && items_read) {
state = *data;
XFree(data);
}
return state;
}
// This is called whenever we update our Client stuff.
void Client::send_config()
{
XConfigureEvent ce;
ce.type = ConfigureNotify;
ce.event = window;
ce.window = window;
ce.x = x;
ce.y = y;
ce.width = width;
ce.height = height;
ce.border_width = 0;
ce.above = (is_always_on_top) ? Above : Below;
ce.override_redirect = False;
XSendEvent(dpy, window, False, StructureNotifyMask, (XEvent *)&ce);
}
void Client::redraw()
{
if (!has_title) return;
if( has_focus ) XSetForeground(dpy, wm->getStringGC(), wm->getFGColor().pixel);
else XSetForeground(dpy, wm->getStringGC(), wm->getBDColor().pixel);
XDrawLine(dpy, title, wm->getBorderGC(),
0, theight() - BW + BW/2,
width, theight() - BW + BW/2);
XDrawLine(dpy, title, wm->getBorderGC(),
width - theight()+ BW/2, 0,
width - theight()+ BW/2, theight());
XPoint points[4];
points[0].x = width - 3*theight()/4 + BW;
points[0].y = 2*theight()/5 - BW;
points[1].x = width - theight()/4 - BW;
points[1].y = points[0].y;
points[2].x = width - theight()/2 - BW/2;
points[2].y = 3*theight()/5 + BW;
points[3].x = points[0].x;
points[3].y = points[0].y;
XFillPolygon(dpy, title, wm->getBorderGC(), points, 4, Convex, CoordModeOrigin);
if (!trans && name)
{
switch(justify_style)
{
case LEFT_JUSTIFY:
text_justify = SPACE;
break;
case CENTER_JUSTIFY:
text_justify = ( (width / 2) - (text_width / 2) );
break;
case RIGHT_JUSTIFY:
text_justify = width - text_width - 25;
break;
}
if(name!=NULL)
{
XDrawString(dpy, title, wm->getStringGC(),
text_justify, SPACE + wm->getFont()->ascent,
name, strlen(name));
}
}
}
/* Window gravity is a mess to explain, but we don't need to do much
* about it since we're using X borders. For NorthWest et al, the top
* left corner of the window when there is no WM needs to match up
* with the top left of our fram once we manage it, and likewise with
* SouthWest and the bottom right (these are the only values I ever
* use, but the others should be obvious.) Our titlebar is on the top
* so we only have to adjust in the first case. */
void Client::gravitate(int multiplier)
{
int dy = 0;
int gravity = (size->flags & PWinGravity) ?
size->win_gravity : NorthWestGravity;
switch (gravity) {
case NorthWestGravity:
case NorthEastGravity:
case NorthGravity: dy = theight(); break;
case CenterGravity: dy = theight()/2; break;
}
y += multiplier * dy;
}
/* Well, the man pages for the shape extension say nothing, but I was
* able to find a shape.PS.Z on the x.org FTP site. What we want to do
* here is make the window shape be a boolean OR (or union, if you
* prefer) of the client's shape and our titlebar. The titlebar
* requires both a bound and a clip because it has a border -- the X
* server will paint the border in the region between the two. (I knew
* that using X borders would get me eventually... ;-)) */
#ifdef SHAPE
void Client::set_shape()
{
int n, order;
XRectangle temp, *dummy;
dummy = XShapeGetRectangles(dpy, window, ShapeBounding, &n, &order);
if (n > 1) {
XShapeCombineShape(dpy, frame, ShapeBounding,
0, theight(), window, ShapeBounding, ShapeSet);
temp.x = -BW;
temp.y = -BW;
temp.width = width + 2*BW;
temp.height = theight() + BW;
XShapeCombineRectangles(dpy, frame, ShapeBounding,
0, 0, &temp, 1, ShapeUnion, YXBanded);
temp.x = 0;
temp.y = 0;
temp.width = width;
temp.height = theight() - BW;
XShapeCombineRectangles(dpy, frame, ShapeClip,
0, theight(), &temp, 1, ShapeUnion, YXBanded);
has_been_shaped = 1;
}
else
if (has_been_shaped)
{
/* I can't find a 'remove all shaping' function... */
temp.x = -BW;
temp.y = -BW;
temp.width = width + 2*BW;
temp.height = height + theight() + 2*BW;
XShapeCombineRectangles(dpy, frame, ShapeBounding,
0, 0, &temp, 1, ShapeSet, YXBanded);
}
XFree(dummy);
}
#endif
void Client::resize()
{
sweep();
XMoveResizeWindow(dpy, frame,
x, y - theight(), width, height + theight());
XResizeWindow(dpy, title, width, theight());
XMoveResizeWindow(dpy, window,
0, theight(), width, height);
send_config();
}
void Client::iconify()
{
if (!ignore_unmap) ignore_unmap++;
if(has_focus) set_focus(false);
XUnmapWindow(dpy, window);
XUnmapWindow(dpy, title);
XUnmapWindow(dpy, frame);
is_iconified=true;
set_wm_state(IconicState);
if(!trans)
{
wm->addClientToIconMenu(this);
wm->findTransientsToMapOrUnmap(window, true);
}
}
void Client::hide()
{
if (!ignore_unmap) ignore_unmap++;
if(has_focus) set_focus(false);
if(window_menu->is_visible()) window_menu->hide();
XUnmapWindow(dpy, window);
XUnmapWindow(dpy, title);
XUnmapWindow(dpy, frame);
set_wm_state(WithdrawnState);
}
void Client::unhide()
{
if(belongsToDesktop == wm->getCurrentDesktop())
{
XMapRaised(dpy, window);
XMapRaised(dpy, title);
XMapRaised(dpy, frame);
if(is_iconified)
{
is_iconified=false;
wm->removeClientFromIconMenu(this);
}
set_wm_state(NormalState);
if(!trans) wm->findTransientsToMapOrUnmap(window, false);
wm->restackOnTopWindows();
}
}
/* The name of this function is a bit misleading: if the client
* doesn't listen to WM_DELETE then we just terminate it with extreme
* prejudice. */
void Client::send_wm_delete()
{
int i, n, found = 0;
Atom *protocols;
if (XGetWMProtocols(dpy, window, &protocols, &n)) {
for (i=0; i<n; i++) if (protocols[i] == wm->atom_wm_delete) found++;
XFree(protocols);
}
if (found)
send_xmessage(window, wm->atom_wm_protos, NoEventMask, wm->atom_wm_delete);
else XKillClient(dpy, window);
}
// This function sets up the initial position of windows when they are mapped
// for the first time. It still needs some work done to it to make it more
// aware of _NET_WM_STRUT's
//
//
// XSizeHints structure definition
//
// typedef struct {
// long flags;
// int x, y;
// int width, height;
// int min_width, min_height;
// int max_width, max_height;
// int width_inc, height_inc;
// struct {
// int x;
// int y;
// } min_aspect, max_aspect;
// int base_width, base_height;
// int win_gravity;
//
// } XSizeHints;
//
void Client::init_position()
{
int mouse_x, mouse_y;
Strut *temp_strut = wm->getMasterStrut();
unsigned int w, h;
unsigned int border_width, depth;
XWindowAttributes attr;
XGetWindowAttributes(dpy, window, &attr);
// If the window is mapped already we want
// to leave it alone!
if (attr.map_state == IsViewable) return;
XGetGeometry(dpy, window, &root, &x, &y, &w, &h, &border_width, &depth);
width = (int)w;
height = (int)h;
if(x+width>xres) width -= (width - xres);
if(y+height>yres) { height -= (height - yres); }
// Let's be strut concious here, hehe =)
fixupPositionBasedOnStruts(temp_strut);
if (size->flags & PPosition)
{
if(!x) x = size->x;
if(!y) y = size->y;
// Let's be strut concious here, hehe =)
fixupPositionBasedOnStruts(temp_strut);
}
else
{
if (size->flags & USPosition)
{
if(!x) x = size->x;
if(!y) y = size->y;
// Let's be strut concious here, hehe =)
fixupPositionBasedOnStruts(temp_strut);
}
else
if ( (x==0) || (y==0) )
{
if( width>=xres && height>=yres )
{
is_always_on_top=true;
x=0;
y=0;
width=xres;
height=yres-theight();
if(!trans) wm->updateClientList();
}
else
{
wm->getMousePosition(&mouse_x, &mouse_y);
if(mouse_x && mouse_y)
{
x = (int) (((long) (xres - width)
* (long) mouse_x) / (long) xres);
y = (int) (((long) (yres - height - theight())
* (long) mouse_y) / (long) yres);
y = (y<theight()) ? theight() : y;
// Let's be strut concious here, hehe =)
// This isn't the end all and be all of
// being strut conscious. This doesn't account
// for some windows.
fixupPositionBasedOnStruts(temp_strut);
gravitate(REMOVE_GRAVITY);
}
}
}
}
}
void Client::fixupPositionBasedOnStruts(Strut* temp_strut)
{
// This has been totally rewritten. The basic aims are:-
// 1. Avoid going under the north strut (else we lose the titlebar)
// 2. Avoid going under struts
// To do this we ensure 2, then ensure 1.
// If ensuring 1 causes 2 to be broken, too bad!
bool size_changed=false;
x = ((unsigned int)x < temp_strut->west) ? (temp_strut->west) : x;
x = (((unsigned int)x + (unsigned int)width) > ((unsigned int)xres - temp_strut->east)) ? (xres - (temp_strut->east) - width) : x;
y = (((unsigned int)y + (unsigned int)height) > ((unsigned int)yres - temp_strut->south)) ? (yres - height - (temp_strut->south)) : y;
y = (((unsigned int)y - (unsigned int)theight()) < temp_strut->north) ? ((temp_strut->north) + theight()) : y;
if(width > xres)
{
width = xres - temp_strut->east - x;
size_changed=true;
}
if(height > yres)
{
height = yres - temp_strut->south - y;
size_changed=true;
}
if(size_changed) send_config();
}
void Client::drag()
{
XEvent ev;
int nx=0, ny=0;
//old_cx = x;
//old_cy = y;
//XGrabServer(dpy);
draw_outline();
for (;;)
{
XMaskEvent(dpy, PointerMotionMask|ButtonReleaseMask, &ev);
draw_outline();
XUngrabServer(dpy);
XSync(dpy, False);
switch(ev.type)
{
case MotionNotify:
{
nx = old_cx + (ev.xmotion.x_root - pointer_x);
ny = old_cy + (ev.xmotion.y_root - pointer_y);
if(wm->getEdgeSnap())
{
// Move beyond edges of screen
if(nx == xres - width) nx = xres - width + 1;
else if(nx == 0) nx = -1;
if(ny == yres - SNAP) ny = yres - SNAP - 1;
else if(ny == theight()) ny = theight() - 1;
// Snap to edges of screen
if( (nx + width >= xres - SNAP) && (nx + width <= xres) ) nx = xres - width;
else if( (nx <= SNAP) && (nx >= 0) ) nx = 0;
if(is_shaded)
{
if( (ny >= yres - SNAP) && (ny <= yres) ) ny = yres;
else if( (ny - theight() <= SNAP) && (ny - theight() >= 0)) ny = theight();
}
else
{
if( (ny + height >= yres - SNAP) && (ny + height <= yres) ) ny = yres - height;
else if( (ny - theight() <= SNAP) && (ny - theight() >= 0)) ny = theight();
}
}
x=nx; y=ny;
}
break;
case ButtonRelease:
{
XMoveWindow(dpy, frame, x, y-theight());
send_config();
return;
}
}
//XGrabServer(dpy);
draw_outline();
}
}
void Client::stick(int sticky_trans)
{
if (sticky_trans == DISALLOW_STICKY_TRANS && trans)
return;
if(! is_sticky)
is_sticky = true;
else
is_sticky = false;
send_config();
update_net_wm_states();
// if we've unset the sticky hint, make sure the window and all
// of its child windows are placed on the desktop it was unstuck
// to (so we can move a window to a different workspace by making
// it sticky, moving to that workspace, and making it unsticky)
if(! is_sticky)
{
set_desktop(wm->getCurrentDesktop());
wm->updateClientList();
wm->setGnomeHint(window, wm->atom_gnome_win_workspace, belongsToDesktop);
wm->setExtendedWMHint(window, wm->atom_extended_net_wm_desktop, belongsToDesktop);
}
if(!trans) wm->findTransientsToStickOrUnstick(window, is_sticky);
}
void Client::maximize()
{
if(trans) return;
if(is_shaded)
shade();
if(is_maximized_vertical && ! is_maximized)
maximize_vertical();
if(is_maximized_horizontal && ! is_maximized)
maximize_horizontal();
if(! is_maximized)
{
old_x=x;
old_y=y;
old_width=width;
old_height=height;
// Check to see if this client sets
// its max size property. If so don't
// maximize it past that size.
if (size->flags & PMaxSize) {
width = size->max_width;
height = size->max_height;
XMoveResizeWindow(dpy, frame, x, y-theight(), width, height+theight());
} else {
x=0;
y=0;
width=xres;
height=yres;
// MAKE SURE WE DON'T MAXIMIZE OVER A STRUT
Strut *temp_strut = wm->getMasterStrut();
x = temp_strut->west;
y = temp_strut->north;
width = xres - temp_strut->east - x;
height = yres - temp_strut->south - y;
XMoveResizeWindow(dpy, frame, x, y, width, height);
y = theight();
height -= theight();
}
is_maximized=true;
is_maximized_vertical=true;
is_maximized_horizontal=true;
} else {
x=old_x;
y=old_y;
width=old_width;
height=old_height;
XMoveResizeWindow(dpy, frame,
old_x, old_y - theight(), old_width, old_height + theight());
is_maximized=false;
is_maximized_vertical=false;
is_maximized_horizontal=false;
if(is_shaded) is_shaded=false;
}
XResizeWindow(dpy, title, width, theight());
XResizeWindow(dpy, window, width, height);
send_config();
update_net_wm_states();
}
void Client::maximize_vertical()
{
if(trans) return;
if(is_shaded)
shade();
if(is_maximized)
maximize();
if(is_maximized_horizontal)
maximize_horizontal();
if(! is_maximized_vertical)
{
old_y=y;
old_height=height;
// Check to see if this client sets
// its max size property. If so don't
// maximize it past that size.
if (size->flags & PMaxSize) {
height = size->max_height;
XMoveResizeWindow(dpy, frame, x, y-theight(), width, height+theight());
} else {
y=0;
height=yres;
// MAKE SURE WE DON'T MAXIMIZE OVER A STRUT
Strut *temp_strut = wm->getMasterStrut();
y = temp_strut->north;
height = yres - temp_strut->south - y;
XMoveResizeWindow(dpy, frame, x, y, width, height);
y = theight();
height -= theight();
}
is_maximized=false;
is_maximized_vertical=true;
is_maximized_horizontal=false;
} else {
y=old_y;
height=old_height;
XMoveResizeWindow(dpy, frame,
x, old_y - theight(), width, old_height + theight());
is_maximized=false;
is_maximized_vertical=false;
is_maximized_horizontal=false;
if(is_shaded) is_shaded=false;
}
XResizeWindow(dpy, title, width, theight());
XResizeWindow(dpy, window, width, height);
send_config();
update_net_wm_states();
}
void Client::maximize_horizontal()
{
if(trans) return;
if(is_shaded)
shade();
if(is_maximized)
maximize();
if(is_maximized_vertical)
maximize_vertical();
if(! is_maximized_horizontal)
{
old_x=x;
old_width=width;
// Check to see if this client sets
// its max size property. If so don't
// maximize it past that size.
if (size->flags & PMaxSize) {
width = size->max_width;
XMoveResizeWindow(dpy, frame, x, y-theight(), width, height+theight());
} else {
x=0;
width=xres;
// MAKE SURE WE DON'T MAXIMIZE OVER A STRUT
Strut *temp_strut = wm->getMasterStrut();
x = temp_strut->west;
width = xres - temp_strut->east - x;
XMoveResizeWindow(dpy, frame, x, y-theight(), width, height+theight());
}
is_maximized=false;
is_maximized_vertical=false;
is_maximized_horizontal=true;
} else {
x=old_x;
width=old_width;
XMoveResizeWindow(dpy, frame,
old_x, y - theight(), old_width, height + theight());
is_maximized=false;
is_maximized_vertical=false;
is_maximized_horizontal=false;
if(is_shaded) is_shaded=false;
}
XResizeWindow(dpy, title, width, theight());
XResizeWindow(dpy, window, width, height);
send_config();
update_net_wm_states();
}
void Client::handle_motion_notify_event(XMotionEvent *ev)
{
int nx=0, ny=0;
if(! wire_move)
{
if(ev->state & Button1Mask)
{
nx = old_cx + (ev->x_root - pointer_x);
ny = old_cy + (ev->y_root - pointer_y);
if(wm->getEdgeSnap())
{
// Move beyond edges of screen
if(nx == xres - width) nx = xres - width + 1;
else if(nx == 0) nx = -1;
if(ny == yres - SNAP) ny = yres - SNAP - 1;
else if(ny == theight()) ny = theight() - 1;
// Snap to edges of screen
if( (nx + width >= xres - SNAP) && (nx + width <= xres) ) nx = xres - width;
else if( (nx <= SNAP) && (nx >= 0) ) nx = 0;
if(is_shaded)
{
if( (ny >= yres - SNAP) && (ny <= yres) ) ny = yres;
else if( (ny - theight() <= SNAP) && (ny - theight() >= 0)) ny = theight();
}
else
{
if( (ny + height >= yres - SNAP) && (ny + height <= yres) ) ny = yres - height;
else if( (ny - theight() <= SNAP) && (ny - theight() >= 0)) ny = theight();
}
}
x=nx; y=ny;
XMoveWindow(dpy, frame, nx, ny-theight());
//send_config();
}
} else {
if(ev->state & Button1Mask)
{
XEvent ev;
// Only call the drag function if we are
// actually dragging the window.
while(1)
{
XNextEvent(dpy,&ev);
if(ev.type==Expose)
{
while(XCheckTypedEvent(dpy, Expose, &ev));
// Make sure our titlebar gets repainted
// if it needs to be.
if(ev.xexpose.count == 0) redraw();
}
else
if(ev.type==MotionNotify)
{
drag();
break;
}
else
{
// Put all events we don't
// want to handle back so
// that the main window manager
// event loop can handle them.
XPutBackEvent(dpy,&ev);
return;
}
}
}
}
}
void Client::sweep()
{
XEvent ev;
old_cx = x;
old_cy = y;
if (!Grab(root, MouseMask, wm->getResizeCursor())) return;
draw_outline();
setmouse(window, width, height);
//XGrabServer(dpy);
for (;;)
{
XMaskEvent(dpy, MouseMask, &ev);
draw_outline(); /* clear */
XUngrabServer(dpy);
switch (ev.type)
{
case MotionNotify:
recalc_sweep(old_cx, old_cy, ev.xmotion.x, ev.xmotion.y);
break;
case ButtonRelease:
Ungrab();
return;
}
XSync(dpy,False);
//XGrabServer(dpy);
draw_outline();
}
}
void Client::recalc_sweep(int x1, int y1, int x2, int y2)
{
if (is_resizable())
{
width = abs(x1 - x2) - BW;
height = abs(y1 - y2) - BW;
if(width > wm->getXRes()) width = wm->getXRes();
if(height > wm->getYRes()) height = wm->getYRes();
get_incsize(&width, &height, PIXELS);
if (size->flags & PMinSize) {
if (width < size->min_width) width = size->min_width;
if (height < size->min_height) height = size->min_height;
}
if (size->flags & PMaxSize) {
if (width > size->max_width) width = size->max_width;
if (height > size->max_height) height = size->max_height;
}
x = (x1 <= x2) ? x1 : x1 - width;
y = (y1 <= y2) ? y1 : y1 - height;
}
}
void Client::draw_outline()
{
char buf[32];
int w, h;
if (!get_incsize(&w, &h, INCREMENTS))
{
w = width;
h = height;
}
if(! is_shaded)
{
XDrawRectangle(dpy, root, wm->getInvertGC(),
x + BW/2, y - theight() + BW/2,
width + BW, height + theight() + BW);
XDrawLine(dpy, root, wm->getInvertGC(), x + BW, y + BW/2,
x + width + BW, y + BW/2);
gravitate(REMOVE_GRAVITY);
snprintf(buf, sizeof buf, "%dx%d+%d+%d", width, height, x, y);
gravitate(APPLY_GRAVITY);
XDrawString(dpy, root, wm->getInvertGC(),
x + width - XTextWidth(wm->getFont(), buf, strlen(buf)) - SPACE,
y + height - SPACE,
buf, strlen(buf));
}
else
{
XDrawRectangle(dpy, root, wm->getInvertGC(),
x + BW/2,
y - theight() + BW/2,
width + BW,
theight() + BW);
}
}
int Client::is_resizable()
{
if ((size->flags & PMinSize) && (size->flags & PMaxSize) &&
size->min_width == size->max_width && size->min_height ==
size->max_height)
return 0;
if ((size->flags & PResizeInc) && size->width_inc < 2 &&
size->height_inc < 2)
return 0;
return 1;
}
/* If the window in question has a ResizeInc int, then it wants to be
* resized in multiples of some (x,y). Here we set x_ret and y_ret to
* the number of multiples (if mode == INCREMENTS) or the correct size
* in pixels for said multiples (if mode == PIXELS). */
int Client::get_incsize(int *x_ret, int *y_ret, int mode)
{
int basex, basey;
if (size->flags & PResizeInc)
{
basex = (size->flags & PBaseSize) ? size->base_width :
(size->flags & PMinSize) ? size->min_width : 0;
basey = (size->flags & PBaseSize) ? size->base_height :
(size->flags & PMinSize) ? size->min_height : 0;
if (mode == PIXELS)
{
*x_ret = width - ((width - basex) % ((size->width_inc == 0) ? 1 : size->width_inc));
*y_ret = height - ((height - basey) % ((size->height_inc == 0) ? 1 : size->height_inc));
}
else /* INCREMENTS */
{
*x_ret = (width - basex) / ((size->width_inc == 0) ? 1 : size->width_inc);
*y_ret = (width - basey) / ((size->height_inc == 0) ? 1 : size->height_inc);
}
return 1;
}
return 0;
}
/* This one does -not- free the data coming back from Xlib; it just
* sends back the pointer to what was allocated. */
MwmHints* Client::get_mwm_hints(Window w)
{
Atom real_type; int real_format;
unsigned long items_read, items_left;
MwmHints *data;
if (XGetWindowProperty(dpy, w, wm->atom_mwm_hints, 0L, 20L, False,
wm->atom_mwm_hints, &real_type, &real_format, &items_read, &items_left,
(unsigned char **) &data) == Success && items_read >= PropMwmHintsElements)
{
return data;
}
else return NULL;
}
// This function makes it so only the titlebar shows.
void Client::shade()
{
raise();
wm->restackOnTopWindows();
if(! is_shaded)
{
XResizeWindow(dpy, frame, width, theight() - 1);
is_shaded=true;
} else {
XResizeWindow(dpy, frame, width, height + theight());
is_shaded=false;
}
update_net_wm_states();
}
/* Because we are redirecting the root window, we get ConfigureRequest
* events from both clients we're handling and ones that we aren't.
* For clients we manage, we need to fiddle with the frame and the
* client window, and for unmanaged windows we have to pass along
* everything unchanged. Thankfully, we can reuse (a) the
* XWindowChanges struct and () the code to configure the client
* window in both cases.
*
* Most of the assignments here are going to be garbage, but only the
* ones that are masked in by e->value_mask will be looked at by the X
* server. */
void Client::handle_configure_request(XConfigureRequestEvent *e)
{
XWindowChanges wc;
gravitate(REMOVE_GRAVITY);
if (e->value_mask & CWX) x = e->x;
if (e->value_mask & CWY) y = e->y;
if (e->value_mask & CWWidth) width = e->width;
if (e->value_mask & CWHeight) height = e->height;
gravitate(APPLY_GRAVITY);
Strut *temp_strut = wm->getMasterStrut();
if((! has_strut) && (has_title))
{
fixupPositionBasedOnStruts(temp_strut);
}
wc.x = x;
wc.y = y - theight();
wc.width = width;
wc.height = height + theight();
wc.border_width = BW;
wc.sibling = e->above;
wc.stack_mode = e->detail;
XConfigureWindow(dpy, frame, e->value_mask, &wc);
if(! is_shaded)
{
XMoveResizeWindow(dpy, frame,x,y-theight(), width, height+theight());
XResizeWindow(dpy, title, width, theight());
XMoveResizeWindow(dpy, window,0,theight(),width,height);
}
// If an app wants to place his window in a bogus position
// like offscreen then fix its position. Lets see if this
// works out okay. Warez and Porn sites have a bad habit
// of trying to place the Mozilla browser window titlebar
// just offscreen so you can't close the window.
// The bastards!
if( (x + width > xres) ||
(height + theight() > yres) ||
(x > xres) ||
(y > yres) ||
(x < 0) ||
(y < 0)
)
init_position();
#ifdef SHAPE
if (e->value_mask & (CWWidth|CWHeight)) set_shape();
#endif
send_config();
if (e->value_mask & CWY) wc.x = 0;
if (e->value_mask & CWY) wc.y = theight();
if (e->value_mask & CWWidth) wc.width = e->width;
if (e->value_mask & CWHeight) wc.height = e->height;
wc.sibling = e->above;
wc.stack_mode = e->detail;
XConfigureWindow(dpy, e->window, e->value_mask, &wc);
wm->restackOnTopWindows();
}
// Two possiblilies if a client is asking to be mapped. One is that
// it's a new window, so we handle that if it isn't in our clients
// list anywhere. The other is that it already exists and wants to
// de-iconify, which is simple to take care of.
void Client::handle_map_request(XMapRequestEvent *e)
{
unhide();
}
void Client::handle_unmap_event(XUnmapEvent *e)
{
if (! ignore_unmap)
{
delete this;
}
}
// This happens when a window is iconified and destroys itself. An
// Unmap event wouldn't happen in that case because the window is
// already unmapped.
void Client::handle_destroy_event(XDestroyWindowEvent *e)
{
delete this;
}
// If a client wants to iconify itself (boo! hiss!) it must send a
// special kind of ClientMessage. We might set up other handlers here
// but there's nothing else required by the ICCCM.
void Client::handle_client_message(XClientMessageEvent *e)
{
bool state_remove=false;
bool state_add=false;
bool state_toggle=false;
if(e->message_type == wm->atom_extended_net_wm_state)
{
if(e->data.l[0]==_NET_WM_STATE_REMOVE) state_remove=true;
if(e->data.l[0]==_NET_WM_STATE_ADD) state_add=true;
if(e->data.l[0]==_NET_WM_STATE_TOGGLE) state_toggle=true;
// There is no modal support in aewm++ yet
//if(
// (e->data.l[1] == wm->atom_extended_net_wm_state_modal)
// ||
// (e->data.l[2] == wm->atom_extended_net_wm_state_modal)
//)
// is_modal=true;
if(
(e->data.l[1] == (long) wm->atom_extended_net_wm_state_sticky)
||
(e->data.l[2] == (long) wm->atom_extended_net_wm_state_sticky)
)
{
if(state_add) is_sticky=true;
if(state_remove) is_sticky=false;
if(state_toggle) is_sticky = (is_sticky) ? true : false;
}
if(
(e->data.l[1] == (long) wm->atom_extended_net_wm_state_maximized_vert)
||
(e->data.l[2] == (long) wm->atom_extended_net_wm_state_maximized_vert)
)
{
if(state_add) is_maximized_vertical=true;
if(state_remove) is_maximized_vertical=false;
if(state_toggle) is_maximized_vertical = (is_maximized_vertical) ? true : false;
}
if(
(e->data.l[1] == (long) wm->atom_extended_net_wm_state_maximized_horz)
||
(e->data.l[2] == (long) wm->atom_extended_net_wm_state_maximized_horz)
)
{
if(state_add) is_maximized_horizontal=true;
if(state_remove) is_maximized_horizontal=false;
if(state_toggle) is_maximized_horizontal = (is_maximized_horizontal) ? true : false;
}
if(is_maximized_vertical || is_maximized_horizontal) maximize();
if(
(e->data.l[1] == (long) wm->atom_extended_net_wm_state_shaded)
||
(e->data.l[2] == (long) wm->atom_extended_net_wm_state_shaded)
)
{
if(state_add) is_maximized_horizontal=true;
if(state_remove) is_maximized_horizontal=false;
if(state_toggle) shade();
}
if(
(e->data.l[1] == (long) wm->atom_extended_net_wm_state_skip_taskbar)
||
(e->data.l[2] == (long) wm->atom_extended_net_wm_state_skip_taskbar)
)
{
if(state_add) skip_taskbar=true;
if(state_remove) skip_taskbar=false;
if(state_toggle) skip_taskbar = (skip_taskbar) ? true : false;
}
if(
(e->data.l[1] == (long) wm->atom_extended_net_wm_state_skip_pager)
||
(e->data.l[2] == (long) wm->atom_extended_net_wm_state_skip_pager)
)
{
if(state_add) skip_pager=true;
if(state_remove) skip_pager=false;
if(state_toggle) skip_pager = (skip_pager) ? true : false;
}
update_net_wm_states();
}
if(e->message_type == wm->atom_extended_net_active_window &&
e->data.l[0] == 0)
{
send_xmessage(window, wm->atom_wm_protos, SubstructureRedirectMask, wm->atom_wm_takefocus );
wm->setFocusedClient(this);
XInstallColormap(dpy, cmap);
}
if(e->message_type == wm->atom_extended_net_close_window &&
e->data.l[0] == 0)
delete this;
if (e->message_type == wm->atom_wm_change_state &&
e->format == 32 && e->data.l[0] == IconicState)
iconify();
}
void Client::handle_property_change(XPropertyEvent *e)
{
long dummy;
if(e->atom == wm->atom_gnome_win_state)
{
if(wm->getHint(window, wm->atom_gnome_win_state)&WIN_STATE_STICKY) is_sticky=true;
else is_sticky=false;
}
if(e->atom == wm->atom_gnome_win_hints)
{
if(wm->getHint(window, wm->atom_gnome_win_hints)&WIN_HINTS_DO_NOT_COVER) is_always_on_top=true;
else is_always_on_top=false;
}
if(e->atom == wm->atom_extended_net_wm_desktop)
{
belongsToDesktop = wm->findExtendedDesktopHint(window);
if(belongsToDesktop != wm->getCurrentDesktop()) hide();
}
if(e->atom == wm->atom_extended_net_wm_strut)
{
// Do we want to update our strut?
int num=0;
CARD32 *temp_strut = NULL;
temp_strut = (CARD32*) wm->getExtendedNetPropertyData(window, wm->atom_extended_net_wm_strut, XA_CARDINAL, &num);
if(has_strut)
{
wm->removeStrut(client_strut);
has_strut=false;
}
if(temp_strut)
{
client_strut->west = temp_strut[0];
client_strut->east = temp_strut[1];
client_strut->north = temp_strut[2];
client_strut->south = temp_strut[3];
wm->addStrut(client_strut);
has_strut=true;
}
}
if(has_extended_net_name)
{
if(e->atom == wm->atom_extended_net_wm_name)
{
getXClientName();
// Yup we totally ignore UTF-8 strings here. Don't
// know how to implement it. And I can't find any
// docs on how to implement it with respect to window
// managers.
XClearWindow(dpy, title);
redraw();
}
}
switch (e->atom)
{
case XA_WM_NAME:
if(! has_extended_net_name)
{
getXClientName();
XClearWindow(dpy, title);
redraw();
}
break;
case XA_WM_NORMAL_HINTS:
XGetWMNormalHints(dpy, window, size, &dummy);
break;
case XA_WM_TRANSIENT_FOR:
if(!trans) XGetTransientForHint(dpy, window, &trans);
break;
}
}
void Client::reparent()
{
XSetWindowAttributes pattr;
//MJR: Don't need this one because the caller has already grabbed
//XGrabServer(dpy);
pattr.background_pixel = wm->getFCColor().pixel;
pattr.border_pixel = wm->getBDColor().pixel;
pattr.do_not_propagate_mask = ButtonPressMask|ButtonReleaseMask|ButtonMotionMask;
pattr.override_redirect=False;
pattr.event_mask = ButtonMotionMask |
ChildMask |
ButtonMask |
ExposureMask |
EnterWindowMask |
LeaveWindowMask ;
frame = XCreateWindow(dpy,
root,
x,
y - theight(),
width,
height + theight(),
BW,
DefaultDepth(dpy, wm->getScreen()),
CopyFromParent,
DefaultVisual(dpy, wm->getScreen()),
CWOverrideRedirect|CWDontPropagate|CWBackPixel|CWBorderPixel|CWEventMask,
&pattr);
// This window is used to house the window title and title bar button
title = XCreateWindow(dpy,
frame,
0,
0,
width,
theight(),
0,
DefaultDepth(dpy, wm->getScreen()),
CopyFromParent,
DefaultVisual(dpy, wm->getScreen()),
CWOverrideRedirect|CWDontPropagate|CWBackPixel|CWBorderPixel|CWEventMask,
&pattr);
#ifdef SHAPE
if (wm->getShape()) {
XShapeSelectInput(dpy, window, ShapeNotifyMask);
set_shape();
}
#endif
// We don't want these masks to be propagated down to the frame
XChangeWindowAttributes(dpy, window, CWDontPropagate, &pattr);
XSelectInput(dpy, window, FocusChangeMask|PropertyChangeMask);
XAddToSaveSet(dpy, window);
XSetWindowBorderWidth(dpy, window, 0);
XReparentWindow(dpy, window, frame, 0, theight());
XResizeWindow(dpy,window,width,height);
send_config();
XGrabButton(dpy,
Button1,
AnyModifier,
frame,
1,
ButtonPressMask,
GrabModeSync,
GrabModeAsync, None, None);
//XSync(dpy, false);
//XUngrabServer(dpy);
}
// This function handles the button events.
// Remember this is designed for a 3 button mouse and to the way I like
// the buttons laid out. If you want something different then edit
// this until your heart is content.
void Client::handle_button_event(XButtonEvent *e)
{
int in_box;
// Formula to tell if the pointer is in the little
// box on the right edge of the window. This box is
// the iconify button, maximize button and close button.
in_box = (e->x >= width - theight()) && (e->y <= theight());
// Used to compute the pointer position on click
// used in the motion handler when doing a window move.
old_cx = x;
old_cy = y;
pointer_x = e->x_root;
pointer_y = e->y_root;
// Allow us to get clicks from anywhere on the window
// so click to raise works.
XAllowEvents(dpy, ReplayPointer, CurrentTime);
window_menu->hide();
switch (e->button)
{
case Button1:
{
if (e->type == ButtonPress)
{
if(e->window == window || e->subwindow == window)
{
// I always use these two functions
// together because I want to raise
// the current client to the top but
// then have the windows that always
// want to be ontop raised above the
// current client.
raise();
wm->restackOnTopWindows();
}
if (e->window == title)
{
window_menu->hideAllVisibleSubmenus();
if (in_box)
{
if(!trans)
{
window_menu->hide();
iconify();
}
}
else
{
raise();
wm->restackOnTopWindows();
}
}
}
if (e->type == ButtonRelease)
{
// Check for a double click then maximize
// the window.
if(e->time-last_button1_time<250)
{
maximize();
last_button1_time=0;
return;
} else
last_button1_time=e->time;
}
}
break;
case Button2:
{
if(e->window == title)
{
if(! in_box)
{
if(e->type == ButtonRelease) shade();
} else {
if(e->type == ButtonPress)
{
if(is_shaded) shade();
raise();
wm->restackOnTopWindows();
resize();
}
}
}
}
break;
case Button3:
{
if(e->window == title)
{
if (e->type == ButtonRelease)
{
if (in_box)
send_wm_delete();
else {
if(!trans)
{
window_menu->setThisClient(this);
window_menu->show();
}
}
}
}
}
break;
}
}
void Client::handle_focus_in_event(XFocusChangeEvent *e)
{
send_xmessage(window, wm->atom_wm_protos, SubstructureRedirectMask, wm->atom_wm_takefocus );
XInstallColormap(dpy, cmap);
wm->setExtendedNetActiveWindow(window);
}
void Client::handle_focus_out_event(XFocusChangeEvent *e)
{
if(e->window == window)
wm->setExtendedNetActiveWindow(None);
}
void Client::set_focus(bool focus)
{
has_focus=focus;
if (has_title)
{
if(has_focus)
XSetWindowBackground(dpy, title, wm->getBGColor().pixel);
else
XSetWindowBackground(dpy, title, wm->getFCColor().pixel);
XClearWindow(dpy, title);
redraw();
}
}
void Client::warpToTitlebarCenter()
{
if (is_shaded)
setmouse(root, x + (width / 2), y);
else
setmouse(root, x + (width / 2), y + 1);
}
void Client::moveToCorner(int whichCorner)
{
Strut *temp_strut = wm->getMasterStrut();
int old_height = height;
switch (whichCorner)
{
case TOP_LEFT:
x = 0;
y = theight() - 1;
break;
case TOP_RIGHT:
x = xres - width;
y = theight() - 1;
break;
case BOTTOM_LEFT:
x = 0;
if (is_shaded)
y = yres;
else
y = (yres - height) + (theight() - 1);
break;
case BOTTOM_RIGHT:
x = xres - width;
if (is_shaded)
y = yres;
else
y = (yres - height) + (theight() - 1);
break;
default:
return;
}
if (is_shaded)
{
height = 0;
fixupPositionBasedOnStruts(temp_strut);
height = old_height;
XMoveResizeWindow(dpy, frame, x, y - theight(), width, theight() - 1);
}
else
{
fixupPositionBasedOnStruts(temp_strut);
XMoveResizeWindow(dpy, frame, x, y - theight(), width, height + (theight() - 1));
}
raise();
send_config();
}
void Client::moveInIncrement(int increment, int direction)
{
switch (direction)
{
case LEFT:
if ((x + width - increment) >= 0)
x -= increment;
break;
case DOWN:
if ((y - theight() + increment) <= yres)
y += increment;
break;
case UP:
if ((y + height - increment) >= 0)
y -= increment;
break;
case RIGHT:
if ((x + increment) <= xres)
x += increment;
break;
default:
return;
}
if (is_shaded)
XMoveResizeWindow(dpy, frame, x, y - theight(), width, theight() - 1);
else
XMoveResizeWindow(dpy, frame, x, y - theight(), width, height + (theight() - 1));
raise();
send_config();
}
// Here's part 2 of our colormap policy: when a client installs a new
// colormap on itself, set the display's colormap to that. Arguably,
// this is bad, because we should only set the colormap if that client
// has the focus. However, clients don't usually set colormaps at
// random when you're not interacting with them, so I think we're
// safe. If you have an 8-bit display and this doesn't work for you,
// by all means yell at me, but very few people have 8-bit displays
// these days.
void Client::handle_colormap_change(XColormapEvent *e)
{
if (e->c_new) {
cmap = e->colormap;
XInstallColormap(dpy, cmap);
}
}
// If we were covered by multiple windows, we will usually get
// multiple expose events, so ignore them unless e->count (the number
// of outstanding exposes) is zero.
void Client::handle_expose_event(XExposeEvent *e)
{
if (e->count == 0) redraw();
if(!trans) wm->findTransientsToRaiseOrLower(window, false);
}
#ifdef SHAPE
void Client::handle_shape_change(XShapeEvent *e)
{
set_shape();
}
#endif
// Currently, only send_wm_delete uses this one...
int Client::send_xmessage(Window w, Atom a, long mask, long x)
{
XEvent e;
e.type = ClientMessage;
e.xclient.window = w;
e.xclient.message_type = a;
e.xclient.format = 32;
e.xclient.data.l[0] = x;
e.xclient.data.l[1] = CurrentTime;
return XSendEvent(dpy, w, False, mask, &e);
}
void Client::set_desktop(int desk)
{
belongsToDesktop=desk;
if(belongsToDesktop != wm->getCurrentDesktop())
{
hide();
wm->setExtendedWMHint(window, wm->atom_extended_net_wm_desktop, belongsToDesktop);
wm->setGnomeHint(window, wm->atom_gnome_win_workspace, belongsToDesktop);
}
}
void Client::raise()
{
XWindowChanges wc;
wc.stack_mode = Above;
XConfigureWindow(dpy, frame, CWStackMode, &wc);
if(!trans) wm->findTransientsToRaiseOrLower(window, false);
}
void Client::lower()
{
XWindowChanges wc;
wc.stack_mode = Below;
XConfigureWindow(dpy, window, CWStackMode, &wc);
if(!trans) wm->findTransientsToRaiseOrLower(window, true);
}
void Client::update_net_wm_states()
{
// Clients will always have a false modal property set
// since there is no support for modal windows in
// aewm++.
wm->setExtendedNetWMState(
window,
false, /* modal*/
is_sticky, /* sticky */
is_maximized_vertical, /* max_vert */
is_maximized_horizontal,/* max_horz */
is_shaded, /* shaded */
skip_taskbar, /* skip_taskbar */
skip_pager /* skip_pager */
);
}
syntax highlighted by Code2HTML, v. 0.9.1