progman.exe^H^H^H^H
1/*
2 * Copyright 2020 joshua stein <jcs@jcs.org>
3 * Copyright 1998-2007 Decklin Foster <decklin@red-bean.com>.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to
7 * deal in the Software without restriction, including without limitation the
8 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 * sell copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
19 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 */
22
23#ifdef __linux__
24#define _GNU_SOURCE
25#endif
26
27#include <stdlib.h>
28#include <stdio.h>
29#include <string.h>
30#include <unistd.h>
31#include <signal.h>
32#include <locale.h>
33#include <errno.h>
34#include <err.h>
35#include <fcntl.h>
36#include <sys/wait.h>
37#include <X11/Xatom.h>
38#include <X11/cursorfont.h>
39#include <X11/extensions/shape.h>
40#ifdef USE_GDK_PIXBUF
41#include <gdk-pixbuf-xlib/gdk-pixbuf-xlib.h>
42#endif
43#include "progman.h"
44#include "atom.h"
45#include "parser.h"
46
47#include "icons/close.xpm"
48#include "icons/utility_close.xpm"
49#include "icons/iconify.xpm"
50#include "icons/zoom.xpm"
51#include "icons/unzoom.xpm"
52#include "icons/default_icon.xpm"
53#include "icons/hidpi-close.xpm"
54#include "icons/hidpi-utility_close.xpm"
55#include "icons/hidpi-iconify.xpm"
56#include "icons/hidpi-zoom.xpm"
57#include "icons/hidpi-unzoom.xpm"
58#include "icons/hidpi-default_icon.xpm"
59
60#ifndef WAIT_ANY
61#define WAIT_ANY (-1)
62#endif
63
64char *orig_argv0;
65Display *dpy;
66Window root;
67client_t *cycle_head;
68client_t *focused, *dragging;
69int screen;
70int ignore_xerrors = 0;
71unsigned long ndesks = DEF_NDESKS;
72unsigned long cur_desk = 0;
73unsigned int focus_order = 0;
74Bool shape_support;
75int shape_event;
76Window supporting_wm_win;
77
78XftFont *font;
79XftFont *iconfont;
80XftColor xft_fg;
81XftColor xft_fg_unfocused;
82XftColor xft_launcher;
83XftColor xft_launcher_highlighted;
84
85Colormap def_cmap;
86XColor fg;
87XColor bg;
88XColor unfocused_fg;
89XColor unfocused_bg;
90XColor button_bg;
91XColor bevel_dark;
92XColor bevel_light;
93XColor border_fg;
94XColor border_bg;
95XColor launcher_fg;
96XColor launcher_bg;
97GC pixmap_gc;
98GC invert_gc;
99Pixmap close_pm;
100Pixmap close_pm_mask;
101XpmAttributes close_pm_attrs;
102Pixmap utility_close_pm;
103Pixmap utility_close_pm_mask;
104XpmAttributes utility_close_pm_attrs;
105Pixmap iconify_pm;
106Pixmap iconify_pm_mask;
107XpmAttributes iconify_pm_attrs;
108Pixmap zoom_pm;
109Pixmap zoom_pm_mask;
110XpmAttributes zoom_pm_attrs;
111Pixmap unzoom_pm;
112Pixmap unzoom_pm_mask;
113XpmAttributes unzoom_pm_attrs;
114Pixmap default_icon_pm;
115Pixmap default_icon_pm_mask;
116XpmAttributes default_icon_pm_attrs;
117Cursor map_curs;
118Cursor move_curs;
119Cursor resize_n_curs;
120Cursor resize_s_curs;
121Cursor resize_e_curs;
122Cursor resize_w_curs;
123Cursor resize_nw_curs;
124Cursor resize_sw_curs;
125Cursor resize_ne_curs;
126Cursor resize_se_curs;
127
128int exitmsg[2];
129
130char *opt_config_file = NULL;
131char *opt_font = DEF_FONT;
132char *opt_iconfont = DEF_ICONFONT;
133char *opt_fg = DEF_FG;
134char *opt_bg = DEF_BG;
135char *opt_unfocused_fg = DEF_UNFOCUSED_FG;
136char *opt_unfocused_bg = DEF_UNFOCUSED_BG;
137char *opt_button_bg = DEF_BUTTON_BG;
138char *opt_bevel_dark = DEF_BEVEL_DARK;
139char *opt_bevel_light = DEF_BEVEL_LIGHT;
140char *opt_border_fg = DEF_BORDER_FG;
141char *opt_border_bg = DEF_BORDER_BG;
142char *opt_launcher_fg = DEF_LAUNCHER_FG;
143char *opt_launcher_bg = DEF_LAUNCHER_BG;
144char *opt_root_bg = DEF_ROOTBG;
145int opt_bw = DEF_BW;
146int opt_pad = DEF_PAD;
147int opt_bevel = DEF_BEVEL;
148int opt_edge_resist = DEF_EDGE_RES;
149int opt_scale = DEF_SCALE;
150int icon_size = ICON_SIZE_MULT * DEF_SCALE;
151int opt_drag_button = 0;
152int opt_drag_mod = 0;
153
154void read_config(void);
155void setup_display(void);
156void scale_icon(void *xpm, void *hidpi_xpm, Pixmap *pm, Pixmap *pm_mask,
157 XpmAttributes *xpm_attrs);
158
159int
160main(int argc, char **argv)
161{
162 struct sigaction act;
163 int ch;
164
165 orig_argv0 = strdup(argv[0]);
166
167 setlocale(LC_ALL, "");
168
169 while ((ch = getopt(argc, argv, "c:")) != -1) {
170 switch (ch) {
171 case 'c':
172 if (opt_config_file)
173 free(opt_config_file);
174 opt_config_file = strdup(optarg);
175 break;
176 default:
177 printf("usage: %s [-c <config file>]\n", argv[0]);
178 exit(1);
179 }
180 }
181 argc -= optind;
182 argv += optind;
183
184 /* parsing the config file may need dpy, so connect early */
185 dpy = XOpenDisplay(NULL);
186 if (!dpy)
187 err(1, "can't open $DISPLAY \"%s\"", getenv("DISPLAY"));
188
189 XSetErrorHandler(handle_xerror);
190 screen = DefaultScreen(dpy);
191 root = RootWindow(dpy, screen);
192
193 read_config();
194
195 if (pipe2(exitmsg, O_CLOEXEC) != 0)
196 err(1, "pipe2");
197
198 act.sa_handler = sig_handler;
199 act.sa_flags = 0;
200 sigaction(SIGTERM, &act, NULL);
201 sigaction(SIGINT, &act, NULL);
202 sigaction(SIGHUP, &act, NULL);
203 sigaction(SIGCHLD, &act, NULL);
204
205 setup_display();
206 launcher_setup();
207 event_loop();
208 cleanup();
209
210 return 0;
211}
212
213void
214read_config(void)
215{
216 FILE *ini = NULL;
217 char *key, *val;
218 action_t *act;
219
220 ini = open_ini(opt_config_file);
221
222 if (find_ini_section(ini, "progman")) {
223 while (get_ini_kv(ini, &key, &val)) {
224 if (strcmp(key, "font") == 0)
225 opt_font = strdup(val);
226 else if (strcmp(key, "iconfont") == 0)
227 opt_iconfont = strdup(val);
228 else if (strcmp(key, "fgcolor") == 0)
229 opt_fg = strdup(val);
230 else if (strcmp(key, "bgcolor") == 0)
231 opt_bg = strdup(val);
232 else if (strcmp(key, "unfocused_fgcolor") == 0)
233 opt_unfocused_fg = strdup(val);
234 else if (strcmp(key, "unfocused_bgcolor") == 0)
235 opt_unfocused_bg = strdup(val);
236 else if (strcmp(key, "button_bgcolor") == 0)
237 opt_button_bg = strdup(val);
238 else if (strcmp(key, "border_fgcolor") == 0)
239 opt_border_fg = strdup(val);
240 else if (strcmp(key, "border_bgcolor") == 0)
241 opt_border_bg = strdup(val);
242 else if (strcmp(key, "launcher_fgcolor") == 0)
243 opt_launcher_fg = strdup(val);
244 else if (strcmp(key, "launcher_bgcolor") == 0)
245 opt_launcher_bg = strdup(val);
246 else if (strcmp(key, "root_bgcolor") == 0)
247 opt_root_bg = strdup(val);
248 else if (strcmp(key, "border_width") == 0) {
249 opt_bw = atoi(val);
250 if (opt_bw < 0) {
251 warnx("invalid value for border_width");
252 opt_bw = DEF_BW;
253 }
254 } else if (strcmp(key, "title_padding") == 0) {
255 opt_pad = atoi(val);
256 if (opt_pad < 0) {
257 warnx("invalid value for "
258 "title_padding");
259 opt_pad = DEF_PAD;
260 }
261 } else if (strcmp(key, "edgeresist") == 0) {
262 opt_edge_resist = atoi(val);
263 if (opt_edge_resist < 0) {
264 warnx("invalid value for edgeresist");
265 opt_edge_resist = DEF_EDGE_RES;
266 }
267 } else if (strcmp(key, "scale") == 0) {
268 opt_scale = atoi(val);
269 if (opt_scale < 0) {
270 warnx("invalid value for scale");
271 opt_scale = DEF_SCALE;
272 }
273 } else if (strcmp(key, "drag_combo") == 0) {
274 act = bind_key(BINDING_TYPE_DRAG, val, "drag");
275 if (act == NULL)
276 warnx("invalid drag_combo \"%s\" in "
277 "ini", val);
278 else {
279 opt_drag_button = act->button;
280 opt_drag_mod = act->mod;
281 }
282 } else
283 warnx("unknown key \"%s\" and value \"%s\" in "
284 "ini", key, val);
285
286 free(key);
287 free(val);
288 }
289 }
290
291 if (find_ini_section(ini, "keyboard"))
292 while (get_ini_kv(ini, &key, &val))
293 bind_key(BINDING_TYPE_KEYBOARD, key, val);
294
295 if (find_ini_section(ini, "desktop"))
296 while (get_ini_kv(ini, &key, &val))
297 bind_key(BINDING_TYPE_DESKTOP, key, val);
298
299 fclose(ini);
300}
301
302void
303setup_display(void)
304{
305 XGCValues gv;
306 XColor exact;
307 XSetWindowAttributes sattr;
308 XWindowAttributes attr;
309 XIconSize *xis;
310 XColor root_bg;
311 Pixmap rootpx;
312 int shape_err;
313 Window qroot, qparent, *wins;
314 unsigned int nwins, i;
315 client_t *c;
316
317 focused = NULL;
318 dragging = NULL;
319
320#ifdef USE_GDK_PIXBUF
321 gdk_pixbuf_xlib_init(dpy, screen);
322#endif
323
324 map_curs = XCreateFontCursor(dpy, XC_dotbox);
325 move_curs = XCreateFontCursor(dpy, XC_fleur);
326 resize_n_curs = XCreateFontCursor(dpy, XC_top_side);
327 resize_s_curs = XCreateFontCursor(dpy, XC_bottom_side);
328 resize_e_curs = XCreateFontCursor(dpy, XC_right_side);
329 resize_w_curs = XCreateFontCursor(dpy, XC_left_side);
330 resize_nw_curs = XCreateFontCursor(dpy, XC_top_left_corner);
331 resize_sw_curs = XCreateFontCursor(dpy, XC_bottom_left_corner);
332 resize_ne_curs = XCreateFontCursor(dpy, XC_top_right_corner);
333 resize_se_curs = XCreateFontCursor(dpy, XC_bottom_right_corner);
334
335#define alloc_color(val, var, name) \
336 if (!XAllocNamedColor(dpy, def_cmap, val, var, &exact)) \
337 warnx("invalid %s value \"%s\"", name, val);
338
339 def_cmap = DefaultColormap(dpy, screen);
340 alloc_color(opt_fg, &fg, "fgcolor");
341 alloc_color(opt_bg, &bg, "opt_fg");
342 alloc_color(opt_unfocused_fg, &unfocused_fg, "unfocused_fgcolor");
343 alloc_color(opt_unfocused_bg, &unfocused_bg, "unfocused_bgcolor");
344 alloc_color(opt_button_bg, &button_bg, "button_bgcolor");
345 alloc_color(opt_bevel_dark, &bevel_dark, "bevel_darkcolor");
346 alloc_color(opt_bevel_light, &bevel_light, "bevel_lightcolor");
347 alloc_color(opt_border_fg, &border_fg, "border_fgcolor");
348 alloc_color(opt_border_bg, &border_bg, "border_bgcolor");
349 alloc_color(opt_launcher_fg, &launcher_fg, "launcher_fgcolor");
350 alloc_color(opt_launcher_bg, &launcher_bg, "launcher_bgcolor");
351
352 XSetLineAttributes(dpy, DefaultGC(dpy, screen), 1, LineSolid, CapButt,
353 JoinBevel);
354 XSetFillStyle(dpy, DefaultGC(dpy, screen), FillSolid);
355
356#define create_xft_color(_xft, _pixel) \
357 (_xft).color.red = (_pixel).red; \
358 (_xft).color.green = (_pixel).green; \
359 (_xft).color.blue = (_pixel).blue; \
360 (_xft).color.alpha = 0xffff; \
361 (_xft).pixel = (_pixel).pixel;
362
363 create_xft_color(xft_fg, fg);
364 create_xft_color(xft_fg_unfocused, unfocused_fg);
365 create_xft_color(xft_launcher, launcher_fg);
366 create_xft_color(xft_launcher_highlighted, launcher_bg);
367
368 font = XftFontOpenName(dpy, screen, opt_font);
369 if (!font)
370 errx(1, "Xft font \"%s\" not found", opt_font);
371
372 iconfont = XftFontOpenName(dpy, screen, opt_iconfont);
373 if (!iconfont)
374 errx(1, "icon Xft font \"%s\" not found", opt_iconfont);
375
376 pixmap_gc = XCreateGC(dpy, root, 0, &gv);
377
378 gv.function = GXinvert;
379 gv.subwindow_mode = IncludeInferiors;
380 invert_gc = XCreateGC(dpy, root,
381 GCFunction | GCSubwindowMode | GCLineWidth, &gv);
382
383 scale_icon(close_xpm, hidpi_close_xpm, &close_pm, &close_pm_mask,
384 &close_pm_attrs);
385 scale_icon(utility_close_xpm, hidpi_utility_close_xpm,
386 &utility_close_pm, &utility_close_pm_mask, &utility_close_pm_attrs);
387 scale_icon(iconify_xpm, hidpi_iconify_xpm, &iconify_pm,
388 &iconify_pm_mask, &iconify_pm_attrs);
389 scale_icon(zoom_xpm, hidpi_zoom_xpm, &zoom_pm, &zoom_pm_mask,
390 &zoom_pm_attrs);
391 scale_icon(unzoom_xpm, hidpi_unzoom_xpm, &unzoom_pm, &unzoom_pm_mask,
392 &unzoom_pm_attrs);
393 scale_icon(default_icon_xpm, hidpi_default_icon_xpm, &default_icon_pm,
394 &default_icon_pm_mask, &default_icon_pm_attrs);
395
396 icon_size = ICON_SIZE_MULT * opt_scale;
397 xis = XAllocIconSize();
398 xis->min_width = icon_size;
399 xis->min_height = icon_size;
400 xis->max_width = icon_size;
401 xis->max_height = icon_size;
402 xis->width_inc = 1;
403 xis->height_inc = 1;
404 XSetIconSizes(dpy, root, xis, 1);
405 XFree(xis);
406
407 find_supported_atoms();
408
409 if (opt_root_bg != NULL && strlen(opt_root_bg) &&
410 XAllocNamedColor(dpy, def_cmap, opt_root_bg, &root_bg, &exact)) {
411 rootpx = XCreatePixmap(dpy, root, 1, 1,
412 DefaultDepth(dpy, screen));
413 XSetForeground(dpy, pixmap_gc, root_bg.pixel);
414 XFillRectangle(dpy, rootpx, pixmap_gc, 0, 0, 1, 1);
415 XSetWindowBackgroundPixmap(dpy, root, rootpx);
416 XClearWindow(dpy, root);
417 set_atoms(root, xrootpmap_id, XA_PIXMAP, &rootpx, 1);
418 } else if (opt_root_bg)
419 warnx("invalid root_bgcolor value \"%s\"", opt_root_bg);
420
421 set_atoms(root, net_num_desks, XA_CARDINAL, &ndesks, 1);
422 get_atoms(root, net_cur_desk, XA_CARDINAL, 0, &cur_desk, 1, NULL);
423 if (cur_desk >= ndesks) {
424 cur_desk = ndesks - 1;
425 set_atoms(root, net_cur_desk, XA_CARDINAL, &cur_desk, 1);
426 }
427
428 shape_support = XShapeQueryExtension(dpy, &shape_event, &shape_err);
429
430 XQueryTree(dpy, root, &qroot, &qparent, &wins, &nwins);
431 for (i = 0; i < nwins; i++) {
432 ignore_xerrors++;
433 XGetWindowAttributes(dpy, wins[i], &attr);
434 ignore_xerrors--;
435 if (!attr.override_redirect && attr.map_state == IsViewable) {
436 c = new_client(wins[i]);
437 c->placed = 1;
438 map_client(c);
439 map_if_desk(c);
440 }
441 }
442 XFree(wins);
443
444 /* become "the" window manager with SubstructureRedirectMask on root */
445 sattr.event_mask = SubMask | ColormapChangeMask | ButtonMask;
446 XChangeWindowAttributes(dpy, root, CWEventMask, &sattr);
447
448 /* create a hidden window for _NET_SUPPORTING_WM_CHECK */
449 supporting_wm_win = XCreateWindow(dpy, root, 0, 0, 1, 1,
450 0, DefaultDepth(dpy, screen), CopyFromParent,
451 DefaultVisual(dpy, screen), 0, NULL);
452 set_string_atom(supporting_wm_win, net_wm_name,
453 (unsigned char *)"progman", 7);
454 set_atoms(root, net_supporting_wm, XA_WINDOW, &supporting_wm_win, 1);
455}
456
457void
458scale_icon(void *xpm, void *hidpi_xpm, Pixmap *pm, Pixmap *pm_mask,
459 XpmAttributes *xpm_attrs)
460{
461 GC scale_gc, mask_scale_gc;
462 Pixmap pm_scaled, pm_scaled_mask;
463 int x, y, i, j;
464
465 if (opt_scale == 2) {
466 /* regular-sized icons are too big to 2x, so use hidpi ones */
467 if (XpmCreatePixmapFromData(dpy, root, hidpi_xpm, pm, pm_mask,
468 xpm_attrs) != XpmSuccess)
469 err(1, "XpmCreatePixmapFromData");
470
471 return;
472 }
473
474 if (XpmCreatePixmapFromData(dpy, root, xpm, pm, pm_mask,
475 xpm_attrs) != XpmSuccess)
476 err(1, "XpmCreatePixmapFromData");
477
478 if (opt_scale == 1)
479 return;
480
481 scale_gc = XCreateGC(dpy, *pm, 0, 0);
482 mask_scale_gc = XCreateGC(dpy, *pm_mask, 0, 0);
483
484 pm_scaled = XCreatePixmap(dpy, *pm,
485 xpm_attrs->width * opt_scale, xpm_attrs->height * opt_scale,
486 DefaultDepth(dpy, screen));
487 pm_scaled_mask = XCreatePixmap(dpy, *pm_mask,
488 xpm_attrs->width * opt_scale, xpm_attrs->height * opt_scale,
489 1);
490
491 for (y = 0; y < xpm_attrs->height; y++) {
492 for (x = 0; x < xpm_attrs->width; x++) {
493 for (i = 0; i < opt_scale; i++) {
494 for (j = 0; j < opt_scale; j++) {
495 XCopyArea(dpy, *pm, pm_scaled, scale_gc,
496 x, y, 1, 1,
497 (x * opt_scale) + i,
498 (y * opt_scale) + j);
499 XCopyArea(dpy, *pm_mask, pm_scaled_mask,
500 mask_scale_gc,
501 x, y, 1, 1,
502 (x * opt_scale) + i,
503 (y * opt_scale) + j);
504 }
505 }
506 }
507 }
508
509 XFreeGC(dpy, scale_gc);
510 XFreeGC(dpy, mask_scale_gc);
511
512 xpm_attrs->width *= opt_scale;
513 xpm_attrs->height *= opt_scale;
514
515 XFreePixmap(dpy, *pm);
516 XFreePixmap(dpy, *pm_mask);
517 *pm = pm_scaled;
518 *pm_mask = pm_scaled_mask;
519}
520
521void
522sig_handler(int signum)
523{
524 pid_t pid;
525 int status;
526
527 switch (signum) {
528 case SIGINT:
529 case SIGTERM:
530 case SIGHUP:
531 quit();
532 break;
533 case SIGCHLD:
534 while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0 ||
535 (pid < 0 && errno == EINTR))
536 ;
537 break;
538 }
539}
540
541void
542quit(void)
543{
544 if (write(exitmsg[1], &exitmsg, 1))
545 return;
546
547 warn("failed to exit cleanly");
548 exit(0);
549}
550
551int
552handle_xerror(Display *dpy, XErrorEvent *e)
553{
554 char msg[255];
555
556 if (e->error_code == BadAccess && e->resourceid == root)
557 errx(1, "root window unavailable");
558
559 if (!ignore_xerrors) {
560 XGetErrorText(dpy, e->error_code, msg, sizeof(msg));
561 warnx("X error (%#lx): %s", e->resourceid, msg);
562 fflush(stdout);
563 fflush(stderr);
564 }
565
566 return 0;
567}
568
569/*
570 * We use XQueryTree here to preserve the window stacking order, since the
571 * order in our linked list is different.
572 */
573void
574cleanup(void)
575{
576 unsigned int nwins, i;
577 XSetWindowAttributes sattr;
578 Window qroot, qparent, *wins;
579 client_t *c;
580
581 XQueryTree(dpy, root, &qroot, &qparent, &wins, &nwins);
582 for (i = 0; i < nwins; i++) {
583 c = find_client(wins[i], MATCH_FRAME);
584 if (c)
585 del_client(c, DEL_REMAP);
586 }
587 XFree(wins);
588
589 XftFontClose(dpy, font);
590 XftFontClose(dpy, iconfont);
591 XFreeCursor(dpy, map_curs);
592 XFreeCursor(dpy, move_curs);
593 XFreeCursor(dpy, resize_n_curs);
594 XFreeCursor(dpy, resize_s_curs);
595 XFreeCursor(dpy, resize_e_curs);
596 XFreeCursor(dpy, resize_w_curs);
597 XFreeCursor(dpy, resize_nw_curs);
598 XFreeCursor(dpy, resize_sw_curs);
599 XFreeCursor(dpy, resize_ne_curs);
600 XFreeCursor(dpy, resize_se_curs);
601 XFreeGC(dpy, pixmap_gc);
602 XFreeGC(dpy, invert_gc);
603 XFreePixmap(dpy, close_pm);
604 XFreePixmap(dpy, close_pm_mask);
605 XFreePixmap(dpy, utility_close_pm);
606 XFreePixmap(dpy, utility_close_pm_mask);
607 XFreePixmap(dpy, iconify_pm);
608 XFreePixmap(dpy, iconify_pm_mask);
609 XFreePixmap(dpy, zoom_pm);
610 XFreePixmap(dpy, zoom_pm_mask);
611 XFreePixmap(dpy, unzoom_pm);
612 XFreePixmap(dpy, unzoom_pm_mask);
613 XFreePixmap(dpy, default_icon_pm);
614 XFreePixmap(dpy, default_icon_pm_mask);
615
616 XInstallColormap(dpy, DefaultColormap(dpy, screen));
617 XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
618
619 XDeleteProperty(dpy, root, net_supporting_wm);
620 XDestroyWindow(dpy, supporting_wm_win);
621
622 XDeleteProperty(dpy, root, net_supported);
623 XDeleteProperty(dpy, root, net_client_list);
624
625 launcher_programs_free();
626
627 /* resign as "the" window manager */
628 sattr.event_mask = 0;
629 XChangeWindowAttributes(dpy, root, CWEventMask, &sattr);
630
631 XCloseDisplay(dpy);
632}