1 /*
2  * Copyright (C) 2004-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 #include "config.h"
24 
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <X11/Xatom.h>
30 #include <X11/Xlib.h>
31 #include <X11/Xutil.h>
32 
33 /* Window property change actions (must match _NET_WM_STATE_... ones) */
34 #define EX_PROP_LIST_REMOVE    0
35 #define EX_PROP_LIST_ADD       1
36 #define EX_PROP_LIST_TOGGLE    2
37 
38 #include "xprop.h"
39 #include "xwin.h"
40 
41 #define _ex_disp disp
42 
43 /*
44  * General stuff
45  */
46 
47 EX_Atom
ex_atom_get(const char * name)48 ex_atom_get(const char *name)
49 {
50    return XInternAtom(_ex_disp, name, False);
51 }
52 
53 void
ex_atoms_get(const char * const * names,unsigned int num,EX_Atom * atoms)54 ex_atoms_get(const char *const *names, unsigned int num, EX_Atom * atoms)
55 {
56 #if SIZEOF_INT == SIZEOF_LONG
57    XInternAtoms(_ex_disp, (char **)names, num, False, (Atom *) atoms);
58 #else
59    unsigned int        i;
60    Atom               *_atoms;
61 
62    _atoms = EMALLOC(Atom, num);
63    if (!_atoms)
64       return;
65 
66    XInternAtoms(_ex_disp, (char **)names, num, False, _atoms);
67    for (i = 0; i < num; i++)
68       atoms[i] = _atoms[i];
69 
70    Efree(_atoms);
71 #endif
72 }
73 
74 /*
75  * Send client message (format 32)
76  */
77 int
ex_client_message32_send(EX_Window win,EX_Atom type,unsigned int mask,unsigned int d0,unsigned int d1,unsigned int d2,unsigned int d3,unsigned int d4)78 ex_client_message32_send(EX_Window win, EX_Atom type,
79 			 unsigned int mask,
80 			 unsigned int d0, unsigned int d1,
81 			 unsigned int d2, unsigned int d3, unsigned int d4)
82 {
83    XEvent              xev;
84 
85    xev.xclient.type = ClientMessage;
86    xev.xclient.window = win;
87    xev.xclient.message_type = type;
88    xev.xclient.format = 32;
89    xev.xclient.data.l[0] = d0;
90    xev.xclient.data.l[1] = d1;
91    xev.xclient.data.l[2] = d2;
92    xev.xclient.data.l[3] = d3;
93    xev.xclient.data.l[4] = d4;
94 
95    return XSendEvent(_ex_disp, win, False, mask, &xev);
96 }
97 
98 /*
99  * Set size 32 item (array) property
100  */
101 static void
_ex_window_prop32_set(EX_Window win,EX_Atom atom,EX_Atom type,const unsigned int * val,int num)102 _ex_window_prop32_set(EX_Window win, EX_Atom atom,
103 		      EX_Atom type, const unsigned int *val, int num)
104 {
105 #if SIZEOF_INT == SIZEOF_LONG
106    XChangeProperty(_ex_disp, win, atom, type, 32, PropModeReplace,
107 		   (unsigned char *)val, num);
108 #else
109    unsigned long      *pl;
110    int                 i;
111 
112    pl = EMALLOC(unsigned long, num);
113 
114    if (!pl)
115       return;
116    for (i = 0; i < num; i++)
117       pl[i] = val[i];
118    XChangeProperty(_ex_disp, win, atom, type, 32, PropModeReplace,
119 		   (unsigned char *)pl, num);
120    Efree(pl);
121 #endif
122 }
123 
124 /*
125  * Get size 32 item (array) property
126  *
127  * If the property was successfully fetched the number of items stored in
128  * val is returned, otherwise -1 is returned.
129  * The returned array must be freed with free().
130  * Note: Return value 0 means that the property exists but has no elements.
131  */
132 static int
_ex_window_prop32_list_get(EX_Window win,EX_Atom atom,EX_Atom type,unsigned int ** val,int num)133 _ex_window_prop32_list_get(EX_Window win, EX_Atom atom,
134 			   EX_Atom type, unsigned int **val, int num)
135 {
136    unsigned char      *prop_ret;
137    Atom                type_ret;
138    unsigned long       bytes_after, num_ret;
139    int                 format_ret;
140    unsigned int       *lst;
141    int                 i;
142 
143    prop_ret = NULL;
144    if (XGetWindowProperty(_ex_disp, win, atom, 0, 0x7fffffff, False,
145 			  type, &type_ret, &format_ret, &num_ret,
146 			  &bytes_after, &prop_ret) != Success)
147       return -1;
148 
149    if (type_ret != type || format_ret != 32)
150      {
151 	num = -1;
152      }
153    else if (num_ret == 0 || !prop_ret)
154      {
155 	num = 0;
156      }
157    else
158      {
159 	if (num >= 0)
160 	  {
161 	     if ((int)num_ret < num)
162 		num = (int)num_ret;
163 	     lst = *val;
164 	  }
165 	else
166 	  {
167 	     num = (int)num_ret;
168 	     lst = EMALLOC(unsigned int, num);
169 
170 	     *val = lst;
171 	     if (!lst)
172 		return 0;
173 	  }
174 	for (i = 0; i < num; i++)
175 	   lst[i] = ((unsigned long *)prop_ret)[i];
176      }
177    if (prop_ret)
178       XFree(prop_ret);
179 
180    return num;
181 }
182 
183 void
ex_window_prop_del(EX_Window win,EX_Atom atom)184 ex_window_prop_del(EX_Window win, EX_Atom atom)
185 {
186    XDeleteProperty(_ex_disp, win, atom);
187 }
188 
189 /*
190  * Set CARD32 (array) property
191  */
192 void
ex_window_prop_card32_set(EX_Window win,EX_Atom atom,const unsigned int * val,unsigned int num)193 ex_window_prop_card32_set(EX_Window win, EX_Atom atom,
194 			  const unsigned int *val, unsigned int num)
195 {
196    _ex_window_prop32_set(win, atom, XA_CARDINAL, val, (int)num);
197 }
198 
199 /*
200  * Get CARD32 (array) property
201  *
202  * At most len items are returned in val.
203  * If the property was successfully fetched the number of items stored in
204  * val is returned, otherwise -1 is returned.
205  * Note: Return value 0 means that the property exists but has no elements.
206  */
207 int
ex_window_prop_card32_get(EX_Window win,EX_Atom atom,unsigned int * val,unsigned int len)208 ex_window_prop_card32_get(EX_Window win, EX_Atom atom,
209 			  unsigned int *val, unsigned int len)
210 {
211    return _ex_window_prop32_list_get(win, atom, XA_CARDINAL, &val, (int)len);
212 }
213 
214 /*
215  * Get CARD32 (array) property of any length
216  *
217  * If the property was successfully fetched the number of items stored in
218  * val is returned, otherwise -1 is returned.
219  * Note: Return value 0 means that the property exists but has no elements.
220  */
221 int
ex_window_prop_card32_list_get(EX_Window win,EX_Atom atom,unsigned int ** plst)222 ex_window_prop_card32_list_get(EX_Window win, EX_Atom atom, unsigned int **plst)
223 {
224    return _ex_window_prop32_list_get(win, atom, XA_CARDINAL, plst, -1);
225 }
226 
227 /*
228  * Set simple string list property
229  */
230 void
ex_window_prop_string_list_set(EX_Window win,EX_Atom atom,char ** lst,int num)231 ex_window_prop_string_list_set(EX_Window win, EX_Atom atom, char **lst, int num)
232 {
233    XTextProperty       xtp;
234 
235    if (XmbTextListToTextProperty(_ex_disp, lst, num,
236 				 XStdICCTextStyle, &xtp) != Success)
237       return;
238    XSetTextProperty(_ex_disp, win, &xtp, atom);
239    XFree(xtp.value);
240 }
241 
242 /*
243  * Get simple string list property
244  *
245  * If the property was successfully fetched the number of items stored in
246  * lst is returned, otherwise -1 is returned.
247  * Note: Return value 0 means that the property exists but has no elements.
248  */
249 int
ex_window_prop_string_list_get(EX_Window win,EX_Atom atom,char *** plst)250 ex_window_prop_string_list_get(EX_Window win, EX_Atom atom, char ***plst)
251 {
252    char              **pstr = NULL;
253    XTextProperty       xtp;
254    int                 i, items;
255    char              **list;
256    Status              s;
257 
258    *plst = NULL;
259 
260    if (!XGetTextProperty(_ex_disp, win, &xtp, atom))
261       return -1;
262 
263    if (xtp.format == 8)
264      {
265 	s = XmbTextPropertyToTextList(_ex_disp, &xtp, &list, &items);
266 	if (s == Success)
267 	  {
268 	     if (items > 0)
269 	       {
270 		  pstr = EMALLOC(char *, items);
271 
272 		  if (!pstr)
273 		     goto done;
274 		  for (i = 0; i < items; i++)
275 		     pstr[i] = (list[i] && (*list[i] || i < items - 1)) ?
276 			Estrdup(list[i]) : NULL;
277 	       }
278 	     if (list)
279 		XFreeStringList(list);
280 	     goto done;
281 	  }
282      }
283 
284    /* Bad format or XmbTextPropertyToTextList failed - Now what? */
285    items = 1;
286    pstr = EMALLOC(char *, 1);
287 
288    if (!pstr)
289       goto done;
290    pstr[0] = (xtp.value) ? Estrdup((char *)xtp.value) : NULL;
291 
292  done:
293    XFree(xtp.value);
294 
295    *plst = pstr;
296    if (!pstr)
297       items = 0;
298    return items;
299 }
300 
301 /*
302  * Set simple string property
303  */
304 void
ex_window_prop_string_set(EX_Window win,EX_Atom atom,const char * str)305 ex_window_prop_string_set(EX_Window win, EX_Atom atom, const char *str)
306 {
307    ex_window_prop_string_list_set(win, atom, (char **)(&str), 1);
308 }
309 
310 /*
311  * Get simple string property
312  */
313 char               *
ex_window_prop_string_get(EX_Window win,EX_Atom atom)314 ex_window_prop_string_get(EX_Window win, EX_Atom atom)
315 {
316    XTextProperty       xtp;
317    char               *str;
318    int                 items;
319    char              **list;
320    Status              s;
321 
322    if (!XGetTextProperty(_ex_disp, win, &xtp, atom))
323       return NULL;
324 
325    if (xtp.format == 8)
326      {
327 	s = XmbTextPropertyToTextList(_ex_disp, &xtp, &list, &items);
328 	if ((s == Success) && (items > 0))
329 	  {
330 	     str = (*list) ? Estrdup(*list) : NULL;
331 	     XFreeStringList(list);
332 	  }
333 	else
334 	   str = (xtp.value) ? Estrdup((char *)xtp.value) : NULL;
335      }
336    else
337       str = (xtp.value) ? Estrdup((char *)xtp.value) : NULL;
338 
339    XFree(xtp.value);
340 
341    return str;
342 }
343 
344 /*
345  * Set UTF-8 string property
346  */
347 static void
_ex_window_prop_string_utf8_set(EX_Window win,EX_Atom atom,const char * str)348 _ex_window_prop_string_utf8_set(EX_Window win, EX_Atom atom, const char *str)
349 {
350    XChangeProperty(_ex_disp, win, atom, ea_m.UTF8_STRING, 8,
351 		   PropModeReplace, (unsigned char *)str, strlen(str));
352 }
353 
354 /*
355  * Get UTF-8 string property
356  */
357 static char        *
_ex_window_prop_string_utf8_get(EX_Window win,EX_Atom atom)358 _ex_window_prop_string_utf8_get(EX_Window win, EX_Atom atom)
359 {
360    char               *str;
361    unsigned char      *prop_ret;
362    Atom                type_ret;
363    unsigned long       bytes_after, num_ret;
364    int                 format_ret;
365 
366    str = NULL;
367    prop_ret = NULL;
368    XGetWindowProperty(_ex_disp, win, atom, 0, 0x7fffffff, False,
369 		      ea_m.UTF8_STRING, &type_ret,
370 		      &format_ret, &num_ret, &bytes_after, &prop_ret);
371    if (prop_ret && num_ret > 0 && format_ret == 8)
372      {
373 	str = EMALLOC(char, num_ret + 1);
374 
375 	if (str)
376 	  {
377 	     memcpy(str, prop_ret, num_ret);
378 	     str[num_ret] = '\0';
379 	  }
380      }
381    if (prop_ret)
382       XFree(prop_ret);
383 
384    return str;
385 }
386 
387 /*
388  * Set X ID (array) property
389  */
390 void
ex_window_prop_xid_set(EX_Window win,EX_Atom atom,EX_Atom type,const EX_ID * lst,unsigned int num)391 ex_window_prop_xid_set(EX_Window win, EX_Atom atom, EX_Atom type,
392 		       const EX_ID * lst, unsigned int num)
393 {
394    _ex_window_prop32_set(win, atom, type, lst, (int)num);
395 }
396 
397 /*
398  * Get X ID (array) property
399  *
400  * At most len items are returned in val.
401  * If the property was successfully fetched the number of items stored in
402  * val is returned, otherwise -1 is returned.
403  * Note: Return value 0 means that the property exists but has no elements.
404  */
405 int
ex_window_prop_xid_get(EX_Window win,EX_Atom atom,EX_Atom type,EX_ID * lst,unsigned int len)406 ex_window_prop_xid_get(EX_Window win, EX_Atom atom, EX_Atom type,
407 		       EX_ID * lst, unsigned int len)
408 {
409    return _ex_window_prop32_list_get(win, atom, type, &lst, (int)len);
410 }
411 
412 /*
413  * Get X ID (array) property
414  *
415  * If the property was successfully fetched the number of items stored in
416  * val is returned, otherwise -1 is returned.
417  * The returned array must be freed with free().
418  * Note: Return value 0 means that the property exists but has no elements.
419  */
420 int
ex_window_prop_xid_list_get(EX_Window win,EX_Atom atom,EX_Atom type,EX_ID ** val)421 ex_window_prop_xid_list_get(EX_Window win, EX_Atom atom,
422 			    EX_Atom type, EX_ID ** val)
423 {
424    return _ex_window_prop32_list_get(win, atom, type, val, -1);
425 }
426 
427 /*
428  * Remove/add/toggle X ID list item.
429  */
430 void
ex_window_prop_xid_list_change(EX_Window win,EX_Atom atom,EX_Atom type,EX_ID item,int op)431 ex_window_prop_xid_list_change(EX_Window win, EX_Atom atom,
432 			       EX_Atom type, EX_ID item, int op)
433 {
434    EX_ID              *lst, *lst_r;
435    int                 i, num;
436 
437    lst = NULL;
438    num = ex_window_prop_xid_list_get(win, atom, type, &lst);
439    if (num < 0)
440       return;			/* Error - assuming invalid window */
441 
442    /* Is it there? */
443    for (i = 0; i < num; i++)
444      {
445 	if (lst[i] == item)
446 	   break;
447      }
448 
449    if (i < num)
450      {
451 	/* Was in list */
452 	if (op == EX_PROP_LIST_ADD)
453 	   goto done;
454 	/* Remove it */
455 	num--;
456 	for (; i < num; i++)
457 	   lst[i] = lst[i + 1];
458      }
459    else
460      {
461 	/* Was not in list */
462 	if (op == EX_PROP_LIST_REMOVE)
463 	   goto done;
464 	/* Add it */
465 	num++;
466 	lst_r = EREALLOC(EX_ID, lst, num);
467 	if (!lst_r)
468 	   goto done;
469 	lst = lst_r;
470 	lst[i] = item;
471      }
472 
473    ex_window_prop_xid_set(win, atom, type, lst, num);
474 
475  done:
476    Efree(lst);
477 }
478 
479 /*
480  * Set Atom (array) property
481  */
482 void
ex_window_prop_atom_set(EX_Window win,EX_Atom atom,const EX_Atom * lst,unsigned int num)483 ex_window_prop_atom_set(EX_Window win, EX_Atom atom,
484 			const EX_Atom * lst, unsigned int num)
485 {
486    ex_window_prop_xid_set(win, atom, XA_ATOM, lst, num);
487 }
488 
489 /*
490  * Get Atom (array) property
491  *
492  * At most len items are returned in val.
493  * If the property was successfully fetched the number of items stored in
494  * val is returned, otherwise -1 is returned.
495  * Note: Return value 0 means that the property exists but has no elements.
496  */
497 int
ex_window_prop_atom_get(EX_Window win,EX_Atom atom,EX_Atom * lst,unsigned int len)498 ex_window_prop_atom_get(EX_Window win, EX_Atom atom,
499 			EX_Atom * lst, unsigned int len)
500 {
501    return ex_window_prop_xid_get(win, atom, XA_ATOM, lst, len);
502 }
503 
504 /*
505  * Get Atom (array) property
506  *
507  * If the property was successfully fetched the number of items stored in
508  * val is returned, otherwise -1 is returned.
509  * The returned array must be freed with free().
510  * Note: Return value 0 means that the property exists but has no elements.
511  */
512 int
ex_window_prop_atom_list_get(EX_Window win,EX_Atom atom,EX_Atom ** plst)513 ex_window_prop_atom_list_get(EX_Window win, EX_Atom atom, EX_Atom ** plst)
514 {
515    return ex_window_prop_xid_list_get(win, atom, XA_ATOM, plst);
516 }
517 
518 /*
519  * Remove/add/toggle atom list item.
520  */
521 void
ex_window_prop_atom_list_change(EX_Window win,EX_Atom atom,EX_Atom item,int op)522 ex_window_prop_atom_list_change(EX_Window win, EX_Atom atom,
523 				EX_Atom item, int op)
524 {
525    ex_window_prop_xid_list_change(win, atom, XA_ATOM, item, op);
526 }
527 
528 /*
529  * Set Window (array) property
530  */
531 void
ex_window_prop_window_set(EX_Window win,EX_Atom atom,const EX_Window * lst,unsigned int num)532 ex_window_prop_window_set(EX_Window win, EX_Atom atom,
533 			  const EX_Window * lst, unsigned int num)
534 {
535    ex_window_prop_xid_set(win, atom, XA_WINDOW, lst, num);
536 }
537 
538 /*
539  * Get Window (array) property
540  *
541  * At most len items are returned in val.
542  * If the property was successfully fetched the number of items stored in
543  * val is returned, otherwise -1 is returned.
544  * Note: Return value 0 means that the property exists but has no elements.
545  */
546 int
ex_window_prop_window_get(EX_Window win,EX_Atom atom,EX_Window * lst,unsigned int len)547 ex_window_prop_window_get(EX_Window win, EX_Atom atom,
548 			  EX_Window * lst, unsigned int len)
549 {
550    return ex_window_prop_xid_get(win, atom, XA_WINDOW, lst, len);
551 }
552 
553 /*
554  * Get Window (array) property
555  *
556  * If the property was successfully fetched the number of items stored in
557  * val is returned, otherwise -1 is returned.
558  * The returned array must be freed with free().
559  * Note: Return value 0 means that the property exists but has no elements.
560  */
561 int
ex_window_prop_window_list_get(EX_Window win,EX_Atom atom,EX_Window ** plst)562 ex_window_prop_window_list_get(EX_Window win, EX_Atom atom, EX_Window ** plst)
563 {
564    return ex_window_prop_xid_list_get(win, atom, XA_WINDOW, plst);
565 }
566 
567 #define S_ATOM_COUNT(s) (sizeof(s) / sizeof(EX_Atom))
568 
569 /*
570  * Misc atom stuff
571  */
572 
573 static const char  *const ea_m_names[] = {
574 #define DEFINE_ATOM_MISC(a) #a,
575 #include "xpropdefs.h"
576 #undef DEFINE_ATOM_MISC
577 };
578 
579 e_atoms_misc_t      ea_m;
580 
581 void
ex_atoms_init(void)582 ex_atoms_init(void)
583 {
584    ex_atoms_get(ea_m_names, E_ARRAY_SIZE(ea_m_names), (EX_Atom *) & ea_m);
585 }
586 
587 /*
588  * ICCCM stuff
589  */
590 
591 static const char  *const ea_i_names[] = {
592 #define DEFINE_ATOM_ICCCM(a) #a,
593 #include "xpropdefs.h"
594 #undef DEFINE_ATOM_ICCCM
595 };
596 
597 e_atoms_icccm_t     ea_i;
598 
599 void
ex_icccm_init(void)600 ex_icccm_init(void)
601 {
602    ex_atoms_get(ea_i_names, E_ARRAY_SIZE(ea_i_names), (EX_Atom *) & ea_i);
603 }
604 
605 static void
ex_icccm_state_set(EX_Window win,unsigned int state)606 ex_icccm_state_set(EX_Window win, unsigned int state)
607 {
608    unsigned long       c[2];
609 
610    c[0] = state;
611    c[1] = 0;
612    XChangeProperty(_ex_disp, win, ea_i.WM_STATE, ea_i.WM_STATE,
613 		   32, PropModeReplace, (unsigned char *)c, 2);
614 }
615 
616 void
ex_icccm_state_set_iconic(EX_Window win)617 ex_icccm_state_set_iconic(EX_Window win)
618 {
619    ex_icccm_state_set(win, IconicState);
620 }
621 
622 void
ex_icccm_state_set_normal(EX_Window win)623 ex_icccm_state_set_normal(EX_Window win)
624 {
625    ex_icccm_state_set(win, NormalState);
626 }
627 
628 void
ex_icccm_state_set_withdrawn(EX_Window win)629 ex_icccm_state_set_withdrawn(EX_Window win)
630 {
631    ex_icccm_state_set(win, WithdrawnState);
632 }
633 
634 static void
ex_icccm_client_message_send(EX_Window win,EX_Atom atom,EX_Time ts)635 ex_icccm_client_message_send(EX_Window win, EX_Atom atom, EX_Time ts)
636 {
637    ex_client_message32_send(win, ea_i.WM_PROTOCOLS, NoEventMask,
638 			    atom, ts, 0, 0, 0);
639 }
640 
641 void
ex_icccm_delete_window_send(EX_Window win,EX_Time ts)642 ex_icccm_delete_window_send(EX_Window win, EX_Time ts)
643 {
644    ex_icccm_client_message_send(win, ea_i.WM_DELETE_WINDOW, ts);
645 }
646 
647 void
ex_icccm_take_focus_send(EX_Window win,EX_Time ts)648 ex_icccm_take_focus_send(EX_Window win, EX_Time ts)
649 {
650    ex_icccm_client_message_send(win, ea_i.WM_TAKE_FOCUS, ts);
651 }
652 
653 #if 0
654 void
655 ex_icccm_save_yourself_send(EX_Window win, EX_Time ts)
656 {
657    ex_icccm_client_message_send(win, ea_i.WM_SAVE_YOURSELF, ts);
658 }
659 #endif
660 
661 void
ex_icccm_title_set(EX_Window win,const char * title)662 ex_icccm_title_set(EX_Window win, const char *title)
663 {
664    ex_window_prop_string_set(win, ea_i.WM_NAME, title);
665 }
666 
667 char               *
ex_icccm_title_get(EX_Window win)668 ex_icccm_title_get(EX_Window win)
669 {
670    return ex_window_prop_string_get(win, ea_i.WM_NAME);
671 }
672 
673 void
ex_icccm_name_class_set(EX_Window win,const char * name,const char * clss)674 ex_icccm_name_class_set(EX_Window win, const char *name, const char *clss)
675 {
676    XClassHint         *xch;
677 
678    xch = XAllocClassHint();
679    if (!xch)
680       return;
681    xch->res_name = (char *)name;
682    xch->res_class = (char *)clss;
683    XSetClassHint(_ex_disp, win, xch);
684    XFree(xch);
685 }
686 
687 void
ex_icccm_name_class_get(EX_Window win,char ** name,char ** clss)688 ex_icccm_name_class_get(EX_Window win, char **name, char **clss)
689 {
690    XClassHint          xch;
691 
692    *name = *clss = NULL;
693    xch.res_name = NULL;
694    xch.res_class = NULL;
695    if (XGetClassHint(_ex_disp, win, &xch))
696      {
697 	if (name && xch.res_name)
698 	   *name = Estrdup(xch.res_name);
699 	if (clss && xch.res_class)
700 	   *clss = Estrdup(xch.res_class);
701 	XFree(xch.res_name);
702 	XFree(xch.res_class);
703      }
704 }
705 
706 /*
707  * _NET_WM hints (EWMH)
708  */
709 
710 static const char  *const ea_n_names[] = {
711 #define DEFINE_ATOM_NETWM(a) #a,
712 #include "xpropdefs.h"
713 #undef DEFINE_ATOM_NETWM
714 };
715 e_atoms_netwm_t     ea_n;
716 
717 void
ex_netwm_init(void)718 ex_netwm_init(void)
719 {
720    ex_atoms_get(ea_n_names, E_ARRAY_SIZE(ea_n_names), (EX_Atom *) & ea_n);
721 }
722 
723 /*
724  * WM identification
725  */
726 void
ex_netwm_wm_identify(EX_Window root,EX_Window check,const char * wm_name)727 ex_netwm_wm_identify(EX_Window root, EX_Window check, const char *wm_name)
728 {
729    unsigned int        pid = getpid();
730 
731    ex_window_prop_window_set(root, ea_n._NET_SUPPORTING_WM_CHECK, &check, 1);
732    ex_window_prop_window_set(check, ea_n._NET_SUPPORTING_WM_CHECK, &check, 1);
733    _ex_window_prop_string_utf8_set(check, ea_n._NET_WM_NAME, wm_name);
734    ex_window_prop_card32_set(check, ea_n._NET_WM_PID, &pid, 1);
735 }
736 
737 /*
738  * Desktop configuration and status
739  */
740 
741 void
ex_netwm_desk_count_set(EX_Window root,unsigned int n_desks)742 ex_netwm_desk_count_set(EX_Window root, unsigned int n_desks)
743 {
744    ex_window_prop_card32_set(root, ea_n._NET_NUMBER_OF_DESKTOPS, &n_desks, 1);
745 }
746 
747 void
ex_netwm_desk_roots_set(EX_Window root,const EX_Window * vroots,unsigned int n_desks)748 ex_netwm_desk_roots_set(EX_Window root, const EX_Window * vroots,
749 			unsigned int n_desks)
750 {
751    ex_window_prop_window_set(root, ea_n._NET_VIRTUAL_ROOTS, vroots, n_desks);
752 }
753 
754 void
ex_netwm_desk_names_set(EX_Window root,const char ** names,unsigned int n_desks)755 ex_netwm_desk_names_set(EX_Window root, const char **names,
756 			unsigned int n_desks)
757 {
758    char                ss[32], *buf, *buf_r;
759    const char         *s;
760    unsigned int        i;
761    int                 l, len;
762 
763    buf = NULL;
764    len = 0;
765 
766    for (i = 0; i < n_desks; i++)
767      {
768 	s = (names) ? names[i] : NULL;
769 	if (!s)
770 	  {
771 	     /* Default to "Desk-<number>" */
772 	     sprintf(ss, "Desk-%u", i);
773 	     s = ss;
774 	  }
775 
776 	l = strlen(s) + 1;
777 	buf_r = EREALLOC(char, buf, len + l);
778 
779 	if (!buf_r)
780 	   goto done;
781 	buf = buf_r;
782 	memcpy(buf + len, s, l);
783 	len += l;
784      }
785 
786    XChangeProperty(_ex_disp, root, ea_n._NET_DESKTOP_NAMES,
787 		   ea_m.UTF8_STRING, 8, PropModeReplace,
788 		   (unsigned char *)buf, len);
789 
790  done:
791    Efree(buf);
792 }
793 
794 void
ex_netwm_desk_size_set(EX_Window root,unsigned int width,unsigned int height)795 ex_netwm_desk_size_set(EX_Window root, unsigned int width, unsigned int height)
796 {
797    unsigned int        size[2];
798 
799    size[0] = width;
800    size[1] = height;
801    ex_window_prop_card32_set(root, ea_n._NET_DESKTOP_GEOMETRY, size, 2);
802 }
803 
804 void
ex_netwm_desk_workareas_set(EX_Window root,const unsigned int * areas,unsigned int n_desks)805 ex_netwm_desk_workareas_set(EX_Window root, const unsigned int *areas,
806 			    unsigned int n_desks)
807 {
808    ex_window_prop_card32_set(root, ea_n._NET_WORKAREA, areas, 4 * n_desks);
809 }
810 
811 void
ex_netwm_desk_current_set(EX_Window root,unsigned int desk)812 ex_netwm_desk_current_set(EX_Window root, unsigned int desk)
813 {
814    ex_window_prop_card32_set(root, ea_n._NET_CURRENT_DESKTOP, &desk, 1);
815 }
816 
817 void
ex_netwm_desk_viewports_set(EX_Window root,const unsigned int * origins,unsigned int n_desks)818 ex_netwm_desk_viewports_set(EX_Window root, const unsigned int *origins,
819 			    unsigned int n_desks)
820 {
821    ex_window_prop_card32_set(root, ea_n._NET_DESKTOP_VIEWPORT,
822 			     origins, 2 * n_desks);
823 }
824 
825 void
ex_netwm_showing_desktop_set(EX_Window root,int on)826 ex_netwm_showing_desktop_set(EX_Window root, int on)
827 {
828    unsigned int        val;
829 
830    val = (on) ? 1 : 0;
831    ex_window_prop_card32_set(root, ea_n._NET_SHOWING_DESKTOP, &val, 1);
832 }
833 
834 /*
835  * Client status
836  */
837 
838 /* Mapping order */
839 void
ex_netwm_client_list_set(EX_Window root,const EX_Window * p_clients,unsigned int n_clients)840 ex_netwm_client_list_set(EX_Window root, const EX_Window * p_clients,
841 			 unsigned int n_clients)
842 {
843    ex_window_prop_window_set(root, ea_n._NET_CLIENT_LIST, p_clients, n_clients);
844 }
845 
846 /* Stacking order */
847 void
ex_netwm_client_list_stacking_set(EX_Window root,const EX_Window * p_clients,unsigned int n_clients)848 ex_netwm_client_list_stacking_set(EX_Window root,
849 				  const EX_Window * p_clients,
850 				  unsigned int n_clients)
851 {
852    ex_window_prop_window_set(root, ea_n._NET_CLIENT_LIST_STACKING,
853 			     p_clients, n_clients);
854 }
855 
856 void
ex_netwm_client_active_set(EX_Window root,EX_Window win)857 ex_netwm_client_active_set(EX_Window root, EX_Window win)
858 {
859    ex_window_prop_window_set(root, ea_n._NET_ACTIVE_WINDOW, &win, 1);
860 }
861 
862 /*
863  * Client window properties
864  */
865 
866 void
ex_netwm_name_set(EX_Window win,const char * name)867 ex_netwm_name_set(EX_Window win, const char *name)
868 {
869    _ex_window_prop_string_utf8_set(win, ea_n._NET_WM_NAME, name);
870 }
871 
872 int
ex_netwm_name_get(EX_Window win,char ** name)873 ex_netwm_name_get(EX_Window win, char **name)
874 {
875    char               *s;
876 
877    s = _ex_window_prop_string_utf8_get(win, ea_n._NET_WM_NAME);
878    *name = s;
879 
880    return !!s;
881 }
882 
883 void
ex_netwm_visible_name_set(EX_Window win,const char * name)884 ex_netwm_visible_name_set(EX_Window win, const char *name)
885 {
886    _ex_window_prop_string_utf8_set(win, ea_n._NET_WM_VISIBLE_NAME, name);
887 }
888 
889 int
ex_netwm_visible_name_get(EX_Window win,char ** name)890 ex_netwm_visible_name_get(EX_Window win, char **name)
891 {
892    char               *s;
893 
894    s = _ex_window_prop_string_utf8_get(win, ea_n._NET_WM_VISIBLE_NAME);
895    *name = s;
896 
897    return !!s;
898 }
899 
900 void
ex_netwm_icon_name_set(EX_Window win,const char * name)901 ex_netwm_icon_name_set(EX_Window win, const char *name)
902 {
903    _ex_window_prop_string_utf8_set(win, ea_n._NET_WM_ICON_NAME, name);
904 }
905 
906 int
ex_netwm_icon_name_get(EX_Window win,char ** name)907 ex_netwm_icon_name_get(EX_Window win, char **name)
908 {
909    char               *s;
910 
911    s = _ex_window_prop_string_utf8_get(win, ea_n._NET_WM_ICON_NAME);
912    *name = s;
913 
914    return !!s;
915 }
916 
917 void
ex_netwm_visible_icon_name_set(EX_Window win,const char * name)918 ex_netwm_visible_icon_name_set(EX_Window win, const char *name)
919 {
920    _ex_window_prop_string_utf8_set(win, ea_n._NET_WM_VISIBLE_ICON_NAME, name);
921 }
922 
923 int
ex_netwm_visible_icon_name_get(EX_Window win,char ** name)924 ex_netwm_visible_icon_name_get(EX_Window win, char **name)
925 {
926    char               *s;
927 
928    s = _ex_window_prop_string_utf8_get(win, ea_n._NET_WM_VISIBLE_ICON_NAME);
929    *name = s;
930 
931    return !!s;
932 }
933 
934 void
ex_netwm_desktop_set(EX_Window win,unsigned int desk)935 ex_netwm_desktop_set(EX_Window win, unsigned int desk)
936 {
937    ex_window_prop_card32_set(win, ea_n._NET_WM_DESKTOP, &desk, 1);
938 }
939 
940 int
ex_netwm_desktop_get(EX_Window win,unsigned int * desk)941 ex_netwm_desktop_get(EX_Window win, unsigned int *desk)
942 {
943    return ex_window_prop_card32_get(win, ea_n._NET_WM_DESKTOP, desk, 1);
944 }
945 
946 int
ex_netwm_user_time_get(EX_Window win,unsigned int * ts)947 ex_netwm_user_time_get(EX_Window win, unsigned int *ts)
948 {
949    return ex_window_prop_card32_get(win, ea_n._NET_WM_USER_TIME, ts, 1);
950 }
951 
952 void
ex_netwm_opacity_set(EX_Window win,unsigned int opacity)953 ex_netwm_opacity_set(EX_Window win, unsigned int opacity)
954 {
955    ex_window_prop_card32_set(win, ea_n._NET_WM_WINDOW_OPACITY, &opacity, 1);
956 }
957 
958 int
ex_netwm_opacity_get(EX_Window win,unsigned int * opacity)959 ex_netwm_opacity_get(EX_Window win, unsigned int *opacity)
960 {
961    return ex_window_prop_card32_get(win, ea_n._NET_WM_WINDOW_OPACITY,
962 				    opacity, 1);
963 }
964 
965 #if 0				/* Not used */
966 void
967 ex_netwm_startup_id_set(EX_Window win, const char *id)
968 {
969    _ex_window_prop_string_utf8_set(win, ea_n._NET_STARTUP_ID, id);
970 }
971 #endif
972 
973 int
ex_netwm_startup_id_get(EX_Window win,char ** id)974 ex_netwm_startup_id_get(EX_Window win, char **id)
975 {
976    char               *s;
977 
978    s = _ex_window_prop_string_utf8_get(win, ea_n._NET_STARTUP_ID);
979    *id = s;
980 
981    return !!s;
982 }
983