💾 Archived View for gemini.rmf-dev.com › repo › Vaati › cwm › files › 1aaa929c784376aa1bd45c01d82bee… captured on 2023-01-29 at 04:31:51. Gemini links have been rewritten to link to archived content
-=-=-=-=-=-=-
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 /*
21 * NOTE:
22 * It is the responsibility of the caller to deal with memory
23 * management of the xevent's.
24 */
25
26 #include <sys/types.h>
27 #include <sys/queue.h>
28
29 #include <err.h>
30 #include <errno.h>
31 #include <limits.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36
37 #include "calmwm.h"
38
39 static void xev_handle_maprequest(XEvent *);
40 static void xev_handle_unmapnotify(XEvent *);
41 static void xev_handle_destroynotify(XEvent *);
42 static void xev_handle_configurerequest(XEvent *);
43 static void xev_handle_propertynotify(XEvent *);
44 static void xev_handle_enternotify(XEvent *);
45 static void xev_handle_buttonpress(XEvent *);
46 static void xev_handle_buttonrelease(XEvent *);
47 static void xev_handle_keypress(XEvent *);
48 static void xev_handle_keyrelease(XEvent *);
49 static void xev_handle_clientmessage(XEvent *);
50 static void xev_handle_randr(XEvent *);
51 static void xev_handle_mappingnotify(XEvent *);
52 static void xev_handle_expose(XEvent *);
53
54 void (*xev_handlers[LASTEvent])(XEvent *) = {
55 [MapRequest] = xev_handle_maprequest,
56 [UnmapNotify] = xev_handle_unmapnotify,
57 [DestroyNotify] = xev_handle_destroynotify,
58 [ConfigureRequest] = xev_handle_configurerequest,
59 [PropertyNotify] = xev_handle_propertynotify,
60 [EnterNotify] = xev_handle_enternotify,
61 [ButtonPress] = xev_handle_buttonpress,
62 [ButtonRelease] = xev_handle_buttonrelease,
63 [KeyPress] = xev_handle_keypress,
64 [KeyRelease] = xev_handle_keyrelease,
65 [ClientMessage] = xev_handle_clientmessage,
66 [MappingNotify] = xev_handle_mappingnotify,
67 [Expose] = xev_handle_expose,
68 };
69
70 static KeySym modkeys[] = { XK_Alt_L, XK_Alt_R, XK_Super_L, XK_Super_R,
71 XK_Control_L, XK_Control_R, XK_ISO_Level3_Shift };
72
73 static void
74 xev_handle_maprequest(XEvent *ee)
75 {
76 XMapRequestEvent *e = &ee->xmaprequest;
77 struct screen_ctx *sc;
78 struct client_ctx *cc, *old_cc;
79
80 LOG_DEBUG3("parent: 0x%lx window: 0x%lx", e->parent, e->window);
81
82 if ((sc = screen_find(e->parent)) == NULL)
83 return;
84
85 if ((old_cc = client_current(sc)) != NULL)
86 client_ptr_save(old_cc);
87
88 if ((cc = client_find(e->window)) == NULL)
89 cc = client_init(e->window, NULL);
90
91 if ((cc != NULL) && (!(cc->flags & CLIENT_IGNORE)))
92 client_ptr_warp(cc);
93 }
94
95 static void
96 xev_handle_unmapnotify(XEvent *ee)
97 {
98 XUnmapEvent *e = &ee->xunmap;
99 struct client_ctx *cc;
100
101 LOG_DEBUG3("window: 0x%lx", e->window);
102
103 if ((cc = client_find(e->window)) != NULL) {
104 if (e->send_event) {
105 xu_set_wm_state(cc->win, WithdrawnState);
106 } else {
107 if (!(cc->flags & CLIENT_HIDDEN))
108 client_remove(cc);
109 }
110 }
111 }
112
113 static void
114 xev_handle_destroynotify(XEvent *ee)
115 {
116 XDestroyWindowEvent *e = &ee->xdestroywindow;
117 struct client_ctx *cc;
118
119 LOG_DEBUG3("window: 0x%lx", e->window);
120
121 if ((cc = client_find(e->window)) != NULL)
122 client_remove(cc);
123 }
124
125 static void
126 xev_handle_configurerequest(XEvent *ee)
127 {
128 XConfigureRequestEvent *e = &ee->xconfigurerequest;
129 struct client_ctx *cc;
130 struct screen_ctx *sc;
131 XWindowChanges wc;
132
133 LOG_DEBUG3("window: 0x%lx", e->window);
134
135 if ((cc = client_find(e->window)) != NULL) {
136 sc = cc->sc;
137
138 if (e->value_mask & CWWidth)
139 cc->geom.w = e->width;
140 if (e->value_mask & CWHeight)
141 cc->geom.h = e->height;
142 if (e->value_mask & CWX)
143 cc->geom.x = e->x;
144 if (e->value_mask & CWY)
145 cc->geom.y = e->y;
146 if (e->value_mask & CWBorderWidth)
147 cc->bwidth = e->border_width;
148 if (e->value_mask & CWSibling)
149 wc.sibling = e->above;
150 if (e->value_mask & CWStackMode)
151 wc.stack_mode = e->detail;
152
153 if (cc->geom.x == 0 && cc->geom.w >= sc->view.w)
154 cc->geom.x -= cc->bwidth;
155
156 if (cc->geom.y == 0 && cc->geom.h >= sc->view.h)
157 cc->geom.y -= cc->bwidth;
158
159 wc.x = cc->geom.x;
160 wc.y = cc->geom.y;
161 wc.width = cc->geom.w;
162 wc.height = cc->geom.h;
163 wc.border_width = cc->bwidth;
164
165 XConfigureWindow(X_Dpy, cc->win, e->value_mask, &wc);
166 client_config(cc);
167 } else {
168 /* let it do what it wants, it'll be ours when we map it. */
169 wc.x = e->x;
170 wc.y = e->y;
171 wc.width = e->width;
172 wc.height = e->height;
173 wc.border_width = e->border_width;
174 wc.stack_mode = Above;
175 e->value_mask &= ~CWStackMode;
176
177 XConfigureWindow(X_Dpy, e->window, e->value_mask, &wc);
178 }
179 }
180
181 static void
182 xev_handle_propertynotify(XEvent *ee)
183 {
184 XPropertyEvent *e = &ee->xproperty;
185 struct screen_ctx *sc;
186 struct client_ctx *cc;
187
188 LOG_DEBUG3("window: 0x%lx", e->window);
189
190 if ((cc = client_find(e->window)) != NULL) {
191 switch (e->atom) {
192 case XA_WM_NORMAL_HINTS:
193 client_get_sizehints(cc);
194 break;
195 case XA_WM_NAME:
196 client_set_name(cc);
197 break;
198 case XA_WM_HINTS:
199 client_wm_hints(cc);
200 client_draw_border(cc);
201 break;
202 case XA_WM_TRANSIENT_FOR:
203 client_transient(cc);
204 client_draw_border(cc);
205 if (cc->gc)
206 group_movetogroup(cc, cc->gc->num);
207 break;
208 default:
209 if (e->atom == ewmh[_NET_WM_NAME])
210 client_set_name(cc);
211 break;
212 }
213 } else {
214 if (e->atom == ewmh[_NET_DESKTOP_NAMES]) {
215 if ((sc = screen_find(e->window)) != NULL)
216 xu_ewmh_net_desktop_names(sc);
217 }
218 }
219 }
220
221 static void
222 xev_handle_enternotify(XEvent *ee)
223 {
224 XCrossingEvent *e = &ee->xcrossing;
225 struct client_ctx *cc;
226
227 LOG_DEBUG3("window: 0x%lx", e->window);
228
229 Last_Event_Time = e->time;
230
231 if ((cc = client_find(e->window)) != NULL)
232 client_set_active(cc);
233 }
234
235 static void
236 xev_handle_buttonpress(XEvent *ee)
237 {
238 XButtonEvent *e = &ee->xbutton;
239 struct client_ctx *cc;
240 struct screen_ctx *sc;
241 struct bind_ctx *mb;
242
243 LOG_DEBUG3("root: 0x%lx window: 0x%lx subwindow: 0x%lx",
244 e->root, e->window, e->subwindow);
245
246 if ((sc = screen_find(e->root)) == NULL)
247 return;
248
249 e->state &= ~IGNOREMODMASK;
250
251 TAILQ_FOREACH(mb, &Conf.mousebindq, entry) {
252 if (e->button == mb->press.button && e->state == mb->modmask)
253 break;
254 }
255 if (mb == NULL)
256 return;
257 mb->cargs->xev = CWM_XEV_BTN;
258 switch (mb->context) {
259 case CWM_CONTEXT_CC:
260 if (((cc = client_find(e->window)) == NULL) &&
261 ((cc = client_current(sc)) == NULL))
262 return;
263 (*mb->callback)(cc, mb->cargs);
264 break;
265 case CWM_CONTEXT_SC:
266 (*mb->callback)(sc, mb->cargs);
267 break;
268 case CWM_CONTEXT_NONE:
269 (*mb->callback)(NULL, mb->cargs);
270 break;
271 }
272 }
273
274 static void
275 xev_handle_buttonrelease(XEvent *ee)
276 {
277 XButtonEvent *e = &ee->xbutton;
278 struct client_ctx *cc;
279
280 LOG_DEBUG3("root: 0x%lx window: 0x%lx subwindow: 0x%lx",
281 e->root, e->window, e->subwindow);
282
283 if ((cc = client_find(e->window)) != NULL) {
284 if (cc->flags & (CLIENT_ACTIVE | CLIENT_HIGHLIGHT)) {
285 cc->flags &= ~CLIENT_HIGHLIGHT;
286 client_draw_border(cc);
287 }
288 }
289 }
290
291 static void
292 xev_handle_keypress(XEvent *ee)
293 {
294 XKeyEvent *e = &ee->xkey;
295 struct client_ctx *cc;
296 struct screen_ctx *sc;
297 struct bind_ctx *kb;
298 KeySym keysym, skeysym;
299 unsigned int modshift;
300
301 LOG_DEBUG3("root: 0x%lx window: 0x%lx subwindow: 0x%lx",
302 e->root, e->window, e->subwindow);
303
304 if ((sc = screen_find(e->root)) == NULL)
305 return;
306
307 keysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 0);
308 skeysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 1);
309
310 e->state &= ~IGNOREMODMASK;
311
312 TAILQ_FOREACH(kb, &Conf.keybindq, entry) {
313 if (keysym != kb->press.keysym && skeysym == kb->press.keysym)
314 modshift = ShiftMask;
315 else
316 modshift = 0;
317
318 if ((kb->modmask | modshift) != e->state)
319 continue;
320
321 if (kb->press.keysym == ((modshift == 0) ? keysym : skeysym))
322 break;
323 }
324 if (kb == NULL)
325 return;
326 kb->cargs->xev = CWM_XEV_KEY;
327 switch (kb->context) {
328 case CWM_CONTEXT_CC:
329 if (((cc = client_find(e->subwindow)) == NULL) &&
330 ((cc = client_current(sc)) == NULL))
331 return;
332 (*kb->callback)(cc, kb->cargs);
333 break;
334 case CWM_CONTEXT_SC:
335 (*kb->callback)(sc, kb->cargs);
336 break;
337 case CWM_CONTEXT_NONE:
338 (*kb->callback)(NULL, kb->cargs);
339 break;
340 }
341 }
342
343 /*
344 * This is only used for the modifier suppression detection.
345 */
346 static void
347 xev_handle_keyrelease(XEvent *ee)
348 {
349 XKeyEvent *e = &ee->xkey;
350 struct screen_ctx *sc;
351 struct client_ctx *cc;
352 KeySym keysym;
353 unsigned int i;
354
355 LOG_DEBUG3("root: 0x%lx window: 0x%lx subwindow: 0x%lx",
356 e->root, e->window, e->subwindow);
357
358 if ((sc = screen_find(e->root)) == NULL)
359 return;
360
361 keysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 0);
362 for (i = 0; i < nitems(modkeys); i++) {
363 if (keysym == modkeys[i]) {
364 if ((cc = client_current(sc)) != NULL) {
365 if (sc->cycling) {
366 sc->cycling = 0;
367 client_mtf(cc);
368 }
369 if (cc->flags & CLIENT_HIGHLIGHT) {
370 cc->flags &= ~CLIENT_HIGHLIGHT;
371 client_draw_border(cc);
372 }
373 }
374 XUngrabKeyboard(X_Dpy, CurrentTime);
375 break;
376 }
377 }
378 }
379
380 static void
381 xev_handle_clientmessage(XEvent *ee)
382 {
383 XClientMessageEvent *e = &ee->xclient;
384 struct client_ctx *cc, *old_cc;
385 struct screen_ctx *sc;
386
387 LOG_DEBUG3("window: 0x%lx", e->window);
388
389 if (e->message_type == cwmh[WM_CHANGE_STATE]) {
390 if ((cc = client_find(e->window)) != NULL) {
391 if (e->data.l[0] == IconicState)
392 client_hide(cc);
393 }
394 } else if (e->message_type == ewmh[_NET_CLOSE_WINDOW]) {
395 if ((cc = client_find(e->window)) != NULL) {
396 client_close(cc);
397 }
398 } else if (e->message_type == ewmh[_NET_ACTIVE_WINDOW]) {
399 if ((cc = client_find(e->window)) != NULL) {
400 if ((old_cc = client_current(NULL)) != NULL)
401 client_ptr_save(old_cc);
402 client_show(cc);
403 client_ptr_warp(cc);
404 }
405 } else if (e->message_type == ewmh[_NET_WM_DESKTOP]) {
406 if ((cc = client_find(e->window)) != NULL) {
407 /*
408 * The EWMH spec states that if the cardinal returned
409 * is 0xFFFFFFFF (-1) then the window should appear
410 * on all desktops, in our case, group 0.
411 */
412 if (e->data.l[0] == (unsigned long)-1)
413 group_movetogroup(cc, 0);
414 else
415 if (e->data.l[0] >= 0 &&
416 e->data.l[0] < Conf.ngroups)
417 group_movetogroup(cc, e->data.l[0]);
418 }
419 } else if (e->message_type == ewmh[_NET_WM_STATE]) {
420 if ((cc = client_find(e->window)) != NULL) {
421 xu_ewmh_handle_net_wm_state_msg(cc,
422 e->data.l[0], e->data.l[1], e->data.l[2]);
423 }
424 } else if (e->message_type == ewmh[_NET_CURRENT_DESKTOP]) {
425 if ((sc = screen_find(e->window)) != NULL) {
426 if (e->data.l[0] >= 0 &&
427 e->data.l[0] < Conf.ngroups)
428 group_only(sc, e->data.l[0]);
429 }
430 }
431 }
432
433 static void
434 xev_handle_randr(XEvent *ee)
435 {
436 XRRScreenChangeNotifyEvent *e = (XRRScreenChangeNotifyEvent *)ee;
437 struct screen_ctx *sc;
438
439 LOG_DEBUG3("size: %d/%d", e->width, e->height);
440
441 if ((sc = screen_find(e->root)) == NULL)
442 return;
443
444 XRRUpdateConfiguration(ee);
445 screen_update_geometry(sc);
446 screen_assert_clients_within(sc);
447 }
448
449 /*
450 * Called when the keymap has changed.
451 * Ungrab all keys, reload keymap and then regrab
452 */
453 static void
454 xev_handle_mappingnotify(XEvent *ee)
455 {
456 XMappingEvent *e = &ee->xmapping;
457 struct screen_ctx *sc;
458
459 LOG_DEBUG3("window: 0x%lx", e->window);
460
461 XRefreshKeyboardMapping(e);
462 if (e->request == MappingKeyboard) {
463 TAILQ_FOREACH(sc, &Screenq, entry)
464 conf_grab_kbd(sc->rootwin);
465 }
466 }
467
468 static void
469 xev_handle_expose(XEvent *ee)
470 {
471 XExposeEvent *e = &ee->xexpose;
472 struct client_ctx *cc;
473
474 LOG_DEBUG3("window: 0x%lx", e->window);
475
476 if ((cc = client_find(e->window)) != NULL && e->count == 0)
477 client_draw_border(cc);
478 }
479
480 void
481 xev_process(void)
482 {
483 XEvent e;
484
485 while (XPending(X_Dpy)) {
486 XNextEvent(X_Dpy, &e);
487 if ((e.type - Conf.xrandr_event_base) == RRScreenChangeNotify)
488 xev_handle_randr(&e);
489 else if ((e.type < LASTEvent) && (xev_handlers[e.type] != NULL))
490 (*xev_handlers[e.type])(&e);
491 }
492 }
493