shod

mouse-based window manager that can tile windows inside floating containers
Log | Files | Refs | README | LICENSE

xevents.c (53735B)


      1 #include <err.h>
      2 #include <spawn.h>
      3 
      4 #include "shod.h"
      5 
      6 #include <X11/XKBlib.h>
      7 
      8 #define MOUSEEVENTMASK          (ButtonReleaseMask | PointerMotionMask)
      9 #define ALTTABMASK              (KeyPressMask | KeyReleaseMask)
     10 #define DOUBLECLICK     250     /* time in miliseconds of a double click */
     11 #define BETWEEN(a, b, c)        ((a) <= (b) && (b) < (c))
     12 
     13 /* moveresize action */
     14 enum {
     15 	_NET_WM_MOVERESIZE_SIZE_TOPLEFT     = 0,
     16 	_NET_WM_MOVERESIZE_SIZE_TOP         = 1,
     17 	_NET_WM_MOVERESIZE_SIZE_TOPRIGHT    = 2,
     18 	_NET_WM_MOVERESIZE_SIZE_RIGHT       = 3,
     19 	_NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT = 4,
     20 	_NET_WM_MOVERESIZE_SIZE_BOTTOM      = 5,
     21 	_NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT  = 6,
     22 	_NET_WM_MOVERESIZE_SIZE_LEFT        = 7,
     23 	_NET_WM_MOVERESIZE_MOVE             = 8,   /* movement only */
     24 	_NET_WM_MOVERESIZE_SIZE_KEYBOARD    = 9,   /* size via keyboard */
     25 	_NET_WM_MOVERESIZE_MOVE_KEYBOARD    = 10,  /* move via keyboard */
     26 	_NET_WM_MOVERESIZE_CANCEL           = 11,  /* cancel operation */
     27 };
     28 
     29 /* focus relative direction */
     30 enum {
     31 	_SHOD_FOCUS_ABSOLUTE            = 0,
     32 	_SHOD_FOCUS_LEFT_CONTAINER      = 1,
     33 	_SHOD_FOCUS_RIGHT_CONTAINER     = 2,
     34 	_SHOD_FOCUS_TOP_CONTAINER       = 3,
     35 	_SHOD_FOCUS_BOTTOM_CONTAINER    = 4,
     36 	_SHOD_FOCUS_PREVIOUS_CONTAINER  = 5,
     37 	_SHOD_FOCUS_NEXT_CONTAINER      = 6,
     38 	_SHOD_FOCUS_LEFT_WINDOW         = 7,
     39 	_SHOD_FOCUS_RIGHT_WINDOW        = 8,
     40 	_SHOD_FOCUS_TOP_WINDOW          = 9,
     41 	_SHOD_FOCUS_BOTTOM_WINDOW       = 10,
     42 	_SHOD_FOCUS_PREVIOUS_WINDOW     = 11,
     43 	_SHOD_FOCUS_NEXT_WINDOW         = 12,
     44 };
     45 
     46 /* floating object type */
     47 enum {
     48 	FLOAT_CONTAINER,
     49 	FLOAT_MENU,
     50 };
     51 
     52 /* motif constants, mostly unused */
     53 enum {
     54 	/*
     55 	 * Constants copied from lib/Xm/MwmUtil.h on motif's source code.
     56 	 */
     57 
     58 	PROP_MWM_HINTS_ELEMENTS                 = 5,
     59 
     60 	/* bit definitions for MwmHints.flags */
     61 	MWM_HINTS_FUNCTIONS                     = (1 << 0),
     62 	MWM_HINTS_DECORATIONS                   = (1 << 1),
     63 	MWM_HINTS_INPUT_MODE                    = (1 << 2),
     64 	MWM_HINTS_STATUS                        = (1 << 3),
     65 
     66 	/* bit definitions for MwmHints.functions */
     67 	MWM_FUNC_ALL                            = (1 << 0),
     68 	MWM_FUNC_RESIZE                         = (1 << 1),
     69 	MWM_FUNC_MOVE                           = (1 << 2),
     70 	MWM_FUNC_MINIMIZE                       = (1 << 3),
     71 	MWM_FUNC_MAXIMIZE                       = (1 << 4),
     72 	MWM_FUNC_CLOSE                          = (1 << 5),
     73 
     74 	/* bit definitions for MwmHints.decorations */
     75 	MWM_DECOR_ALL                           = (1 << 0),
     76 	MWM_DECOR_BORDER                        = (1 << 1),
     77 	MWM_DECOR_RESIZEH                       = (1 << 2),
     78 	MWM_DECOR_TITLE                         = (1 << 3),
     79 	MWM_DECOR_MENU                          = (1 << 4),
     80 	MWM_DECOR_MINIMIZE                      = (1 << 5),
     81 	MWM_DECOR_MAXIMIZE                      = (1 << 6),
     82 
     83 	/* values for MwmHints.input_mode */
     84 	MWM_INPUT_MODELESS                      = 0,
     85 	MWM_INPUT_PRIMARY_APPLICATION_MODAL     = 1,
     86 	MWM_INPUT_SYSTEM_MODAL                  = 2,
     87 	MWM_INPUT_FULL_APPLICATION_MODAL        = 3,
     88 
     89 	/* bit definitions for MwmHints.status */
     90 	MWM_TEAROFF_WINDOW                      = (1 << 0),
     91 };
     92 
     93 /* GNUstep constants, mostly unused */
     94 enum {
     95 	/*
     96 	 * Constants copied from src/GNUstep.h on window-maker's source code.
     97 	 */
     98 
     99 	PROP_GNU_HINTS_ELEMENTS                 = 9,
    100 
    101 	/* flags */
    102 	GNU_FLAG_WINDOWSTYLE    = (1<<0),
    103 	GNU_FLAG_WINDOWLEVEL    = (1<<1),
    104 
    105 	/* window levels */
    106 	GNU_LEVEL_DESKTOP       = -1000,
    107 	GNU_LEVEL_NORMAL        = 0,
    108 	GNU_LEVEL_FLOATING      = 3,
    109 	GNU_LEVEL_SUBMENU       = 3,
    110 	GNU_LEVEL_TORNOFF       = 3,
    111 	GNU_LEVEL_MAINMENU      = 20,
    112 	GNU_LEVEL_DOCK          = 21,
    113 	GNU_LEVEL_STATUS        = 21,
    114 	GNU_LEVEL_PANEL         = 100,
    115 	GNU_LEVEL_POPUP         = 101,
    116 	GNU_LEVEL_SCREENSAVER   = 1000,
    117 
    118 	/* window style */
    119 	GNU_STYLE_TITLED        = 1,
    120 	GNU_STYLE_CLOSABLE      = 2,
    121 	GNU_STYLE_MINIMIZABLE   = 4,
    122 	GNU_STYLE_RESIZEABLE    = 8,
    123 	GNU_STYLE_ICON          = 64,
    124 	GNU_STYLE_MINIWINDOW    = 128,
    125 };
    126 
    127 /* motif window manager (Mwm) hints */
    128 struct MwmHints {
    129 	unsigned long flags;
    130 	unsigned long functions;
    131 	unsigned long decorations;
    132 	long          inputMode;
    133 	unsigned long status;
    134 };
    135 
    136 /* GNUstep window manager hints */
    137 struct GNUHints {
    138 	unsigned long flags;
    139 	unsigned long window_style;
    140 	unsigned long window_level;
    141 	unsigned long reserved;
    142 	unsigned long miniaturize_pixmap;       /* pixmap for miniaturize button */
    143 	unsigned long close_pixmap;             /* pixmap for close button */
    144 	unsigned long miniaturize_mask;         /* miniaturize pixmap mask */
    145 	unsigned long close_mask;               /* close pixmap mask */
    146 	unsigned long extra_flags;
    147 };
    148 
    149 void (*managefuncs[TYPE_LAST])(struct Tab *, struct Monitor *, int, Window, Window, XRectangle, int, int) = {
    150 	[TYPE_NOTIFICATION] = managenotif,
    151 	[TYPE_DOCKAPP] = managedockapp,
    152 	[TYPE_NORMAL] = managecontainer,
    153 	[TYPE_DIALOG] = managedialog,
    154 	[TYPE_SPLASH] = managesplash,
    155 	[TYPE_PROMPT] = manageprompt,
    156 	[TYPE_MENU] = managemenu,
    157 	[TYPE_DOCK] = managebar,
    158 };
    159 
    160 int (*unmanagefuncs[TYPE_LAST])(struct Object *, int) = {
    161 	[TYPE_NOTIFICATION] = unmanagenotif,
    162 	[TYPE_DOCKAPP] = unmanagedockapp,
    163 	[TYPE_NORMAL] = unmanagecontainer,
    164 	[TYPE_DIALOG] = unmanagedialog,
    165 	[TYPE_SPLASH] = unmanagesplash,
    166 	[TYPE_PROMPT] = unmanageprompt,
    167 	[TYPE_MENU] = unmanagemenu,
    168 	[TYPE_DOCK] = unmanagebar,
    169 };
    170 
    171 #define GETMANAGED(head, p, w)                                  \
    172 	TAILQ_FOREACH(p, &(head), entry)                        \
    173 		if (p->win == w)                                \
    174 			return (p);                             \
    175 
    176 /* check whether window was placed by the user */
    177 static int
    178 isuserplaced(Window win)
    179 {
    180 	XSizeHints size;
    181 	long dl;
    182 
    183 	return (XGetWMNormalHints(dpy, win, &size, &dl) && (size.flags & USPosition));
    184 }
    185 
    186 /* check if desktop is visible */
    187 static int
    188 deskisvisible(struct Monitor *mon, int desk)
    189 {
    190 	return mon->seldesk == desk;
    191 }
    192 
    193 /* get pointer to managed object given a window */
    194 static struct Object *
    195 getmanaged(Window win)
    196 {
    197 	struct Object *p, *tab, *dial, *menu;
    198 	struct Container *c;
    199 	struct Column *col;
    200 	struct Row *row;
    201 	int i;
    202 
    203 	TAILQ_FOREACH(c, &wm.focusq, entry) {
    204 		if (c->frame == win)
    205 			return (struct Object *)c->selcol->selrow->seltab;
    206 		for (i = 0; i < BORDER_LAST; i++)
    207 			if (c->curswin[i] == win)
    208 				return (struct Object *)c->selcol->selrow->seltab;
    209 		TAILQ_FOREACH(col, &(c)->colq, entry) {
    210 			TAILQ_FOREACH(row, &col->rowq, entry) {
    211 				if (row->bar == win || row->bl == win || row->br == win)
    212 					return (struct Object *)row->seltab;
    213 				TAILQ_FOREACH(tab, &row->tabq, entry) {
    214 					if (tab->win == win ||
    215 					    ((struct Tab *)tab)->frame == win ||
    216 					    ((struct Tab *)tab)->title == win)
    217 						return tab;
    218 					TAILQ_FOREACH(dial, &((struct Tab *)tab)->dialq, entry) {
    219 						if (dial->win == win ||
    220 						    ((struct Dialog *)dial)->frame == win)
    221 							return dial;
    222 						}
    223 				}
    224 			}
    225 		}
    226 	}
    227 	GETMANAGED(dock.dappq, p, win)
    228 	GETMANAGED(wm.barq, p, win)
    229 	GETMANAGED(wm.notifq, p, win)
    230 	TAILQ_FOREACH(p, &wm.splashq, entry)
    231 		if (p->win == win || ((struct Splash *)p)->frame == win)
    232 			return p;
    233 	TAILQ_FOREACH(menu, &wm.menuq, entry) {
    234 		if (menu->win == win ||
    235 		    ((struct Menu *)menu)->frame == win ||
    236 		    ((struct Menu *)menu)->button == win ||
    237 		    ((struct Menu *)menu)->titlebar == win)
    238 			return menu;
    239 	}
    240 	return NULL;
    241 }
    242 
    243 /* check whether given state matches modifier */
    244 static int
    245 isvalidstate(unsigned int state)
    246 {
    247 	return config.modifier != 0 && (state & config.modifier) == config.modifier;
    248 }
    249 
    250 /* get tab given window is a dialog for */
    251 static struct Tab *
    252 getdialogfor(Window win)
    253 {
    254 	struct Object *obj;
    255 	Window tmpwin;
    256 
    257 	if (XGetTransientForHint(dpy, win, &tmpwin))
    258 		if ((obj = getmanaged(tmpwin)) != NULL && obj->type == TYPE_NORMAL)
    259 			return (struct Tab *)obj;
    260 	return NULL;
    261 }
    262 
    263 /* get tab equal to leader or having leader as group leader */
    264 static struct Tab *
    265 getleaderof(Window leader)
    266 {
    267 	struct Container *c;
    268 	struct Object *tab;
    269 
    270 	TAILQ_FOREACH(c, &wm.focusq, entry) {
    271 		TAB_FOREACH_BEGIN(c, tab){
    272 			if (leader != None && (tab->win == leader || ((struct Tab *)tab)->leader == leader))
    273 				return (struct Tab *)tab;
    274 		}TAB_FOREACH_END
    275 	}
    276 	return NULL;
    277 }
    278 
    279 /* get bitmask of container state from given window */
    280 static int
    281 getwinstate(Window win)
    282 {
    283 	int state;
    284 	unsigned long i, nstates;
    285 	unsigned char *list;
    286 	unsigned long dl;   /* dummy variable */
    287 	int di;             /* dummy variable */
    288 	Atom da;            /* dummy variable */
    289 	Atom *as;
    290 
    291 	list = NULL;
    292 	state = 0;
    293 	if (XGetWindowProperty(dpy, win, atoms[_NET_WM_STATE], 0L, 1024, False, XA_ATOM, &da, &di, &nstates, &dl, &list) == Success && list != NULL) {
    294 		as = (Atom *)list;
    295 		for (i = 0; i < nstates; i++) {
    296 			if (as[i] == atoms[_NET_WM_STATE_STICKY]) {
    297 				state |= STICKY;
    298 			} else if (as[i] == atoms[_NET_WM_STATE_MAXIMIZED_VERT]) {
    299 				state |= MAXIMIZED;
    300 			} else if (as[i] == atoms[_NET_WM_STATE_MAXIMIZED_HORZ]) {
    301 				state |= MAXIMIZED;
    302 			} else if (as[i] == atoms[_NET_WM_STATE_HIDDEN]) {
    303 				state |= MINIMIZED;
    304 			} else if (as[i] == atoms[_NET_WM_STATE_SHADED]) {
    305 				state |= SHADED;
    306 			} else if (as[i] == atoms[_NET_WM_STATE_FULLSCREEN]) {
    307 				state |= FULLSCREEN;
    308 			} else if (as[i] == atoms[_NET_WM_STATE_ABOVE]) {
    309 				state |= ABOVE;
    310 			} else if (as[i] == atoms[_NET_WM_STATE_BELOW]) {
    311 				state |= BELOW;
    312 			}
    313 		}
    314 	}
    315 	if (isuserplaced(win))
    316 		state |= USERPLACED;
    317 	XFree(list);
    318 	return state;
    319 }
    320 
    321 /* get window's WM_STATE property */
    322 static long
    323 getstate(Window w)
    324 {
    325 	long result = -1;
    326 	unsigned char *p = NULL;
    327 	unsigned long n, extra;
    328 	Atom da;
    329 	int di;
    330 
    331 	if (XGetWindowProperty(dpy, w, atoms[WM_STATE], 0L, 2L, False, atoms[WM_STATE],
    332 		&da, &di, &n, &extra, (unsigned char **)&p) != Success)
    333 		return -1;
    334 	if (n != 0)
    335 		result = *p;
    336 	XFree(p);
    337 	return result;
    338 }
    339 
    340 static char *
    341 gettextprop(Window win, Atom atom)
    342 {
    343 	XTextProperty tprop = { .value = NULL };
    344 	int count;
    345 	char **list = NULL;
    346 	char *s = NULL;
    347 
    348 	if (!XGetTextProperty(dpy, win, &tprop, atom))
    349 		goto error;
    350 	if (tprop.nitems == 0)
    351 		goto error;
    352 	if (XmbTextPropertyToTextList(dpy, &tprop, &list, &count) != Success)
    353 		goto error;
    354 	if (count < 1 || list == NULL || *list == NULL)
    355 		goto error;
    356 	s = strdup(list[0]);
    357 error:
    358 	XFreeStringList(list);
    359 	XFree(tprop.value);
    360 	return s;
    361 }
    362 
    363 /* get motif/GNUstep hints from window; return -1 on error */
    364 static int
    365 getextrahints(Window win, Atom prop, unsigned long nmemb, size_t size, void *hints)
    366 {
    367 
    368 	unsigned long dl;
    369 	Atom type;
    370 	int di;
    371 	int status, ret;
    372 	unsigned char *p;
    373 
    374 	status = XGetWindowProperty(
    375 		dpy, win,
    376 		prop,
    377 		0L, nmemb,
    378 		False,
    379 		prop,
    380 		&type, &di, &dl, &dl,
    381 		&p
    382 	);
    383 	ret = -1;
    384 	if (status == Success && p != NULL) {
    385 		memcpy(hints, p, size);
    386 		ret = 0;
    387 	}
    388 	XFree(p);
    389 	return ret;
    390 }
    391 
    392 #define STRCMP(a, b) ((a) != NULL && (b) != NULL && strcmp((a), (b)) == 0)
    393 
    394 /* get window info based on its type */
    395 int
    396 getwintype(Window *win_ret, Window *leader, struct Tab **tab, int *state, XRectangle *rect, int *desk)
    397 {
    398 	/* rules for identifying windows */
    399 	enum { I_APP, I_CLASS, I_INSTANCE, I_ROLE, I_RESOURCE, I_NULL, I_LAST };
    400 	XrmClass class[I_LAST];
    401 	XrmName name[I_LAST];
    402 	struct MwmHints mwmhints = { 0 };
    403 	struct GNUHints gnuhints = { 0 };
    404 	XClassHint classh = { .res_class = NULL, .res_name = NULL };
    405 	XWMHints *wmhints;
    406 	Window win;
    407 	Atom prop;
    408 	size_t i;
    409 	long n;
    410 	int type, isdockapp, pos;
    411 	char *role, *value;
    412 
    413 	pos = 0;
    414 	win = *win_ret;
    415 	*tab = NULL;
    416 	*state = 0;
    417 	type = TYPE_UNKNOWN;
    418 	classh.res_class = NULL;
    419 	classh.res_name = NULL;
    420 
    421 	*state = getwinstate(win);
    422 
    423 	/* get window type (and other info) from default (hardcoded) rules */
    424 	role = gettextprop(win, atoms[WM_WINDOW_ROLE]);
    425 	XGetClassHint(dpy, win, &classh);
    426 	for (i = 0; config.rules[i].class != NULL || config.rules[i].instance != NULL || config.rules[i].role != NULL; i++) {
    427 		if ((config.rules[i].class == NULL    || STRCMP(config.rules[i].class, classh.res_class))
    428 		&&  (config.rules[i].instance == NULL || STRCMP(config.rules[i].instance, classh.res_name))
    429 		&&  (config.rules[i].role == NULL     || STRCMP(config.rules[i].role, role))) {
    430 			if (config.rules[i].type != TYPE_MENU && config.rules[i].type != TYPE_DIALOG) {
    431 				type = config.rules[i].type;
    432 			}
    433 			if (config.rules[i].state >= 0) {
    434 				*state = config.rules[i].state;
    435 			}
    436 			if (config.rules[i].desktop > 0 && config.rules[i].desktop <= config.ndesktops) {
    437 				*desk = config.rules[i].desktop - 1;
    438 			}
    439 		}
    440 	}
    441 
    442 	/* convert strings to quarks for xrm */
    443 	class[I_NULL] = name[I_NULL] = NULLQUARK;
    444 	class[I_APP] = wm.application.class;
    445 	name[I_APP] = wm.application.name;
    446 	if (classh.res_class != NULL)
    447 		class[I_CLASS] = name[I_CLASS] = XrmStringToQuark(classh.res_class);
    448 	else
    449 		class[I_CLASS] = name[I_CLASS] = wm.anyresource;
    450 	if (classh.res_name != NULL)
    451 		class[I_INSTANCE] = name[I_INSTANCE] = XrmStringToQuark(classh.res_name);
    452 	else
    453 		class[I_INSTANCE] = name[I_INSTANCE] = wm.anyresource;
    454 	if (role != NULL)
    455 		class[I_ROLE] = name[I_ROLE] = XrmStringToQuark(role);
    456 	else
    457 		class[I_ROLE] = name[I_ROLE] = wm.anyresource;
    458 	free(role);
    459 	XFree(classh.res_class);
    460 	XFree(classh.res_name);
    461 
    462 	/* get window type from X resources */
    463 	class[I_RESOURCE] = wm.resources[RES_TYPE].class;
    464 	name[I_RESOURCE] = wm.resources[RES_TYPE].name;
    465 	if ((value = getresource(xdb, class, name)) != NULL) {
    466 		if (strcasecmp(value, "DESKTOP") == 0) {
    467 			type = TYPE_DESKTOP;
    468 		} else if (strcasecmp(value, "DOCKAPP") == 0) {
    469 			type = TYPE_DOCKAPP;
    470 		} else if (strcasecmp(value, "PROMPT") == 0) {
    471 			type = TYPE_PROMPT;
    472 		} else if (strcasecmp(value, "NORMAL") == 0) {
    473 			type = TYPE_NORMAL;
    474 		}
    475 	}
    476 
    477 	/* get window state from X resources */
    478 	class[I_RESOURCE] = wm.resources[RES_STATE].class;
    479 	name[I_RESOURCE] = wm.resources[RES_STATE].name;
    480 	if ((value = getresource(xdb, class, name)) != NULL) {
    481 		*state = 0;
    482 		if (strcasestr(value, "above") != NULL) {
    483 			*state |= ABOVE;
    484 		}
    485 		if (strcasestr(value, "below") != NULL) {
    486 			*state |= BELOW;
    487 		}
    488 		if (strcasestr(value, "fullscreen") != NULL) {
    489 			*state |= FULLSCREEN;
    490 		}
    491 		if (strcasestr(value, "maximized") != NULL) {
    492 			*state |= MAXIMIZED;
    493 		}
    494 		if (strcasestr(value, "minimized") != NULL) {
    495 			*state |= MINIMIZED;
    496 		}
    497 		if (strcasestr(value, "shaded") != NULL) {
    498 			*state |= SHADED;
    499 		}
    500 		if (strcasestr(value, "sticky") != NULL) {
    501 			*state |= STICKY;
    502 		}
    503 		if (strcasestr(value, "extend") != NULL) {
    504 			*state |= EXTEND;
    505 		}
    506 		if (strcasestr(value, "shrunk") != NULL) {
    507 			*state |= SHRUNK;
    508 		}
    509 		if (strcasestr(value, "resized") != NULL) {
    510 			*state |= RESIZED;
    511 		}
    512 	}
    513 
    514 	/* get dockapp position from X resources */
    515 	class[I_RESOURCE] = wm.resources[RES_DOCK_POS].class;
    516 	name[I_RESOURCE] = wm.resources[RES_DOCK_POS].name;
    517 	if ((value = getresource(xdb, class, name)) != NULL) {
    518 		if ((n = strtol(value, NULL, 10)) >= 0 && n < INT_MAX) {
    519 			pos = n;
    520 		}
    521 	}
    522 
    523 	/* get desktop id from X resources */
    524 	class[I_RESOURCE] = wm.resources[RES_DESKTOP].class;
    525 	name[I_RESOURCE] = wm.resources[RES_DESKTOP].name;
    526 	if ((value = getresource(xdb, class, name)) != NULL) {
    527 		if ((n = strtol(value, NULL, 10)) > 0 && n <= config.ndesktops) {
    528 			*desk = n - 1;
    529 		}
    530 	}
    531 
    532 	/* we already got the type of the window, return */
    533 	if (type != TYPE_UNKNOWN)
    534 		goto done;
    535 
    536 	/* try to guess window type */
    537 	prop = getatomprop(win, atoms[_NET_WM_WINDOW_TYPE]);
    538 	wmhints = XGetWMHints(dpy, win);
    539 	getextrahints(win, atoms[_MOTIF_WM_HINTS], PROP_MWM_HINTS_ELEMENTS, sizeof(mwmhints), &mwmhints);
    540 	getextrahints(win, atoms[_GNUSTEP_WM_ATTR], PROP_GNU_HINTS_ELEMENTS, sizeof(gnuhints), &gnuhints);
    541 	isdockapp = (wmhints && (wmhints->flags & (IconWindowHint | StateHint)) && wmhints->initial_state == WithdrawnState);
    542 	if (isdockapp && wmhints->icon_window != None)
    543 		*win_ret = wmhints->icon_window;
    544 	*leader = getwinprop(win, atoms[WM_CLIENT_LEADER]);
    545 	if (*leader == None)
    546 		*leader = (wmhints != NULL && (wmhints->flags & WindowGroupHint)) ? wmhints->window_group : None;
    547 	*tab = getdialogfor(win);
    548 	XFree(wmhints);
    549 	if (!(gnuhints.flags & GNU_FLAG_WINDOWSTYLE))
    550 		gnuhints.window_style = 0;
    551 	if (!(gnuhints.flags & GNU_FLAG_WINDOWLEVEL))
    552 		gnuhints.window_level = 0;
    553 	if (isdockapp ||
    554 	    gnuhints.window_style == GNU_STYLE_ICON ||
    555 	    gnuhints.window_style == GNU_STYLE_MINIWINDOW) {
    556 		type = TYPE_DOCKAPP;
    557 	} else if (prop == atoms[_NET_WM_WINDOW_TYPE_DESKTOP]) {
    558 		type = TYPE_DESKTOP;
    559 	} else if (prop == atoms[_NET_WM_WINDOW_TYPE_NOTIFICATION]) {
    560 		type = TYPE_NOTIFICATION;
    561 	} else if (prop == atoms[_NET_WM_WINDOW_TYPE_PROMPT]) {
    562 		type = TYPE_PROMPT;
    563 	} else if (prop == atoms[_NET_WM_WINDOW_TYPE_SPLASH]) {
    564 		type = TYPE_SPLASH;
    565 	} else if (gnuhints.window_level == GNU_LEVEL_POPUP) {
    566 		type = TYPE_POPUP;
    567 	} else if (prop == atoms[_NET_WM_WINDOW_TYPE_MENU] ||
    568 	           prop == atoms[_NET_WM_WINDOW_TYPE_UTILITY] ||
    569 	           prop == atoms[_NET_WM_WINDOW_TYPE_TOOLBAR] ||
    570 	           gnuhints.window_level == GNU_LEVEL_PANEL ||
    571 	           gnuhints.window_level == GNU_LEVEL_SUBMENU ||
    572                    gnuhints.window_level == GNU_LEVEL_MAINMENU ||
    573                    ((mwmhints.flags & MWM_HINTS_STATUS) &&
    574                     (mwmhints.status & MWM_TEAROFF_WINDOW))) {
    575 		if (*tab != NULL)
    576 			*leader = (*tab)->obj.win;
    577 		type = TYPE_MENU;
    578 	} else if (prop == atoms[_NET_WM_WINDOW_TYPE_DOCK]) {
    579 		type = TYPE_DOCK;
    580 	} else if (*tab != NULL) {
    581 		if (*tab != NULL)
    582 			*leader = (*tab)->obj.win;
    583 		type = config.floatdialog ? TYPE_MENU : TYPE_DIALOG;
    584 	} else {
    585 		if (prop != atoms[_NET_WM_WINDOW_TYPE_MENU] &&
    586 		    prop != atoms[_NET_WM_WINDOW_TYPE_DIALOG] &&
    587 		    prop != atoms[_NET_WM_WINDOW_TYPE_UTILITY] &&
    588 		    prop != atoms[_NET_WM_WINDOW_TYPE_TOOLBAR]) {
    589 			*tab = getleaderof(*leader);
    590 		}
    591 		type = TYPE_NORMAL;
    592 	}
    593 
    594 done:
    595 	if (type == TYPE_DOCKAPP)
    596 		rect->x = rect->y = pos;
    597 	return type;
    598 }
    599 
    600 /* select window input events, grab mouse button presses, and clear its border */
    601 static void
    602 preparewin(Window win)
    603 {
    604 	XSelectInput(dpy, win, StructureNotifyMask | PropertyChangeMask | FocusChangeMask);
    605 	XGrabButton(dpy, AnyButton, AnyModifier, win, False, ButtonPressMask,
    606 	            GrabModeSync, GrabModeSync, None, None);
    607 	XSetWindowBorderWidth(dpy, win, 0);
    608 }
    609 
    610 /* check whether window is urgent */
    611 static int
    612 getwinurgency(Window win)
    613 {
    614 	XWMHints *wmh;
    615 	int ret;
    616 
    617 	ret = 0;
    618 	if ((wmh = XGetWMHints(dpy, win)) != NULL) {
    619 		ret = wmh->flags & XUrgencyHint;
    620 		XFree(wmh);
    621 	}
    622 	return ret;
    623 }
    624 
    625 /* get row or column next to division the pointer is on */
    626 static void
    627 getdivisions(struct Container *c, struct Column **cdiv, struct Row **rdiv, int x, int y)
    628 {
    629 	struct Column *col;
    630 	struct Row *row;
    631 
    632 	*cdiv = NULL;
    633 	*rdiv = NULL;
    634 	TAILQ_FOREACH(col, &c->colq, entry) {
    635 		if (TAILQ_NEXT(col, entry) != NULL && x >= col->x + col->w && x < col->x + col->w + config.divwidth) {
    636 			*cdiv = col;
    637 			return;
    638 		}
    639 		if (x >= col->x && x < col->x + col->w) {
    640 			TAILQ_FOREACH(row, &col->rowq, entry) {
    641 				if (TAILQ_NEXT(row, entry) != NULL && y >= row->y + row->h && y < row->y + row->h + config.divwidth) {
    642 					*rdiv = row;
    643 					return;
    644 				}
    645 			}
    646 		}
    647 	}
    648 }
    649 
    650 /* get frame handle (NW/N/NE/W/E/SW/S/SE) the pointer is on */
    651 static enum Octant
    652 getframehandle(int w, int h, int x, int y)
    653 {
    654 	if ((y < config.borderwidth && x <= config.corner) || (x < config.borderwidth && y <= config.corner))
    655 		return NW;
    656 	else if ((y < config.borderwidth && x >= w - config.corner) || (x > w - config.borderwidth && y <= config.corner))
    657 	      return NE;
    658 	else if ((y > h - config.borderwidth && x <= config.corner) || (x < config.borderwidth && y >= h - config.corner))
    659 	      return SW;
    660 	else if ((y > h - config.borderwidth && x >= w - config.corner) || (x > w - config.borderwidth && y >= h - config.corner))
    661 	      return SE;
    662 	else if (y < config.borderwidth)
    663 	      return N;
    664 	else if (y >= h - config.borderwidth)
    665 	      return S;
    666 	else if (x < config.borderwidth)
    667 	      return W;
    668 	else if (x >= w - config.borderwidth)
    669 	      return E;
    670 	return C;
    671 }
    672 
    673 /* get quadrant (NW/NE/SW/SE) the pointer is on */
    674 static enum Octant
    675 getquadrant(int w, int h, int x, int y)
    676 {
    677 	if (x >= w / 2 && y >= h / 2)
    678 		return SE;
    679 	if (x >= w / 2 && y <= h / 2)
    680 		return NE;
    681 	if (x <= w / 2 && y >= h / 2)
    682 		return SW;
    683 	if (x <= w / 2 && y <= h / 2)
    684 		return NW;
    685 	return C;
    686 }
    687 
    688 /* (un)show desktop */
    689 static void
    690 deskshow(int show)
    691 {
    692 	struct Object *obj;
    693 	struct Container *c;
    694 
    695 	TAILQ_FOREACH(c, &wm.focusq, entry)
    696 		if (!c->isminimized)
    697 			containerhide(c, show);
    698 	TAILQ_FOREACH(obj, &wm.splashq, entry)
    699 		splashhide((struct Splash *)obj, show);
    700 	wm.showingdesk = show;
    701 	ewmhsetshowingdesktop(show);
    702 	menuupdate();
    703 }
    704 
    705 /* update desktop */
    706 void
    707 deskupdate(struct Monitor *mon, int desk)
    708 {
    709 	struct Object *obj;
    710 	struct Splash *splash;
    711 	struct Container *c;
    712 
    713 	if (desk < 0 || desk >= config.ndesktops || (mon == wm.selmon && desk == wm.selmon->seldesk))
    714 		return;
    715 	if (wm.showingdesk)
    716 		deskshow(0);
    717 	if (!deskisvisible(mon, desk)) {
    718 		/* unhide cointainers of new current desktop
    719 		 * hide containers of previous current desktop */
    720 		TAILQ_FOREACH(c, &wm.focusq, entry) {
    721 			if (c->mon != mon)
    722 				continue;
    723 			if (!c->isminimized && c->desk == desk) {
    724 				containerhide(c, REMOVE);
    725 			} else if (!c->issticky && c->desk == mon->seldesk) {
    726 				containerhide(c, ADD);
    727 			}
    728 		}
    729 		TAILQ_FOREACH(obj, &wm.splashq, entry) {
    730 			splash = (struct Splash *)obj;
    731 			if (splash->mon != mon)
    732 				continue;
    733 			if (splash->desk == desk) {
    734 				splashhide(splash, REMOVE);
    735 			} else if (splash->desk == mon->seldesk) {
    736 				splashhide(splash, ADD);
    737 			}
    738 		}
    739 	}
    740 	wm.selmon = mon;
    741 	wm.selmon->seldesk = desk;
    742 	ewmhsetcurrentdesktop(desk);
    743 }
    744 
    745 /* change desktop */
    746 static void
    747 deskfocus(struct Monitor *mon, int desk)
    748 {
    749 	struct Container *c;
    750 
    751 	if (desk < 0 || desk >= config.ndesktops || (mon == wm.selmon && desk == wm.selmon->seldesk))
    752 		return;
    753 	deskupdate(mon, desk);
    754 	c = getnextfocused(mon, desk);
    755 	if (c != NULL) {
    756 		tabfocus(c->selcol->selrow->seltab, 0);
    757 	} else {
    758 		tabfocus(NULL, 0);
    759 	}
    760 }
    761 
    762 /* call one of the manage- functions */
    763 static void
    764 manage(Window win, XRectangle rect, int ignoreunmap)
    765 {
    766 	struct Tab *tab;
    767 	Window leader;
    768 	int state, type;
    769 	int desk = wm.selmon->seldesk;
    770 
    771 	if (getmanaged(win) != NULL)
    772 		return;
    773 	type = getwintype(&win, &leader, &tab, &state, &rect, &desk);
    774 	if (type == TYPE_DESKTOP || type == TYPE_POPUP) {
    775 		/* we do not handle desktop windows */
    776 		if (type == TYPE_DESKTOP)
    777 			XLowerWindow(dpy, win);
    778 		else if (type == TYPE_POPUP)
    779 			XRaiseWindow(dpy, win);
    780 		XMapWindow(dpy, win);
    781 		return;
    782 	}
    783 	preparewin(win);
    784 	(*managefuncs[type])(tab, wm.selmon, desk, win, leader, rect, state, ignoreunmap);
    785 }
    786 
    787 /* perform container switching (aka alt-tab) */
    788 static void
    789 alttab(int shift)
    790 {
    791 	struct Container *c, *prevfocused;
    792 	XEvent ev;
    793 
    794 	prevfocused = wm.focused;
    795 	if ((c = TAILQ_FIRST(&wm.focusq)) == NULL)
    796 		return;
    797 	if (XGrabKeyboard(dpy, root, True, GrabModeAsync, GrabModeAsync, CurrentTime) != GrabSuccess)
    798 		goto done;
    799 	if (XGrabPointer(dpy, root, False, 0, GrabModeAsync, GrabModeAsync, None, None, CurrentTime) != GrabSuccess)
    800 		goto done;
    801 	containerbacktoplace(c, 0);
    802 	c = containerraisetemp(c, shift);
    803 	while (!XMaskEvent(dpy, ALTTABMASK, &ev)) {
    804 		switch (ev.type) {
    805 		case KeyPress:
    806 			if (ev.xkey.keycode == config.tabkeycode && isvalidstate(ev.xkey.state)) {
    807 				containerbacktoplace(c, 1);
    808 				c = containerraisetemp(c, (ev.xkey.state & ShiftMask));
    809 			}
    810 			break;
    811 		case KeyRelease:
    812 			if (ev.xkey.keycode == config.altkeycode)
    813 				goto done;
    814 			break;
    815 		}
    816 	}
    817 done:
    818 	XUngrabKeyboard(dpy, CurrentTime);
    819 	XUngrabPointer(dpy, CurrentTime);
    820 	if (c == NULL)
    821 		return;
    822 	containerbacktoplace(c, 1);
    823 	wm.focused = prevfocused;
    824 	tabfocus(c->selcol->selrow->seltab, 0);
    825 	containerraise(c, c->isfullscreen, c->abovebelow);
    826 }
    827 
    828 /* detach tab from window with mouse */
    829 static void
    830 mouseretab(struct Tab *tab, int xroot, int yroot, int x, int y)
    831 {
    832 	struct Monitor *mon;
    833 	struct Object *obj;
    834 	struct Row *row;        /* row to be deleted, if emptied */
    835 	struct Container *c;
    836 	Window win;
    837 	XEvent ev;
    838 
    839 	row = tab->row;
    840 	c = row->col->c;
    841 	if (XGrabPointer(dpy, root, False, ButtonReleaseMask | PointerMotionMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime) != GrabSuccess)
    842 		goto error;
    843 	tabdetach(tab, xroot - x, yroot - y);
    844 	containermoveresize(c, 0);
    845 	XUnmapWindow(dpy, tab->title);
    846 	XMoveWindow(
    847 		dpy, wm.dragwin,
    848 		xroot - DNDDIFF - (2 * config.borderwidth + config.titlewidth),
    849 		yroot - DNDDIFF - (2 * config.borderwidth + config.titlewidth)
    850 	);
    851 	XRaiseWindow(dpy, wm.dragwin);
    852 	while (!XMaskEvent(dpy, MOUSEEVENTMASK, &ev)) {
    853 		switch (ev.type) {
    854 		case MotionNotify:
    855 			XMoveWindow(
    856 				dpy, wm.dragwin,
    857 				ev.xmotion.x_root - DNDDIFF - (2 * config.borderwidth + config.titlewidth),
    858 				ev.xmotion.y_root - DNDDIFF - (2 * config.borderwidth + config.titlewidth)
    859 			);
    860 			break;
    861 		case ButtonRelease:
    862 			goto done;
    863 		}
    864 	}
    865 done:
    866 	XMoveWindow(
    867 		dpy, wm.dragwin,
    868 		- (2 * config.borderwidth + config.titlewidth),
    869 		- (2 * config.borderwidth + config.titlewidth)
    870 	);
    871 	xroot = ev.xbutton.x_root - x;
    872 	yroot = ev.xbutton.y_root - y;
    873 	obj = getmanaged(ev.xbutton.subwindow);
    874 	c = NULL;
    875 	if (obj != NULL && obj->type == TYPE_NORMAL && ev.xbutton.subwindow == ((struct Tab *)obj)->row->col->c->frame) {
    876 		c = ((struct Tab *)obj)->row->col->c;
    877 		XTranslateCoordinates(dpy, ev.xbutton.window, c->frame, ev.xbutton.x_root, ev.xbutton.y_root, &x, &y, &win);
    878 	}
    879 	if (row->col->c != c) {
    880 		XUnmapWindow(dpy, tab->frame);
    881 		XReparentWindow(dpy, tab->frame, root, x, y);
    882 	}
    883 	if (!tabattach(c, tab, x, y)) {
    884 		mon = getmon(x, y);
    885 		if (mon == NULL)
    886 			mon = wm.selmon;
    887 		containernewwithtab(
    888 			tab, mon, mon->seldesk,
    889 			(XRectangle){
    890 				.x = xroot - config.titlewidth,
    891 				.y = yroot,
    892 				.width = tab->winw,
    893 				.height = tab->winh,
    894 			},
    895 			USERPLACED
    896 		);
    897 	}
    898 	containerdelrow(row);
    899 	ewmhsetactivewindow(tab->obj.win);
    900 error:
    901 	XUngrabPointer(dpy, CurrentTime);
    902 }
    903 
    904 /* resize container with mouse */
    905 static void
    906 mouseresize(int type, void *obj, int xroot, int yroot, enum Octant o)
    907 {
    908 	struct Container *c;
    909 	struct Menu *menu;
    910 	Window frame;
    911 	Cursor curs;
    912 	XEvent ev;
    913 	Time lasttime;
    914 	int *nx, *ny, *nw, *nh;
    915 	int x, y, dx, dy;
    916 
    917 	if (type == FLOAT_MENU) {
    918 		menu = (struct Menu *)obj;
    919 		nx = &menu->x;
    920 		ny = &menu->y;
    921 		nw = &menu->w;
    922 		nh = &menu->h;
    923 		frame = menu->frame;
    924 		menudecorate(menu, o != C);
    925 	} else {
    926 		c = (struct Container *)obj;
    927 		if (c->isfullscreen || c->b == 0)
    928 			return;
    929 		if (containerisshaded(c)) {
    930 			if (o & W) {
    931 				o = W;
    932 			} else if (o & E) {
    933 				o = E;
    934 			} else {
    935 				return;
    936 			}
    937 		}
    938 		nx = &c->nx;
    939 		ny = &c->ny;
    940 		nw = &c->nw;
    941 		nh = &c->nh;
    942 		frame = c->frame;
    943 		containerdecorate(c, NULL, NULL, 0, o);
    944 	}
    945 	switch (o) {
    946 	case NW:
    947 		curs = wm.cursors[CURSOR_NW];
    948 		break;
    949 	case NE:
    950 		curs = wm.cursors[CURSOR_NE];
    951 		break;
    952 	case SW:
    953 		curs = wm.cursors[CURSOR_SW];
    954 		break;
    955 	case SE:
    956 		curs = wm.cursors[CURSOR_SE];
    957 		break;
    958 	case N:
    959 		curs = wm.cursors[CURSOR_N];
    960 		break;
    961 	case S:
    962 		curs = wm.cursors[CURSOR_S];
    963 		break;
    964 	case W:
    965 		curs = wm.cursors[CURSOR_W];
    966 		break;
    967 	case E:
    968 		curs = wm.cursors[CURSOR_E];
    969 		break;
    970 	default:
    971 		curs = None;
    972 		break;
    973 	}
    974 	if (o & W)
    975 		x = xroot - *nx - config.borderwidth;
    976 	else if (o & E)
    977 		x = *nx + *nw - config.borderwidth - xroot;
    978 	else
    979 		x = 0;
    980 	if (o & N)
    981 		y = yroot - *ny - config.borderwidth;
    982 	else if (o & S)
    983 		y = *ny + *nh - config.borderwidth - yroot;
    984 	else
    985 		y = 0;
    986 	if (XGrabPointer(dpy, frame, False, ButtonReleaseMask | PointerMotionMask, GrabModeAsync, GrabModeAsync, None, curs, CurrentTime) != GrabSuccess)
    987 		goto done;
    988 	lasttime = 0;
    989 	while (!XMaskEvent(dpy, MOUSEEVENTMASK, &ev)) {
    990 		switch (ev.type) {
    991 		case ButtonRelease:
    992 			goto done;
    993 			break;
    994 		case MotionNotify:
    995 			if (x > *nw)
    996 				x = 0;
    997 			if (y > *nh)
    998 				y = 0;
    999 			if (o & W &&
   1000 			    ((ev.xmotion.x_root < xroot && x > ev.xmotion.x_root - *nx) ||
   1001 			     (ev.xmotion.x_root > xroot && x < ev.xmotion.x_root - *nx))) {
   1002 				dx = xroot - ev.xmotion.x_root;
   1003 				if (*nw + dx >= wm.minsize) {
   1004 					*nx -= dx;
   1005 					*nw += dx;
   1006 				}
   1007 			} else if (o & E &&
   1008 			    ((ev.xmotion.x_root > xroot && x > *nx + *nw - ev.xmotion.x_root) ||
   1009 			     (ev.xmotion.x_root < xroot && x < *nx + *nw - ev.xmotion.x_root))) {
   1010 				dx = ev.xmotion.x_root - xroot;
   1011 				if (*nw + dx >= wm.minsize) {
   1012 					*nw += dx;
   1013 				}
   1014 			}
   1015 			if (o & N &&
   1016 			    ((ev.xmotion.y_root < yroot && y > ev.xmotion.y_root - *ny) ||
   1017 			     (ev.xmotion.y_root > yroot && y < ev.xmotion.y_root - *ny))) {
   1018 				dy = yroot - ev.xmotion.y_root;
   1019 				if (*nh + dy >= wm.minsize) {
   1020 					*ny -= dy;
   1021 					*nh += dy;
   1022 				}
   1023 			} else if (o & S &&
   1024 			    ((ev.xmotion.y_root > yroot && *ny + *nh - ev.xmotion.y_root < y) ||
   1025 			     (ev.xmotion.y_root < yroot && *ny + *nh - ev.xmotion.y_root > y))) {
   1026 				dy = ev.xmotion.y_root - yroot;
   1027 				if (*nh + dy >= wm.minsize) {
   1028 					*nh += dy;
   1029 				}
   1030 			}
   1031 			if (ev.xmotion.time - lasttime > (unsigned)config.resizetime) {
   1032 				if (type == FLOAT_MENU) {
   1033 					menumoveresize(menu);
   1034 					menudecorate(menu, 0);
   1035 				} else {
   1036 					containercalccols(c);
   1037 					containermoveresize(c, 0);
   1038 					containerredecorate(c, NULL, NULL, o);
   1039 				}
   1040 				lasttime = ev.xmotion.time;
   1041 			}
   1042 			xroot = ev.xmotion.x_root;
   1043 			yroot = ev.xmotion.y_root;
   1044 			break;
   1045 		}
   1046 	}
   1047 done:
   1048 	if (type == FLOAT_MENU) {
   1049 		menumoveresize(menu);
   1050 		menudecorate(menu, 0);
   1051 	} else {
   1052 		containercalccols(c);
   1053 		containermoveresize(c, 1);
   1054 		containerdecorate(c, NULL, NULL, 0, 0);
   1055 	}
   1056 	XUngrabPointer(dpy, CurrentTime);
   1057 }
   1058 
   1059 /* move floating entity (container or menu) with mouse */
   1060 static void
   1061 mousemove(Window win, int type, void *p, int xroot, int yroot, enum Octant o)
   1062 {
   1063 	struct Container *c;
   1064 	struct Menu *menu;
   1065 	Window frame;
   1066 	XEvent ev;
   1067 	Time lasttime;
   1068 	int x, y, unmaximize, origyroot;
   1069 
   1070 	origyroot = yroot;
   1071 	unmaximize = 0;
   1072 	if (type == FLOAT_MENU) {
   1073 		menu = (struct Menu *)p;
   1074 		menudecorate(menu, o);
   1075 		frame = menu->frame;
   1076 	} else {
   1077 		c = (struct Container *)p;
   1078 		containerdecorate(c, NULL, NULL, 0, o);
   1079 		frame = c->frame;
   1080 	}
   1081 	lasttime = 0;
   1082 	if (win != None)
   1083 		XDefineCursor(dpy, win, wm.cursors[CURSOR_MOVE]);
   1084 	else if (XGrabPointer(dpy, frame, False, ButtonReleaseMask | PointerMotionMask, GrabModeAsync, GrabModeAsync, None, wm.cursors[CURSOR_MOVE], CurrentTime) != GrabSuccess)
   1085 		goto done;
   1086 	while (!XMaskEvent(dpy, MOUSEEVENTMASK, &ev)) {
   1087 		switch (ev.type) {
   1088 		case ButtonRelease:
   1089 			goto done;
   1090 			break;
   1091 		case MotionNotify:
   1092 			if (ev.xmotion.time - lasttime <= (unsigned)config.movetime)
   1093 				break;
   1094 			x = ev.xmotion.x_root - xroot;
   1095 			y = ev.xmotion.y_root - yroot;
   1096 			if (type == FLOAT_MENU)
   1097 				menuincrmove(menu, x, y);
   1098 			else if (c->ismaximized && ev.xmotion.y_root > 0 && unmaximize) {
   1099 				containersetstate(
   1100 					c->selcol->selrow->seltab,
   1101 					(Atom []){
   1102 						atoms[_NET_WM_STATE_MAXIMIZED_VERT],
   1103 						atoms[_NET_WM_STATE_MAXIMIZED_HORZ],
   1104 					},
   1105 					REMOVE
   1106 				);
   1107 				containermove(
   1108 					c,
   1109 					ev.xmotion.x_root - c->nw / 2,
   1110 					0, 0
   1111 				);
   1112 			} else if (!c->ismaximized && ev.xmotion.y_root <= 0) {
   1113 				containersetstate(
   1114 					c->selcol->selrow->seltab,
   1115 					(Atom []){
   1116 						atoms[_NET_WM_STATE_MAXIMIZED_VERT],
   1117 						atoms[_NET_WM_STATE_MAXIMIZED_HORZ],
   1118 					},
   1119 					ADD
   1120 				);
   1121 			} else if (!c->ismaximized) {
   1122 				containermove(c, x, y, 1);
   1123 			}
   1124 			if (ev.xmotion.y_root < origyroot - config.titlewidth)
   1125 				unmaximize = 1;
   1126 			if (ev.xmotion.y_root > origyroot + config.titlewidth)
   1127 				unmaximize = 1;
   1128 			lasttime = ev.xmotion.time;
   1129 			xroot = ev.xmotion.x_root;
   1130 			yroot = ev.xmotion.y_root;
   1131 			break;
   1132 		}
   1133 	}
   1134 done:
   1135 	if (type == FLOAT_MENU) {
   1136 		menudecorate(menu, 0);
   1137 	} else {
   1138 		containerdecorate(c, NULL, NULL, 0, 0);
   1139 	}
   1140 	if (win != None) {
   1141 		XUndefineCursor(dpy, win);
   1142 	} else {
   1143 		XUngrabPointer(dpy, CurrentTime);
   1144 	}
   1145 }
   1146 
   1147 /* resize tiles by dragging division with mouse */
   1148 static void
   1149 mouseretile(struct Container *c, struct Column *cdiv, struct Row *rdiv, int xprev, int yprev)
   1150 {
   1151 	struct Row *row;
   1152 	XEvent ev;
   1153 	Cursor curs;
   1154 	Time lasttime;
   1155 	double fact;
   1156 	int ignore, len, x, y;
   1157 
   1158 	if (cdiv != NULL && TAILQ_NEXT(cdiv, entry) != NULL)
   1159 		curs = wm.cursors[CURSOR_H];
   1160 	else if (rdiv != NULL && TAILQ_NEXT(rdiv, entry) != NULL)
   1161 		curs = wm.cursors[CURSOR_V];
   1162 	else
   1163 		return;
   1164 	lasttime = 0;
   1165 	containerdecorate(c, cdiv, rdiv, 0, 0);
   1166 	if (XGrabPointer(dpy, c->frame, False, ButtonReleaseMask | PointerMotionMask, GrabModeAsync, GrabModeAsync, None, curs, CurrentTime) != GrabSuccess)
   1167 		goto done;
   1168 	while (!XMaskEvent(dpy, MOUSEEVENTMASK, &ev)) {
   1169 		switch (ev.type) {
   1170 		case ButtonRelease:
   1171 			goto done;
   1172 			break;
   1173 		case MotionNotify:
   1174 			x = ev.xmotion.x - xprev;
   1175 			y = ev.xmotion.y - yprev;
   1176 			ignore = 0;
   1177 			len = 0;
   1178 			if (cdiv != NULL &&
   1179 			    ((x < 0 && ev.xmotion.x < TAILQ_NEXT(cdiv, entry)->x) ||
   1180 			     (x > 0 && ev.xmotion.x > TAILQ_NEXT(cdiv, entry)->x))) {
   1181 				len = containercontentwidth(cdiv->c);
   1182 				fact = (double)x / (double)len;
   1183 				if ((cdiv->fact + fact) * len >= wm.minsize &&
   1184 				    (TAILQ_NEXT(cdiv, entry)->fact - fact) *
   1185 				    len >= wm.minsize) {
   1186 					cdiv->fact += fact;
   1187 					TAILQ_NEXT(cdiv, entry)->fact -= fact;
   1188 				}
   1189 			}
   1190 			if (rdiv != NULL)
   1191 				len = columncontentheight(rdiv->col);
   1192 			for (row = rdiv; row != NULL && y < 0 &&
   1193 			     ev.xmotion.y < TAILQ_NEXT(row, entry)->y;
   1194 			     row = TAILQ_PREV(row, RowQueue, entry)) {
   1195 				fact = (double)y / (double)len;
   1196 				if (row->fact + fact < 0.0) {
   1197 					ignore = 1;
   1198 					continue;
   1199 				}
   1200 				row->fact += fact;
   1201 				TAILQ_NEXT(rdiv, entry)->fact -= fact;
   1202 				if (ignore) {
   1203 					break;
   1204 				}
   1205 			}
   1206 			for (row = rdiv; row != NULL && y > 0 &&
   1207 			     ev.xmotion.y > TAILQ_NEXT(rdiv, entry)->y;
   1208 			     row = TAILQ_NEXT(row, entry)) {
   1209 				fact = (double)y / (double)len;
   1210 				if (rdiv == row || row->fact - fact < 0.0) {
   1211 					ignore = 1;
   1212 					continue;
   1213 				}
   1214 				rdiv->fact += fact;
   1215 				row->fact -= fact;
   1216 				if (ignore) {
   1217 					break;
   1218 				}
   1219 			}
   1220 			if (ev.xmotion.time - lasttime > (unsigned)config.resizetime) {
   1221 				containercalccols(c);
   1222 				containermoveresize(c, 0);
   1223 				containerdecorate(c, cdiv, rdiv, 0, 0);
   1224 				lasttime = ev.xmotion.time;
   1225 			}
   1226 			xprev = ev.xmotion.x;
   1227 			yprev = ev.xmotion.y;
   1228 			break;
   1229 		}
   1230 	}
   1231 done:
   1232 	containercalccols(c);
   1233 	containermoveresize(c, 0);
   1234 	tabfocus(c->selcol->selrow->seltab, 0);
   1235 	XUngrabPointer(dpy, CurrentTime);
   1236 }
   1237 
   1238 /* handle mouse operation, focusing and raising */
   1239 static void
   1240 xeventbuttonpress(XEvent *e)
   1241 {
   1242 	static struct Container *lastc = NULL;
   1243 	static Time lasttime = 0;
   1244 	struct Object *obj;
   1245 	struct Monitor *mon;
   1246 	struct Container *c;
   1247 	struct Column *cdiv;
   1248 	struct Row *rdiv;
   1249 	struct Tab *tab;
   1250 	struct Menu *menu;
   1251 	enum Octant o;
   1252 	XButtonPressedEvent *ev;
   1253 	Window dw;
   1254 	int x, y;
   1255 
   1256 	ev = &e->xbutton;
   1257 
   1258 	if ((obj = getmanaged(ev->window)) == NULL) {
   1259 		/*
   1260 		 * If user clicked in no managed window, focus the
   1261 		 * monitor below the cursor, but only if the click
   1262 		 * occurred inside monitor's window area.
   1263 		 */
   1264 		if ((mon = getmon(ev->x_root, ev->y_root)) != NULL &&
   1265 		    ev->x_root >= mon->wx && ev->x_root < mon->wx + mon->ww &&
   1266 		    ev->y_root >= mon->wy && ev->y_root < mon->wy + mon->wh) {
   1267 			deskfocus(mon, mon->seldesk);
   1268 		}
   1269 		goto done;
   1270 	}
   1271 
   1272 	menu = NULL;
   1273 	tab = NULL;
   1274 	c = NULL;
   1275 	switch (obj->type) {
   1276 	case TYPE_NORMAL:
   1277 		tab = (struct Tab *)obj;
   1278 		c = tab->row->col->c;
   1279 		break;
   1280 	case TYPE_DIALOG:
   1281 		tab = ((struct Dialog *)obj)->tab;
   1282 		c = tab->row->col->c;
   1283 		break;
   1284 	case TYPE_MENU:
   1285 		menu = (struct Menu *)obj;
   1286 		break;
   1287 	case TYPE_SPLASH:
   1288 		splashrise((struct Splash *)obj);
   1289 		goto done;
   1290 	default:
   1291 		if ((mon = getmon(ev->x_root, ev->y_root)) != NULL)
   1292 			deskfocus(mon, mon->seldesk);
   1293 		goto done;
   1294 	}
   1295 
   1296 	/* raise menu above others or focus tab */
   1297 	if (ev->button == Button1) {
   1298 		if (menu != NULL) {
   1299 			menufocusraise(menu);
   1300 		} else {
   1301 			tabfocus(tab, 1);
   1302 		}
   1303 	}
   1304 
   1305 	/* raise client */
   1306 	if (ev->button == Button1) {
   1307 		if (c != NULL) {
   1308 			containerraise(c, c->isfullscreen, c->abovebelow);
   1309 		} else if (menu != NULL) {
   1310 			menuraise(menu);
   1311 		}
   1312 	}
   1313 
   1314 	/* do action performed by mouse */
   1315 	if (menu != NULL) {
   1316 		if (ev->window == menu->titlebar && ev->button == Button1) {
   1317 			mousemove(menu->titlebar, FLOAT_MENU, menu, ev->x_root, ev->y_root, 1);
   1318 		} else if (ev->window == menu->button && ev->button == Button1) {
   1319 			buttonrightdecorate(menu->button, menu->pixbutton, FOCUSED, 1);
   1320 		} else if (isvalidstate(ev->state) && ev->button == Button1) {
   1321 			mousemove(None, FLOAT_MENU, menu, ev->x_root, ev->y_root, 0);
   1322 		} else if (ev->window == menu->frame && ev->button == Button3) {
   1323 			mousemove(None, FLOAT_MENU, menu, ev->x_root, ev->y_root, 0);
   1324 		} else if (isvalidstate(ev->state) && ev->button == Button3) {
   1325 			if (!XTranslateCoordinates(dpy, ev->window, menu->frame, ev->x, ev->y, &x, &y, &dw))
   1326 				goto done;
   1327 			o = getquadrant(menu->w, menu->h, x, y);
   1328 			mouseresize(FLOAT_MENU, menu, ev->x_root, ev->y_root, o);
   1329 		}
   1330 	} else if (tab != NULL && c != NULL) {
   1331 		if (!XTranslateCoordinates(dpy, ev->window, c->frame, ev->x, ev->y, &x, &y, &dw))
   1332 			goto done;
   1333 		if (ev->window == tab->title && ev->button == Button1) {
   1334 			if (lastc == c && ev->time - lasttime < DOUBLECLICK) {
   1335 				rowstretch(tab->row->col, tab->row);
   1336 				tabfocus(tab->row->seltab, 0);
   1337 				lasttime = 0;
   1338 				lastc = NULL;
   1339 				goto done;
   1340 			}
   1341 			lastc = c;
   1342 			lasttime = ev->time;
   1343 		}
   1344 		o = getframehandle(c->w, c->h, x, y);
   1345 		if (ev->window == tab->title && ev->button == Button4) {
   1346 			containersetstate(
   1347 				tab,
   1348 				(Atom [2]){ atoms[_NET_WM_STATE_SHADED], None },
   1349 				ADD
   1350 			);
   1351 		} if (ev->window == tab->title && ev->button == Button5) {
   1352 			containersetstate(
   1353 				tab,
   1354 				(Atom [2]){ atoms[_NET_WM_STATE_SHADED], None },
   1355 				REMOVE
   1356 			);
   1357 		} else if (ev->window == tab->title && ev->button == Button3) {
   1358 			mouseretab(tab, ev->x_root, ev->y_root, ev->x, ev->y);
   1359 		} else if (ev->window == tab->row->bl && ev->button == Button1) {
   1360 			wm.presswin = ev->window;
   1361 			buttonleftdecorate(tab->row->bl, tab->row->pixbl, FOCUSED, 1);
   1362 		} else if (ev->window == tab->row->br && ev->button == Button1) {
   1363 			wm.presswin = ev->window;
   1364 			buttonrightdecorate(tab->row->br, tab->row->pixbr, FOCUSED, 1);
   1365 		} else if (ev->window == c->frame && ev->button == Button1 && o == C) {
   1366 			getdivisions(c, &cdiv, &rdiv, x, y);
   1367 			if (cdiv != NULL || rdiv != NULL) {
   1368 				mouseretile(c, cdiv, rdiv, ev->x, ev->y);
   1369 			}
   1370 		} else if (!c->isfullscreen && !c->isminimized) {
   1371 			if (isvalidstate(ev->state) && ev->button == Button1) {
   1372 				mousemove(None, FLOAT_CONTAINER, c, ev->x_root, ev->y_root, 0);
   1373 			} else if (ev->window == c->frame && ev->button == Button3) {
   1374 				mousemove(None, FLOAT_CONTAINER, c, ev->x_root, ev->y_root, o);
   1375 			} else if (!c->ismaximized &&
   1376 			           ((isvalidstate(ev->state) && ev->button == Button3) ||
   1377 			           (o != C && ev->window == c->frame && ev->button == Button1))) {
   1378 				if (o == C)
   1379 					o = getquadrant(c->w, c->h, x, y);
   1380 				mouseresize(FLOAT_CONTAINER, c, ev->x_root, ev->y_root, o);
   1381 			} else if (ev->window == tab->title && ev->button == Button1) {
   1382 				tabdecorate(tab, 1);
   1383 				mousemove(tab->title, FLOAT_CONTAINER, c, ev->x_root, ev->y_root, 0);
   1384 				tabdecorate(tab, 0);
   1385 			}
   1386 		}
   1387 	}
   1388 
   1389 done:
   1390 	XAllowEvents(dpy, ReplayPointer, CurrentTime);
   1391 }
   1392 
   1393 /* handle click on titlebar button */
   1394 static void
   1395 xeventbuttonrelease(XEvent *e)
   1396 {
   1397 	struct Object *obj;
   1398 	struct Row *row;
   1399 	struct Menu *menu;
   1400 	XButtonReleasedEvent *ev;
   1401 	Window win, button;
   1402 	Pixmap pix;
   1403 	int perform;
   1404 	char buf[16];
   1405 	enum { PRESS_CLOSE, PRESS_STACK } action;
   1406 	extern char **environ;
   1407 
   1408 	ev = &e->xbutton;
   1409 	wm.presswin = None;
   1410 	if (ev->button != Button1)
   1411 		return;
   1412 	if ((obj = getmanaged(ev->window)) == NULL)
   1413 		return;
   1414 	switch (obj->type) {
   1415 	case TYPE_NORMAL:
   1416 		row = ((struct Tab *)obj)->row;
   1417 		if (ev->window == row->br) {
   1418 			action = PRESS_CLOSE;
   1419 			button = row->br;
   1420 			pix = row->pixbr;
   1421 		} else if (ev->window == row->bl) {
   1422 			action = PRESS_STACK;
   1423 			button = row->bl;
   1424 			pix = row->pixbl;
   1425 		} else {
   1426 			return;
   1427 		}
   1428 		win = TAILQ_EMPTY(&row->seltab->dialq)
   1429 		    ? row->seltab->obj.win
   1430 		    : TAILQ_FIRST(&row->seltab->dialq)->win;
   1431 		break;
   1432 	case TYPE_MENU:
   1433 		menu = (struct Menu *)obj;
   1434 		if (ev->window == menu->button) {
   1435 			action = PRESS_CLOSE;
   1436 			button = menu->button;
   1437 			win = menu->obj.win;
   1438 			pix = menu->pixbutton;
   1439 		} else {
   1440 			return;
   1441 		}
   1442 		break;
   1443 	default:
   1444 		return;
   1445 	}
   1446 	perform = BETWEEN(0, ev->x, config.titlewidth) &&
   1447 	          BETWEEN(0, ev->y, config.titlewidth);
   1448 	switch (action) {
   1449 	case PRESS_CLOSE:
   1450 		if (perform)
   1451 			winclose(win);
   1452 		buttonrightdecorate(button, pix, FOCUSED, 0);
   1453 		break;
   1454 	case PRESS_STACK:
   1455 		(void)snprintf(
   1456 			buf,
   1457 			sizeof(buf),
   1458 			"%lu",
   1459 			(unsigned long)row->seltab->obj.win
   1460 		);
   1461 		(void)setenv("WINDOWID", buf, 1);
   1462 		(void)snprintf(
   1463 			buf,
   1464 			sizeof(buf),
   1465 			"%+d%+d",
   1466 			row->col->c->x + row->col->x,
   1467 			row->col->c->y + row->y + config.titlewidth
   1468 		);
   1469 		(void)setenv("WINDOWPOS", buf, 1);
   1470 		(void)posix_spawnp(
   1471 			NULL,
   1472 			config.menucmd,
   1473 			NULL,
   1474 			NULL,
   1475 			(char *[]){ config.menucmd, NULL },
   1476 			environ
   1477 		);
   1478 		buttonleftdecorate(button, pix, FOCUSED, 0);
   1479 		break;
   1480 	}
   1481 }
   1482 
   1483 /* handle client message event */
   1484 static void
   1485 xeventclientmessage(XEvent *e)
   1486 {
   1487 	struct Container *c = NULL;
   1488 	struct Tab *tab = NULL;
   1489 	struct Object *obj;
   1490 	XClientMessageEvent *ev;
   1491 	XWindowChanges wc;
   1492 	unsigned value_mask = 0;
   1493 	int floattype;
   1494 	void *p;
   1495 
   1496 	ev = &e->xclient;
   1497 	if ((obj = getmanaged(ev->window)) != NULL) {
   1498 		switch (obj->type) {
   1499 		case TYPE_NORMAL:
   1500 			tab = (struct Tab *)obj;
   1501 			c = tab->row->col->c;
   1502 			break;
   1503 		case TYPE_DIALOG:
   1504 			tab = ((struct Dialog *)obj)->tab;
   1505 			c = tab->row->col->c;
   1506 			break;
   1507 		}
   1508 	}
   1509 	if (ev->message_type == atoms[_NET_CURRENT_DESKTOP]) {
   1510 		deskfocus(wm.selmon, ev->data.l[0]);
   1511 	} else if (ev->message_type == atoms[_SHOD_CYCLE]) {
   1512 		alttab(ev->data.l[0]);
   1513 	} else if (ev->message_type == atoms[_NET_SHOWING_DESKTOP]) {
   1514 		if (ev->data.l[0]) {
   1515 			deskshow(1);
   1516 		} else {
   1517 			deskfocus(wm.selmon, wm.selmon->seldesk);
   1518 		}
   1519 	} else if (ev->message_type == atoms[_NET_WM_STATE]) {
   1520 		if (obj == NULL || obj->type != TYPE_NORMAL)
   1521 			return;
   1522 		containersetstate(tab, (Atom *)(ev->data.l + 1), ev->data.l[0]);
   1523 	} else if (ev->message_type == atoms[_NET_ACTIVE_WINDOW]) {
   1524 #define ACTIVATECOL(col) { if ((col) != NULL) tabfocus((col)->selrow->seltab, 1); }
   1525 #define ACTIVATEROW(row) { if ((row) != NULL) tabfocus((row)->seltab, 1); }
   1526 		if (tab == NULL && wm.focused != NULL) {
   1527 			c = wm.focused;
   1528 			tab = wm.focused->selcol->selrow->seltab;
   1529 		}
   1530 		if (tab == NULL)
   1531 			return;
   1532 		switch (ev->data.l[3]) {
   1533 		case _SHOD_FOCUS_LEFT_CONTAINER:
   1534 		case _SHOD_FOCUS_RIGHT_CONTAINER:
   1535 		case _SHOD_FOCUS_TOP_CONTAINER:
   1536 		case _SHOD_FOCUS_BOTTOM_CONTAINER:
   1537 			// removed
   1538 			break;
   1539 		case _SHOD_FOCUS_PREVIOUS_CONTAINER:
   1540 			// removed
   1541 			break;
   1542 		case _SHOD_FOCUS_NEXT_CONTAINER:
   1543 			// removed
   1544 			break;
   1545 		case _SHOD_FOCUS_LEFT_WINDOW:
   1546 			ACTIVATECOL(TAILQ_PREV(tab->row->col, ColumnQueue, entry))
   1547 			break;
   1548 		case _SHOD_FOCUS_RIGHT_WINDOW:
   1549 			ACTIVATECOL(TAILQ_NEXT(tab->row->col, entry))
   1550 			break;
   1551 		case _SHOD_FOCUS_TOP_WINDOW:
   1552 			ACTIVATEROW(TAILQ_PREV(tab->row, RowQueue, entry))
   1553 			break;
   1554 		case _SHOD_FOCUS_BOTTOM_WINDOW:
   1555 			ACTIVATEROW(TAILQ_NEXT(tab->row, entry))
   1556 			break;
   1557 		case _SHOD_FOCUS_PREVIOUS_WINDOW:
   1558 			obj = (struct Object *)tab;
   1559 			if (TAILQ_PREV(obj, Queue, entry) != NULL)
   1560 				tabfocus((struct Tab *)TAILQ_PREV(obj, Queue, entry), 1);
   1561 			else
   1562 				tabfocus((struct Tab *)TAILQ_LAST(&tab->row->tabq, Queue), 1);
   1563 			break;
   1564 		case _SHOD_FOCUS_NEXT_WINDOW:
   1565 			obj = (struct Object *)tab;
   1566 			if (TAILQ_NEXT(obj, entry) != NULL)
   1567 				tabfocus((struct Tab *)TAILQ_NEXT(obj, entry), 1);
   1568 			else
   1569 				tabfocus((struct Tab *)TAILQ_FIRST(&tab->row->tabq), 1);
   1570 			break;
   1571 		default:
   1572 			tabfocus(tab, 1);
   1573 			containerraise(c, c->isfullscreen, c->abovebelow);
   1574 			break;
   1575 		}
   1576 	} else if (ev->message_type == atoms[_NET_CLOSE_WINDOW]) {
   1577 		winclose(ev->window);
   1578 	} else if (ev->message_type == atoms[_NET_MOVERESIZE_WINDOW]) {
   1579 		if (c == NULL)
   1580 			return;
   1581 		value_mask = CWX | CWY | CWWidth | CWHeight;
   1582 		wc.x = (ev->data.l[0] & _SHOD_MOVERESIZE_RELATIVE) ? c->x + ev->data.l[1] : ev->data.l[1];
   1583 		wc.y = (ev->data.l[0] & _SHOD_MOVERESIZE_RELATIVE) ? c->y + ev->data.l[2] : ev->data.l[2];
   1584 		wc.width = (ev->data.l[0] & _SHOD_MOVERESIZE_RELATIVE) ? c->w + ev->data.l[3] : ev->data.l[3];
   1585 		wc.height = (ev->data.l[0] & _SHOD_MOVERESIZE_RELATIVE) ? c->h + ev->data.l[4] : ev->data.l[4];
   1586 		if (obj->type == TYPE_DIALOG) {
   1587 			dialogconfigure((struct Dialog *)obj, value_mask, &wc);
   1588 		} else if (obj->type == TYPE_NORMAL) {
   1589 			containerconfigure(c, value_mask, &wc);
   1590 		}
   1591 	} else if (ev->message_type == atoms[_NET_WM_DESKTOP]) {
   1592 		if (obj == NULL || obj->type != TYPE_NORMAL)
   1593 			return;
   1594 		containersendtodeskandfocus(c, c->mon, ev->data.l[0]);
   1595 	} else if (ev->message_type == atoms[_NET_REQUEST_FRAME_EXTENTS]) {
   1596 		if (c == NULL) {
   1597 			/*
   1598 			 * A client can request an estimate for the frame size
   1599 			 * which the window manager will put around it before
   1600 			 * actually mapping its window. Java does this (as of
   1601 			 * openjdk-7).
   1602 			 */
   1603 			ewmhsetframeextents(ev->window, config.borderwidth, config.titlewidth);
   1604 		} else {
   1605 			ewmhsetframeextents(ev->window, c->b, (obj->type == TYPE_DIALOG ? 0 : TITLEWIDTH(c)));
   1606 		}
   1607 	} else if (ev->message_type == atoms[_NET_WM_MOVERESIZE]) {
   1608 		/*
   1609 		 * Client-side decorated Gtk3 windows emit this signal when being
   1610 		 * dragged by their GtkHeaderBar
   1611 		 */
   1612 		if (obj == NULL || (obj->type != TYPE_NORMAL && obj->type != TYPE_MENU))
   1613 			return;
   1614 		if (obj->type == TYPE_MENU) {
   1615 			p = obj;
   1616 			floattype = FLOAT_MENU;
   1617 		} else {
   1618 			p = c;
   1619 			floattype = FLOAT_CONTAINER;
   1620 		}
   1621 		switch (ev->data.l[2]) {
   1622 		case _NET_WM_MOVERESIZE_SIZE_TOPLEFT:
   1623 			mouseresize(floattype, p, ev->data.l[0], ev->data.l[1], NW);
   1624 			break;
   1625 		case _NET_WM_MOVERESIZE_SIZE_TOP:
   1626 			mouseresize(floattype, p, ev->data.l[0], ev->data.l[1], N);
   1627 			break;
   1628 		case _NET_WM_MOVERESIZE_SIZE_TOPRIGHT:
   1629 			mouseresize(floattype, p, ev->data.l[0], ev->data.l[1], NE);
   1630 			break;
   1631 		case _NET_WM_MOVERESIZE_SIZE_RIGHT:
   1632 			mouseresize(floattype, p, ev->data.l[0], ev->data.l[1], E);
   1633 			break;
   1634 		case _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT:
   1635 			mouseresize(floattype, p, ev->data.l[0], ev->data.l[1], SE);
   1636 			break;
   1637 		case _NET_WM_MOVERESIZE_SIZE_BOTTOM:
   1638 			mouseresize(floattype, p, ev->data.l[0], ev->data.l[1], S);
   1639 			break;
   1640 		case _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT:
   1641 			mouseresize(floattype, p, ev->data.l[0], ev->data.l[1], SW);
   1642 			break;
   1643 		case _NET_WM_MOVERESIZE_SIZE_LEFT:
   1644 			mouseresize(floattype, p, ev->data.l[0], ev->data.l[1], W);
   1645 			break;
   1646 		case _NET_WM_MOVERESIZE_MOVE:
   1647 			mousemove(None, floattype, p, ev->data.l[0], ev->data.l[1], C);
   1648 			break;
   1649 		default:
   1650 			XUngrabPointer(dpy, CurrentTime);
   1651 			break;
   1652 		}
   1653 	}
   1654 }
   1655 
   1656 /* handle configure request event */
   1657 static void
   1658 xeventconfigurerequest(XEvent *e)
   1659 {
   1660 	XConfigureRequestEvent *ev;
   1661 	XWindowChanges wc;
   1662 	struct Object *obj;
   1663 
   1664 	ev = &e->xconfigurerequest;
   1665 	wc.x = ev->x;
   1666 	wc.y = ev->y;
   1667 	wc.width = ev->width;
   1668 	wc.height = ev->height;
   1669 	wc.border_width = ev->border_width;
   1670 	wc.sibling = ev->above;
   1671 	wc.stack_mode = ev->detail;
   1672 	obj = getmanaged(ev->window);
   1673 	if (obj == NULL)
   1674 		XConfigureWindow(dpy, ev->window, ev->value_mask, &wc);
   1675 	if (!config.honorconfig)
   1676 		return;
   1677 	if (obj->type == TYPE_DIALOG) {
   1678 		dialogconfigure((struct Dialog *)obj, ev->value_mask, &wc);
   1679 	} else if (obj->type == TYPE_MENU) {
   1680 		menuconfigure((struct Menu *)obj, ev->value_mask, &wc);
   1681 	} else if (obj->type == TYPE_DOCKAPP) {
   1682 		dockappconfigure((struct Dockapp *)obj, ev->value_mask, &wc);
   1683 	} else if (obj->type == TYPE_NORMAL) {
   1684 		containerconfigure(((struct Tab *)obj)->row->col->c, ev->value_mask, &wc);
   1685 	}
   1686 }
   1687 
   1688 /* forget about client */
   1689 static void
   1690 xeventdestroynotify(XEvent *e)
   1691 {
   1692 	XDestroyWindowEvent *ev;
   1693 	struct Object *obj;
   1694 
   1695 	ev = &e->xdestroywindow;
   1696 	if ((obj = getmanaged(ev->window)) != NULL) {
   1697 		if (obj->win == ev->window && (*unmanagefuncs[obj->type])(obj, 0)) {
   1698 			wm.setclientlist = 1;
   1699 		}
   1700 	} else if (ev->window == wm.checkwin) {
   1701 		wm.running = 0;
   1702 	}
   1703 }
   1704 
   1705 /* focus window when cursor enter it (only if there is no focus button) */
   1706 static void
   1707 xevententernotify(XEvent *e)
   1708 {
   1709 	struct Tab *tab;
   1710 	struct Object *obj;
   1711 
   1712 	if (!config.sloppyfocus && !config.sloppytiles)
   1713 		return;
   1714 	while (XCheckTypedEvent(dpy, EnterNotify, e))
   1715 		;
   1716 	if ((obj = getmanaged(e->xcrossing.window)) == NULL)
   1717 		return;
   1718 	if (obj->type == TYPE_DIALOG)
   1719 		tab = ((struct Dialog *)obj)->tab;
   1720 	else if (obj->type == TYPE_NORMAL)
   1721 		tab = (struct Tab *)obj;
   1722 	else return;
   1723 	if (!config.sloppytiles && tab->row->col->c == wm.focused)
   1724 		return;
   1725 	if (!config.sloppyfocus && tab->row->col->c != wm.focused)
   1726 		return;
   1727 	if (config.sloppyfocus && tab->row->col->c != wm.focused)
   1728 		tab = tab->row->col->c->selcol->selrow->seltab;
   1729 	tabfocus(tab, 1);
   1730 }
   1731 
   1732 /* handle focusin event */
   1733 static void
   1734 xeventfocusin(XEvent *e)
   1735 {
   1736 	XFocusChangeEvent *ev;
   1737 	struct Object *obj;
   1738 
   1739 	ev = &e->xfocus;
   1740 	if (wm.focused == NULL) {
   1741 		tabfocus(NULL, 0);
   1742 		return;
   1743 	}
   1744 	obj = getmanaged(ev->window);
   1745 	if (obj == NULL)
   1746 		goto focus;
   1747 	switch (obj->type) {
   1748 	case TYPE_MENU:
   1749 		menufocus((struct Menu *)obj);
   1750 		break;
   1751 	case TYPE_DIALOG:
   1752 		if (((struct Dialog *)obj)->tab != wm.focused->selcol->selrow->seltab)
   1753 			goto focus;
   1754 		break;
   1755 	case TYPE_NORMAL:
   1756 		if ((struct Tab *)obj != wm.focused->selcol->selrow->seltab)
   1757 			goto focus;
   1758 		break;
   1759 	}
   1760 	return;
   1761 focus:
   1762 	tabfocus(wm.focused->selcol->selrow->seltab, 1);
   1763 }
   1764 
   1765 /* key press event on focuswin */
   1766 static void
   1767 xeventkeypress(XEvent *e)
   1768 {
   1769 	XKeyPressedEvent *ev;
   1770 
   1771 	ev = &e->xkey;
   1772 	if (!config.disablealttab && ev->keycode == config.tabkeycode) {
   1773 		alttab(ev->state & ShiftMask);
   1774 	}
   1775 	if (ev->window == wm.checkwin) {
   1776 		e->xkey.window = root;
   1777 		XSendEvent(dpy, root, False, KeyPressMask, e);
   1778 	}
   1779 }
   1780 
   1781 /* manage window */
   1782 static void
   1783 xeventmaprequest(XEvent *e)
   1784 {
   1785 	XMapRequestEvent *ev;
   1786 	XWindowAttributes wa;
   1787 
   1788 	ev = &e->xmaprequest;
   1789 	if (!XGetWindowAttributes(dpy, ev->window, &wa))
   1790 		return;
   1791 	if (wa.override_redirect)
   1792 		return;
   1793 	manage(
   1794 		ev->window,
   1795 		(XRectangle){
   1796 			.x = wa.x,
   1797 			.y = wa.y,
   1798 			.width = wa.width,
   1799 			.height = wa.height,
   1800 		},
   1801 		0
   1802 	);
   1803 }
   1804 
   1805 /* forget about client */
   1806 static void
   1807 xeventmappingnotify(XEvent *e)
   1808 {
   1809 	(void)e;
   1810 	setmod();
   1811 }
   1812 
   1813 /* update client properties */
   1814 static void
   1815 xeventpropertynotify(XEvent *e)
   1816 {
   1817 	XPropertyEvent *ev;
   1818 	struct Container *c;
   1819 	struct Object *obj;
   1820 	struct Tab *tab;
   1821 	struct Menu *menu;
   1822 	char *str;
   1823 
   1824 	ev = &e->xproperty;
   1825 	if (ev->state != PropertyNewValue)
   1826 		return;
   1827 	if (ev->window == root && ev->atom == XA_RESOURCE_MANAGER) {
   1828 		if ((str = gettextprop(root, XA_RESOURCE_MANAGER)) == NULL)
   1829 			return;
   1830 		XrmDestroyDatabase(xdb);
   1831 		setresources(str);
   1832 		free(str);
   1833 		TAILQ_FOREACH(c, &wm.focusq, entry)
   1834 			containerdecorate(c, NULL, NULL, 1, C);
   1835 		TAILQ_FOREACH(obj, &wm.menuq, entry)
   1836 			menudecorate((struct Menu *)obj, 0);
   1837 		TAILQ_FOREACH(obj, &wm.notifq, entry)
   1838 			notifdecorate((struct Notification *)obj);
   1839 		dockreset();
   1840 		return;
   1841 	}
   1842 	obj = getmanaged(ev->window);
   1843 	if (obj == NULL)
   1844 		return;
   1845 	if (obj->type == TYPE_NORMAL && ev->window == obj->win) {
   1846 		tab = (struct Tab *)obj;
   1847 		if (ev->atom == XA_WM_NAME || ev->atom == atoms[_NET_WM_NAME]) {
   1848 			winupdatetitle(tab->obj.win, &tab->name);
   1849 			tabdecorate(tab, 0);
   1850 		} else if (ev->atom == XA_WM_HINTS) {
   1851 			tabupdateurgency(tab, getwinurgency(tab->obj.win));
   1852 		}
   1853 	} else if (obj->type == TYPE_DOCK && (ev->atom == _NET_WM_STRUT_PARTIAL || ev->atom == _NET_WM_STRUT)) {
   1854 		barstrut((struct Bar *)obj);
   1855 		monupdatearea();
   1856 	} else if (obj->type == TYPE_MENU && ev->window == obj->win) {
   1857 		menu = (struct Menu *)obj;
   1858 		if (ev->atom == XA_WM_NAME || ev->atom == atoms[_NET_WM_NAME]) {
   1859 			winupdatetitle(menu->obj.win, &menu->name);
   1860 			menudecorate(menu, 0);
   1861 		}
   1862 	}
   1863 }
   1864 
   1865 /* forget about client */
   1866 static void
   1867 xeventunmapnotify(XEvent *e)
   1868 {
   1869 	XUnmapEvent *ev;
   1870 	struct Object *obj;
   1871 
   1872 	ev = &e->xunmap;
   1873 	if ((obj = getmanaged(ev->window)) != NULL) {
   1874 		if (obj->win == ev->window && (*unmanagefuncs[obj->type])(obj, 1)) {
   1875 			wm.setclientlist = 1;
   1876 		}
   1877 	}
   1878 }
   1879 
   1880 /* scan for already existing windows and adopt them */
   1881 void
   1882 scan(void)
   1883 {
   1884 	unsigned int i, num;
   1885 	Window d1, d2, transwin, *wins = NULL;
   1886 	XWindowAttributes wa;
   1887 
   1888 	if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) {
   1889 		for (i = 0; i < num; i++) {
   1890 			if (!XGetWindowAttributes(dpy, wins[i], &wa)
   1891 			|| wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1))
   1892 				continue;
   1893 			if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState) {
   1894 				manage(
   1895 					wins[i],
   1896 					(XRectangle){
   1897 						.x = wa.x,
   1898 						.y = wa.y,
   1899 						.width = wa.width,
   1900 						.height = wa.height,
   1901 					},
   1902 					IGNOREUNMAP
   1903 				);
   1904 			}
   1905 		}
   1906 		for (i = 0; i < num; i++) {     /* now the transients */
   1907 			if (!XGetWindowAttributes(dpy, wins[i], &wa))
   1908 				continue;
   1909 			if (XGetTransientForHint(dpy, wins[i], &transwin) &&
   1910 			   (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)) {
   1911 				manage(
   1912 					wins[i],
   1913 					(XRectangle){
   1914 						.x = wa.x,
   1915 						.y = wa.y,
   1916 						.width = wa.width,
   1917 						.height = wa.height,
   1918 					},
   1919 					IGNOREUNMAP
   1920 				);
   1921 			}
   1922 		}
   1923 		if (wins != NULL) {
   1924 			XFree(wins);
   1925 		}
   1926 	}
   1927 }
   1928 
   1929 /* set modifier and Alt key code from given key sym */
   1930 void
   1931 setmod(void)
   1932 {
   1933 	config.altkeycode = 0;
   1934 	if ((config.altkeycode = XKeysymToKeycode(dpy, config.altkeysym)) == 0) {
   1935 		warnx("could not get keycode from keysym");
   1936 		return;
   1937 	}
   1938 	if ((config.tabkeycode = XKeysymToKeycode(dpy, config.tabkeysym)) == 0) {
   1939 		warnx("could not get keycode from keysym");
   1940 		return;
   1941 	}
   1942 	if (config.disablealttab)
   1943 		return;
   1944 	XUngrabKey(dpy, config.tabkeycode, config.modifier, root);
   1945 	XGrabKey(dpy, config.tabkeycode, config.modifier, root, False, GrabModeAsync, GrabModeAsync);
   1946 }
   1947 
   1948 void (*xevents[LASTEvent])(XEvent *) = {
   1949 	[ButtonPress]      = xeventbuttonpress,
   1950 	[ButtonRelease]    = xeventbuttonrelease,
   1951 	[ClientMessage]    = xeventclientmessage,
   1952 	[ConfigureRequest] = xeventconfigurerequest,
   1953 	[DestroyNotify]    = xeventdestroynotify,
   1954 	[EnterNotify]      = xevententernotify,
   1955 	[FocusIn]          = xeventfocusin,
   1956 	[KeyPress]         = xeventkeypress,
   1957 	[MapRequest]       = xeventmaprequest,
   1958 	[MappingNotify]    = xeventmappingnotify,
   1959 	[PropertyNotify]   = xeventpropertynotify,
   1960 	[UnmapNotify]      = xeventunmapnotify,
   1961 };