libwm

A small library for X window manipulation
git clone git://git.zepp.club/libwm.git
Log | Files | Refs | README | LICENSE

libwm.c (11292B)


      1 #include <xcb/xcb.h>
      2 #include <xcb/xcb_cursor.h>
      3 #include <xcb/randr.h>
      4 #include <stdlib.h>
      5 #include <stdio.h>
      6 #include <string.h>
      7 
      8 #include "wm.h"
      9 
     10 int
     11 wm_init_xcb()
     12 {
     13 	conn = xcb_connect(NULL, NULL);
     14 	if (xcb_connection_has_error(conn))
     15 		return -1;
     16 	return 0;
     17 }
     18 
     19 int
     20 wm_kill_xcb()
     21 {
     22 	if (!conn)
     23 		return -1;
     24 	xcb_disconnect(conn);
     25 	return 0;
     26 }
     27 
     28 int
     29 wm_is_alive(xcb_window_t wid)
     30 {
     31 	xcb_get_window_attributes_cookie_t c;
     32 	xcb_get_window_attributes_reply_t  *r;
     33 
     34 	c = xcb_get_window_attributes(conn, wid);
     35 	r = xcb_get_window_attributes_reply(conn, c, NULL);
     36 
     37 	if (r == NULL)
     38 		return 0;
     39 
     40 	free(r);
     41 	return 1;
     42 }
     43 
     44 int
     45 wm_is_mapped(xcb_window_t wid)
     46 {
     47 	int ms;
     48 	xcb_get_window_attributes_cookie_t c;
     49 	xcb_get_window_attributes_reply_t  *r;
     50 
     51 	c = xcb_get_window_attributes(conn, wid);
     52 	r = xcb_get_window_attributes_reply(conn, c, NULL);
     53 
     54 	if (r == NULL)
     55 		return 0;
     56 
     57 	ms = r->map_state;
     58 
     59 	free(r);
     60 	return ms == XCB_MAP_STATE_VIEWABLE;
     61 }
     62 
     63 int
     64 wm_is_ignored(xcb_window_t wid)
     65 {
     66 	int or;
     67 	xcb_get_window_attributes_cookie_t c;
     68 	xcb_get_window_attributes_reply_t  *r;
     69 
     70 	c = xcb_get_window_attributes(conn, wid);
     71 	r = xcb_get_window_attributes_reply(conn, c, NULL);
     72 
     73 	if (r == NULL)
     74 		return 0;
     75 
     76 	or = r->override_redirect;
     77 
     78 	free(r);
     79 	return or;
     80 }
     81 
     82 int
     83 wm_is_listable(xcb_window_t wid, int mask)
     84 {
     85 	if (!mask && wm_is_mapped (wid) && !wm_is_ignored(wid))
     86 		return 1;
     87 	if ((mask & LIST_ALL))
     88 		return 1;
     89 	if (!wm_is_mapped (wid) && mask & LIST_HIDDEN)
     90 		return 1;
     91 	if (wm_is_ignored(wid) && mask & LIST_IGNORE)
     92 		return 1;
     93 
     94 	return 0;
     95 }
     96 
     97 int
     98 wm_get_screen()
     99 {
    100 	scrn = xcb_setup_roots_iterator(xcb_get_setup(conn)).data;
    101 	if (scrn == NULL)
    102 		return -1;
    103 	return 0;
    104 }
    105 
    106 int
    107 wm_get_windows(xcb_window_t wid, xcb_window_t **l)
    108 {
    109 	uint32_t childnum = 0;
    110 	xcb_query_tree_cookie_t c;
    111 	xcb_query_tree_reply_t *r;
    112 
    113 	c = xcb_query_tree(conn, wid);
    114 	r = xcb_query_tree_reply(conn, c, NULL);
    115 	if (r == NULL)
    116 		return -1;
    117 
    118 	*l = malloc(sizeof(xcb_window_t) * r->children_len);
    119 	memcpy(*l, xcb_query_tree_children(r),
    120 			sizeof(xcb_window_t) * r->children_len);
    121 
    122 	childnum = r->children_len;
    123 
    124 	free(r);
    125 	return childnum;
    126 }
    127 
    128 xcb_window_t
    129 wm_get_focus(void)
    130 {
    131 	xcb_window_t wid = 0;
    132 	xcb_get_input_focus_cookie_t c;
    133 	xcb_get_input_focus_reply_t *r;
    134 
    135 	c = xcb_get_input_focus(conn);
    136 	r = xcb_get_input_focus_reply(conn, c, NULL);
    137 	if (r == NULL)
    138 		return scrn->root;
    139 
    140 	wid = r->focus;
    141 	free(r);
    142 	return wid;
    143 }
    144 
    145 
    146 int
    147 wm_get_attribute(xcb_window_t wid, int attr)
    148 {
    149 	xcb_get_geometry_cookie_t c;
    150 	xcb_get_geometry_reply_t *r;
    151 
    152 	c = xcb_get_geometry(conn, wid);
    153 	r = xcb_get_geometry_reply(conn, c, NULL);
    154 
    155 	if (r == NULL)
    156 		return -1;
    157 
    158 	switch (attr) {
    159 	case ATTR_X:
    160 		attr = r->x;
    161 		break;
    162 	case ATTR_Y:
    163 		attr = r->y;
    164 		break;
    165 	case ATTR_W:
    166 		attr = r->width;
    167 		break;
    168 	case ATTR_H:
    169 		attr = r->height;
    170 		break;
    171 	case ATTR_B:
    172 		attr = r->border_width;
    173 		break;
    174 	case ATTR_D:
    175 		attr = r->depth;
    176 		break;
    177 	}
    178 
    179 	free(r);
    180 	return attr;
    181 }
    182 
    183 xcb_atom_t
    184 wm_add_atom(char *name, size_t len)
    185 {
    186 	xcb_atom_t atom;
    187 	xcb_intern_atom_cookie_t c;
    188 	xcb_intern_atom_reply_t *r;
    189 
    190 	c = xcb_intern_atom(conn, 0, len, name);
    191 	r = xcb_intern_atom_reply(conn, c, NULL);
    192 	if (!r)
    193 		return 0;
    194 
    195 	atom = r->atom;
    196 	free(r);
    197 
    198 	return atom;
    199 }
    200 
    201 int
    202 wm_set_atom(xcb_window_t wid, xcb_atom_t atom, xcb_atom_t type, size_t len, void *data)
    203 {
    204 	int errcode;
    205 	xcb_void_cookie_t c;
    206 	xcb_generic_error_t *e;
    207 
    208 	c = xcb_change_property_checked(conn, XCB_PROP_MODE_REPLACE,
    209 		wid, atom, type, 32, len, data);
    210 	e = xcb_request_check(conn, c);
    211 	if (!e)
    212 		return 0;
    213 
    214 	errcode = e->error_code;
    215 	free(e);
    216 
    217 	return errcode;
    218 }
    219 
    220 void *
    221 wm_get_atom(xcb_window_t wid, xcb_atom_t atom, xcb_atom_t type, size_t *len)
    222 {
    223 	void *d;
    224 	size_t n;
    225 	xcb_get_property_cookie_t c;
    226 	xcb_get_property_reply_t *r;
    227 
    228 	c = xcb_get_property(conn, 0, wid, atom, type, 0, 32);
    229 	r = xcb_get_property_reply(conn, c, NULL);
    230 	if (!r)
    231 		return NULL;
    232 
    233 	if (!(n = xcb_get_property_value_length(r))) {
    234 		free(r);
    235 		return NULL;
    236 	}
    237 
    238 	if (len)
    239 		*len = n;
    240 
    241 	d = xcb_get_property_value(r);
    242 
    243 	return d;
    244 }
    245 
    246 char *
    247 wm_get_atom_name(xcb_atom_t atom, size_t *len)
    248 {
    249 	size_t n;
    250 	char *name;
    251 	xcb_get_atom_name_cookie_t c;
    252 	xcb_get_atom_name_reply_t *r;
    253 
    254 	c = xcb_get_atom_name(conn, atom);
    255 	r = xcb_get_atom_name_reply(conn, c, NULL);
    256 	if (!r)
    257 		return NULL;
    258 
    259 	n = xcb_get_atom_name_name_length(r) + 1;
    260 	name = malloc(xcb_get_atom_name_name_length(r) + 1);
    261 	if (!name) {
    262 		free(r);
    263 		return NULL;
    264 	}
    265 
    266 	if (len)
    267 		*len = n;
    268 
    269 	memset(name, 0, xcb_get_atom_name_name_length(r) + 1);
    270 	strncpy(name, xcb_get_atom_name_name(r), xcb_get_atom_name_name_length(r));
    271 	free(r);
    272 
    273 	return name;
    274 }
    275 
    276 
    277 int
    278 wm_get_cursor(int mode, uint32_t wid, int *x, int *y)
    279 {
    280 	xcb_query_pointer_reply_t *r;
    281 	xcb_query_pointer_cookie_t c;
    282 
    283 	c = xcb_query_pointer(conn, wid);
    284 	r = xcb_query_pointer_reply(conn, c, NULL);
    285 
    286 	if (r == NULL)
    287 		return -1;
    288 
    289 	if (r->child != XCB_NONE) {
    290 		*x = r->win_x;
    291 		*y = r->win_y;
    292 	} else {
    293 		*x = r->root_x;
    294 		*y = r->root_y;
    295 	}
    296 
    297 	return 0;
    298 }
    299 
    300 int
    301 wm_set_border(int width, int color, xcb_window_t wid)
    302 {
    303 	uint32_t values[1];
    304 	int mask;
    305 
    306 	/* change width if >= 0 */
    307 	if (width > -1) {
    308 		values[0] = width;
    309 		mask = XCB_CONFIG_WINDOW_BORDER_WIDTH;
    310 		xcb_configure_window(conn, wid, mask, values);
    311 	}
    312 
    313 	/*
    314 	 * color is an ARGB representation (eg. 0x80ff0000) for
    315 	 * translucent red.
    316 	 * Absolutely all values are valid color representations, so we
    317 	 * will set it no matter what.
    318 	 */
    319 	values[0] = color;
    320 	mask = XCB_CW_BORDER_PIXEL;
    321 	xcb_change_window_attributes(conn, wid, mask, values);
    322 
    323 	return 0;
    324 }
    325 
    326 int
    327 wm_set_cursor(int x, int y, int mode)
    328 {
    329 	xcb_warp_pointer(conn, XCB_NONE, mode ? XCB_NONE : scrn->root,
    330 			0, 0, 0, 0, x, y);
    331 	return 0;
    332 }
    333 
    334 int
    335 wm_teleport(xcb_window_t wid, int x, int y, int w, int h)
    336 {
    337 	uint32_t values[4];
    338 	uint32_t mask =   XCB_CONFIG_WINDOW_X
    339 	                | XCB_CONFIG_WINDOW_Y
    340 	                | XCB_CONFIG_WINDOW_WIDTH
    341 	                | XCB_CONFIG_WINDOW_HEIGHT;
    342 	values[0] = x;
    343 	values[1] = y;
    344 	values[2] = w;
    345 	values[3] = h;
    346 	xcb_configure_window(conn, wid, mask, values);
    347 
    348 	return 0;
    349 }
    350 
    351 int
    352 wm_move(xcb_window_t wid, int mode, int x, int y)
    353 {
    354 	int curx, cury, curw, curh, curb;
    355 
    356 	if (!wm_is_mapped(wid) || wid == scrn->root)
    357 		return -1;
    358 
    359 	curb = wm_get_attribute(wid, ATTR_B);
    360 	curx = wm_get_attribute(wid, ATTR_X);
    361 	cury = wm_get_attribute(wid, ATTR_Y);
    362 	curw = wm_get_attribute(wid, ATTR_W);
    363 	curh = wm_get_attribute(wid, ATTR_H);
    364 
    365 	if (mode == RELATIVE) {
    366 		x += curx;
    367 		y += cury;
    368 	}
    369 
    370 	/* the following prevent windows from moving off the screen */
    371 	if (x < 0)
    372 		x = 0;
    373 	else if (x > scrn->width_in_pixels - curw - 2*curb)
    374 		x = scrn->width_in_pixels - curw - 2*curb;
    375 
    376 	if (y < 0)
    377 		y = 0;
    378 	else if (y > scrn->height_in_pixels - curh - 2*curb)
    379 		y = scrn->height_in_pixels - curh - 2*curb;
    380 
    381 	wm_teleport(wid, x, y, curw, curh);
    382 	return 0;
    383 }
    384 
    385 int
    386 wm_set_override(xcb_window_t wid, int or)
    387 {
    388 	uint32_t mask = XCB_CW_OVERRIDE_REDIRECT;
    389 	uint32_t val[] = { or };
    390 
    391 	xcb_change_window_attributes(conn, wid, mask, val);
    392 
    393 	return 0;
    394 }
    395 
    396 
    397 int
    398 wm_remap(xcb_window_t wid, int mode)
    399 {
    400 	switch (mode) {
    401 	case MAP:
    402 		xcb_map_window(conn, wid);
    403 		break;
    404 	case UNMAP:
    405 		xcb_unmap_window(conn, wid);
    406 		break;
    407 	case TOGGLE:
    408 		if (wm_is_mapped(wid))
    409 			xcb_unmap_window(conn, wid);
    410 		else
    411 			xcb_map_window(conn, wid);
    412 		break;
    413 	}
    414 
    415 	return 0;
    416 }
    417 
    418 int
    419 wm_resize(xcb_window_t wid, int mode, int w, int h)
    420 {
    421 	int curx, cury, curw, curh, curb;
    422 
    423 	if (!wm_is_mapped(wid) || wid == scrn->root)
    424 		return -1;
    425 
    426 	curb = wm_get_attribute(wid, ATTR_B);
    427 	curx = wm_get_attribute(wid, ATTR_X);
    428 	cury = wm_get_attribute(wid, ATTR_Y);
    429 	curw = wm_get_attribute(wid, ATTR_W);
    430 	curh = wm_get_attribute(wid, ATTR_H);
    431 
    432 	if (mode == RELATIVE) {
    433 		w += curw;
    434 		h += curh;
    435 	} else {
    436 		w -= curx;
    437 		h -= cury;
    438 	}
    439 
    440 	/*
    441 	 * The following prevent windows from growing out of the screen, or
    442 	 * having a negative size
    443 	 */
    444 	if (w < 0)
    445 		w = curw;
    446 	if (curx + w >  scrn->width_in_pixels)
    447 		w = scrn->width_in_pixels - curx - 2*curb;
    448 
    449 	if (h < 0)
    450 		h = curh;
    451 	if (cury + h > scrn->height_in_pixels)
    452 		h = scrn->height_in_pixels - cury - 2*curb;
    453 
    454 	wm_teleport(wid, curx, cury, w, h);
    455 	return 0;
    456 }
    457 
    458 int
    459 wm_restack(xcb_window_t wid, uint32_t mode)
    460 {
    461 	uint32_t values[1] = { mode };
    462 	xcb_configure_window(conn, wid, XCB_CONFIG_WINDOW_STACK_MODE, values);
    463 	return 0;
    464 }
    465 
    466 int
    467 wm_set_focus(xcb_window_t wid)
    468 {
    469 	xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, wid,
    470 	                    XCB_CURRENT_TIME);
    471 	return 0;
    472 }
    473 
    474 int
    475 wm_reg_window_event(xcb_window_t wid, uint32_t mask)
    476 {
    477 	uint32_t val[] = { mask };
    478 	xcb_void_cookie_t c;
    479 	xcb_generic_error_t *e;
    480 
    481 	c = xcb_change_window_attributes_checked(conn, wid, XCB_CW_EVENT_MASK, val);
    482 	e = xcb_request_check(conn, c);
    483 	if (!e)
    484 		return -1;
    485 
    486 	free(e);
    487 	return 0;
    488 }
    489 
    490 
    491 int
    492 wm_reg_cursor_event(xcb_window_t wid, uint32_t mask, char *cursor)
    493 {
    494 	xcb_cursor_t p;
    495 	xcb_cursor_context_t *cx;
    496 	xcb_grab_pointer_cookie_t c;
    497 	xcb_grab_pointer_reply_t *r;
    498 
    499 	p = XCB_NONE;
    500 	if (cursor) {
    501 		if (xcb_cursor_context_new(conn, scrn, &cx) < 0)
    502 			return -1;
    503 
    504 		p = xcb_cursor_load_cursor(cx, cursor);
    505 	}
    506 
    507 	c = xcb_grab_pointer(conn, 1, scrn->root, mask,
    508 		XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC,
    509 		XCB_NONE, p, XCB_CURRENT_TIME);
    510 
    511 	r = xcb_grab_pointer_reply(conn, c, NULL);
    512 	if (!r || r->status != XCB_GRAB_STATUS_SUCCESS)
    513 		return -1;
    514 
    515 	xcb_cursor_context_free(cx);
    516 	return 0;
    517 }
    518 
    519 int
    520 wm_get_monitors(xcb_window_t wid, int *l)
    521 {
    522 	int n;
    523 	xcb_randr_get_monitors_cookie_t c;
    524 	xcb_randr_get_monitors_reply_t *r;
    525 	xcb_randr_monitor_info_iterator_t i;
    526 
    527 	/* get_active: ignore inactive monitors */
    528 	c = xcb_randr_get_monitors(conn, wid, 0);
    529 	r = xcb_randr_get_monitors_reply(conn, c, NULL);
    530 	if (!r)
    531 		return -1;
    532 
    533 	i = xcb_randr_get_monitors_monitors_iterator(r);
    534 	if (!i.data)
    535 		return 0;
    536 
    537 	for (n = 0; l && i.rem > 0; xcb_randr_monitor_info_next(&i))
    538 		l[n++] = i.index;
    539 
    540 	n = r->nMonitors;
    541 	free(r);
    542 
    543 	return n;
    544 }
    545 
    546 xcb_randr_monitor_info_t *
    547 wm_get_monitor(int index)
    548 {
    549 	xcb_randr_monitor_info_t *monitor;
    550 	xcb_randr_get_monitors_cookie_t c;
    551 	xcb_randr_get_monitors_reply_t *r;
    552 	xcb_randr_monitor_info_iterator_t i;
    553 
    554 	/* get_active: ignore inactive monitors */
    555 	c = xcb_randr_get_monitors(conn, scrn->root, 0);
    556 	r = xcb_randr_get_monitors_reply(conn, c, NULL);
    557 	if (!r)
    558 		return NULL;
    559 
    560 	i = xcb_randr_get_monitors_monitors_iterator(r);
    561 	if (!i.data)
    562 		return NULL;
    563 
    564 	for (; i.rem > 0; xcb_randr_monitor_info_next(&i)) {
    565 		if (i.index != index)
    566 			continue;
    567 
    568 		monitor = calloc(1, sizeof(*monitor));
    569 		if (!monitor)
    570 			return NULL;
    571 
    572 		memcpy(monitor, i.data, sizeof(*monitor));
    573 		free(r);
    574 		return monitor;
    575 	}
    576 
    577 	free(r);
    578 	return NULL;
    579 }
    580 
    581 int
    582 wm_find_monitor(int x, int y)
    583 {
    584 	/* patch me if you use more than 64 monitors, and get a reward! */
    585 	int n, monitors[64];
    586 	xcb_randr_monitor_info_t *p;
    587 
    588 	n = wm_get_monitors(scrn->root, monitors);
    589 
    590 	/*
    591 	 * When you have multiple monitors, like so:
    592 	 * - 1920x1080+0+0
    593 	 * - 1920x1080+1920+0
    594 	 * the pixel located at 1920,500 would match both the first AND
    595 	 * second monitor. By crawling monitors backward it ensures that
    596 	 * the "farthest" monitor will match first.
    597 	 * Also I love that backward loop notation.
    598 	 */
    599 	while (n --> 0) {
    600 		p = wm_get_monitor(monitors[n]);
    601 		if (!p)
    602 			continue;
    603 
    604 		if (x >= p->x  && x <= p->x + p->width
    605 		 && y >= p->y  && y <= p->y + p->height) {
    606 			free(p);
    607 			return monitors[n];
    608 		}
    609 		free(p);
    610 	}
    611 
    612 	return -1;
    613 }