/* 
 * Oroborus Window Manager
 *
 * Copyright (C) 2001 Ken Lynch
 * Copyright (C) 2002 Stefan Pfetzing
 *
 * OroboROX Window Manager
 * 
 * Copyright (C) 2004 Guido Schimmels
 *
 * 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, or (at your option)
 * 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 "config.h"
#include <sys/param.h>			/* for MAXPATHLEN  */
#include <stdio.h>
#include <glib.h>

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xmd.h>
#include <X11/extensions/shape.h>
#ifdef HAVE_RANDR
#include <X11/extensions/Xrandr.h>
#endif
#include <X11/XKBlib.h>

#include <ft2build.h>
#include <X11/Xft/Xft.h>

#include "xerror.h"
#include "keyboard.h"
#include "pixmap.h"
#include "hints.h"
#include "client.h"
#include "frame.h"
#include "i18n.h"
#include "xinerama.h"
#include "settings.h"
#include "mouse.h"
#include "workspaces.h"
#include "stacking.h"
#include "main.h"
#include "frame.h"
#include "startup_notification.h"
#include "focus.h"

//#define DBUG_VERBOSE(x) DBUG(x)
#define DBUG_VERBOSE(x)

//#define DBUG(x) fprintf(stderr, "[OROBOROX]: %s\n", x)


#define DBL_CLICK_GRAB          (ButtonMotionMask | \
                                 PointerMotionMask | \
                                 ButtonPressMask | \
                                 ButtonReleaseMask)


extern void show_hidden_clients(void);

static Time current_time = CurrentTime;


static guint32 event_get_time(XEvent * event)
{
	switch (event->type)
	{
		case KeyPress:
		case KeyRelease:
			return event->xkey.time;

		case ButtonPress:
		case ButtonRelease:
			return event->xbutton.time;

		case MotionNotify:
			return event->xmotion.time;

		case PropertyNotify:
			return event->xproperty.time;

		case SelectionClear:
		case SelectionRequest:
		case SelectionNotify:
			return event->xselection.time;

		case EnterNotify:
		case LeaveNotify:
			return event->xcrossing.time;

		case FocusIn:
		case FocusOut:
		case KeymapNotify:
		case Expose:
		case GraphicsExpose:
		case NoExpose:
		case MapNotify:
		case UnmapNotify:
		case VisibilityNotify:
		case ResizeRequest:
		case ColormapNotify:
		case ClientMessage:
		case CreateNotify:
		case DestroyNotify:
		case MapRequest:
		case ReparentNotify:
		case ConfigureNotify:
		case ConfigureRequest:
		case GravityNotify:
		case CirculateNotify:
		case CirculateRequest:
		case MappingNotify:
		default:
			return CurrentTime;
	}
}

static int getKeyPressed(XKeyEvent * ev)
{
	int key, state;

	state = ev->state & KeyMask;
	for (key = 0; key < KEY_COUNT; key++)
	{
		if ((keys[key].keycode == ev->keycode) && (keys[key].modifier == state))
		{
			break;
		}
	}

	return key;
}


inline static void handleKeyPress(XKeyEvent * ev)
{
	int key;

	DBUG("entering handleKeyPress");

	Client *c = client_of_window(get_input_focus());

	if (c)
	{
		/* There may be warnings about left...bottom possibly being used before
		 * set. They can safely be ignored. */
		key = getKeyPressed(ev);
		int m, left, top, right, bottom;
		XWindowChanges wc;

		switch (key)
		{
			case KEY_TOP_LEFT:
			case KEY_TOP_RIGHT:
			case KEY_BOTTOM_LEFT:
			case KEY_BOTTOM_RIGHT:
				if (get_mouse_xy(dpy, root, &left, &top))
				{
					m = xineramaGetMonitorUnderPoint(left, top);
					left = xinerama_screen_info[m].x_org;
					top = xinerama_screen_info[m].y_org;
					right = left + xinerama_screen_info[m].width;
					bottom = top + xinerama_screen_info[m].height;
				}
				/* Strictly, these cases should check whether moving to avoid one
				 * margin makes us infringe on another, but that could lead to
				 * recursion and/or need to reduce size. Yuck, just leave it. */
				switch (key)
				{
					case KEY_TOP_LEFT:
						wc.x =
							left + frameLeft(c) + getMarginWithFrame(c, MARGIN_LEFT, top,
																	 top + c->geometry.height);
						wc.y =
							top + frameTop(c) + getMarginWithFrame(c, MARGIN_TOP, wc.x,
																   wc.x + c->geometry.width);
						break;
					case KEY_TOP_RIGHT:
						wc.x =
							right - c->geometry.width - frameRight(c) - getMarginWithFrame(c, MARGIN_RIGHT,
																				  top,
																				  top + c->geometry.height);
						wc.y =
							top + frameTop(c) + getMarginWithFrame(c, MARGIN_TOP, wc.x,
																   wc.x + c->geometry.width);
						break;
					case KEY_BOTTOM_LEFT:
						wc.x =
							left + frameLeft(c) + getMarginWithFrame(c, MARGIN_LEFT,
																	 bottom - c->geometry.height, bottom);
						wc.y =
							bottom - c->geometry.height - frameBottom(c) - getMarginWithFrame(c,
																					 MARGIN_BOTTOM,
																					 wc.x,
																					 wc.x +
																					 c->geometry.width);
						break;
					case KEY_BOTTOM_RIGHT:
						wc.x =
							right - c->geometry.width - frameRight(c) - getMarginWithFrame(c, MARGIN_RIGHT,
																				  bottom -
																				  c->geometry.height,
																				  bottom);
						wc.y =
							bottom - c->geometry.height - frameBottom(c) - getMarginWithFrame(c,
																					 MARGIN_BOTTOM,
																					 wc.x,
																					 wc.x +
																					 c->geometry.width);
						break;
				}				// switch
				clientConfigure(c, &wc, CWX | CWY);
				warp_mouse_pointer(c);
				break;
			case KEY_QUIT:
				//quit = True;
				reload = True;
				break;
			case KEY_MOVE_UP:
			case KEY_MOVE_DOWN:
			case KEY_MOVE_LEFT:
			case KEY_MOVE_RIGHT:
				clientMove(c, (XEvent *) ev);
				warp_mouse_pointer(c);
				break;
			case KEY_RESIZE_UP:
			case KEY_RESIZE_DOWN:
			case KEY_RESIZE_LEFT:
			case KEY_RESIZE_RIGHT:
				clientResize(c, (XEvent *) ev);
				warp_mouse_pointer(c);
				break;
			case KEY_CYCLE_WINDOWS:
				clientCycle(KEY_CYCLE_WINDOWS);
				break;
			case KEY_CYCLE_APP:
				clientCycle(KEY_CYCLE_APP);
				break;	
			case KEY_CLOSE_WINDOW:
				clientClose(c, ((XEvent *) ev)->xkey.time);
				break;
			case KEY_HIDE_WINDOW:
				clientHide(c, TRUE);
				break;
			case KEY_TOGGLE_FULLSCREEN_WINDOW:
				clientToggleFullscreen(c, 0);
				break;
			case KEY_MAXIMIZE_WINDOW:
				clientToggleMaximized(c, (STATE_MAXIMIZED_VERT | STATE_MAXIMIZED_HORZ), 0);
				warp_mouse_pointer(c);
				break;
			case KEY_MAXIMIZE_VERT:
				clientToggleMaximized(c, STATE_MAXIMIZED_VERT, 0);
				warp_mouse_pointer(c);
				break;
			case KEY_MAXIMIZE_HORIZ:
				clientToggleMaximized(c, STATE_MAXIMIZED_HORZ, 0);
				warp_mouse_pointer(c);
				break;
			case KEY_TOGGLE_FULLSCREEN_WINDOW_ALL:
				clientToggleFullscreen(c, 1);
				break;
			case KEY_MAXIMIZE_WINDOW_ALL:
				clientToggleMaximized(c, (STATE_MAXIMIZED_VERT | STATE_MAXIMIZED_HORZ), 1);
				warp_mouse_pointer(c);
				break;
			case KEY_MAXIMIZE_VERT_ALL:
				clientToggleMaximized(c, STATE_MAXIMIZED_VERT, 1);
				warp_mouse_pointer(c);
				break;
			case KEY_MAXIMIZE_HORIZ_ALL:
				clientToggleMaximized(c, STATE_MAXIMIZED_HORZ, 1);
				warp_mouse_pointer(c);
				break;
			case KEY_SHADE_WINDOW:
				clientToggleShaded(c);
				break;
			case KEY_RAISE_WINDOW_LAYER:
				client_inc_layer(c);
				break;
			case KEY_LOWER_WINDOW_LAYER:
				client_dec_layer(c);
				break;
			case KEY_NEXT_WORKSPACE:
				switch_to_workspace(workspace + 1);
				//warp_mouse_pointer(clientGetFocus());
				break;
			case KEY_PREV_WORKSPACE:
				switch_to_workspace(workspace - 1);
				//warp_mouse_pointer(clientGetFocus());
				break;
			case KEY_ADD_WORKSPACE:
				workspaceSetCount(workspace_count + 1);
				break;
			case KEY_DEL_WORKSPACE:
				workspaceSetCount(workspace_count - 1);
				break;
			case KEY_STICK_WINDOW:
				client_toggle_sticky(c);
				break;
			case KEY_WORKSPACE_1:
			case KEY_WORKSPACE_2:
			case KEY_WORKSPACE_3:
			case KEY_WORKSPACE_4:
			case KEY_WORKSPACE_5:
			case KEY_WORKSPACE_6:
			case KEY_WORKSPACE_7:
			case KEY_WORKSPACE_8:
			case KEY_WORKSPACE_9:
				switch_to_workspace(key - KEY_WORKSPACE_1);
				//warp_mouse_pointer(clientGetFocus());
				break;
			case KEY_MOVE_NEXT_WORKSPACE:
				move_client_to_workspace(c, workspace + 1);
				warp_mouse_pointer(c);
				break;
			case KEY_MOVE_PREV_WORKSPACE:
				move_client_to_workspace(c, workspace - 1);
				warp_mouse_pointer(c);
				break;
			case KEY_MOVE_WORKSPACE_1:
			case KEY_MOVE_WORKSPACE_2:
			case KEY_MOVE_WORKSPACE_3:
			case KEY_MOVE_WORKSPACE_4:
			case KEY_MOVE_WORKSPACE_5:
			case KEY_MOVE_WORKSPACE_6:
			case KEY_MOVE_WORKSPACE_7:
			case KEY_MOVE_WORKSPACE_8:
			case KEY_MOVE_WORKSPACE_9:
				move_client_to_workspace(c, key - KEY_MOVE_WORKSPACE_1);
				warp_mouse_pointer(c);
				break;
			case KEY_TOGGLE_BORDERS:
				c->has_decor = !c->has_decor;
				client_configure_xywh(c);
				break;
			case KEY_RAISE_WINDOW:
				client_raise(c);
				break;
			case KEY_SHOW_DESKTOP:
				if (showing_desktop)
					unshow_desktop();
				else
					show_desktop();
				break;
		}						// switch
	}							// if (c)
	else
	{							// c == 0
		//      fprintf(stderr,"Keypress event when clientGetFocus returned NULL\n");
		key = getKeyPressed(ev);
		switch (key)
		{
			case KEY_SHOW_DESKTOP:
				if (showing_desktop)
					unshow_desktop();
				else
					show_desktop();
				break;
			case KEY_QUIT:
				quit = True;
				break;
			case KEY_CYCLE_WINDOWS:
				//FIXME: clientCycle(clients->prev->data);
				clientCycle(KEY_CYCLE_WINDOWS);
				break;
			case KEY_CYCLE_APP:
				//FIXME: clientCycle(clients->prev->data);
				clientCycle(KEY_CYCLE_APP);
				break;	
			case KEY_NEXT_WORKSPACE:
				switch_to_workspace(workspace + 1);
				//warp_mouse_pointer(clientGetFocus());
				break;
			case KEY_PREV_WORKSPACE:
				switch_to_workspace(workspace - 1);
				//warp_mouse_pointer(clientGetFocus());
				break;
			case KEY_ADD_WORKSPACE:
				workspaceSetCount(workspace_count + 1);
				break;
			case KEY_DEL_WORKSPACE:
				workspaceSetCount(workspace_count - 1);
				break;
			case KEY_WORKSPACE_1:
			case KEY_WORKSPACE_2:
			case KEY_WORKSPACE_3:
			case KEY_WORKSPACE_4:
			case KEY_WORKSPACE_5:
			case KEY_WORKSPACE_6:
			case KEY_WORKSPACE_7:
			case KEY_WORKSPACE_8:
			case KEY_WORKSPACE_9:
				switch_to_workspace(key - KEY_WORKSPACE_1);
				//warp_mouse_pointer(clientGetFocus());
				break;
		}						// switch
	}							// else

	XEvent e;

	while (XCheckTypedEvent(dpy, EnterNotify, &e));
}


enum { WIN_CONTENT = 1, WIN_TITLE = 2, WIN_BUTTON = 4, WIN_SIDE = 8, WIN_CORNER =
		16 } ClickedWindowType;

inline static int clicked_window_type(Client * c, Window w)
{
	int type = WIN_BUTTON;

	if (w == c->window)
		type = WIN_CONTENT;
	else if (w == c->title)
		type = WIN_TITLE;
	/*else if ( w==c->buttons[HIDE_BUTTON]||w==c->buttons[CLOSE_BUTTON] ||
	   w==c->buttons[MAXIMIZE_BUTTON]||w==c->buttons[SHADE_BUTTON] ||
	   w==c->buttons[STICK_BUTTON])
	   type = WIN_BUTTON; */
	else if (w == c->sides[SIDE_BOTTOM] || w == c->sides[SIDE_LEFT] || w == c->sides[SIDE_RIGHT])
		type = WIN_SIDE;
	else if (w == c->corners[CORNER_TOP_LEFT] || w == c->corners[CORNER_TOP_RIGHT] ||
			 w == c->corners[CORNER_BOTTOM_LEFT] || w == c->corners[CORNER_BOTTOM_RIGHT])
		type = WIN_CORNER;

	return type;
}

#define ActionFocus		1
#define ActionRaise		2
#define ActionLower		4
#define ActionMove		8
#define ActionResize	16

inline static void check_frame_action(int click, int drag, int win_type, unsigned char *action) {
	if (click == LAYER_LOWER)
		*action |= ActionLower;
	else if (click == LAYER_RAISE)
		*action |= ActionRaise;
				
	if ((drag == DRAG_MOVE) || win_type == WIN_TITLE)
		*action |= ActionMove;
	else if (drag == DRAG_RESIZE)
		*action |= ActionResize;
}

inline static int check_double_click(Client *c, XButtonEvent * ev, int * event_mode) {
	int dblclk_interval = 250;
	static Time last_button_time;

	if (ev->time - last_button_time <= dblclk_interval && last_button_time != 0)
	{ /// double-click! ///
		last_button_time = 0;
		if (double_click_action!=WINDOW_ACTION_NONE) {
			*event_mode = SyncPointer;
			switch (double_click_action)
			{
				case WINDOW_ACTION_MAXIMIZE:
					clientToggleMaximized(c, (STATE_MAXIMIZED_VERT | STATE_MAXIMIZED_HORZ), 0);
				break;
				case WINDOW_ACTION_SHADE:
					clientToggleShaded(c);
				break;
				case WINDOW_ACTION_HIDE:
					clientHide(c, True);
				break;
			}
			return True;
		}
	}
	last_button_time = ev->time;
	return False;
}

inline static void handleButtonPress(XButtonEvent * ev)
{
	cancel_pending_raise();
	while (XCheckTypedEvent(dpy, ButtonPress, (XEvent *) ev));

	Client *c = client_of_window(ev->window);
	if(!c)
		c = client_of_decor(ev->window);

	if (!c)
	{
		XUngrabPointer(dpy, CurrentTime);
		XSendEvent(dpy, root, False, SubstructureNotifyMask, (XEvent *) ev);
		return;
	}

	int state = ev->state & KeyMask;
	int win_ops = state && state == keys[KEY_WINDOW_OPS].modifier;
	Window win = ev->subwindow;
	int win_type = clicked_window_type(c, win);
	int event_mode = ReplayPointer;
	unsigned char action = 0;

	if (c->type & WINDOW_DESKTOP)
	{ /// Change workspace on scrollwheel ///
			if (ev->button == Button4)
				switch_to_workspace(workspace - 1);
			else if(ev->button == Button5)
				switch_to_workspace(workspace + 1);
		}
			else
	if (win_type == WIN_BUTTON && ev->button <= Button3)
		clientButtonPress(c, win, ev); 	/// Title button pressed ///
	else
	if (win_type != WIN_CONTENT || win_ops)
	{ /// Frame pressed ///
		switch (ev->button)
		{ /// Lets see what we should do... ///
			case Button1:
				if (check_double_click(c,ev,&event_mode)) break;
	
				if (raise_policy & (RAISE_WINDOWS | RAISE_RISCOS))
					action |= ActionRaise;
				if (focus_policy & FOCUS_WINDOWS)
					action |= ActionFocus;

				if ((border_drag == DRAG_MOVE && win_type != WIN_CORNER) || win_type == WIN_TITLE || win_ops)
					action |= ActionMove;
		else
				if (border_drag == DRAG_RESIZE || win_type == WIN_CORNER)
					action |= ActionResize;
			break;

			case Button2:
				check_frame_action(button2_frame_click,button2_frame_drag,win_type,&action);
			break;

			case Button3:
				check_frame_action(button3_frame_click,button3_frame_drag,win_type,&action);
			break;
			
			default:
				if ((ev->button == Button4 && !(c->state & STATE_SHADED)) || (ev->button == Button5 && (c->state & STATE_SHADED)))
					clientToggleShaded(c);
		}
	}
	else
	{ /// contents clicked without win_ops ///
		if (focus_policy & (FOCUS_WINDOWS | FOCUS_RISCOS))
			action |= ActionFocus;
		if (raise_policy & RAISE_WINDOWS)
			action |= ActionRaise;

		/* don't pass click to inactive app */
		/* like MacOS X: tried - not likable */
		//if (c != get_input_focus() && wants_focus(c))
        	//event_mode = SyncPointer;
	}

	/// Focus client? ///
	if ((action & ActionFocus) && (get_input_focus() != c->window) && wants_focus(c))
		set_input_focus(c->window);
	/// Raise or lower client? ///
	if (action & ActionRaise) client_raise(c);
	else
	if(action & ActionLower) client_lower_force(c);
	/// Move or resize client? ///
	if (action & ActionMove) clientMove(c,(XEvent *)ev);
	else
	if(action & ActionResize) clientResize(c,(XEvent *)ev);

	XAllowEvents(dpy, event_mode, CurrentTime);
}

inline static void handleButtonRelease(XButtonEvent * ev)
{
	DBUG_VERBOSE("entering handleButtonRelease");
	XSendEvent(dpy, root, False, SubstructureNotifyMask, (XEvent *) ev);
}

inline static void handleDestroyNotify(XDestroyWindowEvent * ev)
{
	Client *c = client_of_window(ev->window);

	if (!c) return;

	dbg("%s for %s\n", __func__, c->class.res_name);

	client_destroy(c);
}

inline static void handleMapNotify(XMapEvent * ev)
{
	Client *c = client_of_window(ev->window);
	
	dbg("%s()\n", __func__, c ? c->class.res_name : "unmanaged");
	
	
	if (c)
	{
		// c->map_pending = FALSE;
	
#warning "FIXME: the commented code wouldn't make difference, would it?"
/*
		Client *fc = get_focus();
		if ((c->type & WINDOW_DIALOG) && fc && (ev->window == fc->window))
			set_input_focus(c);
		else 	
		  apply_focus_policy(c);
*/

		//compositorMapWindow (display_info, c->frame);
	}
/*
	else if (ev->event == root)
	else if (myDisplayGetScreenFromRoot (display_info, ev->event))
	{
		compositorMapWindow (display_info, ev->window);
	}
*/
}

inline static void handleUnmapNotify(XUnmapEvent * ev)
{
	DBUG_VERBOSE("entering handleUnmapNotify");

	Client *c = client_of_window(ev->window);
	
	if (!c)
	{
		DBUG_VERBOSE("Ignoring UnmapNotify: window unmanaged");
		return;
	}

	if (ev->from_configure)
	{
		DBUG_VERBOSE("Ignoring UnmapNotify caused by parent's resize");
		return;
	}

#ifdef THIS_BREAKS_WIDESTUDIO_SO_DISABLE_FOR_NOW
	if ((ev->event != ev->window) && (ev->event != root || !ev->send_event))
	{
		DBUG_VERBOSE("handleUnmapNotify (): Event ignored");
		return;
	}
#endif

	/*
	 * ICCCM spec states that a client wishing to switch
	 * to WithdrawnState should send a synthetic UnmapNotify 
	 * with the event field set to root if the client window 
	 * is already unmapped.
	 * Therefore, bypass the ignore_unmap counter and
	 * unframe the client.
	 */

/* This caused bugs with keyboard focus, trying without it. */
/* (Does it still?) */
/*	if ((ev->event == root) && (ev->send_event))
	{

		dbg("ICCCM UnmapNotify for \"%s\"", c->class.res_class);
		goto destroy;
	}
*/
	/*if ((map_state(c->window) == WithdrawnState) || (map_state(c->frame) == WithdrawnState))
		goto destroy;*/
	
	if ((c->ignore_unmap > 0))
	{
			
		c->ignore_unmap--;
        dbg("unmapNotify on %#lx: ignored\n",c?c->window:0);
	}
	else
	{
destroy:
		client_destroy(c);
	    dbg("unmapNotify on %#lx\n",c?c->window:0);
	}

}

inline static void handleMapRequest(XMapRequestEvent * ev)
{
	dbg("%s\n", __func__);
	if (ev->window == None)
	{
		dbg("Mapping None ???");
		return;
	}

	if (ev->window == 0)
	{
		dbg("Mapping 0 ???");
		return;
	}

	Client *c = client_of_window(ev->window);

	if (c)
	{

#ifdef DEBUG
		DBUG("MapRequest - showing client:");
		if (c->class.res_name)
			DBUG(c->class.res_name);
#endif

		/*if (c->map_pending)
		{
			dbg("Ignoring MapRequest on window: %lx", ev->window);
			return;
		}*/

		clientShow(c, True);
		focus_new_client(c);
#warning FIXME: "Do we the right thing on MapRequest?"
		/*if (workspaces_match(c, workspace))
			(c);
		*/
	}
	else
	{
		XWindowAttributes attr;

		if (!XGetWindowAttributes(dpy, ev->window, &attr))
		{
			DBUG("Cannot get window attributes");
			return;
		}
		
		/*if (attr.override_redirect)
		{
			DBUG("Not managing override_redirect windows");
			return;
		}*/
		Client *c = clientFrame(ev->window, &attr);
		if (c)
			focus_new_client(c);
	}
}

inline static void _handleConfigureRequest(XConfigureRequestEvent * ev)
{

	/* Compress events - logic taken from kwin, code from xfwm4 */
	XEvent otherEvent;

	while (XCheckTypedWindowEvent(dpy, ev->window, ConfigureRequest, &otherEvent))
	{
		if (otherEvent.xconfigurerequest.value_mask == ev->value_mask)
			ev = &otherEvent.xconfigurerequest;
		else
		{
			XPutBackEvent(dpy, &otherEvent);
			break;
		}
	}

	XWindowChanges wc;

	wc.x = ev->x;
	wc.y = ev->y;
	wc.width = ev->width;
	wc.height = ev->height;
	wc.sibling = ev->above;
	wc.stack_mode = ev->detail;
	wc.border_width = ev->border_width;

	Client *c = client_of_window(ev->window);

	if (!c)
	{
		/* Some app tend or try to manipulate the wm frame to achieve fullscreen mode */
		c = client_of_decor(ev->window);
		if (c)
		{
			dbg("client %s (0x%lx) is attempting to manipulate its frame!",
					c->class.res_name, c->window);
			if (ev->value_mask & CWX)
				wc.x += frameLeft(c);
			if (ev->value_mask & CWY)
				wc.y += frameTop(c);
			if (ev->value_mask & CWWidth)
				wc.width -= frameLeft(c) + frameRight(c);
			if (ev->value_mask & CWHeight)
				wc.height -= frameTop(c) + frameBottom(c);
			/* We don't allow changing stacking order by accessing the frame
			   window because that would break the layer management in xfwm4
			 */
			ev->value_mask &= ~(CWSibling | CWStackMode);
		}
	}

	if (c)
	{
		dbg("ConfigureRequest on managed window: %s\n", c->class.res_name);

		if (c->has_decor)
		{
			Point p;
			gravitation(c, &p);
			wc.x += p.x;
			wc.y += p.y;
		}
		else if (c->type == WINDOW_DESKTOP)	/* Ignore stacking request for DESKTOP windows */
			ev->value_mask &= ~(CWSibling | CWStackMode);
		else if (c->state & STATE_FULLSCREEN)
		{
			/* size request from fullscreen windows get fullscreen */
			client_configure_fullscreen(&wc, 0);
			ev->value_mask |= (CWX | CWY | CWWidth | CWHeight);
		}
	
		/* Clean up buggy requests that set all flags */
		if ((ev->value_mask & CWX) && (wc.x == c->geometry.x))
			ev->value_mask &= ~CWX;
		if ((ev->value_mask & CWY) && (wc.y == c->geometry.y))
			ev->value_mask &= ~CWY;
		if ((ev->value_mask & CWWidth) && (wc.width == c->geometry.width))
			ev->value_mask &= ~CWWidth;
		if ((ev->value_mask & CWHeight) && (wc.height == c->geometry.height))
			ev->value_mask &= ~CWHeight;	
	
		/* Still a move/resize after cleanup? */
		if (ev->value_mask & (CWX | CWY | CWWidth | CWHeight))
		{
			if (c->state & (STATE_MAXIMIZED_VERT | STATE_MAXIMIZED_HORZ))
			{
				c->state &= ~(STATE_MAXIMIZED_VERT | STATE_MAXIMIZED_HORZ);
				set_net_wm_state(c);
				//fprintf(stderr, "[OROBOROX]: %s\n", __func__);
				frameDraw(c);
			}
		}

		clientConfigure(c, &wc, ev->value_mask);
	}
	else
		XConfigureWindow(dpy, ev->window, ev->value_mask, &wc);
}

inline static void handleConfigureRequest(XConfigureRequestEvent * ev)
{

	unsigned long value_mask = ev->value_mask;
	unsigned long tmp_mask = value_mask;
	Window window = ev->window;
	XWindowChanges wc;
	XEvent otherEvent;

	goto WindowChanges;

	while (XCheckTypedWindowEvent(dpy, window, ConfigureRequest, &otherEvent))
	{
		ev = &otherEvent.xconfigurerequest;
		tmp_mask = ev->value_mask;
		value_mask |= tmp_mask;

WindowChanges:
		if (tmp_mask & CWX)
			wc.x = ev->x;
		if (tmp_mask & CWY)
			wc.y = ev->y;
		if (tmp_mask & CWWidth)
			wc.width = ev->width;
		if (tmp_mask & CWHeight)
			wc.height = ev->height;
		if (tmp_mask & CWSibling)
			wc.sibling = ev->above;
		if (tmp_mask & CWStackMode)
			wc.stack_mode = ev->detail;
		if (tmp_mask & CWBorderWidth)
			wc.border_width = ev->border_width;						
	}
		

	Client *c = client_of_window(window);

	if (!c)
	{
		/* Some app tend or try to manipulate the wm frame to achieve fullscreen mode */
		c = client_of_decor(window);
		if (c)
		{
			dbg("client %s (0x%lx) is attempting to manipulate its frame!",
					c->class.res_name, window);
			if (value_mask & CWX)
				wc.x += frameLeft(c);
			if (value_mask & CWY)
				wc.y += frameTop(c);
			if (value_mask & CWWidth)
				wc.width -= frameLeft(c) + frameRight(c);
			if (value_mask & CWHeight)
				wc.height -= frameTop(c) + frameBottom(c);
			/* We don't allow changing stacking order by accessing the frame
			   window because that would break the layer management
			 */
			value_mask &= ~(CWSibling | CWStackMode);
		}
	}

	if (c)
	{
		dbg("ConfigureRequest on managed window: %s\n", c->class.res_name);

		if (c->has_decor)
		{
			Point p;
			gravitation(c, &p);
			wc.x += p.x;
			wc.y += p.y;
		}
		else if (c->type == WINDOW_DESKTOP)	/* Ignore stacking request for DESKTOP windows */
			value_mask &= ~(CWSibling | CWStackMode);
		else if (c->state & STATE_FULLSCREEN)
		{
			/* size request from fullscreen windows get fullscreen */
			client_configure_fullscreen(&wc, 0);
			value_mask |= (CWX | CWY | CWWidth | CWHeight);
		}
	
		/* Clean up buggy requests that set all flags */
		if ((value_mask & CWX) && (wc.x == c->geometry.x))
			value_mask &= ~CWX;
		if ((value_mask & CWY) && (wc.y == c->geometry.y))
			value_mask &= ~CWY;
		if ((value_mask & CWWidth) && (wc.width == c->geometry.width))
			value_mask &= ~CWWidth;
		if ((value_mask & CWHeight) && (wc.height == c->geometry.height))
			value_mask &= ~CWHeight;	
	
		/* Still a move/resize after cleanup? */
		if (value_mask & (CWX | CWY | CWWidth | CWHeight))
		{
			if (c->state & (STATE_MAXIMIZED_VERT | STATE_MAXIMIZED_HORZ))
			{
				c->state &= ~(STATE_MAXIMIZED_VERT | STATE_MAXIMIZED_HORZ);
				set_net_wm_state(c);
				fprintf(stderr, "[OROBOROX]: %s\n", __func__);
				frameDraw(c);
			}
		}

		clientConfigure(c, &wc, value_mask);
	}
	else
		XConfigureWindow(dpy, window, value_mask, &wc);
}

inline static void handleEnterNotify(XCrossingEvent * ev)
{
	dbg("EnterNotify on window (0x%lx)", ev->window);

	// the semantics of ffm makes us only care for the last EnterNotify
	while (XCheckTypedEvent(dpy, EnterNotify, (XEvent *) ev));
		
	if (focus_policy == FOCUS_UNIX)
	{
		if ((ev->mode == NotifyGrab ) || (ev->detail > NotifyNonlinearVirtual))
			return;		/* We're not interested in such notifications */

		cancel_pending_raise();
		// client_of_decor() is correct - why?
		Client *c = client_of_decor(ev->window);
		if (c)
		{
			if (wants_focus(c))
				set_input_focus(c->window);			
			apply_raise_policy(c);
		}
	}
}



static void net_active_window(Client * c)
{
	dbg("%s\n", __func__);
	if (get_wm_state(c->window) != NormalState)
		clientShow(c, False);
	else if (switch_to_active_win && !workspaces_match(c, workspace))
		switch_to_workspace(c->win_workspace);

	set_mapping_state(c, NormalState);

	/* terry blunt */

	apply_focus_policy(c);

	if (get_input_focus() == c->window)
	{
		client_raise(c);
		warp_mouse_pointer(c);
	}
}

/* (This function is taken from fvwm) */
/* Similar function for certain types of PropertyNotify. */
static int flush_property_notify(Atom atom, Window w)
{
	XEvent e;
	int count;

	XSync(dpy, 0);
	for (count = 0; XCheckTypedWindowEvent(dpy, w, PropertyNotify, &e); count++)
	{
		if (e.xproperty.atom != atom)
		{
			XPutBackEvent(dpy, &e);
			break;
		}
	}

	return count;
}


inline static void handlePropertyNotify(XPropertyEvent * ev)
{
	Bool update_titlebar = False;

	Client *c = client_of_window(ev->window);

	if (!c)
	{
		DBUG_VERBOSE("Ignoring PropertyNotify: window unmanaged");
		return;
	}
	
#ifdef DEBUG
 {
   char *atomname = XGetAtomName(dpy, ev->atom);

   dbg("%s() on %s, atom is %li ( %s )\n", 
       __func__, c->class.res_class, ev->atom, atomname );

   if (atomname) free(atomname);
 }
#endif
	
	if (ev->atom == XA_WM_NORMAL_HINTS)
	{
		dbg("PropertyNotify WM_NORMAL_HINTS on window: %s\n", c->class.res_class);
		get_size_hints(c);
	}
	else if (ev->atom == XA_WM_NAME)
	{
		dbg("PropertyNotify WM_NAME on window: %s\n", c->class.res_class);

		//fprintf(stderr, "[OROBOROX]: %s\n", __func__);
		get_wm_name(c);
		if (get_window_title(c))
			frameDraw(c);
	}
	else if (ev->atom == intern_atoms[WM_TRANSIENT_FOR])
    {
      Client *new_trans_client;
      Window  trans_win = 0;
      int     success = 0;

      misc_trap_xerrors(); 

      success = XGetTransientForHint(dpy, c->window, &trans_win);

      if (!misc_untrap_xerrors() && success)
	if ((new_trans_client = client_of_window(trans_win)) != NULL)
	  {
	    c->transientFor = trans_win;
	    return;
	  }

      c->transientFor = 0;

    }
	else if ((ev->atom == intern_atoms[NET_WM_NAME]))
	{

#ifdef DBUG
		DBUG("PropertyNotify NET_WM_NAME on window:");
		if (c->class.res_class)
			DBUG(c->class.res_class);
#endif
		
		get_net_wm_name(c);
		if (get_window_title(c))
		{
			//fprintf(stderr, "[OROBOROX]: New NET_WM_NAME\n");
			frameDraw(c);
		}
	}
	
	else if (ev->atom == XA_WM_ICON_NAME)
	{
#ifdef USE_ICON_NAME
#ifdef DBUG
		DBUG("PropertyNotify WM_ICON_NAME on window:");
		if (c->class.res_name)
			DBUG(c->class.res_name);
#endif
		if (!c->net_wm_icon_name)
		{
			get_wm_icon_name(c);
			//fprintf(stderr, "[OROBOROX]: New Icon Name\n");
			//// why would we redraw the frame?
			// fixes slow GIMP painting bug
			//frameDraw(c);
		}
#endif
	}
	else if ((ev->atom == intern_atoms[NET_WM_ICON_NAME]))
	{
#ifdef USE_ICON_NAME
#ifdef DBUG
		DBUG("PropertyNotify NET_WM_ICON_NAME on window:");
		if (c->class.res_name)
			DBUG(c->class.res_name);
#endif
		get_net_wm_icon_name(c);
		//fprintf(stderr, "[OROBOROX]: New NET Icon Name\n");
		// why would we redraw frame?
		// fixes slow GIMP painting bug
		//frameDraw(c);
#endif
	}
	/*else if ((ev->atom == intern_atoms[NET_WM_ICON])) {
	   // TODO
	   }

	   else if ((ev->atom == intern_atoms[NET_WM_ICON_GEOMETRY])) {
	   // TODO

	   } */
	else if (ev->atom == intern_atoms[NET_WM_STRUT])
	{

#ifdef DBUG
		DBUG("PropertyNotify NET_WM_STRUT on window:");
		if (c->class.res_name)
			DBUG(c->class.res_name);
#endif
		delNetWMStrut(c);
		getNetWMStrut(c->window, c->margins);
	}
	else if (ev->atom == xa_wm_protocols)
	{
#ifdef DBUG
		DBUG("PropertyNotify wm_protocols on window:");
		if (c->class.res_name)
			DBUG(c->class.res_name);
#endif
		get_wm_protocols(c);
	}
	else  if (ev->atom == intern_atoms[XA_WM_CHANGE_STATE])
    {
      dbg("%s() state change, name is %s\n", __func__, c->class.res_class);
      if(get_wm_state(c->window) == WithdrawnState)
		clientKill(c);
    }
 /*
  else if (e->atom == intern_atoms[CM_TRANSLUCENCY])
    {
      comp_engine_client_get_trans_prop(w, c);
      comp_engine_client_repair(w, c);
    }
 */
#ifdef HAVE_STARTUP_NOTIFICATION
	else if (ev->atom == intern_atoms[NET_STARTUP_ID])
	{
#ifdef DBUG
		DBUG("PropertyNotify NET_STARTUP_ID on window:");
		if (c->class.res_name)
			DBUG(c->class.res_name);
#endif
		sn_client_startup_properties(c);
		//getWindowStartupId(dpy, c->window, &c->startup_id);
	}
#endif

}

inline static void net_request_frame_extents_client_message(Client * c, XClientMessageEvent * ev)
{
	gulong extents[4] = { frameLeft(c), frameRight(c), frameTop(c), frameBottom(c) };
	XChangeProperty(dpy, root, intern_atoms[NET_FRAME_EXTENTS], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)extents, 4);
}

inline static void net_wm_desktop_client_message(Client * c, XClientMessageEvent * ev)
{
	if (ev->data.l[0] == -1)
	{							//all workspaces, set sticky
		if (!c->state & STATE_STICKY)
			client_toggle_sticky(c);
	}
	else
	{
		if (c->state & STATE_STICKY)	//unset sticky
			client_toggle_sticky(c);
		client_set_workspace(c, ev->data.l[0]);
	}
}

inline static gboolean check_toggle(XClientMessageEvent * ev, Client * c, WindowState state)
{
	return (ev->data.l[0] == NET_WM_STATE_TOGGLE ||
			(ev->data.l[0] == NET_WM_STATE_ADD && !(c->state & state)) ||
			(ev->data.l[0] == NET_WM_STATE_REMOVE && (c->state & state)));
}

inline static gboolean check_atom(XClientMessageEvent * ev, InternAtom a)
{
	return (ev->data.l[1] == intern_atoms[a] || ev->data.l[2] == intern_atoms[a]);
}

inline static void toggle_net_wm_state(Client * c, int state)
{
	c->state ^= state;
}

inline static void net_wm_state_client_message(Client * c, XClientMessageEvent * ev)
{
	if (check_atom(ev, STATE_MAXIMIZED_HORZ) && check_atom(ev, STATE_MAXIMIZED_VERT) &&
		check_toggle(ev, c, STATE_MAXIMIZED_VERT | STATE_MAXIMIZED_HORZ))
		clientToggleMaximized(c, (STATE_MAXIMIZED_VERT | STATE_MAXIMIZED_HORZ), 0);
	else
	{
		if (check_atom(ev, STATE_MAXIMIZED_HORZ) && check_toggle(ev, c, STATE_MAXIMIZED_HORZ))
			clientToggleMaximized(c, STATE_MAXIMIZED_HORZ, 0);
		if (check_atom(ev, STATE_MAXIMIZED_VERT) && check_toggle(ev, c, STATE_MAXIMIZED_VERT))
			clientToggleMaximized(c, STATE_MAXIMIZED_VERT, 0);
	}

	if (check_atom(ev, NET_WM_STATE_FULLSCREEN) && check_toggle(ev, c, STATE_FULLSCREEN))
		clientToggleFullscreen(c, 0);
	if (check_atom(ev, NET_WM_STATE_SHADED) && check_toggle(ev, c, STATE_SHADED))
		clientToggleShaded(c);
	if (check_atom(ev, NET_WM_STATE_STICKY) && check_toggle(ev, c, STATE_STICKY))
		client_toggle_sticky(c);

	if (check_atom(ev, NET_WM_STATE_ABOVE) && check_toggle(ev, c, STATE_ABOVE))
		toggle_net_wm_state(c, STATE_ABOVE);
	if (check_atom(ev, NET_WM_STATE_BELOW) && check_toggle(ev, c, STATE_BELOW))
		toggle_net_wm_state(c, STATE_BELOW);
	if (check_atom(ev, NET_WM_STATE_SKIP_PAGER) && check_toggle(ev, c, STATE_SKIP_PAGER))
	{
		//recalc_window_features (c);
		toggle_net_wm_state(c, STATE_SKIP_PAGER);
	}
	if (check_atom(ev, NET_WM_STATE_SKIP_TASKBAR) && check_toggle(ev, c, STATE_SKIP_TASKBAR))
	{
		//recalc_window_features (c);
		toggle_net_wm_state(c, STATE_SKIP_TASKBAR);
	}
	if (check_atom(ev, NET_WM_STATE_MODAL) && check_toggle(ev, c, STATE_MODAL))
	{
		//recalc_window_type (window); 
		toggle_net_wm_state(c, STATE_MODAL);
	}

	set_net_wm_state(c);

}


/* when changing this enum, there are various switch statements
 * you have to update
 */
typedef enum {
	META_GRAB_OP_NONE,

	/* Mouse ops */
	META_GRAB_OP_MOVING,
	META_GRAB_OP_RESIZING_SE,
	META_GRAB_OP_RESIZING_S,
	META_GRAB_OP_RESIZING_SW,
	META_GRAB_OP_RESIZING_N,
	META_GRAB_OP_RESIZING_NE,
	META_GRAB_OP_RESIZING_NW,
	META_GRAB_OP_RESIZING_W,
	META_GRAB_OP_RESIZING_E,

	/* Keyboard ops */
	META_GRAB_OP_KEYBOARD_MOVING,
	META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN,
	META_GRAB_OP_KEYBOARD_RESIZING_S,
	META_GRAB_OP_KEYBOARD_RESIZING_N,
	META_GRAB_OP_KEYBOARD_RESIZING_W,
	META_GRAB_OP_KEYBOARD_RESIZING_E,
	META_GRAB_OP_KEYBOARD_RESIZING_SE,
	META_GRAB_OP_KEYBOARD_RESIZING_NE,
	META_GRAB_OP_KEYBOARD_RESIZING_SW,
	META_GRAB_OP_KEYBOARD_RESIZING_NW,

	/* Alt+Tab */
	META_GRAB_OP_KEYBOARD_TABBING_NORMAL,
	META_GRAB_OP_KEYBOARD_TABBING_DOCK,

	/* Alt+Esc */
	META_GRAB_OP_KEYBOARD_ESCAPING_NORMAL,
	META_GRAB_OP_KEYBOARD_ESCAPING_DOCK,

	META_GRAB_OP_KEYBOARD_WORKSPACE_SWITCHING,

	/* Frame button ops */
	META_GRAB_OP_CLICKING_MINIMIZE,
	META_GRAB_OP_CLICKING_MAXIMIZE,
	META_GRAB_OP_CLICKING_UNMAXIMIZE,
	META_GRAB_OP_CLICKING_DELETE,
	META_GRAB_OP_CLICKING_MENU
} MetaGrabOp;

inline static gboolean net_wm_moveresize_client_message(Client * c, XClientMessageEvent * ev)
{
	int x_root;
	int y_root;
	int action;
	MetaGrabOp op;
	int button;

	x_root = ev->data.l[0];
	y_root = ev->data.l[1];
	action = ev->data.l[2];
	button = ev->data.l[3];

	/*meta_topic (META_DEBUG_WINDOW_OPS,
	   "Received _NET_WM_MOVERESIZE message on %s, %d,%d action = %d, button %d\n",
	   window->desc,
	   x_root, y_root, action, button); */

	op = META_GRAB_OP_NONE;
	switch (action)
	{
		case MOVERESIZE_SIZE_TOPLEFT:
			op = META_GRAB_OP_RESIZING_NW;
			break;
		case MOVERESIZE_SIZE_TOP:
			op = META_GRAB_OP_RESIZING_N;
			break;
		case MOVERESIZE_SIZE_TOPRIGHT:
			op = META_GRAB_OP_RESIZING_NE;
			break;
		case MOVERESIZE_SIZE_RIGHT:
			op = META_GRAB_OP_RESIZING_E;
			break;
		case MOVERESIZE_SIZE_BOTTOMRIGHT:
			op = META_GRAB_OP_RESIZING_SE;
			break;
		case MOVERESIZE_SIZE_BOTTOM:
			op = META_GRAB_OP_RESIZING_S;
			break;
		case MOVERESIZE_SIZE_BOTTOMLEFT:
			op = META_GRAB_OP_RESIZING_SW;
			break;
		case MOVERESIZE_SIZE_LEFT:
			op = META_GRAB_OP_RESIZING_W;
			break;
		case MOVERESIZE_MOVE:
			op = META_GRAB_OP_MOVING;
			break;
		case MOVERESIZE_SIZE_KEYBOARD:
			op = META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN;
			break;
		case MOVERESIZE_MOVE_KEYBOARD:
			op = META_GRAB_OP_KEYBOARD_MOVING;
			break;
		default:
			break;
	}

	if (op != META_GRAB_OP_NONE &&
		(((c->actions & ACTION_MOVE) && op == META_GRAB_OP_KEYBOARD_MOVING) ||
		 ((c->actions & ACTION_RESIZE) && op == META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN)))
		;						//meta_window_begin_grab_op (window, op, meta_display_get_current_time (window->display));
	else if (op != META_GRAB_OP_NONE &&
			 (((c->actions & ACTION_MOVE) && op == META_GRAB_OP_MOVING) ||
			  ((c->actions & ACTION_RESIZE) && (op != META_GRAB_OP_MOVING &&
												op != META_GRAB_OP_KEYBOARD_MOVING))))
	{
		/*
		 * the button SHOULD already be included in the message
		 */
		if (button == 0)
		{
			int x, y, query_root_x, query_root_y;
			Window root, child;
			guint mask;

			/* The race conditions in this _NET_WM_MOVERESIZE thing
			 * are mind-boggling
			 */
			mask = 0;
			//meta_error_trap_push (window->display);
			XQueryPointer(dpy, c->window, &root, &child, &query_root_x, &query_root_y, &x, &y,
						  &mask);
			//meta_error_trap_pop (window->display, TRUE);

			if (mask & Button1Mask)
				button = 1;
			else if (mask & Button2Mask)
				button = 2;
			else if (mask & Button3Mask)
				button = 3;
			else
				button = 0;
		}

		if (button != 0)
		{
			;					/*meta_topic (META_DEBUG_WINDOW_OPS,
								   "Beginning move/resize with button = %d\n", button); */
			//meta_display_begin_grab_op (dpy, screen, c, op, FALSE, 0 /* event_serial */,
			//                        button, 0, meta_display_get_current_time (window->display),
			//                        x_root, y_root);
		}
	}

	return TRUE;
}



#ifdef HAVE_RANDR
inline static void handleRRScreenChangeNotify(XRRScreenChangeNotifyEvent * xrrsc)
{
	DBUG("handleRRScreenChangeNotify");
#if RANDR_MAJOR >= 1
	XRRUpdateConfiguration((XEvent *) xrrsc);
#endif

	if (display_width != xrrsc->width || display_height != xrrsc->height)
	{

//      msg("xrandr: %d %d", xrrsc.width, xrrsc.height);
//      setSize(xrrsc.width, xrrsc.height);
		cache_screen_info();
	}
}
#endif

inline static void handleClientMessage(XClientMessageEvent * ev)
{
	DBUG("entering handleClientMessage");

	if ((ev->format != 32))
		return;

	if (ev->window == root)
	{
		DBUG("Client Message to root window");
		/* atoms that can be sent by a client and do not need a window to operate */

		if (ev->message_type == intern_atoms[NET_CURRENT_DESKTOP])
			switch_to_workspace(ev->data.l[0]);
		/*else if (ev->message_type == intern_atoms[NET_DESKTOP_GEOMETRY]) {
		   // TODO
		   }
		   else if (ev->message_type == intern_atoms[NET_DESKTOP_NAMES]) {
		   // TODO
		   }
		   else if (ev->message_type == intern_atoms[NET_DESKTOP_VIEWPORT]) {
		   // TODO
		   } */
		else if (ev->message_type == intern_atoms[NET_SHOWING_DESKTOP])
		{
			if (ev->data.l[0] && !showing_desktop)
				show_desktop();
			else if (!ev->data.l[0] && showing_desktop)
				unshow_desktop();
		}
		else if (ev->message_type == intern_atoms[NET_NUMBER_OF_DESKTOPS])
			workspaceSetCount(ev->data.l[0]);
		else if (ev->message_type == xa_wm_protocols)
		{
			dbg("Received WM_PROTOCOLS message\n");
			if ((Atom) ev->data.l[0] == intern_atoms[NET_WM_PING])
			{
				dbg("process_pong_message\n");
				process_pong_message ((XEvent *)ev);
			}
		}
		return;
     }
		
	Client *c = client_of_window(ev->window);

	if (!c)
	{
		dbg("Client Message to unmanaged window\n");
		return;
	}

	/* atoms that can be sent by a client and do need a window to operate */
	dbg("Client Message to managed window\n");

	if (ev->message_type == intern_atoms[XA_WM_CHANGE_STATE])
    {
       	dbg("%s() message type is change state\n", __func__ );
       	if (ev->data.l[0] == IconicState)
	   		clientHide(c, True);
	}
	/* NET_WM client messages */
	else if (ev->message_type == intern_atoms[NET_ACTIVE_WINDOW])
		net_active_window(c);
	else if (ev->message_type == intern_atoms[NET_CLOSE_WINDOW])
		clientClose(c, current_time);
	else if (ev->message_type == intern_atoms[NET_WM_MOVERESIZE])
		net_wm_moveresize_client_message(c, ev);
	// TODO else if (ev->message_type == intern_atoms[NET_MOVERESIZE_WINDOW])
	// TODO else if (ev->message_type == intern_atoms[NET_RESTACK_WINDOW]) 
	else if (ev->message_type == intern_atoms[NET_WM_DESKTOP])
		net_wm_desktop_client_message(c, ev);
	else if (ev->message_type == intern_atoms[NET_WM_STATE])
		net_wm_state_client_message(c, ev);
	else if (ev->message_type == intern_atoms[NET_REQUEST_FRAME_EXTENTS])
		net_request_frame_extents_client_message(c, ev);



}

inline static void handleShape(XShapeEvent * ev)
{
	DBUG("entering handleShape");
	Client *c = client_of_window(ev->window);
	if (c)
		frameDraw(c);
}

inline static void handleFocusIn(XFocusChangeEvent * ev)
{
}

inline static void handleFocusOut(XFocusChangeEvent * ev)
{
#ifdef HANDLEFOCUSOUT
	if (Scr.UnknownWinFocused != None && Scr.StolenFocusWin != None &&
	    ea->exc->x.etrigger->xfocus.window == Scr.UnknownWinFocused)
	{
		FOCUS_SET(Scr.StolenFocusWin);
		Scr.UnknownWinFocused = None;
		Scr.StolenFocusWin = None;
	}
#endif
}


inline static void handleConfigureNotify(XConfigureEvent * ev)
{
	dbg("ConfigureNotify");
	if (((XConfigureEvent *) ev)->window == root)
	{
		dbg("ConfigureNotify on root");
#ifdef HAVE_RANDR
#ifdef VERBOSE
		dbg("ev->type=%d %d %d\n", ev->type, xrandrEventBase, xrandrSupported);
#endif
#warning "FIXME: XRANDR change notification by ConfigureNotify on root window?"

#if RANDR_MAJOR >= 1
		if (xrandrSupported)
			XRRUpdateConfiguration((XEvent *) ev);
#endif

#endif
		cache_screen_info();
	}
}

inline static void 	handleDefaultEvent(XEvent * ev)
{			
	DBUG_VERBOSE("default event");
	if (shape && ev->type == shape_event)
		handleShape((XShapeEvent *) ev);

#ifdef HAVE_RANDR
#ifdef VERBOSE 
	dbg("ev->type=%d %d %d\n", ev->type, xrandrEventBase, xrandrSupported);
#endif
	if (xrandrSupported && ev->type == (xrandrEventBase + RRScreenChangeNotify))
			handleRRScreenChangeNotify((XRRScreenChangeNotifyEvent *) ev);
#endif
}

void handleEvent(XEvent * ev)
{
	//DBUG("entering handleEvent");
	current_time = event_get_time(ev);
	sn_process_event(ev);
	switch (ev->type)
	{
		case MappingNotify:
			XRefreshKeyboardMapping(&(ev->xmapping));
			initModifiers(dpy);
			break;
		case KeyPress:
			handleKeyPress((XKeyEvent *) ev);
			break;
		case KeyRelease:
			break;
		case ButtonPress:
			handleButtonPress((XButtonEvent *) ev);
			break;
		case ButtonRelease:
			handleButtonRelease((XButtonEvent *) ev);
			break;
		case DestroyNotify:
			handleDestroyNotify((XDestroyWindowEvent *) ev);
			break;
		case UnmapNotify:
			handleUnmapNotify((XUnmapEvent *) ev);
			break;
		case MapNotify:
			// nothing to do here so far
			// when we implement the composite manager,
			// this will change
			//handleMapNotify((XMapEvent *) ev);
			break;
		case MapRequest:
			handleMapRequest((XMapRequestEvent *) ev);
			break;
		case ConfigureRequest:
			handleConfigureRequest((XConfigureRequestEvent *) ev);
			break;
		case EnterNotify:
			handleEnterNotify((XCrossingEvent *) ev);
			break;
		case FocusIn:
			handleFocusIn((XFocusChangeEvent *) ev);
			break;
		case FocusOut:
			handleFocusOut((XFocusChangeEvent *) ev);
			break;
		case PropertyNotify:
			handlePropertyNotify((XPropertyEvent *) ev);
			break;
		case ClientMessage:
			handleClientMessage((XClientMessageEvent *) ev);
			break;
		case ConfigureNotify:
			handleConfigureNotify((XConfigureEvent *) ev);
			break;
		default:
			handleDefaultEvent(ev);
			break;

	}	// switch

	/* desperate effort to get rid of occasional zombie windows (mapped+WithdrawnState) */
	/*GList *l;
restart:
	for (l = clients; l != NULL; l = l->next)
	{
		Client *c = (Client *) l->data;
		if ((map_state(c->window) == WithdrawnState) || (map_state(c->frame) == WithdrawnState))
		{
				client_destroy(c);
				goto restart;
		}
	}*/
	
	update_focus();	

	//show_hidden_clients();
} /* handleEvent() */


