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 }