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