1 /*
2 * Copyright (C) 2003-2021 Kim Woelders
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to
6 * deal in the Software without restriction, including without limitation the
7 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 * sell copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies of the Software, its documentation and marketing & publicity
13 * materials, and acknowledgment shall be given in the documentation, materials
14 * and software packages that this Software was used.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23 /*
24 * Extended Window Manager Hints.
25 */
26 #include "config.h"
27
28 #include <X11/Xlib.h>
29
30 #include "E.h"
31 #include "desktops.h"
32 #include "events.h"
33 #include "ewins.h"
34 #include "hints.h"
35 #include "xprop.h"
36
37 #define ATOM_ADD_IF(atom, cond) \
38 do { \
39 if (cond) { \
40 atom_list[atom_count++] = atom; \
41 atom_mask |= 1U << atom_bit; \
42 } \
43 atom_bit++; \
44 } while(0)
45
46 /*
47 * _NET_WM_MOVERESIZE client message actions
48 */
49 #define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0
50 #define _NET_WM_MOVERESIZE_SIZE_TOP 1
51 #define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2
52 #define _NET_WM_MOVERESIZE_SIZE_RIGHT 3
53 #define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4
54 #define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5
55 #define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6
56 #define _NET_WM_MOVERESIZE_SIZE_LEFT 7
57 #define _NET_WM_MOVERESIZE_MOVE 8
58 #define _NET_WM_MOVERESIZE_SIZE_KEYBOARD 9
59 #define _NET_WM_MOVERESIZE_MOVE_KEYBOARD 10
60 #define _NET_WM_MOVERESIZE_CANCEL 11
61
62 /* Window state property change actions */
63 #define _NET_WM_STATE_REMOVE 0
64 #define _NET_WM_STATE_ADD 1
65 #define _NET_WM_STATE_TOGGLE 2
66
67 /* Source indication */
68 #define _NET_WM_SOURCE_UNKNOWN 0
69 #define _NET_WM_SOURCE_APP 1
70 #define _NET_WM_SOURCE_USER 2
71
72 #define OPSRC(src) (((src) == _NET_WM_SOURCE_USER) ? _NET_WM_SOURCE_USER : _NET_WM_SOURCE_APP)
73
74 /*
75 * Initialize EWMH stuff
76 */
77 void
EWMH_Init(EX_Window win_wm_check)78 EWMH_Init(EX_Window win_wm_check)
79 {
80 EX_Atom atom_list[64];
81 int atom_count;
82
83 ex_netwm_init();
84
85 atom_count = 0;
86
87 atom_list[atom_count++] = ea_n._NET_SUPPORTED;
88 atom_list[atom_count++] = ea_n._NET_SUPPORTING_WM_CHECK;
89
90 atom_list[atom_count++] = ea_n._NET_NUMBER_OF_DESKTOPS;
91 atom_list[atom_count++] = ea_n._NET_DESKTOP_GEOMETRY;
92 atom_list[atom_count++] = ea_n._NET_DESKTOP_NAMES;
93 atom_list[atom_count++] = ea_n._NET_CURRENT_DESKTOP;
94 atom_list[atom_count++] = ea_n._NET_DESKTOP_VIEWPORT;
95 atom_list[atom_count++] = ea_n._NET_WORKAREA;
96 atom_list[atom_count++] = ea_n._NET_VIRTUAL_ROOTS;
97 atom_list[atom_count++] = ea_n._NET_SHOWING_DESKTOP;
98
99 atom_list[atom_count++] = ea_n._NET_ACTIVE_WINDOW;
100 atom_list[atom_count++] = ea_n._NET_CLIENT_LIST;
101 atom_list[atom_count++] = ea_n._NET_CLIENT_LIST_STACKING;
102
103 atom_list[atom_count++] = ea_n._NET_CLOSE_WINDOW;
104 atom_list[atom_count++] = ea_n._NET_MOVERESIZE_WINDOW;
105 atom_list[atom_count++] = ea_n._NET_WM_MOVERESIZE;
106
107 atom_list[atom_count++] = ea_n._NET_WM_NAME;
108 atom_list[atom_count++] = ea_n._NET_WM_ICON_NAME;
109 atom_list[atom_count++] = ea_n._NET_WM_DESKTOP;
110
111 atom_list[atom_count++] = ea_n._NET_WM_WINDOW_TYPE;
112 atom_list[atom_count++] = ea_n._NET_WM_WINDOW_TYPE_DESKTOP;
113 atom_list[atom_count++] = ea_n._NET_WM_WINDOW_TYPE_DOCK;
114 atom_list[atom_count++] = ea_n._NET_WM_WINDOW_TYPE_TOOLBAR;
115 atom_list[atom_count++] = ea_n._NET_WM_WINDOW_TYPE_MENU;
116 atom_list[atom_count++] = ea_n._NET_WM_WINDOW_TYPE_UTILITY;
117 atom_list[atom_count++] = ea_n._NET_WM_WINDOW_TYPE_SPLASH;
118 atom_list[atom_count++] = ea_n._NET_WM_WINDOW_TYPE_DIALOG;
119 atom_list[atom_count++] = ea_n._NET_WM_WINDOW_TYPE_NORMAL;
120
121 atom_list[atom_count++] = ea_n._NET_WM_STATE;
122 atom_list[atom_count++] = ea_n._NET_WM_STATE_MODAL;
123 atom_list[atom_count++] = ea_n._NET_WM_STATE_STICKY;
124 atom_list[atom_count++] = ea_n._NET_WM_STATE_MAXIMIZED_VERT;
125 atom_list[atom_count++] = ea_n._NET_WM_STATE_MAXIMIZED_HORZ;
126 atom_list[atom_count++] = ea_n._NET_WM_STATE_SHADED;
127 atom_list[atom_count++] = ea_n._NET_WM_STATE_SKIP_TASKBAR;
128 atom_list[atom_count++] = ea_n._NET_WM_STATE_SKIP_PAGER;
129 atom_list[atom_count++] = ea_n._NET_WM_STATE_HIDDEN;
130 atom_list[atom_count++] = ea_n._NET_WM_STATE_FULLSCREEN;
131 atom_list[atom_count++] = ea_n._NET_WM_STATE_ABOVE;
132 atom_list[atom_count++] = ea_n._NET_WM_STATE_BELOW;
133 atom_list[atom_count++] = ea_n._NET_WM_STATE_DEMANDS_ATTENTION;
134 atom_list[atom_count++] = ea_n._NET_WM_STATE_FOCUSED;
135
136 atom_list[atom_count++] = ea_n._NET_WM_ALLOWED_ACTIONS;
137 atom_list[atom_count++] = ea_n._NET_WM_ACTION_MOVE;
138 atom_list[atom_count++] = ea_n._NET_WM_ACTION_RESIZE;
139 atom_list[atom_count++] = ea_n._NET_WM_ACTION_MINIMIZE;
140 atom_list[atom_count++] = ea_n._NET_WM_ACTION_SHADE;
141 atom_list[atom_count++] = ea_n._NET_WM_ACTION_STICK;
142 atom_list[atom_count++] = ea_n._NET_WM_ACTION_MAXIMIZE_HORZ;
143 atom_list[atom_count++] = ea_n._NET_WM_ACTION_MAXIMIZE_VERT;
144 atom_list[atom_count++] = ea_n._NET_WM_ACTION_FULLSCREEN;
145 atom_list[atom_count++] = ea_n._NET_WM_ACTION_CHANGE_DESKTOP;
146 atom_list[atom_count++] = ea_n._NET_WM_ACTION_CLOSE;
147 atom_list[atom_count++] = ea_n._NET_WM_ACTION_ABOVE;
148 atom_list[atom_count++] = ea_n._NET_WM_ACTION_BELOW;
149
150 atom_list[atom_count++] = ea_n._NET_WM_STRUT_PARTIAL;
151 atom_list[atom_count++] = ea_n._NET_WM_STRUT;
152
153 atom_list[atom_count++] = ea_n._NET_FRAME_EXTENTS;
154
155 atom_list[atom_count++] = ea_n._NET_WM_USER_TIME;
156 atom_list[atom_count++] = ea_n._NET_WM_USER_TIME_WINDOW;
157
158 atom_list[atom_count++] = ea_n._NET_WM_WINDOW_OPACITY;
159
160 ex_window_prop_atom_set(WinGetXwin(VROOT),
161 ea_n._NET_SUPPORTED, atom_list, atom_count);
162
163 /* Set WM info properties */
164 ex_netwm_wm_identify(WinGetXwin(VROOT), win_wm_check, e_wm_name);
165 }
166
167 /*
168 * Desktops
169 */
170
171 void
EWMH_SetDesktopCount(void)172 EWMH_SetDesktopCount(void)
173 {
174 ex_netwm_desk_count_set(WinGetXwin(VROOT), DesksGetNumber());
175 }
176
177 void
EWMH_SetDesktopRoots(void)178 EWMH_SetDesktopRoots(void)
179 {
180 int i, n_desks;
181 EX_Window *wl;
182
183 n_desks = DesksGetNumber();
184 wl = EMALLOC(EX_Window, n_desks);
185 if (!wl)
186 return;
187
188 for (i = 0; i < n_desks; i++)
189 wl[i] = EoGetXwin(DeskGet(i));
190
191 ex_netwm_desk_roots_set(WinGetXwin(VROOT), wl, n_desks);
192
193 Efree(wl);
194 }
195
196 void
EWMH_SetDesktopNames(void)197 EWMH_SetDesktopNames(void)
198 {
199 /* Fall back to defaults */
200 ex_netwm_desk_names_set(WinGetXwin(VROOT), NULL, DesksGetNumber());
201 }
202
203 void
EWMH_SetDesktopSize(void)204 EWMH_SetDesktopSize(void)
205 {
206 int ax, ay;
207
208 DesksGetAreaSize(&ax, &ay);
209 ex_netwm_desk_size_set(WinGetXwin(VROOT), ax * WinGetW(VROOT),
210 ay * WinGetH(VROOT));
211 }
212
213 void
EWMH_SetWorkArea(void)214 EWMH_SetWorkArea(void)
215 {
216 unsigned int *p_coord;
217 int n_coord, i, n_desks;
218
219 n_desks = DesksGetNumber();
220 n_coord = 4 * n_desks;
221 p_coord = EMALLOC(unsigned int, n_coord);
222
223 if (!p_coord)
224 return;
225
226 for (i = 0; i < n_desks; i++)
227 {
228 p_coord[4 * i] = 0;
229 p_coord[4 * i + 1] = 0;
230 p_coord[4 * i + 2] = WinGetW(VROOT);
231 p_coord[4 * i + 3] = WinGetH(VROOT);
232 }
233
234 ex_netwm_desk_workareas_set(WinGetXwin(VROOT), p_coord, n_desks);
235
236 Efree(p_coord);
237 }
238
239 void
EWMH_SetCurrentDesktop(void)240 EWMH_SetCurrentDesktop(void)
241 {
242 ex_netwm_desk_current_set(WinGetXwin(VROOT), DesksGetCurrentNum());
243 }
244
245 void
EWMH_SetDesktopViewport(void)246 EWMH_SetDesktopViewport(void)
247 {
248 unsigned int *p_coord;
249 int n_coord, i, ax, ay, n_desks;
250
251 n_desks = DesksGetNumber();
252 n_coord = 2 * n_desks;
253 p_coord = EMALLOC(unsigned int, n_coord);
254
255 if (!p_coord)
256 return;
257
258 for (i = 0; i < n_desks; i++)
259 {
260 DeskGetArea(DeskGet(i), &ax, &ay);
261 p_coord[2 * i] = ax * WinGetW(VROOT);
262 p_coord[2 * i + 1] = ay * WinGetH(VROOT);
263 }
264
265 ex_netwm_desk_viewports_set(WinGetXwin(VROOT), p_coord, n_desks);
266
267 Efree(p_coord);
268 }
269
270 void
EWMH_SetShowingDesktop(int on)271 EWMH_SetShowingDesktop(int on)
272 {
273 ex_netwm_showing_desktop_set(WinGetXwin(VROOT), on);
274 }
275
276 /*
277 * Window status
278 */
279
280 void
EWMH_SetClientList(void)281 EWMH_SetClientList(void)
282 {
283 EX_Window *wl;
284 int i, num;
285 EWin *const *lst;
286
287 /* Mapping order */
288 lst = EwinListOrderGet(&num);
289 if (num > 0)
290 {
291 wl = EMALLOC(EX_Window, num);
292 for (i = 0; i < num; i++)
293 wl[i] = EwinGetClientXwin(lst[i]);
294 ex_netwm_client_list_set(WinGetXwin(VROOT), wl, num);
295 Efree(wl);
296 }
297 else
298 {
299 ex_netwm_client_list_set(WinGetXwin(VROOT), NULL, 0);
300 }
301 }
302
303 void
EWMH_SetClientStacking(void)304 EWMH_SetClientStacking(void)
305 {
306 EX_Window *wl;
307 int i, num;
308 EWin *const *lst;
309
310 /* Stacking order */
311 lst = EwinListStackGet(&num);
312 if (num > 0)
313 {
314 wl = EMALLOC(EX_Window, num);
315 for (i = 0; i < num; i++)
316 wl[i] = EwinGetClientXwin(lst[num - i - 1]);
317 ex_netwm_client_list_stacking_set(WinGetXwin(VROOT), wl, num);
318 Efree(wl);
319 }
320 else
321 {
322 ex_netwm_client_list_stacking_set(WinGetXwin(VROOT), NULL, 0);
323 }
324 }
325
326 void
EWMH_SetActiveWindow(EX_Window win)327 EWMH_SetActiveWindow(EX_Window win)
328 {
329 static EX_Window win_last_set = NoXID;
330
331 if (win == win_last_set)
332 return;
333
334 ex_netwm_client_active_set(WinGetXwin(VROOT), win);
335 win_last_set = win;
336 }
337
338 /*
339 * Functions that set X11-properties from E-window internals
340 */
341
342 void
EWMH_SetWindowName(EX_Window win,const char * name)343 EWMH_SetWindowName(EX_Window win, const char *name)
344 {
345 const char *str;
346
347 str = EstrInt2Enc(name, 1);
348 ex_netwm_name_set(win, str);
349 EstrInt2EncFree(str, 1);
350 }
351
352 void
EWMH_SetWindowDesktop(const EWin * ewin)353 EWMH_SetWindowDesktop(const EWin * ewin)
354 {
355 unsigned int val;
356
357 if (EoIsSticky(ewin))
358 val = 0xFFFFFFFF;
359 else
360 val = EoGetDeskNum(ewin);
361 ex_netwm_desktop_set(EwinGetClientXwin(ewin), val);
362 }
363
364 void
EWMH_SetWindowState(const EWin * ewin)365 EWMH_SetWindowState(const EWin * ewin)
366 {
367 EX_Atom atom_list[16];
368 int atom_count;
369 unsigned int atom_mask, atom_bit;
370
371 atom_count = 0;
372 atom_mask = atom_bit = 0;
373
374 ATOM_ADD_IF(ea_n._NET_WM_STATE_MODAL, ewin->state.modal);
375 ATOM_ADD_IF(ea_n._NET_WM_STATE_STICKY, EoIsSticky(ewin));
376 ATOM_ADD_IF(ea_n._NET_WM_STATE_SHADED, ewin->state.shaded);
377 ATOM_ADD_IF(ea_n._NET_WM_STATE_SKIP_TASKBAR, ewin->props.skip_ext_task);
378 ATOM_ADD_IF(ea_n._NET_WM_STATE_HIDDEN, ewin->state.iconified
379 || ewin->state.shaded);
380 ATOM_ADD_IF(ea_n._NET_WM_STATE_MAXIMIZED_VERT, ewin->state.maximized_vert);
381 ATOM_ADD_IF(ea_n._NET_WM_STATE_MAXIMIZED_HORZ, ewin->state.maximized_horz);
382 ATOM_ADD_IF(ea_n._NET_WM_STATE_FULLSCREEN, ewin->state.fullscreen);
383 ATOM_ADD_IF(ea_n._NET_WM_STATE_SKIP_PAGER, ewin->props.skip_ext_pager);
384 ATOM_ADD_IF(ea_n._NET_WM_STATE_ABOVE, EoGetLayer(ewin) >= 6);
385 ATOM_ADD_IF(ea_n._NET_WM_STATE_BELOW, EoGetLayer(ewin) <= 2);
386 ATOM_ADD_IF(ea_n._NET_WM_STATE_DEMANDS_ATTENTION, ewin->state.attention);
387 ATOM_ADD_IF(ea_n._NET_WM_STATE_FOCUSED, ewin->state.active);
388
389 if (ewin->ewmh.current_state == atom_mask)
390 return;
391 ((EWin *) ewin)->ewmh.current_state = atom_mask;
392
393 ex_window_prop_atom_set(EwinGetClientXwin(ewin), ea_n._NET_WM_STATE,
394 atom_list, atom_count);
395 }
396
397 void
EWMH_SetWindowBorder(const EWin * ewin)398 EWMH_SetWindowBorder(const EWin * ewin)
399 {
400 unsigned int val[4];
401
402 if (ewin->border)
403 {
404 int bl, br, bt, bb;
405
406 EwinBorderGetSize(ewin, &bl, &br, &bt, &bb);
407 val[0] = (unsigned int)bl;
408 val[1] = (unsigned int)br;
409 val[2] = (unsigned int)bt;
410 val[3] = (unsigned int)bb;
411 }
412 else
413 val[0] = val[1] = val[2] = val[3] = 0;
414
415 ex_window_prop_card32_set(EwinGetClientXwin(ewin),
416 ea_n._NET_FRAME_EXTENTS, val, 4);
417 }
418
419 void
EWMH_SetWindowOpacity(EWin * ewin)420 EWMH_SetWindowOpacity(EWin * ewin)
421 {
422 unsigned int opacity;
423
424 if (!ewin->ewmh.opacity_update)
425 return;
426
427 opacity = ewin->props.opacity;
428 if (opacity != 0)
429 {
430 if (opacity != ewin->ewmh.opacity)
431 {
432 ewin->ewmh.opacity = opacity;
433 ex_netwm_opacity_set(EwinGetClientXwin(ewin), opacity);
434 }
435 ex_netwm_opacity_set(EoGetXwin(ewin), opacity);
436 }
437 else
438 {
439 if (opacity != ewin->ewmh.opacity)
440 {
441 ewin->ewmh.opacity = opacity;
442 ex_window_prop_del(EwinGetClientXwin(ewin),
443 ea_n._NET_WM_WINDOW_OPACITY);
444 }
445 ex_window_prop_del(EoGetXwin(ewin), ea_n._NET_WM_WINDOW_OPACITY);
446 ewin->ewmh.opacity_update = 0;
447 }
448 }
449
450 /*
451 * Functions that set E-window internals from X11-properties
452 */
453
454 static void
EWMH_GetWindowName(EWin * ewin)455 EWMH_GetWindowName(EWin * ewin)
456 {
457 char *val;
458
459 EFREE_NULL(ewin->ewmh.wm_name);
460
461 ex_netwm_name_get(EwinGetClientXwin(ewin), &val);
462 if (!val)
463 return;
464 ewin->ewmh.wm_name = EstrUtf82Int(val, 0);
465 Efree(val);
466
467 EwinChange(ewin, EWIN_CHANGE_NAME);
468 }
469
470 static void
EWMH_GetWindowIconName(EWin * ewin)471 EWMH_GetWindowIconName(EWin * ewin)
472 {
473 char *val;
474
475 EFREE_NULL(ewin->ewmh.wm_icon_name);
476
477 ex_netwm_icon_name_get(EwinGetClientXwin(ewin), &val);
478 if (!val)
479 return;
480 ewin->ewmh.wm_icon_name = EstrUtf82Int(val, 0);
481 Efree(val);
482
483 EwinChange(ewin, EWIN_CHANGE_ICON_NAME);
484 }
485
486 static void
EWMH_GetWindowDesktop(EWin * ewin)487 EWMH_GetWindowDesktop(EWin * ewin)
488 {
489 int num;
490 unsigned int desk;
491
492 num = ex_netwm_desktop_get(EwinGetClientXwin(ewin), &desk);
493 if (num <= 0)
494 return;
495
496 if (desk == 0xFFFFFFFF)
497 {
498 /* It is possible to distinguish between "sticky" and "on all desktops". */
499 /* E doesn't */
500 EoSetSticky(ewin, 1);
501 }
502 else
503 {
504 EoSetDesk(ewin, DeskGet(desk));
505 EoSetSticky(ewin, 0);
506 }
507 EwinChange(ewin, EWIN_CHANGE_DESKTOP);
508 }
509
510 static void
EWMH_GetWindowState(EWin * ewin)511 EWMH_GetWindowState(EWin * ewin)
512 {
513 EX_Atom *p_atoms, atom;
514 int i, n_atoms;
515
516 n_atoms = ex_window_prop_atom_list_get(EwinGetClientXwin(ewin),
517 ea_n._NET_WM_STATE, &p_atoms);
518 if (n_atoms <= 0)
519 return;
520
521 /* We must clear/set all according to not present/present */
522 /* EoSetSticky(ewin, 0); Do not override if set via _NET_WM_DESKTOP */
523 ewin->state.shaded = 0;
524 ewin->state.modal = 0;
525 ewin->props.skip_ext_task = ewin->props.skip_ext_pager = 0;
526 ewin->state.maximized_horz = ewin->state.maximized_vert = 0;
527 ewin->state.fullscreen = ewin->state.attention = 0;
528 /* ewin->layer = No ... TBD */
529
530 for (i = 0; i < n_atoms; i++)
531 {
532 atom = p_atoms[i];
533 if (atom == ea_n._NET_WM_STATE_MODAL)
534 ewin->state.modal = 1;
535 else if (atom == ea_n._NET_WM_STATE_STICKY)
536 EoSetSticky(ewin, 1);
537 else if (atom == ea_n._NET_WM_STATE_SHADED)
538 ewin->state.shaded = 1;
539 else if (atom == ea_n._NET_WM_STATE_SKIP_TASKBAR)
540 ewin->props.skip_ext_task = 1;
541 else if (atom == ea_n._NET_WM_STATE_SKIP_PAGER)
542 ewin->props.skip_ext_pager = 1;
543 else if (atom == ea_n._NET_WM_STATE_HIDDEN)
544 ; /* ewin->state.iconified = 1; No - WM_STATE does this */
545 else if (atom == ea_n._NET_WM_STATE_MAXIMIZED_VERT)
546 ewin->state.maximized_vert = 1;
547 else if (atom == ea_n._NET_WM_STATE_MAXIMIZED_HORZ)
548 ewin->state.maximized_horz = 1;
549 else if (atom == ea_n._NET_WM_STATE_FULLSCREEN)
550 ewin->state.fullscreen = 1;
551 else if (atom == ea_n._NET_WM_STATE_ABOVE)
552 EoSetLayer(ewin, 6);
553 else if (atom == ea_n._NET_WM_STATE_BELOW)
554 EoSetLayer(ewin, 2);
555 else if (atom == ea_n._NET_WM_STATE_DEMANDS_ATTENTION)
556 ewin->state.attention = 1;
557 }
558 Efree(p_atoms);
559 }
560
561 static void
EWMH_GetWindowType(EWin * ewin)562 EWMH_GetWindowType(EWin * ewin)
563 {
564 EX_Atom *p_atoms, atom;
565 int n_atoms, i;
566
567 ewin->ewmh.type.all = 0;
568
569 n_atoms = ex_window_prop_atom_list_get(EwinGetClientXwin(ewin),
570 ea_n._NET_WM_WINDOW_TYPE, &p_atoms);
571 if (n_atoms <= 0)
572 {
573 if (EwinIsTransient(ewin))
574 ewin->ewmh.type.b.dialog = 1;
575 else
576 ewin->ewmh.type.b.normal = 1;
577 return;
578 }
579
580 for (i = 0; i < n_atoms; i++)
581 {
582 atom = p_atoms[i];
583 if (atom == ea_n._NET_WM_WINDOW_TYPE_DESKTOP)
584 ewin->ewmh.type.b.desktop = 1;
585 else if (atom == ea_n._NET_WM_WINDOW_TYPE_DOCK)
586 ewin->ewmh.type.b.dock = 1;
587 else if (atom == ea_n._NET_WM_WINDOW_TYPE_UTILITY)
588 ewin->ewmh.type.b.utility = 1;
589 else if (atom == ea_n._NET_WM_WINDOW_TYPE_TOOLBAR)
590 ewin->ewmh.type.b.toolbar = 1;
591 else if (atom == ea_n._NET_WM_WINDOW_TYPE_MENU)
592 ewin->ewmh.type.b.menu = 1;
593 else if (atom == ea_n._NET_WM_WINDOW_TYPE_SPLASH)
594 ewin->ewmh.type.b.splash = 1;
595 else if (atom == ea_n._NET_WM_WINDOW_TYPE_DIALOG)
596 ewin->ewmh.type.b.dialog = 1;
597 else if (atom == ea_n._NET_WM_WINDOW_TYPE_NORMAL)
598 ewin->ewmh.type.b.normal = 1;
599 }
600
601 Efree(p_atoms);
602 }
603
604 static void
EWMH_GetWindowIcons(EWin * ewin)605 EWMH_GetWindowIcons(EWin * ewin)
606 {
607 unsigned int *val;
608 int num;
609
610 EFREE_NULL(ewin->ewmh.wm_icon);
611
612 num = ex_window_prop_card32_list_get(EwinGetClientXwin(ewin),
613 ea_n._NET_WM_ICON, &val);
614 ewin->ewmh.wm_icon_len = num;
615 if (num <= 0)
616 return;
617
618 if (num < 2 || num < (int)(2 + val[0] * val[1]))
619 {
620 Eprintf
621 ("*** EWMH_GetWindowIcons Icon data/size mismatch (ignoring): %s: N=%d WxH=%dx%d\n",
622 EwinGetTitle(ewin), num, val[0], (num >= 2) ? val[1] : 0);
623 Efree(val);
624 return;
625 }
626
627 ewin->ewmh.wm_icon = val;
628
629 EwinChange(ewin, EWIN_CHANGE_ICON_PMAP);
630 }
631
632 static void
EWMH_GetWindowUserTime(EWin * ewin __UNUSED__)633 EWMH_GetWindowUserTime(EWin * ewin __UNUSED__)
634 {
635 #if 0 /* TBD */
636 int num;
637 unsigned int ts;
638
639 num = ex_netwm_user_time_get(EwinGetClientXwin(ewin), &ts);
640 if (num <= 0)
641 return;
642
643 Eprintf("%s %#x\n", __func__, ts);
644 #endif
645 }
646
647 static void
EWMH_GetWindowStartupId(EWin * ewin)648 EWMH_GetWindowStartupId(EWin * ewin)
649 {
650 #define TryGroup(e) (((e)->icccm.group != NoXID) && ((e)->icccm.group != EwinGetClientXwin(e)))
651 char *str;
652
653 if (!Conf.testing.enable_startup_id)
654 return;
655
656 ex_netwm_startup_id_get(EwinGetClientXwin(ewin), &str);
657 if (!str && TryGroup(ewin))
658 ex_netwm_startup_id_get(ewin->icccm.group, &str);
659 if (str && EDebug(1))
660 Eprintf("Startup id: %s: %s\n", EwinGetTitle(ewin), str);
661
662 Efree(str); /* Well... just free for now */
663 }
664
665 static void
EWMH_GetWindowMisc(EWin * ewin)666 EWMH_GetWindowMisc(EWin * ewin)
667 {
668 int num;
669 EX_Window win;
670
671 num = ex_window_prop_window_get(EwinGetClientXwin(ewin),
672 ea_n._NET_SUPPORTING_WM_CHECK, &win, 1);
673 if (num <= 0)
674 return;
675
676 ewin->props.vroot = 1;
677 EoSetDesk(ewin, DesksGetCurrent());
678 }
679
680 static void
EWMH_GetWindowOpacity(EWin * ewin)681 EWMH_GetWindowOpacity(EWin * ewin)
682 {
683 unsigned int opacity;
684
685 opacity = 0; /* Value zero is treated as unset, i.e. default */
686 ex_netwm_opacity_get(EwinGetClientXwin(ewin), &opacity);
687 if (ewin->ewmh.opacity == opacity)
688 return;
689
690 ewin->ewmh.opacity_update = 1;
691 ewin->ewmh.opacity = opacity;
692 ewin->props.opacity = opacity;
693
694 EwinChange(ewin, EWIN_CHANGE_OPACITY);
695 }
696
697 static void
EWMH_GetWindowStrut(EWin * ewin)698 EWMH_GetWindowStrut(EWin * ewin)
699 {
700 int num;
701 unsigned int val[12];
702
703 num = ex_window_prop_card32_get(EwinGetClientXwin(ewin),
704 ea_n._NET_WM_STRUT_PARTIAL, val, 12);
705
706 if (num < 4)
707 num = ex_window_prop_card32_get(EwinGetClientXwin(ewin),
708 ea_n._NET_WM_STRUT, val, 4);
709 if (num < 4)
710 return;
711
712 ewin->strut.left = val[0];
713 ewin->strut.right = val[1];
714 ewin->strut.top = val[2];
715 ewin->strut.bottom = val[3];
716 #if 0 /* FIXME - Handle in placement code */
717 if (num < 12)
718 return;
719 ewin->strut.left_start_y = val[4];
720 ewin->strut.left_end_y = val[5];
721 ewin->strut.right_start_y = val[6];
722 ewin->strut.right_end_y = val[7];
723 ewin->strut.top_start_x = val[8];
724 ewin->strut.top_end_x = val[9];
725 ewin->strut.bottom_start_x = val[10];
726 ewin->strut.bottom_end_x = val[11];
727 #endif
728 }
729
730 void
EWMH_SetWindowActions(const EWin * ewin)731 EWMH_SetWindowActions(const EWin * ewin)
732 {
733 EX_Atom atom_list[16];
734 int atom_count;
735 unsigned int atom_mask, atom_bit;
736
737 atom_count = 0;
738 atom_mask = atom_bit = 0;
739
740 ATOM_ADD_IF(ea_n._NET_WM_ACTION_MOVE, !ewin->state.inhibit_move);
741 ATOM_ADD_IF(ea_n._NET_WM_ACTION_RESIZE, !ewin->state.inhibit_resize);
742 ATOM_ADD_IF(ea_n._NET_WM_ACTION_MINIMIZE, !ewin->state.inhibit_iconify);
743 ATOM_ADD_IF(ea_n._NET_WM_ACTION_SHADE, !ewin->state.inhibit_shade);
744 ATOM_ADD_IF(ea_n._NET_WM_ACTION_STICK, !ewin->state.inhibit_stick);
745 ATOM_ADD_IF(ea_n._NET_WM_ACTION_MAXIMIZE_HORZ, !ewin->state.inhibit_max_hor);
746 ATOM_ADD_IF(ea_n._NET_WM_ACTION_MAXIMIZE_VERT, !ewin->state.inhibit_max_ver);
747 ATOM_ADD_IF(ea_n._NET_WM_ACTION_FULLSCREEN,
748 !ewin->state.inhibit_fullscreeen);
749 ATOM_ADD_IF(ea_n._NET_WM_ACTION_CHANGE_DESKTOP,
750 !ewin->state.inhibit_change_desk);
751 ATOM_ADD_IF(ea_n._NET_WM_ACTION_CLOSE, !ewin->state.inhibit_close);
752 ATOM_ADD_IF(ea_n._NET_WM_ACTION_ABOVE, !ewin->state.inhibit_stacking);
753 ATOM_ADD_IF(ea_n._NET_WM_ACTION_BELOW, !ewin->state.inhibit_stacking);
754
755 if (ewin->ewmh.current_actions == atom_mask)
756 return;
757 ((EWin *) ewin)->ewmh.current_actions = atom_mask;
758
759 ex_window_prop_atom_set(EwinGetClientXwin(ewin),
760 ea_n._NET_WM_ALLOWED_ACTIONS, atom_list, atom_count);
761 }
762
763 void
EWMH_GetWindowHints(EWin * ewin)764 EWMH_GetWindowHints(EWin * ewin)
765 {
766 EWMH_GetWindowMisc(ewin);
767 EWMH_GetWindowOpacity(ewin);
768 EWMH_GetWindowName(ewin);
769 EWMH_GetWindowIconName(ewin);
770 EWMH_GetWindowDesktop(ewin);
771 EWMH_GetWindowState(ewin);
772 EWMH_GetWindowType(ewin);
773 EWMH_GetWindowIcons(ewin);
774 EWMH_GetWindowStrut(ewin);
775 EWMH_GetWindowUserTime(ewin);
776 EWMH_GetWindowStartupId(ewin);
777 }
778
779 /*
780 * Delete all (_NET_...) properties set on window
781 */
782 void
EWMH_DelWindowHints(const EWin * ewin)783 EWMH_DelWindowHints(const EWin * ewin)
784 {
785 ex_window_prop_del(EwinGetClientXwin(ewin), ea_n._NET_WM_DESKTOP);
786 ex_window_prop_del(EwinGetClientXwin(ewin), ea_n._NET_WM_STATE);
787 }
788
789 /*
790 * Process property change
791 */
792 int
EWMH_ProcessPropertyChange(EWin * ewin,EX_Atom atom_change)793 EWMH_ProcessPropertyChange(EWin * ewin, EX_Atom atom_change)
794 {
795 if (atom_change == ea_n._NET_WM_NAME)
796 {
797 EWMH_GetWindowName(ewin);
798 return 1;
799 }
800 if (atom_change == ea_n._NET_WM_ICON_NAME)
801 {
802 EWMH_GetWindowIconName(ewin);
803 return 1;
804 }
805 if (atom_change == ea_n._NET_WM_STRUT_PARTIAL ||
806 atom_change == ea_n._NET_WM_STRUT)
807 {
808 EWMH_GetWindowStrut(ewin);
809 return 1;
810 }
811 if (atom_change == ea_n._NET_WM_WINDOW_OPACITY)
812 {
813 EWMH_GetWindowOpacity(ewin);
814 return 1;
815 }
816 if (atom_change == ea_n._NET_WM_USER_TIME)
817 {
818 #if 0 /* Remove? */
819 EWMH_GetWindowUserTime(ewin);
820 #endif
821 return 1;
822 }
823
824 return 0;
825 }
826
827 /*
828 * Process configuration requests from clients
829 */
830 static int
do_set(int is_set,int action)831 do_set(int is_set, int action)
832 {
833 switch (action)
834 {
835 case _NET_WM_STATE_REMOVE:
836 return 0;
837 case _NET_WM_STATE_ADD:
838 return 1;
839 case _NET_WM_STATE_TOGGLE:
840 return !is_set;
841 }
842 return -1;
843 }
844
845 int
EWMH_ProcessClientClientMessage(EWin * ewin,XClientMessageEvent * ev)846 EWMH_ProcessClientClientMessage(EWin * ewin, XClientMessageEvent * ev)
847 {
848 int source;
849
850 /* EX_Time ts; */
851
852 if (ev->message_type == ea_n._NET_ACTIVE_WINDOW)
853 {
854 source = OPSRC(ev->data.l[0]);
855 /* ts = ev->data.l[1]; */
856 /* cwin = ev->data.l[2]; */
857 EwinOpActivate(ewin, source, 1);
858 return 1;
859 }
860 if (ev->message_type == ea_n._NET_CLOSE_WINDOW)
861 {
862 /* ts = ev->data.l[0]; */
863 source = OPSRC(ev->data.l[1]);
864 EwinOpClose(ewin, source);
865 return 1;
866 }
867 if (ev->message_type == ea_n._NET_WM_DESKTOP)
868 {
869 source = OPSRC(ev->data.l[1]);
870 if ((unsigned)ev->data.l[0] == 0xFFFFFFFF)
871 {
872 if (!EoIsSticky(ewin))
873 EwinOpStick(ewin, source, 1);
874 }
875 else
876 {
877 if (EoIsSticky(ewin))
878 EwinOpStick(ewin, source, 0);
879 else
880 EwinMoveToDesktop(ewin, DeskGet(ev->data.l[0]));
881 }
882 return 1;
883 }
884 if (ev->message_type == ea_n._NET_WM_STATE)
885 {
886 /*
887 * It is assumed(!) that only the MAXIMIZE H/V ones can be set
888 * in one message.
889 */
890 unsigned int action;
891 EX_Atom atom, atom2;
892
893 action = ev->data.l[0];
894 atom = ev->data.l[1];
895 atom2 = ev->data.l[2];
896 source = OPSRC(ev->data.l[3]);
897 if (atom == ea_n._NET_WM_STATE_MODAL)
898 {
899 action = do_set(ewin->state.modal, action);
900 /* TBD */
901 ewin->state.modal = action;
902 }
903 else if (atom == ea_n._NET_WM_STATE_STICKY)
904 {
905 action = do_set(EoIsSticky(ewin), action);
906 EwinOpStick(ewin, source, action);
907 }
908 else if (atom == ea_n._NET_WM_STATE_SHADED)
909 {
910 action = do_set(ewin->state.shaded, action);
911 EwinOpShade(ewin, source, action);
912 }
913 else if (atom == ea_n._NET_WM_STATE_SKIP_TASKBAR)
914 {
915 action = do_set(ewin->props.skip_ext_task, action);
916 ewin->props.skip_ext_task = action;
917 EWMH_SetWindowState(ewin);
918 }
919 else if (atom == ea_n._NET_WM_STATE_SKIP_PAGER)
920 {
921 action = do_set(ewin->props.skip_ext_pager, action);
922 ewin->props.skip_ext_pager = action;
923 EWMH_SetWindowState(ewin);
924 }
925 else if (atom == ea_n._NET_WM_STATE_MAXIMIZED_VERT ||
926 atom == ea_n._NET_WM_STATE_MAXIMIZED_HORZ)
927 {
928 int maxh, maxv;
929
930 maxh = ewin->state.maximized_horz;
931 maxv = ewin->state.maximized_vert;
932 if (atom2 == ea_n._NET_WM_STATE_MAXIMIZED_VERT ||
933 atom2 == ea_n._NET_WM_STATE_MAXIMIZED_HORZ)
934 {
935 /* (ok - ok) */
936 maxh = do_set(maxh, action);
937 maxv = do_set(maxv, action);
938 }
939 else if (atom == ea_n._NET_WM_STATE_MAXIMIZED_VERT)
940 {
941 maxv = do_set(maxv, action);
942 }
943 else
944 {
945 maxh = do_set(maxh, action);
946 }
947
948 maxh = ewin->state.maximized_horz != maxh;
949 maxv = ewin->state.maximized_vert != maxv;
950 if (maxh || maxv)
951 {
952 MaxSizeHV(ewin, "available", maxh, maxv, 0);
953 EWMH_SetWindowState(ewin);
954 }
955 }
956 else if (atom == ea_n._NET_WM_STATE_FULLSCREEN)
957 {
958 action = do_set(ewin->state.fullscreen, action);
959 if (ewin->state.fullscreen != action)
960 EwinOpFullscreen(ewin, source, action);
961 }
962 else if (atom == ea_n._NET_WM_STATE_ABOVE)
963 {
964 action = do_set(EoGetLayer(ewin) >= 6, action);
965 if (action)
966 {
967 if (EoGetLayer(ewin) < 6)
968 EwinOpSetLayer(ewin, source, 6);
969 }
970 else
971 {
972 if (EoGetLayer(ewin) >= 6)
973 EwinOpSetLayer(ewin, source, 4);
974 }
975 }
976 else if (atom == ea_n._NET_WM_STATE_BELOW)
977 {
978 action = do_set(EoGetLayer(ewin) <= 2, action);
979 if (action)
980 {
981 if (EoGetLayer(ewin) > 2)
982 EwinOpSetLayer(ewin, source, 2);
983 }
984 else
985 {
986 if (EoGetLayer(ewin) <= 2)
987 EwinOpSetLayer(ewin, source, 4);
988 }
989 }
990 else if (atom == ea_n._NET_WM_STATE_DEMANDS_ATTENTION)
991 {
992 action = do_set(ewin->state.attention, action);
993 ewin->state.attention = action;
994 EWMH_SetWindowState(ewin);
995 }
996 return 1;
997 }
998 if (ev->message_type == ea_n._NET_MOVERESIZE_WINDOW)
999 {
1000 int flags, grav, x, y, w, h;
1001
1002 flags = ev->data.l[0];
1003 grav = flags & 0xf; /* 0 means use client gravity */
1004 x = (flags & 0x0100) ? ev->data.l[1] : EoGetX(ewin);
1005 y = (flags & 0x0200) ? ev->data.l[2] : EoGetY(ewin);
1006 w = (flags & 0x0400) ? ev->data.l[3] : ewin->client.w;
1007 h = (flags & 0x0800) ? ev->data.l[4] : ewin->client.h;
1008 /* source = OPSRC((flags & 0xF000) >> 12); */
1009 EwinMoveResizeWithGravity(ewin, x, y, w, h, grav);
1010 return 1;
1011 }
1012 if (ev->message_type == ea_n._NET_WM_MOVERESIZE)
1013 {
1014 /* source = OPSRC(ev->data.l[4]); */
1015
1016 EventsUpdateXY(NULL, NULL);
1017
1018 switch (ev->data.l[2])
1019 {
1020 case _NET_WM_MOVERESIZE_SIZE_TOPLEFT:
1021 case _NET_WM_MOVERESIZE_SIZE_TOPRIGHT:
1022 case _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT:
1023 case _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT:
1024 MoveResizeResizeStart(ewin, 0, MODE_RESIZE);
1025 break;
1026 case _NET_WM_MOVERESIZE_SIZE_RIGHT:
1027 case _NET_WM_MOVERESIZE_SIZE_LEFT:
1028 MoveResizeResizeStart(ewin, 0, MODE_RESIZE_H);
1029 break;
1030 case _NET_WM_MOVERESIZE_SIZE_TOP:
1031 case _NET_WM_MOVERESIZE_SIZE_BOTTOM:
1032 MoveResizeResizeStart(ewin, 0, MODE_RESIZE_V);
1033 break;
1034
1035 case _NET_WM_MOVERESIZE_MOVE:
1036 MoveResizeMoveStart(ewin, 0, 0, 0);
1037 break;
1038
1039 case _NET_WM_MOVERESIZE_SIZE_KEYBOARD:
1040 MoveResizeResizeStart(ewin, 1, MODE_RESIZE);
1041 break;
1042 case _NET_WM_MOVERESIZE_MOVE_KEYBOARD:
1043 MoveResizeMoveStart(ewin, 1, 0, 0);
1044 break;
1045 case _NET_WM_MOVERESIZE_CANCEL:
1046 MoveResizeEnd(ewin);
1047 break;
1048 }
1049 return 1;
1050 }
1051 if (ev->message_type == ea_n._NET_RESTACK_WINDOW)
1052 {
1053 /* source = OPSRC(ev->data.l[0]); */
1054 /* FIXME - Implement */
1055 return 1;
1056 }
1057
1058 return 0;
1059 }
1060
1061 int
EWMH_ProcessRootClientMessage(XClientMessageEvent * ev)1062 EWMH_ProcessRootClientMessage(XClientMessageEvent * ev)
1063 {
1064 if (ev->message_type == ea_n._NET_CURRENT_DESKTOP)
1065 {
1066 DeskGotoNum(ev->data.l[0]);
1067 return 1;
1068 }
1069 if (ev->message_type == ea_n._NET_DESKTOP_VIEWPORT)
1070 {
1071 DeskCurrentGotoArea(ev->data.l[0] / WinGetW(VROOT),
1072 ev->data.l[1] / WinGetH(VROOT));
1073 return 1;
1074 }
1075 if (ev->message_type == ea_n._NET_SHOWING_DESKTOP)
1076 {
1077 EwinsShowDesktop(ev->data.l[0]);
1078 return 1;
1079 }
1080 #if 0 /* These messages are sent to dedicated window */
1081 if (ev->message_type == ea_n._NET_STARTUP_INFO_BEGIN)
1082 {
1083 Eprintf("ea_n._NET_STARTUP_INFO_BEGIN: %lx: %s\n",
1084 ev->window, (char *)ev->data.l);
1085 return 1;
1086 }
1087 if (ev->message_type == ea_n._NET_STARTUP_INFO)
1088 {
1089 Eprintf("ea_n._NET_STARTUP_INFO : %lx: %s\n",
1090 ev->window, (char *)ev->data.l);
1091 return 1;
1092 }
1093 #endif
1094
1095 return 0;
1096 }
1097