💾 Archived View for gemini.rmf-dev.com › repo › Vaati › cwm › files › 230a2c57182ca854f660145213707f… captured on 2023-03-20 at 18:15:15. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2023-01-29)
-=-=-=-=-=-=-
0 /*
1 * calmwm - the calm window manager
2 *
3 * Copyright (c) 2004 Marius Aamodt Eriksen <marius@monkey.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 *
17 * $OpenBSD$
18 */
19
20 #include <sys/types.h>
21 #include <sys/queue.h>
22
23 #include <err.h>
24 #include <errno.h>
25 #include <limits.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30
31 #include "calmwm.h"
32
33 void
34 xu_ptr_get(Window win, int *x, int *y)
35 {
36 Window w0, w1;
37 int tmp0, tmp1;
38 unsigned int tmp2;
39
40 XQueryPointer(X_Dpy, win, &w0, &w1, &tmp0, &tmp1, x, y, &tmp2);
41 }
42
43 void
44 xu_ptr_set(Window win, int x, int y)
45 {
46 XWarpPointer(X_Dpy, None, win, 0, 0, 0, 0, x, y);
47 }
48
49 int
50 xu_get_prop(Window win, Atom atm, Atom type, long len, unsigned char **p)
51 {
52 Atom realtype;
53 unsigned long n, extra;
54 int format;
55
56 if (XGetWindowProperty(X_Dpy, win, atm, 0L, len, False, type,
57 &realtype, &format, &n, &extra, p) != Success || *p == NULL)
58 return -1;
59
60 if (n == 0)
61 XFree(*p);
62
63 return n;
64 }
65
66 int
67 xu_get_strprop(Window win, Atom atm, char **text) {
68 XTextProperty prop;
69 char **list;
70 int nitems = 0;
71
72 *text = NULL;
73
74 XGetTextProperty(X_Dpy, win, &prop, atm);
75 if (!prop.nitems) {
76 XFree(prop.value);
77 return 0;
78 }
79
80 if (Xutf8TextPropertyToTextList(X_Dpy, &prop, &list,
81 &nitems) == Success && nitems > 0 && *list) {
82 if (nitems > 1) {
83 XTextProperty prop2;
84 if (Xutf8TextListToTextProperty(X_Dpy, list, nitems,
85 XUTF8StringStyle, &prop2) == Success) {
86 *text = xstrdup((const char *)prop2.value);
87 XFree(prop2.value);
88 }
89 } else {
90 *text = xstrdup(*list);
91 }
92 XFreeStringList(list);
93 }
94 XFree(prop.value);
95
96 return nitems;
97 }
98
99 void
100 xu_send_clientmsg(Window win, Atom proto, Time ts)
101 {
102 XClientMessageEvent cm;
103
104 (void)memset(&cm, 0, sizeof(cm));
105 cm.type = ClientMessage;
106 cm.window = win;
107 cm.message_type = cwmh[WM_PROTOCOLS];
108 cm.format = 32;
109 cm.data.l[0] = proto;
110 cm.data.l[1] = ts;
111
112 XSendEvent(X_Dpy, win, False, NoEventMask, (XEvent *)&cm);
113 }
114
115 void
116 xu_get_wm_state(Window win, long *state)
117 {
118 long *p;
119
120 *state = -1;
121 if (xu_get_prop(win, cwmh[WM_STATE], cwmh[WM_STATE], 2L,
122 (unsigned char **)&p) > 0) {
123 *state = *p;
124 XFree(p);
125 }
126 }
127
128 void
129 xu_set_wm_state(Window win, long state)
130 {
131 long data[] = { state, None };
132
133 XChangeProperty(X_Dpy, win, cwmh[WM_STATE], cwmh[WM_STATE], 32,
134 PropModeReplace, (unsigned char *)data, 2);
135 }
136 void
137 xu_xorcolor(XftColor a, XftColor b, XftColor *r)
138 {
139 r->pixel = a.pixel ^ b.pixel;
140 r->color.red = a.color.red ^ b.color.red;
141 r->color.green = a.color.green ^ b.color.green;
142 r->color.blue = a.color.blue ^ b.color.blue;
143 r->color.alpha = 0xffff;
144 }
145
146 void
147 xu_atom_init(void)
148 {
149 char *cwmhints[] = {
150 "WM_STATE",
151 "WM_DELETE_WINDOW",
152 "WM_TAKE_FOCUS",
153 "WM_PROTOCOLS",
154 "_MOTIF_WM_HINTS",
155 "UTF8_STRING",
156 "WM_CHANGE_STATE",
157 };
158 char *ewmhints[] = {
159 "_NET_SUPPORTED",
160 "_NET_SUPPORTING_WM_CHECK",
161 "_NET_ACTIVE_WINDOW",
162 "_NET_CLIENT_LIST",
163 "_NET_CLIENT_LIST_STACKING",
164 "_NET_NUMBER_OF_DESKTOPS",
165 "_NET_CURRENT_DESKTOP",
166 "_NET_DESKTOP_VIEWPORT",
167 "_NET_DESKTOP_GEOMETRY",
168 "_NET_VIRTUAL_ROOTS",
169 "_NET_SHOWING_DESKTOP",
170 "_NET_DESKTOP_NAMES",
171 "_NET_WORKAREA",
172 "_NET_WM_NAME",
173 "_NET_WM_DESKTOP",
174 "_NET_CLOSE_WINDOW",
175 "_NET_WM_STATE",
176 "_NET_WM_STATE_STICKY",
177 "_NET_WM_STATE_MAXIMIZED_VERT",
178 "_NET_WM_STATE_MAXIMIZED_HORZ",
179 "_NET_WM_STATE_HIDDEN",
180 "_NET_WM_STATE_FULLSCREEN",
181 "_NET_WM_STATE_DEMANDS_ATTENTION",
182 "_NET_WM_STATE_SKIP_PAGER",
183 "_NET_WM_STATE_SKIP_TASKBAR",
184 "_CWM_WM_STATE_FREEZE",
185 };
186
187 XInternAtoms(X_Dpy, cwmhints, nitems(cwmhints), False, cwmh);
188 XInternAtoms(X_Dpy, ewmhints, nitems(ewmhints), False, ewmh);
189 }
190
191 /* Root Window Properties */
192 void
193 xu_ewmh_net_supported(struct screen_ctx *sc)
194 {
195 XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_SUPPORTED],
196 XA_ATOM, 32, PropModeReplace, (unsigned char *)ewmh, EWMH_NITEMS);
197 }
198
199 void
200 xu_ewmh_net_supported_wm_check(struct screen_ctx *sc)
201 {
202 Window w;
203
204 w = XCreateSimpleWindow(X_Dpy, sc->rootwin, -1, -1, 1, 1, 0, 0, 0);
205 XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_SUPPORTING_WM_CHECK],
206 XA_WINDOW, 32, PropModeReplace, (unsigned char *)&w, 1);
207 XChangeProperty(X_Dpy, w, ewmh[_NET_SUPPORTING_WM_CHECK],
208 XA_WINDOW, 32, PropModeReplace, (unsigned char *)&w, 1);
209 XChangeProperty(X_Dpy, w, ewmh[_NET_WM_NAME],
210 cwmh[UTF8_STRING], 8, PropModeReplace,
211 (unsigned char *)Conf.wmname, strlen(Conf.wmname));
212 }
213
214 void
215 xu_ewmh_net_desktop_geometry(struct screen_ctx *sc)
216 {
217 long geom[2] = { sc->view.w, sc->view.h };
218
219 XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_DESKTOP_GEOMETRY],
220 XA_CARDINAL, 32, PropModeReplace, (unsigned char *)geom , 2);
221 }
222
223 void
224 xu_ewmh_net_desktop_viewport(struct screen_ctx *sc)
225 {
226 long viewports[2] = {0, 0};
227
228 /* We don't support large desktops, so this is (0, 0). */
229 XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_DESKTOP_VIEWPORT],
230 XA_CARDINAL, 32, PropModeReplace, (unsigned char *)viewports, 2);
231 }
232
233 void
234 xu_ewmh_net_workarea(struct screen_ctx *sc)
235 {
236 unsigned long *workarea;
237 int i, ngroups = Conf.ngroups;
238
239 workarea = xreallocarray(NULL, ngroups * 4, sizeof(unsigned long));
240 for (i = 0; i < ngroups; i++) {
241 workarea[4 * i + 0] = sc->work.x;
242 workarea[4 * i + 1] = sc->work.y;
243 workarea[4 * i + 2] = sc->work.w;
244 workarea[4 * i + 3] = sc->work.h;
245 }
246 XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_WORKAREA],
247 XA_CARDINAL, 32, PropModeReplace, (unsigned char *)workarea,
248 ngroups * 4);
249 free(workarea);
250 }
251
252 void
253 xu_ewmh_net_client_list(struct screen_ctx *sc)
254 {
255 struct client_ctx *cc;
256 Window *winlist;
257 int i = 0, j = 0;
258
259 TAILQ_FOREACH(cc, &sc->clientq, entry)
260 i++;
261 if (i == 0)
262 return;
263
264 winlist = xreallocarray(NULL, i, sizeof(*winlist));
265 TAILQ_FOREACH(cc, &sc->clientq, entry)
266 winlist[j++] = cc->win;
267 XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_CLIENT_LIST],
268 XA_WINDOW, 32, PropModeReplace, (unsigned char *)winlist, i);
269 free(winlist);
270 }
271
272 void
273 xu_ewmh_net_client_list_stacking(struct screen_ctx *sc)
274 {
275 struct client_ctx *cc;
276 Window *winlist;
277 int i = 0, j;
278
279 TAILQ_FOREACH(cc, &sc->clientq, entry)
280 i++;
281 if (i == 0)
282 return;
283
284 j = i;
285 winlist = xreallocarray(NULL, i, sizeof(*winlist));
286 TAILQ_FOREACH(cc, &sc->clientq, entry)
287 winlist[--j] = cc->win;
288 XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_CLIENT_LIST_STACKING],
289 XA_WINDOW, 32, PropModeReplace, (unsigned char *)winlist, i);
290 free(winlist);
291 }
292
293 void
294 xu_ewmh_net_active_window(struct screen_ctx *sc, Window w)
295 {
296 XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_ACTIVE_WINDOW],
297 XA_WINDOW, 32, PropModeReplace, (unsigned char *)&w, 1);
298 }
299
300 void
301 xu_ewmh_net_number_of_desktops(struct screen_ctx *sc)
302 {
303 long ndesks = Conf.ngroups;
304
305 XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_NUMBER_OF_DESKTOPS],
306 XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&ndesks, 1);
307 }
308
309 void
310 xu_ewmh_net_showing_desktop(struct screen_ctx *sc)
311 {
312 long zero = 0;
313
314 /* We don't support `showing desktop' mode, so this is zero.
315 * Note that when we hide all groups, or when all groups are
316 * hidden we could technically set this later on.
317 */
318 XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_SHOWING_DESKTOP],
319 XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&zero, 1);
320 }
321
322 void
323 xu_ewmh_net_virtual_roots(struct screen_ctx *sc)
324 {
325 /* We don't support virtual roots, so delete if set by previous wm. */
326 XDeleteProperty(X_Dpy, sc->rootwin, ewmh[_NET_VIRTUAL_ROOTS]);
327 }
328
329 void
330 xu_ewmh_net_current_desktop(struct screen_ctx *sc)
331 {
332 long num = sc->group_active->num;
333
334 XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_CURRENT_DESKTOP],
335 XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&num, 1);
336 }
337
338 void
339 xu_ewmh_net_desktop_names(struct screen_ctx *sc)
340 {
341 struct group_ctx *gc;
342 char *p, *q;
343 unsigned char *prop_ret;
344 int i = 0, j = 0, nstrings = 0, n = 0;
345 size_t len = 0, tlen, slen;
346
347 /* Let group names be overwritten if _NET_DESKTOP_NAMES is set. */
348
349 if ((j = xu_get_prop(sc->rootwin, ewmh[_NET_DESKTOP_NAMES],
350 cwmh[UTF8_STRING], 0xffffff, (unsigned char **)&prop_ret)) > 0) {
351 prop_ret[j - 1] = '\0'; /* paranoia */
352 while (i < j) {
353 if (prop_ret[i++] == '\0')
354 nstrings++;
355 }
356 }
357
358 p = (char *)prop_ret;
359 while (n < nstrings) {
360 TAILQ_FOREACH(gc, &sc->groupq, entry) {
361 if (gc->num == n) {
362 free(gc->name);
363 gc->name = xstrdup(p);
364 p += strlen(p) + 1;
365 break;
366 }
367 }
368 n++;
369 }
370 if (prop_ret != NULL)
371 XFree(prop_ret);
372
373 TAILQ_FOREACH(gc, &sc->groupq, entry)
374 len += strlen(gc->name) + 1;
375 q = p = xreallocarray(NULL, len, sizeof(*p));
376
377 tlen = len;
378 TAILQ_FOREACH(gc, &sc->groupq, entry) {
379 slen = strlen(gc->name) + 1;
380 (void)strlcpy(q, gc->name, tlen);
381 tlen -= slen;
382 q += slen;
383 }
384
385 XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_DESKTOP_NAMES],
386 cwmh[UTF8_STRING], 8, PropModeReplace, (unsigned char *)p, len);
387 free(p);
388 }
389
390 /* Application Window Properties */
391 int
392 xu_ewmh_get_net_wm_desktop(struct client_ctx *cc, long *n)
393 {
394 long *p;
395
396 if (xu_get_prop(cc->win, ewmh[_NET_WM_DESKTOP], XA_CARDINAL, 1L,
397 (unsigned char **)&p) <= 0)
398 return 0;
399 *n = *p;
400 XFree(p);
401 return 1;
402 }
403
404 void
405 xu_ewmh_set_net_wm_desktop(struct client_ctx *cc)
406 {
407 long num = 0xffffffff;
408
409 if (cc->gc)
410 num = cc->gc->num;
411
412 XChangeProperty(X_Dpy, cc->win, ewmh[_NET_WM_DESKTOP],
413 XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&num, 1);
414 }
415
416 Atom *
417 xu_ewmh_get_net_wm_state(struct client_ctx *cc, int *n)
418 {
419 Atom *state, *p = NULL;
420
421 if ((*n = xu_get_prop(cc->win, ewmh[_NET_WM_STATE], XA_ATOM, 64L,
422 (unsigned char **)&p)) <= 0)
423 return NULL;
424
425 state = xreallocarray(NULL, *n, sizeof(Atom));
426 (void)memcpy(state, p, *n * sizeof(Atom));
427 XFree((char *)p);
428
429 return state;
430 }
431
432 void
433 xu_ewmh_handle_net_wm_state_msg(struct client_ctx *cc, int action,
434 Atom first, Atom second)
435 {
436 unsigned int i;
437 struct handlers {
438 Atom atom;
439 int flag;
440 void (*toggle)(struct client_ctx *);
441 } handlers[] = {
442 { _NET_WM_STATE_STICKY,
443 CLIENT_STICKY,
444 client_toggle_sticky },
445 { _NET_WM_STATE_MAXIMIZED_VERT,
446 CLIENT_VMAXIMIZED,
447 client_toggle_vmaximize },
448 { _NET_WM_STATE_MAXIMIZED_HORZ,
449 CLIENT_HMAXIMIZED,
450 client_toggle_hmaximize },
451 { _NET_WM_STATE_HIDDEN,
452 CLIENT_HIDDEN,
453 client_toggle_hidden },
454 { _NET_WM_STATE_FULLSCREEN,
455 CLIENT_FULLSCREEN,
456 client_toggle_fullscreen },
457 { _NET_WM_STATE_DEMANDS_ATTENTION,
458 CLIENT_URGENCY,
459 client_urgency },
460 { _NET_WM_STATE_SKIP_PAGER,
461 CLIENT_SKIP_PAGER,
462 client_toggle_skip_pager},
463 { _NET_WM_STATE_SKIP_TASKBAR,
464 CLIENT_SKIP_TASKBAR,
465 client_toggle_skip_taskbar},
466 { _CWM_WM_STATE_FREEZE,
467 CLIENT_FREEZE,
468 client_toggle_freeze },
469 };
470
471 for (i = 0; i < nitems(handlers); i++) {
472 if (first != ewmh[handlers[i].atom] &&
473 second != ewmh[handlers[i].atom])
474 continue;
475 switch (action) {
476 case _NET_WM_STATE_ADD:
477 if (!(cc->flags & handlers[i].flag))
478 handlers[i].toggle(cc);
479 break;
480 case _NET_WM_STATE_REMOVE:
481 if (cc->flags & handlers[i].flag)
482 handlers[i].toggle(cc);
483 break;
484 case _NET_WM_STATE_TOGGLE:
485 handlers[i].toggle(cc);
486 }
487 }
488 }
489
490 void
491 xu_ewmh_restore_net_wm_state(struct client_ctx *cc)
492 {
493 Atom *atoms;
494 int i, n;
495
496 atoms = xu_ewmh_get_net_wm_state(cc, &n);
497 for (i = 0; i < n; i++) {
498 if (atoms[i] == ewmh[_NET_WM_STATE_STICKY])
499 client_toggle_sticky(cc);
500 if (atoms[i] == ewmh[_NET_WM_STATE_MAXIMIZED_VERT])
501 client_toggle_vmaximize(cc);
502 if (atoms[i] == ewmh[_NET_WM_STATE_MAXIMIZED_HORZ])
503 client_toggle_hmaximize(cc);
504 if (atoms[i] == ewmh[_NET_WM_STATE_HIDDEN])
505 client_toggle_hidden(cc);
506 if (atoms[i] == ewmh[_NET_WM_STATE_FULLSCREEN])
507 client_toggle_fullscreen(cc);
508 if (atoms[i] == ewmh[_NET_WM_STATE_DEMANDS_ATTENTION])
509 client_urgency(cc);
510 if (atoms[i] == ewmh[_NET_WM_STATE_SKIP_PAGER])
511 client_toggle_skip_pager(cc);
512 if (atoms[i] == ewmh[_NET_WM_STATE_SKIP_TASKBAR])
513 client_toggle_skip_taskbar(cc);
514 if (atoms[i] == ewmh[_CWM_WM_STATE_FREEZE])
515 client_toggle_freeze(cc);
516 }
517 free(atoms);
518 }
519
520 void
521 xu_ewmh_set_net_wm_state(struct client_ctx *cc)
522 {
523 Atom *atoms, *oatoms;
524 int n, i, j;
525
526 oatoms = xu_ewmh_get_net_wm_state(cc, &n);
527 atoms = xreallocarray(NULL, (n + _NET_WM_STATES_NITEMS), sizeof(Atom));
528 for (i = j = 0; i < n; i++) {
529 if (oatoms[i] != ewmh[_NET_WM_STATE_STICKY] &&
530 oatoms[i] != ewmh[_NET_WM_STATE_MAXIMIZED_VERT] &&
531 oatoms[i] != ewmh[_NET_WM_STATE_MAXIMIZED_HORZ] &&
532 oatoms[i] != ewmh[_NET_WM_STATE_HIDDEN] &&
533 oatoms[i] != ewmh[_NET_WM_STATE_FULLSCREEN] &&
534 oatoms[i] != ewmh[_NET_WM_STATE_DEMANDS_ATTENTION] &&
535 oatoms[i] != ewmh[_NET_WM_STATE_SKIP_PAGER] &&
536 oatoms[i] != ewmh[_NET_WM_STATE_SKIP_TASKBAR] &&
537 oatoms[i] != ewmh[_CWM_WM_STATE_FREEZE])
538 atoms[j++] = oatoms[i];
539 }
540 free(oatoms);
541 if (cc->flags & CLIENT_STICKY)
542 atoms[j++] = ewmh[_NET_WM_STATE_STICKY];
543 if (cc->flags & CLIENT_HIDDEN)
544 atoms[j++] = ewmh[_NET_WM_STATE_HIDDEN];
545 if (cc->flags & CLIENT_FULLSCREEN)
546 atoms[j++] = ewmh[_NET_WM_STATE_FULLSCREEN];
547 else {
548 if (cc->flags & CLIENT_VMAXIMIZED)
549 atoms[j++] = ewmh[_NET_WM_STATE_MAXIMIZED_VERT];
550 if (cc->flags & CLIENT_HMAXIMIZED)
551 atoms[j++] = ewmh[_NET_WM_STATE_MAXIMIZED_HORZ];
552 }
553 if (cc->flags & CLIENT_URGENCY)
554 atoms[j++] = ewmh[_NET_WM_STATE_DEMANDS_ATTENTION];
555 if (cc->flags & CLIENT_SKIP_PAGER)
556 atoms[j++] = ewmh[_NET_WM_STATE_SKIP_PAGER];
557 if (cc->flags & CLIENT_SKIP_TASKBAR)
558 atoms[j++] = ewmh[_NET_WM_STATE_SKIP_TASKBAR];
559 if (cc->flags & CLIENT_FREEZE)
560 atoms[j++] = ewmh[_CWM_WM_STATE_FREEZE];
561 if (j > 0)
562 XChangeProperty(X_Dpy, cc->win, ewmh[_NET_WM_STATE],
563 XA_ATOM, 32, PropModeReplace, (unsigned char *)atoms, j);
564 else
565 XDeleteProperty(X_Dpy, cc->win, ewmh[_NET_WM_STATE]);
566 free(atoms);
567 }
568