/*
 * windowmanager.cc
 * Copyright (C) 2000 Frank Hale
 * frankhale@yahoo.com
 * http://sapphire.sourceforge.net/
 *
 * Updated: 1 Feb 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"

WindowManager* wm;

using namespace std;

#define AEWM_KEY_ALT_COUNT 23
KeySym WindowManager::AltKeys[]={
			XK_y, XK_u, XK_b, XK_n,
			XK_h, XK_j, XK_k, XK_l,
			XK_a, XK_f,
			XK_z, XK_x, XK_c,
			XK_o, XK_m,
			XK_space,
			XK_Tab,
			XK_Return,
			XK_Escape,
			XK_Delete,
			XK_End,
			XK_Page_Up,
			XK_Page_Down};

WindowManager::WindowManager(int argc, char** argv)
{
    	int i;
    	struct sigaction act;

	// Set the global window manager object to this please =)
    	wm = this;
	
	window_manager_name=(char *)"jewel";

	// Make the default options equal something
    	opt_font = (char *)DEF_FONT;
	   opt_fm = (char *)DEF_FM;
    	opt_fg = (char *)DEF_FG;
    	opt_fc = (char *)DEF_FC;
    	opt_bg = (char *)DEF_BG;
    	opt_bd = (char *)DEF_BD;
    	opt_tj = (char *)TEXT_JUSTIFY;
	   opt_wm = (char *)WIRE_MOVE;
	   opt_es = (char *)EDGE_SNAP;
	   opt_newkey = (char *)DEF_NEWKEY;
    	opt_new1 = (char *)DEF_NEW1;
    	opt_new2 = (char *)DEF_NEW2;
    	opt_bw = DEF_BW;
    	opt_display=NULL;
	   maxDesktops=MAX_DESKTOPS;

// These macro's are nice to test values passed in 
// the command line arguments	
#define OPT_STR(name, variable)                                      \
    if (strcmp(argv[i], name) == 0 && i+1<argc) {                    \
        variable = argv[++i];                                        \
        continue;                                                    \
    }
#define OPT_INT(name, variable)                                      \
    if (strcmp(argv[i], name) == 0 && i+1<argc) {                    \
        variable = atoi(argv[++i]);                                  \
        continue;                                                    \
    }

	for (i = 0; i < argc; i++) 
		command_line = command_line + argv[i] + " ";

	// Get the args and test for different options
	for (i = 1; i < argc; i++) 
    	{
        	OPT_STR("-fn", opt_font)
	        OPT_STR("-fg", opt_fg)
        	OPT_STR("-bg", opt_bg)
		OPT_STR("-fc", opt_fc)
		OPT_STR("-fm", opt_fm)
        	OPT_STR("-bd", opt_bd)
	        OPT_INT("-bw", opt_bw)
		OPT_STR("-tj", opt_tj)
		OPT_STR("-wm", opt_wm)
		OPT_STR("-es", opt_es)
		OPT_INT("-md", maxDesktops)
		OPT_STR("-newkey", opt_newkey)
        	OPT_STR("-new1", opt_new1)
	        OPT_STR("-new2", opt_new2)
		OPT_STR("-display", opt_display)

	       	if (strcmp(argv[i], "-version") == 0) {
            		cout << window_manager_name << ": version " << VERSION << EXTRA_VERSION_INFO << endl;
            		exit(0);
        	}
        	
		if(strcmp(argv[i], "-usage")==0) {

			cerr << "usage: jewel [options]" << endl;
			cerr << "   options are: -display <display>, -fn <font>, -fg|-bg|-bd <color>, " << endl;
			cerr << "   -bw <width>, -md <max desktops>, -mw <true|false> -tj <left|center|right>," << endl;
        		cerr << "   -wm <true|false>, -new1|-new2|-newkey <cmd>, -fm <follow|sloppy|click>, -usage" << endl;
			exit(0);
		}
	}

	// Set the focus model based on user defined option
	if (strcmp(opt_fm, "follow")==0) setFocusModel(FOCUS_FOLLOW);
	else if (strcmp(opt_fm, "sloppy")==0) setFocusModel(FOCUS_SLOPPY);
	else if (strcmp(opt_fm, "click")==0) setFocusModel(FOCUS_CLICK);
	else setFocusModel(FOCUS_SLOPPY);	

	// Set up the window title justification per user defined option
	if(strcmp(opt_tj, "left")==0) opt_text_justify = LEFT_JUSTIFY;
	else if(strcmp(opt_tj, "center")==0) opt_text_justify = CENTER_JUSTIFY;
	else if(strcmp(opt_tj, "right")==0) opt_text_justify = RIGHT_JUSTIFY;
	else opt_text_justify = LEFT_JUSTIFY;

	// Set wire move based on user defined option
	if(strcmp(opt_wm, "true")==0) wire_move=true;
	else if(strcmp(opt_wm, "false")==0) wire_move=false;
	else wire_move=false;

	// Set edge snapping based on user defined option
	if(strcmp(opt_es, "true")==0) edge_snap=true;
	else if(strcmp(opt_es, "false")==0) edge_snap=false;
	else edge_snap=false;

	// Don't let the user request more virtual desktops then
	// our max desktop setting. 
    	if(maxDesktops <= 0) maxDesktops=MAX_DESKTOPS;

	// The current desktop on start is Zero
    	currentDesktop=0;

	// The focused client is NULL (no clients yet!)
	focusedClient=NULL;

	clientCount=-1;

	master_strut = new Strut;
		
	master_strut->east	= 0;
	master_strut->west	= 0;
	master_strut->north	= 0;
	master_strut->south	= 0;
		
	clientStruts = new list<Strut *>;
	
	// Set up the signal handlers so we can catch signals
	// and respond accordingly.
    	act.sa_handler = sigHandler;
    	act.sa_flags = 0;
	sigaction(SIGTERM, &act, NULL);
    	sigaction(SIGINT, &act, NULL);
    	sigaction(SIGHUP, &act, NULL);
    	sigaction(SIGCHLD, &act, NULL);

	// The client list holds all client objects which
	// represent X applications. Clients paint the titlebars
	// and window decorations and manage the windows
	// associated with the X application.
    	clientList = new list<Client *>; 

	setupDisplay();
    	scanWins();
	updateIconMenu();
    	doEventLoop();
}

// The destructor cleans up allocated memory and exits cleanly. Hopefully! =)
WindowManager::~WindowManager()
{
}

void WindowManager::setFocusModel(int new_fm)
{
	if (new_fm == FOCUS_FOLLOW || new_fm == FOCUS_SLOPPY || new_fm == FOCUS_CLICK)
		focusModel = new_fm;
}

void WindowManager::grabKeys(Window w)
{
//MJR: Mask changed
	for(int i=0;i<AEWM_KEY_ALT_COUNT;i++) {
		XGrabKey(dpy,XKeysymToKeycode(dpy,AltKeys[i]), (ControlMask|Mod1Mask),w,True,GrabModeAsync,GrabModeAsync);
		XGrabKey(dpy,XKeysymToKeycode(dpy,AltKeys[i]), (ControlMask|Mod1Mask|NumLockMask),w,True,GrabModeAsync,GrabModeAsync);
	}
}

void WindowManager::ungrabKeys(Window w)
{
//MJR: mask changed
	for(int i=0;i<AEWM_KEY_ALT_COUNT;i++) {
		XUngrabKey(dpy,XKeysymToKeycode(dpy,AltKeys[i]), (ControlMask|Mod1Mask),w);
		XUngrabKey(dpy,XKeysymToKeycode(dpy,AltKeys[i]), (ControlMask|Mod1Mask|NumLockMask),w);
	}
}

void WindowManager::focusFocusedClient()
{
	Window focus_win;
	int revert;
	Client *c;
	XGetInputFocus(dpy, &focus_win, &revert);
	c = findClient(focus_win);
	if (focusedClient && focusedClient != c)
	{
		XSetInputFocus(dpy, focusedClient->getAppWindow(), RevertToNone, CurrentTime);
#ifdef DEBUG
		cerr << "Focused client ";
		if (focusedClient->getClientName())
			cerr << focusedClient->getClientName() << endl;
		else
			cerr << "no name" << endl;
#endif
	}
	else if (!focusedClient && focus_win != gnome_button_proxy_win)
	{
		XSetInputFocus(dpy, gnome_button_proxy_win, RevertToNone, CurrentTime);
#ifdef DEBUG
		cerr << "Focused gnome_button_proxy_win" << endl;
#endif
	}
}

void WindowManager::setFocusedClient(Client *c)
{
	if (!blockFocusChange) focusedClient = c;
}

void WindowManager::unsetFocusedClient()
{
	Window p_root, p_par, *p_win;
	Client* cand = NULL;
	unsigned int n;

	//MJR: FIXME: needs some work to find sensible focusee
	XQueryTree(dpy, root, &p_root, &p_par, &p_win, &n);

	//MJR: Find uppermost one with border
	while (n>0)
	{
		cand = findClient(p_win[--n]);
		if ((cand != NULL) && (cand->hasWindowDecorations())) n=0;
	}

	focusedClient = cand;
	XFree(p_win);
}

void WindowManager::setFocusedClientUnderPointer()
{
	Window p_root, p_win;
	int x, y, win_x, win_y;
	unsigned int mask;

	XQueryPointer(dpy, root, &p_root, &p_win, &x, &y, &win_x, &win_y, &mask);

	focusedClient = findClient(p_win);
}

void WindowManager::setCurrentDesktop(int desk)
{
	if ( (desk < maxDesktops) && (desk > 0) ) currentDesktop = desk;
	
	updateIconMenu();
}

void WindowManager::addClientToIconMenu(Client *c)
{
	iconmenu->hide();
	
	updateIconMenu();
}

void WindowManager::removeClientFromIconMenu(Client *c)
{
	iconmenu->hide();
	updateIconMenu();
}

void WindowManager::updateIconMenu()
{

	list<Client *> *normalWindows = new list<Client *>;
	list<Client *> *iconWindows = new list<Client *>;
	
	iconmenu->hide();
	iconmenu->removeAll();
	iconmenu->initialSetup();
	
	for(it = clientList->begin(); it != clientList->end(); it++)
	{
		if((*it)->belongsToWhichDesktop() == currentDesktop)
		{
			if((*it)->isIconified())
			{
				iconWindows->push_back((*it));
			}
			else {
				normalWindows->push_back((*it));
			}
		}
	}

	if ( ! iconWindows->empty() ) {
		iconmenu->insert("separator", "Icon List", -1);
		for ( it = iconWindows->begin(); it != iconWindows->end(); it++ ) {
			iconmenu->addThisClient((*it));
		}
	}

	if ( !normalWindows->empty() ) {
		iconmenu->insert("separator", "Window List", -1);
		for ( it = normalWindows->begin(); it != normalWindows->end(); it++ ) {
			iconmenu->addThisClient((*it));
		}
	}
	
	iconmenu->updateIconMenu();
}

void WindowManager::goToDesktop(int d)
{
	unsigned int nwins, i;
	Window dummyw1, dummyw2, *wins;
	Client* c;

	if( (d < maxDesktops) && (d >= 0) )
	{
		currentDesktop = d;

		updateIconMenu();

		setGnomeHint(root, atom_gnome_win_workspace, currentDesktop);
		setExtendedWMHint(root, atom_extended_net_current_desktop, currentDesktop);

		// Preserve stacking order
		XQueryTree(dpy, root, &dummyw1, &dummyw2, &wins, &nwins);
		for (i = 0; i < nwins; i++) 
		{
			c = findClient(wins[i]);
				
			if(c)
			{
				if(! c->isSticky())
				{
					if(c->belongsToWhichDesktop() == currentDesktop)
					{
						if(! (c->isIconified())) 
							c->unhide();
					}
					else
					{
						if(! (c->isIconified()))
							c->hide();
					}
				}
			}
		}
		XFree(wins);
		switch (focusModel)
		{
			case FOCUS_FOLLOW:

			case FOCUS_SLOPPY:
				unsetFocusedClient();
				setFocusedClientUnderPointer();
				break;

			case FOCUS_CLICK:
				unsetFocusedClient();
				break;
		}
	}
}

void WindowManager::scanWins(void)
{
    	unsigned int nwins, i;
    	Window dummyw1, dummyw2, *wins;
    	XWindowAttributes attr;
	Client *c=None;

	XQueryTree(dpy, root, &dummyw1, &dummyw2, &wins, &nwins);
	for(i = 0; i < nwins; i++) 
	{
        	XGetWindowAttributes(dpy, wins[i], &attr);
	        if (!attr.override_redirect && attr.map_state == IsViewable)
    			c = new Client(dpy, wins[i], clientList);
    	}
    	XFree(wins);
    
	XMapWindow(dpy, gnome_button_proxy_win);
	grabKeys(gnome_button_proxy_win);
	unsetFocusedClient();
}

void WindowManager::setupDisplay(void)
{
    	XColor dummyc;
    	XGCValues gv;
    	XSetWindowAttributes sattr;

#ifdef SHAPE
    	int dummy;
#endif

	if (opt_display)
        	setenv("DISPLAY", opt_display, 1);
	else
        	opt_display = getenv("DISPLAY");

    	dpy = XOpenDisplay(opt_display);

    	if (!dpy) {
		cerr << "can't open display! check your DISPLAY variable." << endl;
	        exit(1);
	}

    	XSetErrorHandler(handleXError);
    	screen = DefaultScreen(dpy);
    	root = RootWindow(dpy, screen);

	xres = WidthOfScreen(ScreenOfDisplay(dpy, screen));
	yres = HeightOfScreen(ScreenOfDisplay(dpy, screen));

	// BJP: Find the NumLockMask for keycombos
	NumLockMask = 0;
	unsigned int modmasks[] = {
		Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
	};

	int row,col;

	XModifierKeymap *mods;

	mods = XGetModifierMapping(dpy);

	for (row = 3; row < 8; row++) {
		for (col = 0; col < mods->max_keypermod; col++) {
			KeyCode code = mods->modifiermap[(row * mods->max_keypermod) + col];
			if (code == 0) { continue; }
			switch (XKeycodeToKeysym(dpy, code, 0)) {
				case XK_Num_Lock:
					NumLockMask |= modmasks[row - 3];
			}
		}
	}

	XFreeModifiermap (mods);

	// ICCCM atoms
	atom_wm_state 		= XInternAtom(dpy, "WM_STATE", False);
	atom_wm_change_state 	= XInternAtom(dpy, "WM_CHANGE_STATE", False);
	atom_wm_protos 		= XInternAtom(dpy, "WM_PROTOCOLS", False);
	atom_wm_delete 		= XInternAtom(dpy, "WM_DELETE_WINDOW", False);
	atom_wm_cmapwins 	= XInternAtom(dpy, "WM_COLORMAP_WINDOWS", False);
	atom_wm_takefocus 	= XInternAtom(dpy, "WM_TAKE_FOCUS", False);

 	// Motif hints 
    	atom_mwm_hints 		= XInternAtom(dpy, "_MOTIF_WM_HINTS", False);

	// GNOME atoms
	atom_gnome_win_client_list	= XInternAtom(dpy, "_WIN_CLIENT_LIST", False);
	atom_gnome_win_state		= XInternAtom(dpy, "_WIN_STATE", False);
	atom_gnome_win_hints		= XInternAtom(dpy, "_WIN_HINTS", False);
	atom_gnome_win_layer		= XInternAtom(dpy, "_WIN_LAYER", False);
	atom_gnome_win_supporting_wm_check = XInternAtom(dpy, "_WIN_SUPPORTING_WM_CHECK", False); 
	atom_gnome_win_desktop_button_proxy= XInternAtom(dpy, "_WIN_DESKTOP_BUTTON_PROXY", False); 
	atom_gnome_win_workspace	= XInternAtom(dpy, "_WIN_WORKSPACE", False); 
	atom_gnome_win_workspace_count	= XInternAtom(dpy, "_WIN_WORKSPACE_COUNT", False); 
	atom_gnome_win_protocols	= XInternAtom(dpy, "_WIN_PROTOCOLS", False); 

	gnome_check_win=XCreateSimpleWindow(dpy, root, -200, -200, 5, 5, 0, 0, 0);
	gnome_button_proxy_win=XCreateSimpleWindow(dpy, root, -80, -80, 24, 24,0,0,0);
	
	XChangeProperty(dpy, gnome_button_proxy_win, atom_gnome_win_desktop_button_proxy, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&gnome_button_proxy_win, 1);
	
  	/* Set up GNOME properties */ 
  	setGnomeHint(gnome_check_win, atom_gnome_win_supporting_wm_check, gnome_check_win); 
  	setGnomeHint(root, atom_gnome_win_supporting_wm_check, gnome_check_win); 
  	setGnomeHint(gnome_check_win, atom_gnome_win_desktop_button_proxy, gnome_check_win); 
  	setGnomeHint(root, atom_gnome_win_desktop_button_proxy, gnome_check_win); 
  	setGnomeHint(root, atom_gnome_win_workspace_count, maxDesktops); 
  	setGnomeHint(root, atom_gnome_win_workspace, 0); 

        Atom gnome_protocols[5];

        gnome_protocols[0] = atom_gnome_win_state;
        gnome_protocols[1] = atom_gnome_win_hints;
        gnome_protocols[2] = atom_gnome_win_client_list;
        gnome_protocols[3] = atom_gnome_win_workspace;
        gnome_protocols[4] = atom_gnome_win_workspace_count;
  
        XChangeProperty(dpy, root, atom_gnome_win_protocols, XA_ATOM, 32, PropModeReplace,
                  (unsigned char *)gnome_protocols, 5);

	// Extended WM Hints
	atom_extended_net_supported 			= XInternAtom(dpy, "_NET_SUPPORTED", False); 
	atom_extended_net_client_list 			= XInternAtom(dpy, "_NET_CLIENT_LIST", False); 
	atom_extended_net_client_list_stacking 		= XInternAtom(dpy, "_NET_CLIENT_LIST_STACKING", False);
	atom_extended_net_number_of_desktops 		= XInternAtom(dpy, "_NET_NUMBER_OF_DESKTOPS", False); 
	atom_extended_net_desktop_geometry 		= XInternAtom(dpy, "_NET_DESKTOP_GEOMETRY", False); 
	atom_extended_net_desktop_viewport 		= XInternAtom(dpy, "_NET_DESKTOP_VIEWPORT", False); 
	atom_extended_net_current_desktop 		= XInternAtom(dpy, "_NET_CURRENT_DESKTOP", False); 
	//atom_extended_net_desktop_names 		= XInternAtom(dpy, "_NET_DESKTOP_NAMES", False); 
	atom_extended_net_active_window 		= XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); 
	atom_extended_net_workarea 			= XInternAtom(dpy, "_NET_WORKAREA", False); 
	atom_extended_net_supporting_wm_check 		= XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); 
	//atom_extended_net_virtual_roots 		= XInternAtom(dpy, "_NET_VIRTUAL_ROOTS", False); 
	atom_extended_net_close_window 			= XInternAtom(dpy, "_NET_CLOSE_WINDOW", False); 
	//atom_extended_net_wm_moveresize 		= XInternAtom(dpy, "_NET_WM_MOVERESIZE", False); 
	atom_extended_net_wm_name 			= XInternAtom(dpy, "_NET_WM_NAME", False); 
	//atom_extended_net_wm_visible_name 		= XInternAtom(dpy, "_NET_WM_VISIBLE_NAME", False); 
	//atom_extended_net_wm_icon_name 		= XInternAtom(dpy, "_NET_WM_ICON_NAME", False); 
	//atom_extended_net_wm_visible_icon_name 	= XInternAtom(dpy, "_NET_WM_VISIBLE_ICON_NAME", False); 
	atom_extended_net_wm_desktop 			= XInternAtom(dpy, "_NET_WM_DESKTOP", False); 
	//atom_extended_net_wm_window_type 		= XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); 
	//atom_extended_net_wm_window_type_desktop 	= XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DESKTOP", False); 
	//atom_extended_net_wm_window_type_dock   	= XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", False); 
	//atom_extended_net_wm_window_type_toolbar 	= XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_TOOLBAR", False); 
	//atom_extended_net_wm_window_type_menu   	= XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_MENU", False); 
	//atom_extended_net_wm_window_type_dialog 	= XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); 
	//atom_extended_net_wm_window_type_normal 	= XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_NORMAL", False); 
	atom_extended_net_wm_state 		  	= XInternAtom(dpy, "_NET_WM_STATE", False); 
	atom_extended_net_wm_state_modal 	  	= XInternAtom(dpy, "_NET_WM_STATE_MODAL", False); 
	atom_extended_net_wm_state_sticky 	  	= XInternAtom(dpy, "_NET_WM_STATE_STICKY", False); 
	atom_extended_net_wm_state_maximized_vert 	= XInternAtom(dpy, "_NET_WM_STATE_MAXIMIZED_VERT", False); 
	atom_extended_net_wm_state_maximized_horz 	= XInternAtom(dpy, "_NET_WM_STATE_MAXIMIZED_HORZ", False); 
	atom_extended_net_wm_state_shaded 	  	= XInternAtom(dpy, "_NET_WM_STATE_SHADED", False); 
// Looked wrong, changed and fixed, hopefully. BJP 22042002
//	atom_extended_net_wm_state_skip_taskbar   	= XInternAtom(dpy, "_NET_WM_STATE_SKIP_TOOLBAR", False); 
	atom_extended_net_wm_state_skip_taskbar   	= XInternAtom(dpy, "_NET_WM_STATE_SKIP_TASKBAR", False); 
// Looked wrong, changed and fixed, hopefully. BJP 22042002
//	atom_extended_net_wm_state_skip_pager 	  	= XInternAtom(dpy, "_NET_WM_STATE_PAGER", False); 
	atom_extended_net_wm_state_skip_pager 	  	= XInternAtom(dpy, "_NET_WM_STATE_SKIP_PAGER", False); 
	atom_extended_net_wm_strut 		  	= XInternAtom(dpy, "_NET_WM_STRUT", False); 
	//atom_extended_net_wm_icon_geometry 	  	= XInternAtom(dpy, "_NET_WM_ICON_GEOMETRY", False); 
	//atom_extended_net_wm_icon 		  	= XInternAtom(dpy, "_NET_WM_ICON", False); 
	//atom_extended_net_wm_pid 		  	= XInternAtom(dpy, "_NET_WM_PID", False); 
	//atom_extended_net_wm_handled_icons 	  	= XInternAtom(dpy, "_NET_WM_HANDLED_ICONS", False); 
	//atom_extended_net_wm_ping 		  	= XInternAtom(dpy, "_NET_WM_PING", False); 

	// This window is does nothing more than store properties which let
	// other apps know we are supporting the extended wm hints
	extended_hints_win=XCreateSimpleWindow(dpy, root, -200, -200, 5, 5, 0, 0, 0);

	XSetWindowAttributes pattr;
	pattr.override_redirect=True;
	XChangeWindowAttributes(dpy, extended_hints_win, CWOverrideRedirect, &pattr);
	XChangeWindowAttributes(dpy, gnome_check_win, CWOverrideRedirect, &pattr);

	setExtendedWMHintString(extended_hints_win, atom_extended_net_wm_name, window_manager_name);
	setExtendedWMHint(extended_hints_win, atom_extended_net_supporting_wm_check, extended_hints_win); 
	setExtendedWMHint(root, atom_extended_net_supporting_wm_check, extended_hints_win); 
	setExtendedNetSupported();
	setExtendedWMHint(root, atom_extended_net_number_of_desktops, maxDesktops);
	setExtendedNetDesktopGeometry();
	setExtendedNetDesktopViewport();
	setExtendedWMHint(root, atom_extended_net_current_desktop, 0);

    	XAllocNamedColor(dpy, DefaultColormap(dpy, screen), opt_fg, &fg, &dummyc);
    	XAllocNamedColor(dpy, DefaultColormap(dpy, screen), opt_bg, &bg, &dummyc);
    	XAllocNamedColor(dpy, DefaultColormap(dpy, screen), opt_bd, &bd, &dummyc);
    	XAllocNamedColor(dpy, DefaultColormap(dpy, screen), opt_fc, &fc, &dummyc);
    
    	font = XLoadQueryFont(dpy, opt_font);
    	if (!font) font = XLoadQueryFont(dpy, DEF_FONT);
    	if (!font) { cerr << "DEF_FONT not found, aborting." << endl; exit(1); }

#ifdef SHAPE
    	shape = XShapeQueryExtension(dpy, &shape_event, &dummy);
#endif

    	move_curs = XCreateFontCursor(dpy, XC_fleur);
    	resize_curs = XCreateFontCursor(dpy, XC_plus);
	arrow_curs = XCreateFontCursor(dpy, XC_left_ptr);

	XDefineCursor(dpy, root, arrow_curs);

    	gv.function = GXcopy;
    	gv.foreground = fg.pixel;
    	gv.font = font->fid;
    	string_gc = XCreateGC(dpy, root, GCFunction|GCForeground|GCFont, &gv);

    	gv.foreground = bd.pixel;
    	gv.line_width = opt_bw;
    	border_gc = XCreateGC(dpy, root, GCFunction|GCForeground|GCLineWidth, &gv);

	gv.foreground = fg.pixel;
    	gv.function = GXinvert; 
    	gv.subwindow_mode = IncludeInferiors;
    	invert_gc = XCreateGC(dpy, root, GCForeground|GCFunction|GCSubwindowMode|GCLineWidth|GCFont, &gv);

	sattr.event_mask = ChildMask 		|
			   ColormapChangeMask	|
			   ButtonMask 		|
			   FocusChangeMask	|
			   EnterWindowMask	|
			   LeaveWindowMask	|
			   PropertyChangeMask   |
			   ButtonMotionMask	;
			   
    	XChangeWindowAttributes(dpy, root, CWEventMask, &sattr);
	
	windowmenu = new WindowMenu();
	iconmenu = new IconMenu();
	
//	grabKeys(root);
}

/* We may want to put in some sort of check for unknown events at some
 * point. TWM has an interesting and different way of doing this... */

// In the original aewm code client functions searched for a client to operate
// on. This is bad in my opinion. The Window Manager functions should find 
// a client based on the XEvent and then dispatch events to it. I believe
// this to be a better way to do it. Though the other way works just fine
// as well.
void WindowManager::doEventLoop()
{
 	XEvent ev;

	BaseMenu *mu=NULL;
	Client *c=NULL;

	for (;;)
	{
		blockFocusChange = false;
		XNextEvent(dpy, &ev);

		switch (ev.type) 
		{
			case KeyPress:
			{
#ifdef DEBUG
				cerr << "KeyPress" << endl;
#endif
				keyPressEvent(ev);
			}
			break;

			case ButtonPress:
			{
#ifdef DEBUG
				cerr << "ButtonPress" << endl;
#endif
				buttonPressEvent(ev, mu, c);
				focusFocusedClient();
			}
			break;
 
			case ButtonRelease:
    			{
#ifdef DEBUG
				cerr << "ButtonRelease" << endl;
#endif
				buttonReleaseEvent(ev, mu, c);
			}
			break;
 	
			case ConfigureRequest:
	    		{
#ifdef DEBUG
				cerr << "ConfigureRequest" << endl;
#endif
				configureRequestEvent(ev, c);
			}
			break;

			case MotionNotify:
			{
#ifdef DEBUG
				cerr << "MotionNotify" << endl;
#endif
				motionNotifyEvent(ev, mu, c);
			}
			break;

			case MapNotify:
	   		{
#ifdef DEBUG
				cerr << "MapNotify" << endl;
#endif
				mapNotifyEvent(ev, c);
			}
			break;

			case MapRequest:
	   		{
#ifdef DEBUG
				cerr << "MapRequest" << endl;
#endif
				mapRequestEvent(ev, c);
			}
			break;

			case UnmapNotify:
			{
#ifdef DEBUG
				cerr << "UnmapNotify" << endl;
#endif
				unmapNotifyEvent(ev, c);
			}
			break;
	    
			case DestroyNotify:
			{
#ifdef DEBUG
				cerr << "DestroyNotify" << endl;
#endif
				destroyNotifyEvent(ev, c);
			}
			break;

			case EnterNotify:
			{
#ifdef DEBUG
				cerr << "EnterNotify" << endl;
#endif
				enterNotifyEvent(ev, mu, c);
				focusFocusedClient();
			}
			break;

			case LeaveNotify:
			{
#ifdef DEBUG
				cerr << "LeaveNotify" << endl;
#endif
				leaveNotifyEvent(ev, mu);
				focusFocusedClient();
			}
			break;

			case FocusIn:
			{
#ifdef DEBUG
				cerr << "FocusIn" << endl;
#endif
				focusInEvent(ev, c);
			}
			break;

			case FocusOut:
			{
#ifdef DEBUG
				cerr << "FocusOut" << endl;
#endif
				focusOutEvent(ev, c);
			}
			break;

			case ClientMessage:
			{
#ifdef DEBUG
				cerr << "ClientMessage" << endl;
#endif
				clientMessageEvent(ev, c);
			}
			break;

			case ColormapNotify:
	    		{
#ifdef DEBUG
				cerr << "ColormapNotify" << endl;
#endif
				colormapNotifyEvent(ev, c);
			}
			break;

			case PropertyNotify:
			{
#ifdef DEBUG
				cerr << "PropertyNotify" << endl;
#endif
				propertyNotifyEvent(ev, c);
			}
			break;

			case Expose:
	    		{
#ifdef DEBUG
				cerr << "Expose" << endl;
#endif
				exposeEvent(ev, mu, c);
				focusFocusedClient();
			}
			break;

#ifdef SHAPE
			default:
			{
#ifdef DEBUG
				cerr << "(default)" << endl;
#endif
				defaultEvent(ev, c);
			}
	    		break;
#endif
		}
	}
}

void WindowManager::keyPressEvent(XEvent& ev) 
{
	KeySym ks;

	ks=XKeycodeToKeysym(dpy,ev.xkey.keycode,0);
	if (ks==NoSymbol) return;

	switch(ks) 
	{
		case XK_y:
		{
			if (focusedClient)
			{
#ifdef DEBUG
				cerr << "Moving client to top left corner..." << endl;
#endif
				focusedClient->moveToCorner(TOP_LEFT);
				focusedClient->warpToTitlebarCenter();
			}
		}
		break;

		case XK_u:
		{
			if (focusedClient)
			{
#ifdef DEBUG
				cerr << "Moving client to top right corner..." << endl;
#endif
				focusedClient->moveToCorner(TOP_RIGHT);
				focusedClient->warpToTitlebarCenter();
			}
		}
		break;

		case XK_b:
		{
			if (focusedClient)
			{
#ifdef DEBUG
				cerr << "Moving client to bottom left corner..." << endl;
#endif
				focusedClient->moveToCorner(BOTTOM_LEFT);
				focusedClient->warpToTitlebarCenter();
			}
		}
		break;

		case XK_n:
		{
			if (focusedClient)
			{
#ifdef DEBUG
				cerr << "Moving client to bottom right corner..." << endl;
#endif
				focusedClient->moveToCorner(BOTTOM_RIGHT);
				focusedClient->warpToTitlebarCenter();
			}
		}
		break;

		case XK_h:
		{
			if (focusedClient)
			{
#ifdef DEBUG
				cerr << "Moving client left 10 pixels..." << endl;
#endif
				focusedClient->moveInIncrement(10, LEFT);
				focusedClient->warpToTitlebarCenter();
			}
		}
		break;

		case XK_j:
		{
			if (focusedClient)
			{
#ifdef DEBUG
				cerr << "Moving client down 10 pixels..." << endl;
#endif
				focusedClient->moveInIncrement(10, DOWN);
				focusedClient->warpToTitlebarCenter();
			}
		}
		break;

		case XK_k:
		{
			if (focusedClient)
			{
#ifdef DEBUG
				cerr << "Moving client up 10 pixels..." << endl;
#endif
				focusedClient->moveInIncrement(10, UP);
				focusedClient->warpToTitlebarCenter();
			}
		}
		break;

		case XK_l:
		{
			if (focusedClient)
			{
#ifdef DEBUG
				cerr << "Moving client right 10 pixels..." << endl;
#endif
				focusedClient->moveInIncrement(10, RIGHT);
				focusedClient->warpToTitlebarCenter();
			}
		}
		break;

		case XK_a:
		{
			if (focusedClient)
			{
#ifdef DEBUG
				cerr << "Iconifying client..." << endl;
#endif
				focusedClient->iconify();
			}
		}
		break;

		case XK_f:
		{
			if (focusedClient)
			{
#ifdef DEBUG
				cerr << "Making client (un)sticky..." << endl;
#endif
				focusedClient->stick(DISALLOW_STICKY_TRANS);
			}
		}
		break;

		case XK_z:
		{
			if (focusedClient)
			{
#ifdef DEBUG
				cerr << "Horizontally (un)maximizing client..." << endl;
#endif
				focusedClient->maximize_horizontal();
				focusedClient->warpToTitlebarCenter();
			}
		}
		break;

		case XK_x:
		{
			if (focusedClient)
			{
#ifdef DEBUG
				cerr << "Fully (un)maximizing client..." << endl;
#endif
				focusedClient->maximize();
				focusedClient->warpToTitlebarCenter();
			}
		}
		break;

		case XK_c:
		{
			if (focusedClient)
			{
#ifdef DEBUG
				cerr << "Vertically (un)maximizing client..." << endl;
#endif
				focusedClient->maximize_vertical();
				focusedClient->warpToTitlebarCenter();
			}
		}
		break;

		case XK_o:
		{
			if(iconmenu->getItemCount())
			{
#ifdef DEBUG
				cerr << "Hiding/showing icon menu..." << endl;
#endif
				if (iconmenu->is_visible())
					iconmenu->hide();
				else
					iconmenu->show();
			}
		}
		break;

		case XK_m:
		{
			if (focusedClient)
			{
#ifdef DEBUG
				cerr << "Hiding/showing window menu..." << endl;
#endif
				focusedClient->warpToTitlebarCenter();
				windowmenu->setThisClient(focusedClient);
				if (windowmenu->is_visible())
					windowmenu->hide();
				else
					windowmenu->show();
			}
		}
		break;

		case XK_space:
		{
			if (focusedClient)
			{
#ifdef DEBUG
				cerr << "(Un)shading client..." << endl;
#endif
				focusedClient->shade();
				focusedClient->warpToTitlebarCenter();
			}
		}
		break;

		case XK_Tab:
		{
#ifdef DEBUG
			cerr << "Switching to next client..." << endl;
#endif
			focusNextWindowInStackingOrder();
			// it's not guaranteed that there will be something focused...
			// for example, if all the windows are minimized
			if (focusedClient)
				focusedClient->warpToTitlebarCenter();
		}
		break;

		case XK_Return:
		{
#ifdef DEBUG
			cerr << "Spawning client \"" << opt_newkey << "\"..." << endl;
#endif
			forkExec(opt_newkey);
		}
		break;

		case XK_Escape:
		{
			if (focusedClient)
			{
#ifdef DEBUG
				cerr << "Deleting client..." << endl;
#endif
				focusedClient->send_wm_delete();
			}
		}
		break;

		case XK_Delete:
			cerr << "aewm++ is restarting..." << endl;
			restart();
		break;

		case XK_End:
			cerr << "aewm++ is quitting." << endl;
			quitNicely();
		break;
	
		case XK_Page_Up:
		{
			if( currentDesktop < maxDesktops - 1 )
			{
				currentDesktop++;
#ifdef DEBUG
				cerr << "Switching to desktop " << currentDesktop << "..." << endl;
#endif
				goToDesktop(currentDesktop);
			}
		}
		break;
		
		case XK_Page_Down:
		{
                        if( currentDesktop > 0 )
			{
				currentDesktop--;
#ifdef DEBUG
				cerr << "Switching to desktop " << currentDesktop << "..." << endl;
#endif
				goToDesktop(currentDesktop);
			}			
		}
		break;
	}
}

void WindowManager::buttonPressEvent(XEvent& ev, BaseMenu *mu, Client *c)
{
	c = findClient(ev.xbutton.window);

	if (ev.xbutton.window == root) 
	{
		switch (ev.xbutton.button) 
		{
		case Button1: 
				forkExec(opt_new1); 

				if(iconmenu->is_visible())
					iconmenu->hide();
				if(windowmenu->is_visible())
					windowmenu->hide();
			break;

		case Button2: 
				if(iconmenu->getItemCount())
				{
					if(iconmenu->is_visible())
						iconmenu->hide();
					else 
						iconmenu->show();
					if(windowmenu->is_visible())
						windowmenu->hide();
				}
			break;

		case Button3: 
				forkExec(opt_new2);

				if(iconmenu->is_visible())
					iconmenu->hide();
				if(windowmenu->is_visible())
					windowmenu->hide();
			break;

		//DLR: Mousewheel support
		//MJR: Only on left edge, order reversed to match PgUp/PgDn
		case Button5:
			if( (currentDesktop > 0) && (ev.xbutton.y < 32))
			{
				currentDesktop--;
#ifdef DEBUG
				cerr << "Switching to desktop " << currentDesktop << "..." << endl;
#endif
				goToDesktop(currentDesktop);
			}
			break;

		case Button4:
			if( ( currentDesktop < maxDesktops - 1 ) && (ev.xbutton.y < 32))
			{
				currentDesktop++;
#ifdef DEBUG
				cerr << "Switching to desktop " << currentDesktop << "..." << endl;
#endif
				goToDesktop(currentDesktop);
			}
			break;
		}
	}
	else
	{
		switch (focusModel)
		{
			case FOCUS_FOLLOW:

			case FOCUS_SLOPPY:
				// focus the window and handle the button click
				if(c)
				{
					setFocusedClient(c);
					c->handle_button_event(&ev.xbutton);
				}
			break;

			case FOCUS_CLICK:
				// the first time the window's clicked,
				// focus it; handle the button click
				// unconditionally
				if(c)
				{
					if(c != focusedClient)
						setFocusedClient(c);
					c->handle_button_event(&ev.xbutton);
				}
			break;
		}

		mu = windowmenu->findMenu(ev.xbutton.window);

		if(!mu)
			mu = iconmenu->findMenu(ev.xbutton.window);

		if(mu)
			mu->handle_button_press_event(&ev.xbutton);
	}

	if(ev.xbutton.window == root)
		XSendEvent(dpy, gnome_button_proxy_win, False, SubstructureNotifyMask, &ev);
}
				
void WindowManager::buttonReleaseEvent(XEvent& ev, BaseMenu *mu, Client *c)
{
	c = findClient(ev.xbutton.window);

	if(c)
		c->handle_button_event(&ev.xbutton);
	else
	{
		mu = windowmenu->findMenu(ev.xbutton.window);

		if(!mu)
			mu = iconmenu->findMenu(ev.xbutton.window);

		if(mu)
		{
			mu->hide();
			mu->handle_button_release_event(&ev.xbutton);
		}
	}

	if(ev.xbutton.window == root)
		XSendEvent(dpy, gnome_button_proxy_win, False, SubstructureNotifyMask, &ev);
}

void WindowManager::configureRequestEvent(XEvent& ev, Client *c)
{
	c = findClient(ev.xconfigurerequest.window);

	if(c)
		c->handle_configure_request(&ev.xconfigurerequest);
	else
	{
		// Since this window isn't yet a client lets delegate
		// the configure request back to the window so it can
		// use it.

		XWindowChanges wc;

	        wc.x = ev.xconfigurerequest.x;
		wc.y = ev.xconfigurerequest.y;
		wc.width = ev.xconfigurerequest.width;
		wc.height = ev.xconfigurerequest.height;
		wc.sibling = ev.xconfigurerequest.above;
		wc.stack_mode = ev.xconfigurerequest.detail;
		XConfigureWindow(dpy, ev.xconfigurerequest.window, ev.xconfigurerequest.value_mask, &wc);
	}
}

void WindowManager::motionNotifyEvent(XEvent& ev, BaseMenu *mu, Client *c)
{
	c = findClient(ev.xmotion.window);

	if(c)
		c->handle_motion_notify_event(&ev.xmotion); 

	mu = windowmenu->findMenu(ev.xmotion.window);

	if(!mu)
		mu = iconmenu->findMenu(ev.xmotion.window);

	if(mu)
		mu->handle_motion_notify_event(&ev.xmotion);
}

void WindowManager::mapNotifyEvent(XEvent& ev, Client *c)
{
	c = findClient(ev.xmap.window);

	if(c)
		setFocusedClient(c);
}

void WindowManager::mapRequestEvent(XEvent& ev, Client *c)
{
	bool newClient = false;

	c = findClient(ev.xmaprequest.window);

	if(c)
		c->handle_map_request(&ev.xmaprequest);
	else
	{
		c = new Client(dpy, ev.xmaprequest.window, clientList);
		newClient = true;
	}

	if (newClient)
		setFocusedClient(c);
	
	updateIconMenu();
}

void WindowManager::unmapNotifyEvent(XEvent& ev, Client *c)
{
	c = findClient(ev.xunmap.window);

	if(c)
	{
		c->handle_unmap_event(&ev.xunmap);
		switch (focusModel)
		{
			case FOCUS_FOLLOW:
				// focus under pointer

			case FOCUS_SLOPPY:
				// focus under pointer
				setFocusedClientUnderPointer();
				break;

			case FOCUS_CLICK:
				// focus root
				unsetFocusedClient();
				break;
		}
	}

}

void WindowManager::destroyNotifyEvent(XEvent& ev, Client *c)
{
	c = findClient(ev.xdestroywindow.window);

	if(c)
	{
		c->handle_destroy_event(&ev.xdestroywindow);
		removeClientFromIconMenu(c);
		switch (focusModel)
		{
			case FOCUS_FOLLOW:
				// focus under pointer

			case FOCUS_SLOPPY:
				// focus under pointer
				setFocusedClientUnderPointer();
				break;

			case FOCUS_CLICK:
				// focus root
				unsetFocusedClient();
				break;
		}
	}

	if ( iconmenu->is_visible()) {
		iconmenu->hide();
		updateIconMenu();
	}
	else {
		updateIconMenu();
	}
}

void WindowManager::enterNotifyEvent(XEvent& ev, BaseMenu *mu, Client *c)
{
	c = findClient(ev.xcrossing.window);

	mu = windowmenu->findMenu(ev.xcrossing.window);

	if(!mu)
		mu = iconmenu->findMenu(ev.xcrossing.window);

	if(mu)
		mu->handle_enter_notify(&ev.xcrossing);	

	switch (focusModel)
	{
		case FOCUS_FOLLOW:
			// focus the entered window; focus root if there
			// is none
			if(c)
				setFocusedClient(c);
			else
				unsetFocusedClient();
		break;

		case FOCUS_SLOPPY:
			// focus the entered window
			if(c)
				setFocusedClient(c);
		break;

		case FOCUS_CLICK:
			// don't change focus
		break;
	}
}

void WindowManager::leaveNotifyEvent(XEvent& ev, BaseMenu *mu)
{
	mu = windowmenu->findMenu(ev.xcrossing.window);

	if(!mu)
		mu = iconmenu->findMenu(ev.xcrossing.window);

	if(mu) mu->handle_leave_notify(&ev.xcrossing);

	switch (focusModel)
	{
		case FOCUS_FOLLOW:
			// focus window under pointer
			setFocusedClientUnderPointer();
		break;

		case FOCUS_SLOPPY:

		case FOCUS_CLICK:
			// don't change focus
		break;
	}
}

void WindowManager::focusInEvent(XEvent& ev, Client *c)
{
	c = findClient(ev.xfocus.window);

	if(c)
	{
		unfocusAnyStrayClients();

		c->handle_focus_in_event(&ev.xfocus);
		c->set_focus(true);
		setFocusedClient(c);
		blockFocusChange = true;

		grabKeys(ev.xfocus.window);
	}
}

void WindowManager::focusOutEvent(XEvent& ev, Client *c)
{
	c = findClient(ev.xfocus.window);

	// don't take focus from the currently focused client
	if(c && c != focusedClient)
	{
		c->handle_focus_out_event(&ev.xfocus);
		c->set_focus(false);
		unsetFocusedClient();
		blockFocusChange = true;

		ungrabKeys(ev.xfocus.window);
	}
}

void WindowManager::clientMessageEvent(XEvent& ev, Client *c)
{
	c = findClient(ev.xclient.window);

	if(c)
		c->handle_client_message(&ev.xclient);

	if(ev.xclient.window == root)
	{
		if(ev.xclient.message_type == atom_gnome_win_workspace && ev.xclient.format == 32)
			goToDesktop(ev.xclient.data.l[0]);

		if(ev.xclient.message_type == atom_extended_net_current_desktop && ev.xclient.format == 32)
			goToDesktop(ev.xclient.data.l[0]);

		if(ev.xclient.message_type == atom_gnome_win_workspace_count && ev.xclient.format == 32)
			maxDesktops = ev.xclient.data.l[0];

		if(ev.xclient.message_type == atom_extended_net_number_of_desktops && ev.xclient.format == 32)
			maxDesktops = ev.xclient.data.l[0];
	}
}

void WindowManager::colormapNotifyEvent(XEvent& ev, Client *c)
{
	c = findClient(ev.xcolormap.window);

	if(c)
		c->handle_colormap_change(&ev.xcolormap);
}

void WindowManager::propertyNotifyEvent(XEvent& ev, Client *c)
{
	c = findClient(ev.xproperty.window);

	if(c)
		c->handle_property_change(&ev.xproperty);
}

void WindowManager::exposeEvent(XEvent& ev, BaseMenu *mu, Client *c)
{
	mu = windowmenu->findMenu(ev.xexpose.window);

	if(!mu)
		mu = iconmenu->findMenu(ev.xexpose.window);

	if(mu)
		mu->handle_expose_event(&ev.xexpose);

	c = findClient(ev.xexpose.window);

	if(c)
		c->handle_expose_event(&ev.xexpose);
}

void WindowManager::defaultEvent(XEvent& ev, Client *c)
{
	c = findClient(ev.xany.window);

	if(c)
	{
		if (shape && ev.type == shape_event)
			c->handle_shape_change((XShapeEvent *)&ev);
	}
}

void WindowManager::focusNextWindowInStackingOrder()
{
	Client *c = focusedClient;
	bool noClient;
	unsigned int cycles = 0;

	if (!c)
		noClient = true;
	else
		noClient = false;

	if(!clientList->empty())
	{
		if (clientList->size() < 2 && !noClient)
		{
#ifdef DEBUG
			cerr << "Fewer than two clients and a client's already focused; returning." << endl;
#endif
			return;
		}
		
		it = clientList->begin();
		
		if (noClient) {
			c = *it;
		}
	
		// find the client in the list...
		it = find(clientList->begin(), clientList->end(), c);

		if ( it == clientList->end()) {
			it = clientList->begin();
		}

		do {
			cycles++;
			if ( it == clientList->end() ) {
				it = clientList->begin();
			}
			else {
				it++;
				if ( it == clientList->end() ) {
					it = clientList->begin();
				}
			}
		} while (cycles < clientList->size() && (shouldSkipThisWindow((*it)->getAppWindow()) || (*it)->isIconified() || (*it)->belongsToWhichDesktop() != currentDesktop));
		
#ifdef DEBUG
		if ( c != NULL ) {
			cerr << "Original client: " << c->getClientName() << endl;
		}
		else {
			cerr << "No original client: " << endl;
		}
		cerr << "Current client: " << (*it)->getClientName() << endl;
		cerr << "Ending client: " << (*it)->getClientName() << endl;
		cerr.flush();
#endif

		// if we found a suitable "next client", raise and focus
		// it; otherwise, leave focus where it is
		if ( cycles < clientList->size() && !(it == clientList->end()) && !shouldSkipThisWindow((*it)->getAppWindow()) && !(*it)->isIconified() && (*it)->belongsToWhichDesktop() == currentDesktop && !shouldSkipThisWindow((*it)->getAppWindow())) {
			c = *it;
			c->raise();
			setFocusedClient(c);
		}
		return;
	}
}

int WindowManager::shouldSkipThisWindow(Window w)
{
	NetWMStates* netwm_state;
	unsigned long gnome_state;

	netwm_state = getExtendedNetWMStates(w);
	if (netwm_state)
	{
		if (netwm_state->skip_taskbar || netwm_state->skip_pager)
			return true;
	}

	gnome_state = getHint(w, atom_gnome_win_hints);

	if (gnome_state)
	{
		if ((gnome_state & WIN_STATE_SKIP_FOCUS) || (gnome_state & WIN_STATE_SKIP_WINLIST) || (gnome_state & WIN_STATE_SKIP_TASKBAR))
			return true;
	}

	return false;
}

void WindowManager::unfocusAnyStrayClients()
{
	// To prevent two windows titlebars from being painted with the
	// focus color we will set all windows to false.
	
	for(it = clientList->begin(); it != clientList->end(); it++)
		(*it)->set_focus(false);
}

void WindowManager::getMousePosition(int *x, int *y)
{
	Window mouse_root, mouse_win;
	int win_x, win_y;
	unsigned int mask;

	XQueryPointer(dpy, root, &mouse_root, &mouse_win, x, y, &win_x, &win_y, &mask);
}

void WindowManager::restackOnTopWindows()
{
	if(clientList->size() > 1) 
	{
		for(it = clientList->begin(); it != clientList->end(); it++)
		{
			if ((*it)->isAlwaysOnTop()) (*it)->raise();
		}
	}
}

Client* WindowManager::findClient(Window w)
{
	if (w && w != root) {
		if(!clientList->empty()) 
		{
			it = find_if(clientList->begin(), clientList->end(), ClientWindow_eq(&w));
			if ( it != clientList->end() ) {
				return *it;
			}
		}
	}
	return NULL;
}

void WindowManager::findTransientsToMapOrUnmap(Window win, bool hide)
{
	list<Client *>::iterator cit;
	
	if(!clientList->empty())
	{
		for(cit = clientList->begin(); cit != clientList->end(); cit++)
		{
			if((*cit)->getTransientWindow() == win)
			{
				if(hide)
				{
					if(! (*cit)->isIconified())
						(*cit)->iconify();
				}
				else
				{
					if((*cit)->isIconified())
						(*cit)->unhide();
				}
			}
		}
	}
}

void WindowManager::findTransientsToRaiseOrLower(Window win, bool low)
{
	list<Client *>::iterator cit;
	
	if(!clientList->empty())
	{
		for(cit = clientList->begin(); cit != clientList->end(); cit++)
		{
			if((*cit)->getTransientWindow() == win)
			{
				if(low)
					(*cit)->lower();
				else
					(*cit)->raise();
			}
		}
	}
}

void WindowManager::findTransientsToStickOrUnstick(Window win, bool stick)
{
	list<Client *>::iterator cit;
	
	if(!clientList->empty())
	{
		for(cit = clientList->begin(); cit != clientList->end(); cit++)
		{
			if((*cit)->getTransientWindow() == win)
			{
				if(stick)
				{
					if(! (*cit)->isSticky())
						(*cit)->stick(ALLOW_STICKY_TRANS);
				}
				else
				{
					if((*cit)->isSticky())
						(*cit)->stick(ALLOW_STICKY_TRANS);
				}
			}
		}
	}
}

void WindowManager::setGnomeHint(Window w, int a, long value)
{
	XChangeProperty(dpy, w, a, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&value, 1);
}

unsigned long WindowManager::getHint(Window w, int a)
{
	Atom real_type;
  	int real_format;
  	unsigned long items_read, items_left;
  	unsigned long value=0;
	unsigned char *data = NULL;

   XGetWindowProperty (dpy, w, a, 0, 0x7fffffff, False,
              XA_CARDINAL, &real_type, &real_format, &items_read,
              &items_left, &data);
   if (data) {
      value = (unsigned long) *data;
  		XFree(data);
	}

  	return value;
}

void WindowManager::updateClientList() 
{
	int i=0, client_count=0; 
  	CARD32 *wins=NULL; 
	
	Window  *extended_wins=NULL;
 	
	for(it = clientList->begin(); it != clientList->end(); it++)
	{
		// We don't want to include transients in our client list
		if(! ((*it)->getTransientWindow())) client_count++;
	}
  
  	wins = new CARD32[client_count];
	
	extended_wins = new Window[client_count];
 	
  	if(wins == NULL)
  	{
    		cerr << "Memory allocation failed in function update_gnome_client." << endl; 
    		exit(1);
  	}
	
	
	for(it = clientList->begin(); it != clientList->end(); it++)
	{
		// We don't want to include transients in our client list
		if(! ((*it)->getTransientWindow()))
		{
			Window t = (*it)->getAppWindow();
			
			wins[i] = t;
			
			extended_wins[i] = t;
						
			i++;
		}
	}
 
  	XChangeProperty(dpy, root, atom_gnome_win_client_list, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)wins, client_count);
	XChangeProperty(dpy, root, atom_extended_net_client_list, XA_WINDOW, 32, PropModeReplace, (unsigned char*)extended_wins, client_count);
	XChangeProperty(dpy, root, atom_extended_net_client_list_stacking, XA_WINDOW, 32, PropModeReplace, (unsigned char*)extended_wins, client_count);
	
  	delete [] wins;
	delete [] extended_wins;
} 

int WindowManager::sendExtendedHintMessage(Window w, Atom a, long mask, long int data[])
{
    	XEvent e;

    	e.type = ClientMessage;
    	e.xclient.window = w;
    	e.xclient.message_type = a;
    	e.xclient.format = 32;

	// xclient.data.l is a long int[5]
	e.xclient.data.l[0] = data[0];
	e.xclient.data.l[1] = data[1];
	e.xclient.data.l[2] = data[2];
	//e.xclient.data.l[3] = data[3];
	//e.xclient.data.l[4] = data[4];

    	return XSendEvent(dpy, 
			  w, 
			  False, 
			  SubstructureNotifyMask|SubstructureRedirectMask, 
			  &e);
}

void WindowManager::setExtendedWMHint(Window w, int a, long value)
{ 
  	XChangeProperty(dpy, w, a, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&value, 1); 
} 

void WindowManager::setExtendedWMHintString(Window w, int a, char* value)
{
	XChangeProperty(
		dpy, 
		w, 
		a,	
		XA_STRING,
		8, 
		PropModeReplace, 
		(unsigned char*) value, 
		strlen (value));
}

// Borrowed from:
//
// http://capderec.udg.es:81/ebt-bin/nph-dweb/dynaweb/SGI_Developer/XLib_PG/%40Generic__BookTextView/45827
Status WindowManager::getExtendedWMHintString(Window w, int a, char** name)
{
    Atom actual_type;
    int actual_format;
    unsigned long nitems;
    unsigned long leftover;
    unsigned char *data = NULL;
    
    if (XGetWindowProperty(dpy, w, a, 0L, (long)BUFSIZ,
            False, XA_STRING, &actual_type, &actual_format,
            &nitems, &leftover, &data) != Success) {
        *name = NULL;
        return (0);
    }
    if ( (actual_type == XA_STRING) && (actual_format == 8) ) {
        /* The data returned by XGetWindowProperty is guaranteed
         * to contain one extra byte that is null terminated to
         * make retrieving string properties easy */
        *name = (char *)data;
	
        return(1);
        }
    if (data) XFree ((char *)data);
    *name = NULL;
    return(0);
}

void WindowManager::setExtendedNetSupported()
{
	int total_net_supported = 22;

	Atom net_supported_list[] = {
		atom_extended_net_supported,
		atom_extended_net_client_list,
		atom_extended_net_client_list_stacking,
		atom_extended_net_number_of_desktops,
		atom_extended_net_desktop_geometry,
		atom_extended_net_desktop_viewport,
		atom_extended_net_current_desktop,
		//atom_extended_net_desktop_names,
		atom_extended_net_active_window,
		atom_extended_net_workarea,
		atom_extended_net_supporting_wm_check,
		//atom_extended_net_virtual_roots,
		atom_extended_net_close_window,
		//atom_extended_net_wm_moveresize,
		atom_extended_net_wm_name,
		//atom_extended_net_wm_visible_name,
		//atom_extended_net_wm_icon_name,
		//atom_extended_net_wm_visible_icon_name,
		atom_extended_net_wm_desktop,
		//atom_extended_net_wm_window_type,
		//atom_extended_net_wm_window_type_desktop,
		//atom_extended_net_wm_window_type_dock,
		//atom_extended_net_wm_window_type_toolbar,
		//atom_extended_net_wm_window_type_menu,
		//atom_extended_net_wm_window_type_dialog,
		//atom_extended_net_wm_window_type_normal,
		atom_extended_net_wm_state,
		atom_extended_net_wm_state_modal,
		atom_extended_net_wm_state_sticky,
		atom_extended_net_wm_state_maximized_vert,
		atom_extended_net_wm_state_maximized_horz,
		atom_extended_net_wm_state_shaded,
		atom_extended_net_wm_state_skip_taskbar,
		atom_extended_net_wm_state_skip_pager,
		atom_extended_net_wm_strut,
		//atom_extended_net_wm_icon_geometry,
		//atom_extended_net_wm_icon,
		//atom_extended_net_wm_pid,
		//atom_extended_net_wm_handled_icons,
		//atom_extended_net_wm_ping
	};
	
	XChangeProperty(dpy, root, atom_extended_net_supported, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)net_supported_list, total_net_supported);
}

void WindowManager::setExtendedNetDesktopGeometry()
{
	CARD32 geometry[] = { xres, yres };
	
	XChangeProperty(dpy, root, atom_extended_net_desktop_geometry, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)geometry, 2);
}

void WindowManager::setExtendedNetDesktopViewport()
{
	CARD32 viewport[] = { 0, 0 };
	
	XChangeProperty(dpy, root, atom_extended_net_desktop_viewport, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)viewport, 2);
}

void WindowManager::setExtendedNetActiveWindow(Window w)
{
	setExtendedWMHint(root, atom_extended_net_active_window, w);
}

void WindowManager::setExtendedNetWorkArea()
{
	int work_x, work_y, work_width, work_height;
		
	work_x = master_strut->west;
	work_y = master_strut->north;
	work_width = xres - master_strut->east - work_x;
	work_height = yres - master_strut->south - work_y;
	
	CARD32 workarea[] = { work_x, work_y, work_width, work_height };
		
	XChangeProperty(dpy, root, atom_extended_net_workarea, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)workarea, 4);
}

void* WindowManager::getExtendedNetPropertyData(Window win, Atom prop, Atom type, int *items)
{
	Atom type_ret;
	int format_ret;
	unsigned long items_ret;
	unsigned long after_ret;
	unsigned char *prop_data;

	prop_data = 0;

	XGetWindowProperty (dpy, win, prop, 0, 0x7fffffff, False,
				  type, &type_ret, &format_ret, &items_ret,
				  &after_ret, &prop_data);
	if (items)
		*items = items_ret;

	return prop_data;
}

NetWMStates* WindowManager::getExtendedNetWMStates(Window win)
{
	NetWMStates *win_state;

	win_state = new NetWMStates;

	win_state->modal=false;
	win_state->sticky=false;
 	win_state->max_vert=false;
 	win_state->max_horz=false;
 	win_state->shaded=false;
 	win_state->skip_taskbar=false;
	win_state->skip_pager=false;

	int num=0;
 	//Atom* states;
	//unsigned long *state;

 	/*states = (Atom*) getExtendedNetPropertyData(win,
					atom_extended_net_wm_state,	
					XA_ATOM,
					&num);*/
 	

	if ( getExtendedNetPropertyData(win, atom_extended_net_wm_state_modal, XA_CARDINAL, &num) ) win_state->modal=true;
	if ( getExtendedNetPropertyData(win, atom_extended_net_wm_state_sticky, XA_CARDINAL, &num) ) win_state->sticky=true;
	if ( getExtendedNetPropertyData(win, atom_extended_net_wm_state_maximized_vert, XA_CARDINAL, &num) ) win_state->max_vert=true;
	if ( getExtendedNetPropertyData(win, atom_extended_net_wm_state_maximized_horz, XA_CARDINAL, &num) ) win_state->max_horz=true;
	if ( getExtendedNetPropertyData(win, atom_extended_net_wm_state_shaded, XA_CARDINAL, &num) ) win_state->shaded=true;
	if ( getExtendedNetPropertyData(win, atom_extended_net_wm_state_skip_taskbar, XA_CARDINAL, &num) ) win_state->skip_taskbar=true;
	if ( getExtendedNetPropertyData(win, atom_extended_net_wm_state_skip_pager, XA_CARDINAL, &num) ) win_state->skip_pager=true;


//	if(states!=NULL)
//	{
//		for (int a = 0; a < NET_WM_STATE_MAX_STATES; a++) {
//			if(states[a]==atom_extended_net_wm_state_modal) win_state->modal=true;
//			if(states[a]==atom_extended_net_wm_state_sticky) win_state->sticky=true;
//			if(states[a]==atom_extended_net_wm_state_maximized_vert) win_state->max_vert=true;
//			if(states[a]==atom_extended_net_wm_state_maximized_horz)	win_state->max_horz=true;
//			if(states[a]==atom_extended_net_wm_state_shaded) win_state->shaded=true;
//			if(states[a]==atom_extended_net_wm_state_skip_taskbar) win_state->skip_taskbar=true;
//			if(states[a]==atom_extended_net_wm_state_skip_pager) win_state->skip_pager=true;
//		}
//	}

	return win_state;
}

void WindowManager::setExtendedNetWMState(
				Window win,
				bool modal,
				bool sticky,
				bool max_vert,
				bool max_horz,
				bool shaded,
				bool skip_taskbar,
				bool skip_pager
				)
{
	//atom_extended_net_wm_state_modal,
	//atom_extended_net_wm_state_sticky,
	//atom_extended_net_wm_state_maximized_vert,
	//atom_extended_net_wm_state_maximized_horz,
	//atom_extended_net_wm_state_shaded,
	//atom_extended_net_wm_state_skip_taskbar,
	//atom_extended_net_wm_state_skip_pager,

	//int counter=0;

	if(modal) 	
		net_wm_states[0] = atom_extended_net_wm_state_modal;
	else
		net_wm_states[0] = 0;
	
	if(sticky) 	
		net_wm_states[1] = atom_extended_net_wm_state_sticky;
	else 
		net_wm_states[1] = 0;
		
	if(max_vert)	
		net_wm_states[2] = atom_extended_net_wm_state_maximized_vert;
	else
		net_wm_states[2] = 0;

	if(max_horz)	
		net_wm_states[3] = atom_extended_net_wm_state_maximized_horz;
	else
		net_wm_states[3] = 0;
			
	if(shaded)	
		net_wm_states[4] = atom_extended_net_wm_state_shaded;
	else
		net_wm_states[4] = 0;	
	
	if(skip_taskbar) 
		net_wm_states[5] = atom_extended_net_wm_state_skip_taskbar;
	else
		net_wm_states[5] = 0;
	
	if(skip_pager)	
		net_wm_states[6] = atom_extended_net_wm_state_skip_pager;
	else
		net_wm_states[6] = 0;
	
	//if (counter < NET_WM_STATE_MAX_STATES)
	//{
	//	for(;counter < NET_WM_STATE_MAX_STATES; counter++)
	//		net_wm_states[counter] = 0;
	//}

	XChangeProperty(dpy, win, atom_extended_net_wm_state, XA_ATOM, 32, PropModeReplace, (unsigned char *) net_wm_states, NET_WM_STATE_MAX_STATES);
}

void WindowManager::addStrut(Strut *new_strut)
{
	clientStruts->push_back(new_strut);
	
	if(!clientStruts->empty())
	{
		for(struts_it = clientStruts->begin(); struts_it != clientStruts->end(); struts_it++)
		{
			if(master_strut->east < (*struts_it)->east) master_strut->east = (*struts_it)->east;
			
			if(master_strut->west < (*struts_it)->west) master_strut->west = (*struts_it)->west;
			
			if(master_strut->north < (*struts_it)->north) master_strut->north = (*struts_it)->north;

			if(master_strut->south < (*struts_it)->south) master_strut->south = (*struts_it)->south;
		}
	
	} 
	
	setExtendedNetWorkArea();
}

void WindowManager::removeStrut(Strut *rem_strut)
{
	if(!clientStruts->empty())
		clientStruts->remove(rem_strut);

	master_strut->east = 0;
	master_strut->west = 0;
	master_strut->north = 0;	
	master_strut->south = 0;

	if(!clientStruts->empty())
	{
		for(struts_it = clientStruts->begin(); struts_it != clientStruts->end(); struts_it++)
		{
			if(master_strut->east < (*struts_it)->east) master_strut->east = (*struts_it)->east;
			
			if(master_strut->west < (*struts_it)->west) master_strut->west = (*struts_it)->west;
			
			if(master_strut->north < (*struts_it)->north) master_strut->north = (*struts_it)->north;

			if(master_strut->south < (*struts_it)->south) master_strut->south = (*struts_it)->south;
		}
	} 
	
	setExtendedNetWorkArea();
}

int WindowManager::findExtendedDesktopHint(Window win)
{
	int desktop=-1;
	
	desktop = getDesktopHint(win, atom_extended_net_wm_desktop);

	return desktop;
}

int WindowManager::findGnomeDesktopHint(Window win)
{
	int desktop=-1;
	
	desktop = getDesktopHint(win, atom_gnome_win_workspace);

	return desktop;	
}

long WindowManager::getDesktopHint(Window win, int a)
{
	Atom real_type; 
  	int real_format; 
  	unsigned long items_read, items_left; 
  	long *data=NULL, value=-1; 
 
  	if(XGetWindowProperty(dpy, win, a, 0L, 1L, False, 
			      XA_CARDINAL, &real_type, 
			      &real_format, &items_read, 
			      &items_left, 
			      (unsigned char **)&data)==Success && items_read) 
  	{ 
    		value=*data; 
    		XFree(data); 
  	}
	
	return value;
}

void WindowManager::restart()
{
	cleanup();
	
	execl("/bin/sh", "sh", "-c", command_line.c_str(), 0);
}

void WindowManager::quitNicely()
{
	cleanup();
	exit(0);
}

void WindowManager::cleanup()
{
        unsigned int nwins, i;
    	Window dummyw1, dummyw2, *wins;
    	Client* c;

//    	ungrabKeys(root);

	XDestroyWindow(dpy, gnome_button_proxy_win);
	XDestroyWindow(dpy, gnome_check_win);
	XDestroyWindow(dpy, extended_hints_win);

    	// Preserve stacking order when removing the clients
    	// from the list.
    	XQueryTree(dpy, root, &dummyw1, &dummyw2, &wins, &nwins);
    	for (i = 0; i < nwins; i++) 
    	{
		c = findClient(wins[i]);
		
		if(c)
		{
			XMapWindow(dpy, c->getAppWindow());
			
			delete c;
		}
    	}
    	XFree(wins);
    
    	delete windowmenu;
	delete iconmenu;
	delete clientList;
	delete master_strut;
	delete clientStruts;

    	XFreeFont(dpy, font);
    	XFreeCursor(dpy, move_curs);
    	XFreeCursor(dpy, resize_curs);
	XFreeGC(dpy, invert_gc);
    	XFreeGC(dpy, border_gc);
    	XFreeGC(dpy, string_gc);

    	XInstallColormap(dpy, DefaultColormap(dpy, screen));
    	XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);

    	XCloseDisplay(dpy);
}


syntax highlighted by Code2HTML, v. 0.9.1