A tiling window manager
at master 794 lines 19 kB view raw
1/* 2 * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts <sabetts@vcn.bc.ca> 3 * Copyright (C) 2016 Mathieu OTHACEHE <m.othacehe@gmail.com> 4 * 5 * This program is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License as published by the Free 7 * Software Foundation; either version 2 of the License, or (at your option) 8 * any later version. 9 * 10 * This program is distributed in the hope that it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 * more details. 14 * 15 * You should have received a copy of the GNU General Public License along with 16 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 17 * Place, Suite 330, Boston, MA 02111-1307 USA. 18 */ 19 20#include <string.h> 21#include <err.h> 22#include <X11/cursorfont.h> 23 24#include "sdorfehs.h" 25 26static void init_screen(rp_screen *s); 27 28int 29screen_width(rp_screen *s) 30{ 31 return s->width - defaults.padding_right - defaults.padding_left; 32} 33 34int 35screen_height(rp_screen *s) 36{ 37 int ret = s->height - defaults.padding_bottom - defaults.padding_top; 38 39 if (defaults.bar_sticky && screen_primary() == s) { 40 ret -= sticky_bar_height(s); 41 42 if (!defaults.bar_in_padding) 43 switch (defaults.bar_location) { 44 case NorthEastGravity: 45 case NorthGravity: 46 case NorthWestGravity: 47 ret -= defaults.padding_top; 48 break; 49 } 50 } 51 52 return ret; 53} 54 55int 56screen_left(rp_screen *s) 57{ 58 return s->left + defaults.padding_left; 59} 60 61int 62screen_right(rp_screen *s) 63{ 64 return s->left + s->width - defaults.padding_right; 65} 66 67int 68screen_top(rp_screen *s) 69{ 70 int ret = s->top + defaults.padding_top; 71 72 if (defaults.bar_sticky && screen_primary() == s) { 73 switch (defaults.bar_location) { 74 case NorthEastGravity: 75 case NorthGravity: 76 case NorthWestGravity: 77 ret += sticky_bar_height(s); 78 if (!defaults.bar_in_padding) 79 ret += defaults.padding_top; 80 break; 81 } 82 } 83 84 return ret; 85} 86 87int 88screen_bottom(rp_screen *s) 89{ 90 int ret = s->top + s->height - defaults.padding_bottom; 91 92 if (defaults.bar_sticky && screen_primary() == s) { 93 switch (defaults.bar_location) { 94 case SouthEastGravity: 95 case SouthGravity: 96 case SouthWestGravity: 97 ret -= sticky_bar_height(s); 98 break; 99 } 100 } 101 102 return ret; 103} 104 105/* Given a root window, return the rp_screen struct */ 106rp_screen * 107find_screen(Window w) 108{ 109 rp_screen *cur; 110 111 list_for_each_entry(cur, &rp_screens, node) { 112 if (cur->root == w) 113 return cur; 114 } 115 116 return NULL; 117} 118 119/* Given a window attr, return the rp_screen struct */ 120rp_screen * 121find_screen_by_attr(XWindowAttributes attr) 122{ 123 rp_screen *cur; 124 125 list_for_each_entry(cur, &rp_screens, node) { 126 if (attr.x >= cur->left && 127 attr.x <= cur->left + cur->width && 128 attr.y >= cur->top && 129 attr.y <= cur->top + cur->height) 130 return cur; 131 } 132 133 return NULL; 134} 135 136/* Return 1 if w is a root window of any of the screens. */ 137int 138is_a_root_window(unsigned int w) 139{ 140 rp_screen *cur; 141 142 list_for_each_entry(cur, &rp_screens, node) { 143 if (cur->root == w) 144 return 1; 145 } 146 147 return 0; 148} 149 150rp_screen * 151screen_number(int number) 152{ 153 rp_screen *cur; 154 155 list_for_each_entry(cur, &rp_screens, node) { 156 if (cur->number == number) 157 return cur; 158 } 159 160 return NULL; 161} 162 163static int 164screen_cmp(void *priv, struct list_head *a, struct list_head *b) 165{ 166 rp_screen *sc_a = container_of(a, typeof(*sc_a), node); 167 rp_screen *sc_b = container_of(b, typeof(*sc_b), node); 168 169 if (sc_a->left < sc_b->left) 170 return -1; 171 if (sc_a->left > sc_b->left) 172 return 1; 173 174 if (sc_a->top > sc_b->top) 175 return -1; 176 if (sc_a->top < sc_b->top) 177 return 1; 178 179 return 0; 180} 181 182void 183screen_sort(void) 184{ 185 return list_sort(NULL, &rp_screens, screen_cmp); 186} 187 188static void 189screen_set_numbers(void) 190{ 191 rp_screen *cur; 192 193 list_for_each_entry(cur, &rp_screens, node) { 194 cur->number = numset_request(rp_glob_screen.numset); 195 } 196} 197 198rp_screen * 199screen_primary(void) 200{ 201 rp_screen *first, *cur; 202 203 /* By default, take the first screen as current screen */ 204 list_first(first, &rp_screens, node); 205 206 if (!rp_have_xrandr) 207 return first; 208 209 list_for_each_entry(cur, &rp_screens, node) 210 if (xrandr_is_primary(cur)) 211 return cur; 212 213 /* nothing is primary? */ 214 return first; 215} 216 217static void 218screen_select_primary(void) 219{ 220 rp_screen *cur = screen_primary(); 221 222 if (!rp_current_screen) 223 rp_current_screen = cur; 224 225 rp_current_screen = cur; 226 PRINT_DEBUG(("Xrandr primary screen %d detected\n", 227 rp_current_screen->number)); 228} 229 230static void 231init_global_screen(rp_global_screen *s) 232{ 233 XColor color, junk; 234 int screen_num; 235 236 screen_num = DefaultScreen(dpy); 237 s->root = RootWindow(dpy, screen_num); 238 239 s->numset = numset_new(); 240 s->fgcolor = WhitePixel(dpy, screen_num); 241 242 if (XAllocNamedColor(dpy, DefaultColormap(dpy, screen_num), 243 defaults.fgcolor_string, &color, &junk)) 244 rp_glob_screen.fgcolor = color.pixel | (0xff << 24); 245 else { 246 warnx("failed allocating fgcolor %s", defaults.fgcolor_string); 247 s->fgcolor = WhitePixel(dpy, screen_num); 248 } 249 250 if (XAllocNamedColor(dpy, DefaultColormap(dpy, screen_num), 251 defaults.bgcolor_string, &color, &junk)) 252 rp_glob_screen.bgcolor = color.pixel | (0xff << 24); 253 else { 254 warnx("failed allocating bgcolor %s", defaults.bgcolor_string); 255 s->bgcolor = BlackPixel(dpy, screen_num); 256 } 257 258 if (XAllocNamedColor(dpy, DefaultColormap(dpy, screen_num), 259 defaults.fwcolor_string, &color, &junk)) 260 rp_glob_screen.fwcolor = color.pixel | (0xff << 24); 261 else { 262 warnx("failed allocating fwcolor %s", defaults.fwcolor_string); 263 s->fwcolor = BlackPixel(dpy, screen_num); 264 } 265 266 if (XAllocNamedColor(dpy, DefaultColormap(dpy, screen_num), 267 defaults.bwcolor_string, &color, &junk)) 268 rp_glob_screen.bwcolor = color.pixel | (0xff << 24); 269 else { 270 warnx("failed allocating bwcolor %s", defaults.bwcolor_string); 271 s->bwcolor = BlackPixel(dpy, screen_num); 272 } 273 274 if (XAllocNamedColor(dpy, DefaultColormap(dpy, screen_num), 275 defaults.barbordercolor_string, &color, &junk)) 276 rp_glob_screen.bar_bordercolor = color.pixel | (0xff << 24); 277 else { 278 warnx("failed allocating barbordercolor %s", 279 defaults.barbordercolor_string); 280 s->bar_bordercolor = BlackPixel(dpy, screen_num); 281 } 282 283 s->wm_check = XCreateSimpleWindow(dpy, s->root, 0, 0, 1, 1, 284 0, 0, rp_glob_screen.bgcolor); 285 set_atom(s->wm_check, _net_supporting_wm_check, XA_WINDOW, 286 &s->wm_check, 1); 287 set_atom(s->root, _net_supporting_wm_check, XA_WINDOW, 288 &s->wm_check, 1); 289 XChangeProperty(dpy, s->root, _net_wm_name, 290 xa_utf8_string, 8, PropModeReplace, 291 (unsigned char *)PROGNAME, strlen(PROGNAME)); 292 XChangeProperty(dpy, s->wm_check, _net_wm_name, 293 xa_utf8_string, 8, PropModeReplace, 294 (unsigned char *)PROGNAME, strlen(PROGNAME)); 295} 296 297void 298init_screens(void) 299{ 300 int i; 301 int screen_count = 0; 302 int *rr_outputs = NULL; 303 rp_screen *screen; 304 305 /* Get the number of screens */ 306 if (rp_have_xrandr) { 307 screen_count = xrandr_query_screen(&rr_outputs); 308 if (!screen_count) { 309 rp_have_xrandr = 0; 310 warnx("XRandR reported no screens, not using it\n"); 311 } 312 } 313 314 if (!screen_count) 315 screen_count = ScreenCount(dpy); 316 317 /* Create our global frame numset */ 318 rp_frame_numset = numset_new(); 319 320 init_global_screen(&rp_glob_screen); 321 322 for (i = 0; i < screen_count; i++) { 323 screen = xmalloc(sizeof(*screen)); 324 memset(screen, 0, sizeof(*screen)); 325 list_add(&screen->node, &rp_screens); 326 327 if (rp_have_xrandr && rr_outputs != NULL) 328 xrandr_fill_screen(rr_outputs[i], screen); 329 else 330 xrandr_fill_screen(i, screen); 331 332 init_screen(screen); 333 } 334 335 screen_sort(); 336 screen_set_numbers(); 337 screen_select_primary(); 338 339 set_atom(rp_glob_screen.root, _net_number_of_desktops, XA_CARDINAL, 340 (unsigned long *)&defaults.vscreens, 1); 341 342 free(rr_outputs); 343} 344 345static void 346init_rat_cursor(rp_screen *s) 347{ 348 s->rat = XCreateFontCursor(dpy, XC_icon); 349} 350 351static void 352init_screen(rp_screen *s) 353{ 354 XGCValues gcv; 355 struct sbuf *buf; 356 rp_vscreen *vscreen; 357 char *colon; 358 int screen_num, x; 359 360 screen_num = DefaultScreen(dpy); 361 362 if (!rp_have_xrandr) { 363 s->left = 0; 364 s->top = 0; 365 s->width = DisplayWidth(dpy, screen_num); 366 s->height = DisplayHeight(dpy, screen_num); 367 } 368 /* 369 * Select on some events on the root window, if this fails, then there 370 * is already a WM running and the X Error handler will catch it, 371 * terminating us. 372 */ 373 XSelectInput(dpy, RootWindow(dpy, screen_num), 374 PropertyChangeMask | ColormapChangeMask 375 | SubstructureRedirectMask | SubstructureNotifyMask 376 | StructureNotifyMask); 377 XSync(dpy, False); 378 379 s->scratch_buffer = NULL; 380 381 /* Build the display string for each screen */ 382 buf = sbuf_new(0); 383 sbuf_printf(buf, "DISPLAY=%s", DisplayString(dpy)); 384 colon = strrchr(sbuf_get(buf), ':'); 385 if (colon) { 386 char *dot; 387 388 dot = strrchr(sbuf_get(buf), '.'); 389 if (!dot || dot < colon) { 390 /* 391 * no dot was found or it belongs to fqdn - append 392 * screen_num to the end 393 */ 394 sbuf_printf_concat(buf, ".%d", screen_num); 395 } 396 } 397 s->display_string = sbuf_free_struct(buf); 398 399 PRINT_DEBUG(("display string: %s\n", s->display_string)); 400 401 s->root = RootWindow(dpy, screen_num); 402 s->screen_num = screen_num; 403 s->def_cmap = DefaultColormap(dpy, screen_num); 404 s->full_screen_win = NULL; 405 406 init_rat_cursor(s); 407 408 /* Setup the GC for drawing the font. */ 409 gcv.foreground = rp_glob_screen.fgcolor; 410 gcv.background = rp_glob_screen.bgcolor; 411 gcv.function = GXcopy; 412 gcv.line_width = 1; 413 gcv.subwindow_mode = IncludeInferiors; 414 s->normal_gc = XCreateGC(dpy, s->root, 415 GCForeground | GCBackground | GCFunction 416 | GCLineWidth | GCSubwindowMode, 417 &gcv); 418 gcv.foreground = rp_glob_screen.bgcolor; 419 gcv.background = rp_glob_screen.fgcolor; 420 s->inverse_gc = XCreateGC(dpy, s->root, 421 GCForeground | GCBackground | GCFunction 422 | GCLineWidth | GCSubwindowMode, 423 &gcv); 424 425 s->xft_font = XftFontOpenName(dpy, screen_num, DEFAULT_XFT_FONT); 426 if (!s->xft_font) 427 errx(1, "failed to open default font \"%s\"", DEFAULT_XFT_FONT); 428 429 memset(s->xft_font_cache, 0, sizeof(s->xft_font_cache)); 430 431 if (!XftColorAllocName(dpy, DefaultVisual(dpy, screen_num), 432 DefaultColormap(dpy, screen_num), 433 defaults.fgcolor_string, &s->xft_fgcolor)) 434 errx(1, "failed to allocate font fg color %s", 435 defaults.fgcolor_string); 436 437 if (!XftColorAllocName(dpy, DefaultVisual(dpy, screen_num), 438 DefaultColormap(dpy, screen_num), 439 defaults.bgcolor_string, &s->xft_bgcolor)) 440 errx(1, "failed to allocate font bg color %s", 441 defaults.bgcolor_string); 442 443 /* Create the program bar window. */ 444 s->bar_is_raised = 0; 445 s->bar_window = XCreateSimpleWindow(dpy, s->root, 0, 0, 1, 1, 446 defaults.bar_border_width, rp_glob_screen.bar_bordercolor, 447 rp_glob_screen.bgcolor); 448 set_atom(s->bar_window, _net_wm_window_type, XA_ATOM, 449 &_net_wm_window_type_dock, 1); 450 XSelectInput(dpy, s->bar_window, ButtonPressMask); 451 452 /* 453 * Setup the window that will receive all keystrokes once the prefix 454 * key has been pressed. 455 */ 456 s->key_window = XCreateSimpleWindow(dpy, s->root, 0, 0, 1, 1, 0, 457 WhitePixel(dpy, screen_num), 458 BlackPixel(dpy, screen_num)); 459 set_atom(s->key_window, _net_wm_window_type, XA_ATOM, 460 &_net_wm_window_type_dock, 1); 461 XSelectInput(dpy, s->key_window, KeyPressMask | KeyReleaseMask); 462 463 /* Create the input window. */ 464 s->input_window = XCreateSimpleWindow(dpy, s->root, 0, 0, 1, 1, 465 defaults.bar_border_width, rp_glob_screen.bar_bordercolor, 466 rp_glob_screen.bgcolor); 467 set_atom(s->input_window, _net_wm_window_type, XA_ATOM, 468 &_net_wm_window_type_dock, 1); 469 XSelectInput(dpy, s->input_window, KeyPressMask | KeyReleaseMask); 470 471 /* Create the frame indicator window */ 472 s->frame_window = XCreateSimpleWindow(dpy, s->root, 1, 1, 1, 1, 473 defaults.bar_border_width, rp_glob_screen.bar_bordercolor, 474 rp_glob_screen.bgcolor); 475 set_atom(s->frame_window, _net_wm_window_type, XA_ATOM, 476 &_net_wm_window_type_tooltip, 1); 477 478 /* Create the help window */ 479 s->help_window = XCreateSimpleWindow(dpy, s->root, s->left, s->top, 480 s->width, s->height, 0, rp_glob_screen.bar_bordercolor, 481 rp_glob_screen.bgcolor); 482 set_atom(s->help_window, _net_wm_window_type, XA_ATOM, 483 &_net_wm_window_type_splash, 1); 484 XSelectInput(dpy, s->help_window, KeyPressMask); 485 486 activate_screen(s); 487 488 XSync(dpy, 0); 489 490 INIT_LIST_HEAD(&s->vscreens); 491 s->vscreens_numset = numset_new(); 492 493 for (x = 0; x < defaults.vscreens; x++) { 494 vscreen = xmalloc(sizeof(rp_vscreen)); 495 init_vscreen(vscreen, s); 496 list_add_tail(&vscreen->node, &s->vscreens); 497 498 if (x == 0) { 499 s->current_vscreen = vscreen; 500 vscreen_announce_current(vscreen); 501 } 502 } 503 504 screen_update_workarea(s); 505} 506 507void 508activate_screen(rp_screen *s) 509{ 510 XMapWindow(dpy, s->key_window); 511} 512 513void 514deactivate_screen(rp_screen *s) 515{ 516 /* Unmap its key window */ 517 XUnmapWindow(dpy, s->key_window); 518} 519 520static int 521is_rp_window_for_given_screen(Window w, rp_screen *s) 522{ 523 if (w != s->key_window && 524 w != s->bar_window && 525 w != s->input_window && 526 w != s->frame_window && 527 w != s->help_window) 528 return 0; 529 return 1; 530} 531 532int 533is_rp_window(Window w) 534{ 535 rp_screen *cur; 536 537 list_for_each_entry(cur, &rp_screens, node) { 538 if (is_rp_window_for_given_screen(w, cur)) 539 return 1; 540 } 541 542 return 0; 543} 544 545char * 546screen_dump(rp_screen *screen) 547{ 548 char *tmp; 549 struct sbuf *s; 550 551 s = sbuf_new(0); 552 if (rp_have_xrandr) 553 sbuf_printf(s, "%s ", screen->xrandr.name); 554 555 sbuf_printf_concat(s, "%d %d %d %d %d %d", 556 screen->number, 557 screen->left, 558 screen->top, 559 screen->width, 560 screen->height, 561 (rp_current_screen == screen) ? 1 : 0 /* is current? */ 562 ); 563 564 /* Extract the string and return it, and don't forget to free s. */ 565 tmp = sbuf_get(s); 566 free(s); 567 return tmp; 568} 569 570int 571screen_count(void) 572{ 573 return list_size(&rp_screens); 574} 575 576rp_screen * 577screen_next(void) 578{ 579 return list_next_entry(rp_current_screen, &rp_screens, node); 580} 581 582rp_screen * 583screen_prev(void) 584{ 585 return list_prev_entry(rp_current_screen, &rp_screens, node); 586} 587 588static void 589screen_remove_current(void) 590{ 591 rp_screen *new_screen; 592 rp_frame *new_frame; 593 rp_window *cur_win; 594 int cur_frame; 595 596 cur_win = current_window(); 597 new_screen = screen_next(); 598 599 cur_frame = new_screen->current_vscreen->current_frame; 600 new_frame = vscreen_get_frame(new_screen->current_vscreen, cur_frame); 601 602 set_active_frame(new_frame, 1); 603 604 hide_window(cur_win); 605} 606 607void 608screen_update(rp_screen *s, int left, int top, int width, int height) 609{ 610 rp_frame *f; 611 rp_vscreen *v; 612 int oldwidth, oldheight; 613 614 PRINT_DEBUG(("screen_update (left=%d, top=%d, width=%d, height=%d)\n", 615 left, top, width, height)); 616 617 oldwidth = s->width; 618 oldheight = s->height; 619 620 s->left = left; 621 s->top = top; 622 s->width = width; 623 s->height = height; 624 625 if (defaults.bar_sticky && screen_primary() == s) 626 hide_bar(s, 0); 627 628 XMoveResizeWindow(dpy, s->help_window, s->left, s->top, s->width, 629 s->height); 630 631 list_for_each_entry(v, &s->vscreens, node) { 632 list_for_each_entry(f, &v->frames, node) { 633 f->x = (f->x * width) / oldwidth; 634 f->width = (f->width * width) / oldwidth; 635 f->y = (f->y * height) / oldheight; 636 f->height = (f->height * height) / oldheight; 637 maximize_all_windows_in_frame(f); 638 } 639 } 640 641 screen_update_workarea(s); 642} 643 644void 645screen_update_frames(rp_screen *s) 646{ 647 rp_vscreen *v; 648 rp_frame *f; 649 int diff; 650 651 list_for_each_entry(v, &s->vscreens, node) { 652 list_for_each_entry(f, &v->frames, node) { 653 if (frame_left_screen_edge(f) || 654 (f->edges & EDGE_LEFT)) { 655 diff = screen_left(v->screen) - f->x; 656 f->x = screen_left(v->screen); 657 f->width -= diff; 658 } 659 660 if (frame_top_screen_edge(f) || 661 (f->edges & EDGE_TOP)) { 662 diff = screen_top(v->screen) - f->y; 663 f->y = screen_top(v->screen); 664 f->height -= diff; 665 } 666 667 if (frame_right_screen_edge(f) || 668 (f->edges & EDGE_RIGHT)) 669 f->width = screen_right(v->screen) - f->x; 670 671 if (frame_bottom_screen_edge(f) || 672 (f->edges & EDGE_BOTTOM)) 673 f->height = screen_bottom(v->screen) - f->y; 674 675 maximize_all_windows_in_frame(f); 676 } 677 } 678 679 redraw_sticky_bar_text(1); 680} 681 682void 683screen_update_workarea(rp_screen *s) 684{ 685 unsigned long workarea[4]; 686 687 workarea[0] = screen_left(s) - s->left; 688 workarea[1] = screen_top(s) - s->top; 689 workarea[2] = screen_width(s); 690 workarea[3] = screen_height(s); 691 692 set_atom(s->root, _net_workarea, XA_CARDINAL, workarea, 4); 693} 694 695rp_screen * 696screen_add(int rr_output) 697{ 698 rp_screen *screen; 699 700 screen = xmalloc(sizeof(*screen)); 701 memset(screen, 0, sizeof(*screen)); 702 list_add(&screen->node, &rp_screens); 703 704 screen->number = numset_request(rp_glob_screen.numset); 705 706 xrandr_fill_screen(rr_output, screen); 707 init_screen(screen); 708 709 if (screen_count() == 1) { 710 rp_current_screen = screen; 711 change_windows_vscreen(NULL, rp_current_vscreen); 712 set_window_focus(rp_current_screen->key_window); 713 } 714 return screen; 715} 716 717void 718screen_del(rp_screen *s) 719{ 720 rp_vscreen *v; 721 struct list_head *iter, *tmp; 722 723 if (s == rp_current_screen) { 724 if (screen_count() == 1) { 725 list_for_each_safe_entry(v, iter, tmp, &s->vscreens, 726 node) 727 hide_vscreen_windows(v); 728 729 rp_current_screen = NULL; 730 } else { 731 /* 732 * The deleted screen cannot be the current screen 733 * anymore, focus the next one. 734 */ 735 screen_remove_current(); 736 } 737 } else { 738 list_for_each_safe_entry(v, iter, tmp, &s->vscreens, node) 739 hide_vscreen_windows(v); 740 } 741 742 numset_release(rp_glob_screen.numset, s->number); 743 744 list_for_each_safe_entry(v, iter, tmp, &s->vscreens, node) 745 vscreen_del(v); 746 747 screen_free(s); 748 749 list_del(&s->node); 750 free(s); 751} 752 753void 754screen_free(rp_screen *s) 755{ 756 deactivate_screen(s); 757 758 XDestroyWindow(dpy, s->bar_window); 759 XDestroyWindow(dpy, s->key_window); 760 XDestroyWindow(dpy, s->input_window); 761 XDestroyWindow(dpy, s->frame_window); 762 XDestroyWindow(dpy, s->help_window); 763 764 if (s->xft_font) { 765 XftColorFree(dpy, DefaultVisual(dpy, s->screen_num), 766 DefaultColormap(dpy, s->screen_num), &s->xft_fgcolor); 767 XftColorFree(dpy, DefaultVisual(dpy, s->screen_num), 768 DefaultColormap(dpy, s->screen_num), &s->xft_bgcolor); 769 XftFontClose(dpy, s->xft_font); 770 } 771 rp_clear_cached_fonts(s); 772 773 XFreeCursor(dpy, s->rat); 774 XFreeColormap(dpy, s->def_cmap); 775 XFreeGC(dpy, s->normal_gc); 776 XFreeGC(dpy, s->inverse_gc); 777 778 free(s->display_string); 779 free(s->xrandr.name); 780} 781 782void 783screen_free_final(void) 784{ 785 /* Relinquish our hold on the root window. */ 786 XSelectInput(dpy, RootWindow(dpy, DefaultScreen(dpy)), 0); 787 788 numset_free(rp_glob_screen.numset); 789 790 XDeleteProperty(dpy, rp_glob_screen.root, _net_wm_name); 791 XDeleteProperty(dpy, rp_glob_screen.root, _net_supporting_wm_check); 792 XDeleteProperty(dpy, rp_glob_screen.root, _net_supported); 793 XDestroyWindow(dpy, rp_glob_screen.wm_check); 794}