glazier

the original window manipulator
Log | Files | Refs | Submodules | README | LICENSE

glazier.c (17766B)


      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <xcb/xcb.h>
      4 #include <xcb/xcb_cursor.h>
      5 
      6 #include "arg.h"
      7 #include "wm.h"
      8 
      9 #define LEN(x) (sizeof(x)/sizeof(x[0]))
     10 #define XEV(x) (evname[(x)->response_type & ~0x80])
     11 #define MIN(x,y) ((x)>(y)?(y):(x))
     12 #define MAX(x,y) ((x)>(y)?(x):(y))
     13 
     14 struct ev_callback_t {
     15 	uint32_t type;
     16 	int (*handle)(xcb_generic_event_t *);
     17 };
     18 
     19 struct cursor_t {
     20 	int x, y, b;
     21 	int mode;
     22 };
     23 
     24 enum {
     25 	XHAIR_DFLT,
     26 	XHAIR_MOVE,
     27 	XHAIR_SIZE,
     28 	XHAIR_TELE,
     29 };
     30 
     31 enum {
     32 	GRAB_NONE = 0,
     33 	GRAB_MOVE,
     34 	GRAB_SIZE,
     35 	GRAB_TELE,
     36 };
     37 
     38 #include "config.h"
     39 
     40 void usage(char *);
     41 static int takeover();
     42 static int adopt(xcb_window_t);
     43 static int inflate(xcb_window_t, int);
     44 static int outline(xcb_drawable_t, int, int, int, int);
     45 static int ev_callback(xcb_generic_event_t *);
     46 
     47 /* XCB events callbacks */
     48 static int cb_default(xcb_generic_event_t *);
     49 static int cb_create(xcb_generic_event_t *);
     50 static int cb_mapreq(xcb_generic_event_t *);
     51 static int cb_mouse_press(xcb_generic_event_t *);
     52 static int cb_mouse_release(xcb_generic_event_t *);
     53 static int cb_motion(xcb_generic_event_t *);
     54 static int cb_enter(xcb_generic_event_t *);
     55 static int cb_focus(xcb_generic_event_t *);
     56 static int cb_configreq(xcb_generic_event_t *);
     57 static int cb_configure(xcb_generic_event_t *);
     58 
     59 int verbose = 0;
     60 xcb_connection_t *conn;
     61 xcb_screen_t     *scrn;
     62 xcb_window_t      curwid;
     63 struct cursor_t   cursor;
     64 
     65 static const char *evname[] = {
     66 	[0]                     = "EVENT_ERROR",
     67 	[XCB_CREATE_NOTIFY]     = "CREATE_NOTIFY",
     68 	[XCB_DESTROY_NOTIFY]    = "DESTROY_NOTIFY",
     69 	[XCB_BUTTON_PRESS]      = "BUTTON_PRESS",
     70 	[XCB_BUTTON_RELEASE]    = "BUTTON_RELEASE",
     71 	[XCB_MOTION_NOTIFY]     = "MOTION_NOTIFY",
     72 	[XCB_ENTER_NOTIFY]      = "ENTER_NOTIFY",
     73 	[XCB_CONFIGURE_NOTIFY]  = "CONFIGURE_NOTIFY",
     74 	[XCB_KEY_PRESS]         = "KEY_PRESS",
     75 	[XCB_FOCUS_IN]          = "FOCUS_IN",
     76 	[XCB_FOCUS_OUT]         = "FOCUS_OUT",
     77 	[XCB_KEYMAP_NOTIFY]     = "KEYMAP_NOTIFY",
     78 	[XCB_EXPOSE]            = "EXPOSE",
     79 	[XCB_GRAPHICS_EXPOSURE] = "GRAPHICS_EXPOSURE",
     80 	[XCB_NO_EXPOSURE]       = "NO_EXPOSURE",
     81 	[XCB_VISIBILITY_NOTIFY] = "VISIBILITY_NOTIFY",
     82 	[XCB_UNMAP_NOTIFY]      = "UNMAP_NOTIFY",
     83 	[XCB_MAP_NOTIFY]        = "MAP_NOTIFY",
     84 	[XCB_MAP_REQUEST]       = "MAP_REQUEST",
     85 	[XCB_REPARENT_NOTIFY]   = "REPARENT_NOTIFY",
     86 	[XCB_CONFIGURE_REQUEST] = "CONFIGURE_REQUEST",
     87 	[XCB_GRAVITY_NOTIFY]    = "GRAVITY_NOTIFY",
     88 	[XCB_RESIZE_REQUEST]    = "RESIZE_REQUEST",
     89 	[XCB_CIRCULATE_NOTIFY]  = "CIRCULATE_NOTIFY",
     90 	[XCB_PROPERTY_NOTIFY]   = "PROPERTY_NOTIFY",
     91 	[XCB_SELECTION_CLEAR]   = "SELECTION_CLEAR",
     92 	[XCB_SELECTION_REQUEST] = "SELECTION_REQUEST",
     93 	[XCB_SELECTION_NOTIFY]  = "SELECTION_NOTIFY",
     94 	[XCB_COLORMAP_NOTIFY]   = "COLORMAP_NOTIFY",
     95 	[XCB_CLIENT_MESSAGE]    = "CLIENT_MESSAGE",
     96 	[XCB_MAPPING_NOTIFY]    = "MAPPING_NOTIFY"
     97 };
     98 
     99 static const struct ev_callback_t cb[] = {
    100 	/* event,                function */
    101 	{ XCB_CREATE_NOTIFY,     cb_create },
    102 	{ XCB_MAP_REQUEST,       cb_mapreq },
    103 	{ XCB_BUTTON_PRESS,      cb_mouse_press },
    104 	{ XCB_BUTTON_RELEASE,    cb_mouse_release },
    105 	{ XCB_MOTION_NOTIFY,     cb_motion },
    106 	{ XCB_ENTER_NOTIFY,      cb_enter },
    107 	{ XCB_FOCUS_IN,          cb_focus },
    108 	{ XCB_FOCUS_OUT,         cb_focus },
    109 	{ XCB_CONFIGURE_REQUEST, cb_configreq },
    110 	{ XCB_CONFIGURE_NOTIFY,  cb_configure },
    111 };
    112 
    113 void
    114 usage(char *name)
    115 {
    116 	fprintf(stderr, "usage: %s [-vh]\n", name);
    117 }
    118 
    119 /*
    120  * Every window that shouldn't be ignored (override_redirect) is adoped
    121  * by the WM when it is created, or when the WM is started.
    122  * When a window is created, it is centered on the cursor, before it
    123  * gets mapped on screen. Windows that are already visible are not moved.
    124  * Some events are also registered by the WM for these windows.
    125  */
    126 int
    127 adopt(xcb_window_t wid)
    128 {
    129 	int x, y, w, h;
    130 
    131 	if (wm_is_ignored(wid))
    132 		return -1;
    133 
    134 	x = wm_get_attribute(wid, ATTR_X);
    135 	y = wm_get_attribute(wid, ATTR_Y);
    136 
    137 	if (!wm_is_mapped(wid)) {
    138 		w = wm_get_attribute(wid, ATTR_W);
    139 		h = wm_get_attribute(wid, ATTR_H);
    140 
    141 		if (!x && !y) {
    142 			wm_get_cursor(0, scrn->root, &x, &y);
    143 			x = MAX(0, x - w/2);
    144 			y = MAX(0, y - h/2);
    145 		}
    146 
    147 		wm_teleport(wid, MAX(0, x), MAX(0, y), w, h);
    148 	}
    149 
    150 	return wm_reg_window_event(wid, XCB_EVENT_MASK_ENTER_WINDOW
    151 		| XCB_EVENT_MASK_FOCUS_CHANGE
    152 		| XCB_EVENT_MASK_STRUCTURE_NOTIFY);
    153 }
    154 
    155 /*
    156  * Inflating a window will grow it both vertically and horizontally in
    157  * all 4 directions, thus making it look like it is inflating.
    158  * The window can be "deflated" by providing a negative `step` value.
    159  */
    160 int
    161 inflate(xcb_window_t wid, int step)
    162 {
    163 	int x, y, w, h;
    164 
    165 	x = wm_get_attribute(wid, ATTR_X) - step/2;
    166 	y = wm_get_attribute(wid, ATTR_Y) - step/2;
    167 	w = wm_get_attribute(wid, ATTR_W) + step;
    168 	h = wm_get_attribute(wid, ATTR_H) + step;
    169 
    170 	return wm_teleport(wid, x, y, w, h);
    171 }
    172 
    173 /*
    174  * When the WM is started, it will take control of the existing windows.
    175  * This means registering events on them and setting the borders if they
    176  * are mapped. This function is only supposed to run once at startup,
    177  * as the callback functions will take control of new windows
    178  */
    179 int
    180 takeover()
    181 {
    182 	int i, n;
    183 	xcb_window_t *orphans, wid;
    184 
    185 	n = wm_get_windows(scrn->root, &orphans);
    186 
    187 	for (i = 0; i < n; i++) {
    188 		wid = orphans[i];
    189 		if (wm_is_ignored(wid))
    190 			continue;
    191 
    192 		if (verbose)
    193 			fprintf(stderr, "Adopting 0x%08x\n", wid);
    194 
    195 		adopt(wid);
    196 		if (wm_is_mapped(wid))
    197 			wm_set_border(border, border_color, wid);
    198 	}
    199 
    200 	wid = wm_get_focus();
    201 	if (wid != scrn->root)
    202 		wm_set_border(border, border_color_active, wid);
    203 
    204 	return n;
    205 }
    206 
    207 /*
    208  * Draws a rectangle selection on the screen.
    209  * The trick here is to invert the color on the selection, so that
    210  * redrawing the same rectangle will "clear" it.
    211  * This function is used to dynamically draw a region for moving/resizing
    212  * a window using the cursor. As such, we need to make sure that whenever
    213  * we draw a rectangle, we clear out the last drawn one by redrawing
    214  * the latest coordinates again, so we have to save them from one call to
    215  * the other.
    216  */
    217 int
    218 outline(xcb_drawable_t wid, int x, int y, int w, int h)
    219 {
    220 	int mask, val[3];
    221 	static int X = 0, Y = 0, W = 0, H = 0;
    222 	xcb_gcontext_t gc;
    223 	xcb_rectangle_t r;
    224 
    225 	gc = xcb_generate_id(conn);
    226 	mask = XCB_GC_FUNCTION | XCB_GC_LINE_WIDTH | XCB_GC_SUBWINDOW_MODE;
    227 	val[0] = XCB_GX_INVERT;
    228 	val[1] = 0;
    229 	val[2] = XCB_SUBWINDOW_MODE_INCLUDE_INFERIORS;
    230 	xcb_create_gc(conn, gc, wid, mask, val);
    231 
    232 	/* redraw last rectangle to clear it */
    233 	r.x = X;
    234 	r.y = Y;
    235 	r.width = W;
    236 	r.height = H;
    237 	xcb_poly_rectangle(conn, wid, gc, 1, &r);
    238 
    239 	/* draw rectangle and save its coordinates for later removal */
    240 	X = r.x = x;
    241 	Y = r.y = y;
    242 	W = r.width = w;
    243 	H = r.height = h;
    244 	xcb_poly_rectangle(conn, wid, gc, 1, &r);
    245 
    246 	return 0;
    247 }
    248 
    249 /*
    250  * Callback used for all events that are not explicitely registered.
    251  * This is not at all necessary, and used for debugging purposes.
    252  */
    253 int
    254 cb_default(xcb_generic_event_t *ev)
    255 {
    256 	if (verbose < 2)
    257 		return 0;
    258 
    259 	if (XEV(ev)) {
    260 		fprintf(stderr, "%s not handled\n", XEV(ev));
    261 	} else {
    262 		fprintf(stderr, "EVENT %d not handled\n", ev->response_type);
    263 	}
    264 
    265 	return 0;
    266 }
    267 
    268 /*
    269  * XCB_CREATE_NOTIFY is the first event triggered by new windows, and
    270  * is used to prepare the window for use by the WM.
    271  * The attribute `override_redirect` allow windows to specify that they
    272  * shouldn't be handled by the WM.
    273  */
    274 int
    275 cb_create(xcb_generic_event_t *ev)
    276 {
    277 	xcb_create_notify_event_t *e;
    278 
    279 	e = (xcb_create_notify_event_t *)ev;
    280 
    281 	if (e->override_redirect)
    282 		return 0;
    283 
    284 	if (verbose)
    285 		fprintf(stderr, "%s 0x%08x\n", XEV(e), e->window);
    286 
    287 	adopt(e->window);
    288 
    289 	return 0;
    290 }
    291 
    292 /*
    293  * XCB_MAP_REQUEST is triggered by a window that wants to be mapped on
    294  * screen. This is then the responsibility of the WM to map it on screen
    295  * and eventually decorate it. This event require that the WM register
    296  * XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT on the root window to intercept
    297  * map requests.
    298  */
    299 int
    300 cb_mapreq(xcb_generic_event_t *ev)
    301 {
    302 	int x, y;
    303 	xcb_map_request_event_t *e;
    304 
    305 	e = (xcb_map_request_event_t *)ev;
    306 
    307 	if (verbose)
    308 		fprintf(stderr, "%s 0x%08x\n", XEV(e), e->window);
    309 
    310 	wm_remap(e->window, MAP);
    311 	wm_set_border(border, border_color, e->window);
    312 	wm_set_focus(e->window);
    313 
    314 	/* prevent window to pop outside the screen */
    315 	x = wm_get_attribute(e->window, ATTR_X);
    316 	y = wm_get_attribute(e->window, ATTR_Y);
    317 	wm_move(e->window, ABSOLUTE, x, y);
    318 
    319 	return 0;
    320 }
    321 
    322 /*
    323  * The WM grabs XCB_BUTTON_PRESS events when the modifier is held.
    324  * Once pressed, we'll grab the pointer entirely (without modifiers)
    325  * and wait for motion/release events.
    326  * The special mouse buttons 4/5 (scroll up/down) are treated especially,
    327  * as they do not trigger any "release" event.
    328  *
    329  * This function must also save the window ID where the mouse press
    330  * occured so we know which window to move/resize, even if the focus
    331  * changes to another window.
    332  * For similar reasons, we must save the cursor position.
    333  */
    334 int
    335 cb_mouse_press(xcb_generic_event_t *ev)
    336 {
    337 	int mask;
    338 	static xcb_timestamp_t lasttime = 0;
    339 	xcb_button_press_event_t *e;
    340 	xcb_window_t wid;
    341 
    342 	e = (xcb_button_press_event_t *)ev;
    343 
    344 	/* ignore some motion events if they happen too often */
    345 	if (e->time - lasttime < 8)
    346 		return -1;
    347 
    348 	wid = e->child ? e->child : e->event;
    349 
    350 	if (verbose)
    351 		fprintf(stderr, "%s 0x%08x %d\n", XEV(e), wid, e->detail);
    352 
    353 	cursor.x = e->root_x - wm_get_attribute(wid, ATTR_X);
    354 	cursor.y = e->root_y - wm_get_attribute(wid, ATTR_Y);
    355 	cursor.b = e->detail;
    356 	lasttime = e->time;
    357 
    358 	mask = XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_BUTTON_MOTION;
    359 
    360 	switch(e->detail) {
    361 	case 1:
    362 		curwid = wid;
    363 		cursor.mode = GRAB_MOVE;
    364 		wm_reg_cursor_event(scrn->root, mask, xhair[XHAIR_MOVE]);
    365 		break;
    366 	case 2:
    367 		/* teleport acts on the last focused window */
    368 		cursor.x = e->root_x;
    369 		cursor.y = e->root_y;
    370 		cursor.mode = GRAB_TELE;
    371 		wm_reg_cursor_event(scrn->root, mask, xhair[XHAIR_TELE]);
    372 		break;
    373 	case 3:
    374 		curwid = wid;
    375 		cursor.mode = GRAB_SIZE;
    376 		wm_reg_cursor_event(scrn->root, mask, xhair[XHAIR_SIZE]);
    377 		break;
    378 	case 4:
    379 		inflate(wid, move_step);
    380 		wm_restack(e->child, XCB_STACK_MODE_ABOVE);
    381 		break;
    382 	case 5:
    383 		inflate(wid, - move_step);
    384 		wm_restack(wid, XCB_STACK_MODE_ABOVE);
    385 		break;
    386 	default:
    387 		return -1;
    388 	}
    389 
    390 	return 0;
    391 }
    392 
    393 /*
    394  * When XCB_BUTTON_RELEASE is triggered, this will "commit" any
    395  * move/resize initiated on a previous mouse press.
    396  * This will also ungrab the mouse pointer.
    397  */
    398 int
    399 cb_mouse_release(xcb_generic_event_t *ev)
    400 {
    401 	int x, y, w, h;
    402 	xcb_cursor_t p;
    403 	xcb_cursor_context_t *cx;
    404 	xcb_button_release_event_t *e;
    405 
    406 	e = (xcb_button_release_event_t *)ev;
    407 	if (verbose)
    408 		fprintf(stderr, "%s 0x%08x %d\n", XEV(e), e->event, e->detail);
    409 
    410 	/* only respond to release events for the current grab mode */
    411 	if (cursor.mode != GRAB_NONE && e->detail != cursor.b)
    412 		return -1;
    413 
    414 	if (xcb_cursor_context_new(conn, scrn, &cx) < 0) {
    415 		fprintf(stderr, "cannot instantiate cursor\n");
    416 		exit(1);
    417 	}
    418 
    419 	p = xcb_cursor_load_cursor(cx, xhair[XHAIR_DFLT]);
    420 	xcb_change_window_attributes(conn, e->event, XCB_CW_CURSOR, &p);
    421 	xcb_ungrab_pointer(conn, XCB_CURRENT_TIME);
    422 
    423 	xcb_cursor_context_free(cx);
    424 
    425 	switch (e->detail) {
    426 	case 1:
    427 		w = wm_get_attribute(curwid, ATTR_W);
    428 		h = wm_get_attribute(curwid, ATTR_H);
    429 		wm_teleport(curwid, e->root_x - cursor.x, e->root_y - cursor.y, w, h);
    430 		break;
    431 	case 2:
    432 		x = MIN(e->root_x,cursor.x);
    433 		y = MIN(e->root_y,cursor.y);
    434 		w = MAX(e->root_x,cursor.x) - x;
    435 		h = MAX(e->root_y,cursor.y) - y;
    436 		wm_teleport(curwid, x, y, w, h);
    437 		break;
    438 	case 3:
    439 		x = wm_get_attribute(curwid, ATTR_X);
    440 		y = wm_get_attribute(curwid, ATTR_Y);
    441 		wm_teleport(curwid, x, y, e->root_x - x, e->root_y - y);
    442 		break;
    443 	}
    444 
    445 	cursor.x = 0;
    446 	cursor.y = 0;
    447 	cursor.b = 0;
    448 	cursor.mode = GRAB_NONE;
    449 
    450 	wm_restack(curwid, XCB_STACK_MODE_ABOVE);
    451 
    452 	/* clear last drawn rectangle to avoid leaving artefacts */
    453 	outline(scrn->root, 0, 0, 0, 0);
    454 
    455 	return 0;
    456 }
    457 
    458 /*
    459  * When the pointer is grabbed, every move triggers a XCB_MOTION_NOTIFY.
    460  * Events are reported for every single move by 1 pixel.
    461  *
    462  * This can spam a huge lot of events, and treating them all can be
    463  * resource hungry and make the interface feels laggy.
    464  * To get around this, we must ignore some of these events. This is done
    465  * by using the `time` attribute, and only processing new events every
    466  * X milliseconds.
    467  *
    468  * This callback is different from the others because it does not uses
    469  * the ID of the window that reported the event, but an ID previously
    470  * saved in cb_mouse_press().
    471  * This makes sense as we want to move the last window we clicked on,
    472  * and not the window we are moving over.
    473  */
    474 int
    475 cb_motion(xcb_generic_event_t *ev)
    476 {
    477 	int x, y, w, h;
    478 	static xcb_timestamp_t lasttime = 0;
    479 	xcb_motion_notify_event_t *e;
    480 
    481 	e = (xcb_motion_notify_event_t *)ev;
    482 
    483 	/* ignore some motion events if they happen too often */
    484 	if (e->time - lasttime < 32)
    485 		return 0;
    486 
    487 	if (curwid == scrn->root)
    488 		return -1;
    489 
    490 	if (verbose)
    491 		fprintf(stderr, "%s 0x%08x %d,%d\n", XEV(e), curwid,
    492 			e->root_x, e->root_y);
    493 
    494 	lasttime = e->time;
    495 
    496 	switch (e->state & (XCB_BUTTON_MASK_1|XCB_BUTTON_MASK_2|XCB_BUTTON_MASK_3)) {
    497 	case XCB_BUTTON_MASK_1:
    498 		x = e->root_x - cursor.x;
    499 		y = e->root_y - cursor.y;
    500 		w = wm_get_attribute(curwid, ATTR_W);
    501 		h = wm_get_attribute(curwid, ATTR_H);
    502 		outline(scrn->root, x, y, w, h);
    503 		break;
    504 	case XCB_BUTTON_MASK_2:
    505 		x = MIN(cursor.x, e->root_x);
    506 		y = MIN(cursor.y, e->root_y);
    507 		w = MAX(cursor.x - e->root_x, e->root_x - cursor.x);
    508 		h = MAX(cursor.y - e->root_y, e->root_y - cursor.y);
    509 		outline(scrn->root, x, y, w, h);
    510 		break;
    511 	case XCB_BUTTON_MASK_3:
    512 		x = wm_get_attribute(curwid, ATTR_X);
    513 		y = wm_get_attribute(curwid, ATTR_Y);
    514 		w = e->root_x - x;
    515 		h = e->root_y - y;
    516 		outline(scrn->root, x, y, w, h);
    517 		break;
    518 	default:
    519 		return -1;
    520 	}
    521 
    522 	return 0;
    523 }
    524 
    525 /*
    526  * Each time the pointer moves from one window to another, an
    527  * XCB_ENTER_NOTIFY event is fired. This is used to switch input focus
    528  * between windows to follow where the pointer is.
    529  */
    530 int
    531 cb_enter(xcb_generic_event_t *ev)
    532 {
    533 	xcb_enter_notify_event_t *e;
    534 
    535 	e = (xcb_enter_notify_event_t *)ev;
    536 
    537 	if (wm_is_ignored(e->event))
    538 		return 0;
    539 
    540 	if (cursor.mode != GRAB_NONE)
    541 		return 0;
    542 
    543 	if (verbose)
    544 		fprintf(stderr, "%s 0x%08x\n", XEV(e), e->event);
    545 
    546 	return wm_set_focus(e->event);
    547 }
    548 
    549 /*
    550  * Whenever the input focus change from one window to another, both an
    551  * XCB_FOCUS_OUT and XCB_FOCUS_IN are fired.
    552  * This is the occasion to change the border color to represent focus.
    553  */
    554 int
    555 cb_focus(xcb_generic_event_t *ev)
    556 {
    557 	xcb_focus_in_event_t *e;
    558 
    559 	e = (xcb_focus_in_event_t *)ev;
    560 
    561 	if (verbose)
    562 		fprintf(stderr, "%s 0x%08x\n", XEV(e), e->event);
    563 
    564 	switch(e->response_type & ~0x80) {
    565 	case XCB_FOCUS_IN:
    566 		curwid = e->event;
    567 		return wm_set_border(-1, border_color_active, e->event);
    568 		break; /* NOTREACHED */
    569 	case XCB_FOCUS_OUT:
    570 		return wm_set_border(-1, border_color, e->event);
    571 		break; /* NOTREACHED */
    572 	}
    573 
    574 	return -1;
    575 }
    576 
    577 /*
    578  * XCB_CONFIGURE_REQUEST is triggered by every window that wants to
    579  * change its attributes like size, stacking order or border.
    580  * These must now be handled by the WM because of the
    581  * XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT registration.
    582  */
    583 int
    584 cb_configreq(xcb_generic_event_t *ev)
    585 {
    586 	int x, y, w, h;
    587 	xcb_configure_request_event_t *e;
    588 
    589 	e = (xcb_configure_request_event_t *)ev;
    590 
    591 	if (verbose)
    592 		fprintf(stderr, "%s 0x%08x 0x%08x:%dx%d+%d+%d\n",
    593 			XEV(e), e->parent, e->window,
    594 			e->width, e->height,
    595 			e->x, e->y);
    596 
    597 	x = wm_get_attribute(e->window, ATTR_X);
    598 	y = wm_get_attribute(e->window, ATTR_Y);
    599 	w = wm_get_attribute(e->window, ATTR_W);
    600 	h = wm_get_attribute(e->window, ATTR_H);
    601 
    602 	if (e->value_mask & XCB_CONFIG_WINDOW_X) x = e->x;
    603 	if (e->value_mask & XCB_CONFIG_WINDOW_Y) y = e->y;
    604 	if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH)  w = e->width;
    605 	if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) h = e->height;
    606 
    607 	wm_teleport(e->window, x, y, w, h);
    608 
    609 	if (e->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH)
    610 		wm_set_border(e->border_width, border_color, e->window);
    611 
    612 	if (e->value_mask & XCB_CONFIG_WINDOW_STACK_MODE)
    613 		wm_restack(e->window, e->stack_mode);
    614 
    615 	return 0;
    616 }
    617 
    618 int
    619 cb_configure(xcb_generic_event_t *ev)
    620 {
    621 	xcb_configure_notify_event_t *e;
    622 
    623 	e = (xcb_configure_notify_event_t *)ev;
    624 
    625 	if (verbose)
    626 		fprintf(stderr, "%s 0x%08x %dx%d+%d+%d\n",
    627 			XEV(e), e->window,
    628 			e->width, e->height,
    629 			e->x, e->y);
    630 
    631 	/* update screen size when root window's size change */
    632 	if (e->window == scrn->root) {
    633 		scrn->width_in_pixels = e->width;
    634 		scrn->height_in_pixels = e->height;
    635 	}
    636 
    637 	return 0;
    638 }
    639 
    640 /*
    641  * This functions uses the ev_callback_t structure to call out a specific
    642  * callback function for each EVENT fired.
    643  */
    644 int
    645 ev_callback(xcb_generic_event_t *ev)
    646 {
    647 	uint8_t i;
    648 	uint32_t type;
    649 
    650 	if (!ev)
    651 		return -1;
    652 
    653 	type = ev->response_type & ~0x80;
    654 	for (i=0; i<LEN(cb); i++)
    655 		if (type == cb[i].type)
    656 			return cb[i].handle(ev);
    657 
    658 	return cb_default(ev);
    659 }
    660 
    661 int
    662 main (int argc, char *argv[])
    663 {
    664 	int mask;
    665 	char *argv0;
    666 	xcb_generic_event_t *ev = NULL;
    667 
    668 	ARGBEGIN {
    669 	case 'v':
    670 		verbose++;
    671 		break;
    672 	case 'h':
    673 		usage(argv0);
    674 		return 0;
    675 		break; /* NOTREACHED */
    676 	default:
    677 		usage(argv0);
    678 		return -1;
    679 		break; /* NOTREACHED */
    680 	} ARGEND;
    681 
    682 	wm_init_xcb();
    683 	wm_get_screen();
    684 
    685 	curwid = scrn->root;
    686 
    687 	/* needed to get notified of windows creation */
    688 	mask = XCB_EVENT_MASK_STRUCTURE_NOTIFY
    689 		| XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY
    690 		| XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT;
    691 
    692 	if (!wm_reg_window_event(scrn->root, mask)) {
    693 		fprintf(stderr, "Cannot redirect root window event.\n");
    694 		return -1;
    695 	}
    696 
    697 	xcb_grab_button(conn, 0, scrn->root, XCB_EVENT_MASK_BUTTON_PRESS,
    698 		XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, scrn->root,
    699 		XCB_NONE, XCB_BUTTON_INDEX_ANY, modifier);
    700 
    701 	takeover();
    702 
    703 	for (;;) {
    704 		xcb_flush(conn);
    705 		ev = xcb_wait_for_event(conn);
    706 		if (!ev)
    707 			break;
    708 
    709 		ev_callback(ev);
    710 		free(ev);
    711 	}
    712 
    713 	return wm_kill_xcb();
    714 }