1 /*
2  * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
3  * Copyright (C) 2004-2021 Kim Woelders
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to
7  * deal in the Software without restriction, including without limitation the
8  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9  * sell copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies of the Software, its documentation and marketing & publicity
14  * materials, and acknowledgment shall be given in the documentation, materials
15  * and software packages that this Software was used.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 #include "config.h"
25 
26 #include <X11/Xlib.h>
27 #include <X11/Xutil.h>
28 #if USE_XSYNC
29 #include <X11/extensions/sync.h>
30 #endif
31 
32 #include "E.h"
33 #include "desktops.h"
34 #include "ewins.h"
35 #include "hints.h"
36 #include "session.h"
37 #include "xprop.h"
38 #include "xwin.h"
39 
40 static void         ICCCM_SetIconSizes(void);
41 
42 void
ICCCM_Init(void)43 ICCCM_Init(void)
44 {
45    ex_icccm_init();
46 
47    Mode.current_cmap = WinGetCmap(VROOT);
48 
49    ICCCM_SetIconSizes();
50 
51    if (Mode.wm.window)
52      {
53 	EX_Atom             wm_props[1];
54 
55 	wm_props[0] = ea_i.WM_DELETE_WINDOW;
56 	ex_window_prop_atom_set(WinGetXwin(VROOT), ea_i.WM_PROTOCOLS,
57 				wm_props, 1);
58      }
59 }
60 
61 int
ICCCM_ProcessClientClientMessage(EWin * ewin,XClientMessageEvent * event)62 ICCCM_ProcessClientClientMessage(EWin * ewin, XClientMessageEvent * event)
63 {
64    if (event->message_type == ea_i.WM_CHANGE_STATE)
65      {
66 	if (event->data.l[0] == IconicState)
67 	  {
68 	     EwinIconify(ewin);
69 	  }
70 	return 1;
71      }
72 
73    return 0;
74 }
75 
76 int
ICCCM_ProcessRootClientMessage(XClientMessageEvent * event)77 ICCCM_ProcessRootClientMessage(XClientMessageEvent * event)
78 {
79    EX_Atom             a;
80 
81    if (event->message_type == ea_i.WM_PROTOCOLS)
82      {
83 	a = event->data.l[0];
84 	if (a == ea_i.WM_DELETE_WINDOW)
85 	   SessionExit(EEXIT_EXIT, NULL);
86 	return 1;
87      }
88 
89    return 0;
90 }
91 
92 void
ICCCM_GetTitle(EWin * ewin)93 ICCCM_GetTitle(EWin * ewin)
94 {
95    EFREE_NULL(EwinGetIcccmName(ewin));
96 
97    EwinGetIcccmName(ewin) = ex_icccm_title_get(EwinGetClientXwin(ewin));
98 
99    EwinChange(ewin, EWIN_CHANGE_NAME);
100 }
101 
102 void
ICCCM_Delete(const EWin * ewin)103 ICCCM_Delete(const EWin * ewin)
104 {
105    if (EwinIsInternal(ewin))
106      {
107 	EwinHide((EWin *) ewin);
108 	return;
109      }
110 
111    if (ewin->icccm.delete_window)
112       ex_icccm_delete_window_send(EwinGetClientXwin(ewin), CurrentTime);
113    else
114       XKillClient(disp, EwinGetClientXwin(ewin));
115 }
116 
117 #if 0				/* Deprecated */
118 void
119 ICCCM_Save(const EWin * ewin)
120 {
121    if (EwinIsInternal(ewin))
122       return;
123 
124    ex_icccm_send_save_yourself(EwinGetClientXwin(ewin));
125 }
126 #endif
127 
128 void
ICCCM_Iconify(const EWin * ewin)129 ICCCM_Iconify(const EWin * ewin)
130 {
131    EUnmapWindow(EwinGetClientWin(ewin));
132    ex_icccm_state_set_iconic(EwinGetClientXwin(ewin));
133 }
134 
135 void
ICCCM_DeIconify(const EWin * ewin)136 ICCCM_DeIconify(const EWin * ewin)
137 {
138    EMapWindow(EwinGetClientWin(ewin));
139    ex_icccm_state_set_normal(EwinGetClientXwin(ewin));
140 }
141 
142 void
ICCCM_Withdraw(const EWin * ewin)143 ICCCM_Withdraw(const EWin * ewin)
144 {
145    /* We have a choice of deleting the WM_STATE property
146     * or changing the value to Withdrawn. Since twm/fvwm does
147     * it that way, we change it to Withdrawn.
148     */
149    ex_icccm_state_set_withdrawn(EwinGetClientXwin(ewin));
150 
151    XRemoveFromSaveSet(disp, EwinGetClientXwin(ewin));
152 }
153 
154 void
ICCCM_SizeMatch(const EWin * ewin,int wi,int hi,int * pwo,int * pho)155 ICCCM_SizeMatch(const EWin * ewin, int wi, int hi, int *pwo, int *pho)
156 {
157    int                 w, h;
158    int                 i, j;
159    float               aspect, dw, dh;
160 
161    w = wi;
162    h = hi;
163 
164    if (w < ewin->icccm.width_min)
165       w = ewin->icccm.width_min;
166    if (w > ewin->icccm.width_max)
167       w = ewin->icccm.width_max;
168    if (h < ewin->icccm.height_min)
169       h = ewin->icccm.height_min;
170    if (h > ewin->icccm.height_max)
171       h = ewin->icccm.height_max;
172 
173    if (w <= 0 || h <= 0)
174       return;
175 
176    w -= ewin->icccm.base_w;
177    h -= ewin->icccm.base_h;
178    if ((w > 0) && (h > 0))
179      {
180 	/* Ignore aspect ratio constraints when fullscreening */
181 	if (!ewin->state.fullscreen)
182 	  {
183 	     aspect = ((float)w) / ((float)h);
184 	     dw = ewin->icccm.w_inc / 4.f;
185 	     dh = ewin->icccm.h_inc / 4.f;
186 	     if (Mode.mode == MODE_RESIZE_H)
187 	       {
188 		  if (aspect < ewin->icccm.aspect_min)
189 		     h = (int)((float)w / ewin->icccm.aspect_min + dh);
190 		  else if (aspect > ewin->icccm.aspect_max)
191 		     h = (int)((float)w / ewin->icccm.aspect_max + dh);
192 	       }
193 	     else if (Mode.mode == MODE_RESIZE_V)
194 	       {
195 		  if (aspect < ewin->icccm.aspect_min)
196 		     w = (int)((float)h * ewin->icccm.aspect_min + dw);
197 		  else if (aspect > ewin->icccm.aspect_max)
198 		     w = (int)((float)h * ewin->icccm.aspect_max + dw);
199 	       }
200 	     else
201 	       {
202 		  if (aspect < ewin->icccm.aspect_min)
203 		    {
204 		       if (ewin->icccm.aspect_min >= 1.f)
205 			  h = (int)((float)w / ewin->icccm.aspect_min + dh);
206 		       else
207 			  w = (int)((float)h * ewin->icccm.aspect_min + dw);
208 		    }
209 		  else if (aspect > ewin->icccm.aspect_max)
210 		    {
211 		       if (ewin->icccm.aspect_max >= 1.f)
212 			  h = (int)((float)w / ewin->icccm.aspect_max + dh);
213 		       else
214 			  w = (int)((float)h * ewin->icccm.aspect_max + dw);
215 		    }
216 	       }
217 	  }
218 	i = w / ewin->icccm.w_inc;
219 	j = h / ewin->icccm.h_inc;
220 	w = i * ewin->icccm.w_inc;
221 	h = j * ewin->icccm.h_inc;
222      }
223    w += ewin->icccm.base_w;
224    h += ewin->icccm.base_h;
225 
226    *pwo = w;
227    *pho = h;
228 }
229 
230 #if 0				/* Unused */
231 void
232 ICCCM_MatchSize(EWin * ewin)
233 {
234    ICCCM_SizeMatch(ewin, ewin->client.w, ewin->client.h, &ewin->client.w,
235 		   &ewin->client.h);
236 }
237 #endif
238 
239 void
ICCCM_GetIncrementalSize(EWin * ewin,unsigned int w,unsigned int h,unsigned int * wi,unsigned int * hi)240 ICCCM_GetIncrementalSize(EWin * ewin, unsigned int w, unsigned int h,
241 			 unsigned int *wi, unsigned int *hi)
242 {
243    *wi = (w - ewin->icccm.base_w) / ewin->icccm.w_inc;
244    *hi = (h - ewin->icccm.base_h) / ewin->icccm.h_inc;
245 }
246 
247 void
ICCCM_SetSizeConstraints(EWin * ewin,unsigned int wmin,unsigned int hmin,unsigned int wmax,unsigned int hmax,unsigned int wbase,unsigned int hbase,unsigned int winc,unsigned int hinc,float amin,float amax)248 ICCCM_SetSizeConstraints(EWin * ewin, unsigned int wmin, unsigned int hmin,
249 			 unsigned int wmax, unsigned int hmax,
250 			 unsigned int wbase, unsigned int hbase,
251 			 unsigned int winc, unsigned int hinc,
252 			 float amin, float amax)
253 {
254    ewin->icccm.width_min = wmin;
255    ewin->icccm.height_min = hmin;
256    ewin->icccm.width_max = wmax;
257    ewin->icccm.height_max = hmax;
258 
259    ewin->icccm.base_w = wbase;
260    ewin->icccm.base_h = hbase;
261    ewin->icccm.w_inc = winc;
262    ewin->icccm.h_inc = hinc;
263 
264    ewin->icccm.aspect_min = amin;
265    ewin->icccm.aspect_max = amax;
266 
267    ewin->props.no_resize_h = (wmin == wmax);
268    ewin->props.no_resize_v = (hmin == hmax);
269 }
270 
271 void
ICCCM_Configure(EWin * ewin)272 ICCCM_Configure(EWin * ewin)
273 {
274    XEvent              ev;
275 
276    if (EwinIsInternal(ewin))
277       return;
278 
279    ev.type = ConfigureNotify;
280    ev.xconfigure.display = disp;
281    ev.xconfigure.event = EwinGetClientXwin(ewin);
282    ev.xconfigure.window = EwinGetClientXwin(ewin);
283 #if 0				/* FIXME - Remove? */
284    Desk               *dsk;
285 
286    dsk = EoGetDesk(ewin);
287    ev.xconfigure.x = EoGetX(dsk) + ewin->client.x;
288    ev.xconfigure.y = EoGetY(dsk) + ewin->client.y;
289 #else
290    ev.xconfigure.x = ewin->client.x;
291    ev.xconfigure.y = ewin->client.y;
292 #endif
293    if (Mode.wm.window)
294      {
295 	ev.xconfigure.x += Mode.wm.win_x;
296 	ev.xconfigure.y += Mode.wm.win_y;
297      }
298    ev.xconfigure.width = ewin->client.w;
299    ev.xconfigure.height = ewin->client.h;
300    ev.xconfigure.border_width = 0;
301    ev.xconfigure.above = EoGetXwin(ewin);
302    ev.xconfigure.override_redirect = False;
303    EXSendEvent(EwinGetClientXwin(ewin), StructureNotifyMask, &ev);
304 }
305 
306 void
ICCCM_AdoptStart(const EWin * ewin)307 ICCCM_AdoptStart(const EWin * ewin)
308 {
309    EX_Window           win = EwinGetClientXwin(ewin);
310 
311    if (!EwinIsInternal(ewin))
312       XAddToSaveSet(disp, win);
313 }
314 
315 void
ICCCM_Adopt(const EWin * ewin)316 ICCCM_Adopt(const EWin * ewin)
317 {
318    EX_Window           win = EwinGetClientXwin(ewin);
319 
320    if (ewin->icccm.start_iconified)
321       ex_icccm_state_set_iconic(win);
322    else
323       ex_icccm_state_set_normal(win);
324 }
325 
326 void
ICCCM_Cmap(EWin * ewin)327 ICCCM_Cmap(EWin * ewin)
328 {
329    EX_Colormap         ecmap, dcmap, ccmap;
330    XWindowAttributes   xwa;
331    int                 i, num;
332    EX_Window          *wlist;
333 
334    ecmap = Mode.current_cmap;
335    dcmap = WinGetCmap(VROOT);
336 
337    if (!ewin)
338      {
339 	if (ecmap == dcmap)
340 	   return;
341 	ccmap = dcmap;
342 	goto set_cmap;
343      }
344 
345    ccmap = EwinGetClientWin(ewin)->cmap;
346 
347    if (ccmap == ecmap || EoGetWin(ewin)->argb)
348       return;
349 
350    /* Hack - assume that if client cmap is default cmap it doesn't have
351     * WM_COLORMAP_WINDOWS */
352    if (ccmap == dcmap)
353       goto set_cmap;
354 
355    num = ex_window_prop_window_list_get(EwinGetClientXwin(ewin),
356 					ea_i.WM_COLORMAP_WINDOWS, &wlist);
357    if (num > 0)
358      {
359 	for (i = 0; i < num; i++)
360 	  {
361 	     if (EXGetWindowAttributes(wlist[i], &xwa))
362 	       {
363 		  if (xwa.colormap != dcmap)
364 		    {
365 		       XInstallColormap(disp, xwa.colormap);
366 		       Mode.current_cmap = xwa.colormap;
367 		    }
368 	       }
369 	  }
370 	Efree(wlist);
371 	return;
372      }
373 
374  set_cmap:
375    if (EDebug(EDBUG_TYPE_FOCUS))
376       Eprintf("%s: %#x\n", __func__, ccmap);
377    XInstallColormap(disp, ccmap);
378    Mode.current_cmap = ccmap;
379 }
380 
381 void
ICCCM_Focus(const EWin * ewin)382 ICCCM_Focus(const EWin * ewin)
383 {
384    if (EDebug(EDBUG_TYPE_FOCUS))
385      {
386 	if (ewin)
387 	   Eprintf("%s: T=%#x R=%#x %#x %s\n", __func__, Mode.events.time,
388 		   (int)NextRequest(disp), EwinGetClientXwin(ewin),
389 		   EwinGetTitle(ewin));
390 	else
391 	   Eprintf("%s: T=%#x R=%#x None\n", __func__, Mode.events.time,
392 		   (int)NextRequest(disp));
393      }
394 
395    if (!ewin)
396      {
397 	XSetInputFocus(disp, WinGetXwin(VROOT), RevertToPointerRoot,
398 		       Mode.events.time);
399 	HintsSetActiveWindow(NoXID);
400 	return;
401      }
402 
403    if (ewin->icccm.take_focus)
404      {
405 	ex_icccm_take_focus_send(EwinGetClientXwin(ewin), Mode.events.time);
406      }
407 
408    if (ewin->icccm.need_input)
409       XSetInputFocus(disp, EwinGetClientXwin(ewin),
410 		     RevertToPointerRoot, Mode.events.time);
411 
412    HintsSetActiveWindow(EwinGetClientXwin(ewin));
413 }
414 
415 void
ICCCM_GetGeoms(EWin * ewin)416 ICCCM_GetGeoms(EWin * ewin)
417 {
418    XSizeHints          hint;
419    long                mask;
420 
421    if (XGetWMNormalHints(disp, EwinGetClientXwin(ewin), &hint, &mask))
422      {
423 	if (!(ewin->state.placed))
424 	  {
425 	     if ((hint.flags & USPosition) || ((hint.flags & PPosition)))
426 	       {
427 		  if ((hint.flags & PPosition) && (!EoIsSticky(ewin)))
428 		    {
429 		       Desk               *dsk;
430 
431 		       dsk = EoGetDesk(ewin);
432 		       if (!dsk)
433 			  dsk = DesksGetCurrent();
434 		       ewin->client.x -= EoGetX(dsk);
435 		       ewin->client.y -= EoGetY(dsk);
436 		       if (ewin->client.x + ewin->client.w >= WinGetW(VROOT))
437 			 {
438 			    ewin->client.x += EoGetX(dsk);
439 			 }
440 		       else if (ewin->client.x < 0)
441 			 {
442 			    ewin->client.x += EoGetX(dsk);
443 			 }
444 		       if (ewin->client.y + ewin->client.h >= WinGetH(VROOT))
445 			 {
446 			    ewin->client.y += EoGetY(dsk);
447 			 }
448 		       else if (ewin->client.y < 0)
449 			 {
450 			    ewin->client.y += EoGetY(dsk);
451 			 }
452 		    }
453 		  ewin->state.placed = 1;
454 	       }
455 	  }
456 
457 	if (hint.flags & PMinSize)
458 	  {
459 	     ewin->icccm.width_min = MAX(0, hint.min_width);
460 	     ewin->icccm.height_min = MAX(0, hint.min_height);
461 	  }
462 	else
463 	  {
464 	     ewin->icccm.width_min = 0;
465 	     ewin->icccm.height_min = 0;
466 	  }
467 
468 	if (hint.flags & PMaxSize)
469 	  {
470 	     ewin->icccm.width_max = MIN(hint.max_width, 65535);
471 	     ewin->icccm.height_max = MIN(hint.max_height, 65535);
472 	  }
473 	else
474 	  {
475 	     ewin->icccm.width_max = 65535;
476 	     ewin->icccm.height_max = 65535;
477 	  }
478 
479 	if (hint.flags & PResizeInc)
480 	  {
481 	     ewin->icccm.w_inc = MAX(1, hint.width_inc);
482 	     ewin->icccm.h_inc = MAX(1, hint.height_inc);
483 	  }
484 	else
485 	  {
486 	     ewin->icccm.w_inc = 1;
487 	     ewin->icccm.h_inc = 1;
488 	  }
489 
490 	if (hint.flags & PAspect)
491 	  {
492 	     if (hint.min_aspect.y > 0 && hint.min_aspect.x > 0)
493 	       {
494 		  ewin->icccm.aspect_min =
495 		     ((float)hint.min_aspect.x) / ((float)hint.min_aspect.y);
496 	       }
497 	     else
498 	       {
499 		  ewin->icccm.aspect_min = 0.f;
500 	       }
501 	     if (hint.max_aspect.y > 0 && hint.max_aspect.x > 0)
502 	       {
503 		  ewin->icccm.aspect_max =
504 		     ((float)hint.max_aspect.x) / ((float)hint.max_aspect.y);
505 	       }
506 	     else
507 	       {
508 		  ewin->icccm.aspect_max = 65535.f;
509 	       }
510 	  }
511 	else
512 	  {
513 	     ewin->icccm.aspect_min = 0.f;
514 	     ewin->icccm.aspect_max = 65535.f;
515 	  }
516 
517 	if (hint.flags & PBaseSize)
518 	  {
519 	     ewin->icccm.base_w = hint.base_width;
520 	     ewin->icccm.base_h = hint.base_height;
521 	  }
522 	else
523 	  {
524 	     ewin->icccm.base_w = ewin->icccm.width_min;
525 	     ewin->icccm.base_h = ewin->icccm.height_min;
526 	  }
527 
528 	if (ewin->icccm.width_min < ewin->icccm.base_w)
529 	   ewin->icccm.width_min = ewin->icccm.base_w;
530 	if (ewin->icccm.height_min < ewin->icccm.base_h)
531 	   ewin->icccm.height_min = ewin->icccm.base_h;
532 
533 	if (ewin->icccm.width_max < ewin->icccm.base_w)
534 	   ewin->icccm.width_max = ewin->icccm.base_w;
535 	if (ewin->icccm.height_max < ewin->icccm.base_h)
536 	   ewin->icccm.height_max = ewin->icccm.base_h;
537 
538 	if (hint.flags & PWinGravity)
539 	   ewin->icccm.grav = hint.win_gravity;
540 	else
541 	   ewin->icccm.grav = NorthWestGravity;
542      }
543 
544    ewin->props.no_resize_h = (ewin->icccm.width_min == ewin->icccm.width_max);
545    ewin->props.no_resize_v = (ewin->icccm.height_min == ewin->icccm.height_max);
546 
547    if (EDebug(EDBUG_TYPE_SNAPS))
548       Eprintf("Snap get icccm %#x: %4d+%4d %4dx%4d: %s\n",
549 	      EwinGetClientXwin(ewin), ewin->client.x, ewin->client.y,
550 	      ewin->client.w, ewin->client.h, EwinGetTitle(ewin));
551 }
552 
553 #define TryGroup(e) (((e)->icccm.group != NoXID) && ((e)->icccm.group != EwinGetClientXwin(e)))
554 
555 static void
ICCCM_GetWmClass(EWin * ewin)556 ICCCM_GetWmClass(EWin * ewin)
557 {
558    EFREE_NULL(EwinGetIcccmCName(ewin));
559    EFREE_NULL(EwinGetIcccmClass(ewin));
560 
561    ex_icccm_name_class_get(EwinGetClientXwin(ewin),
562 			   &EwinGetIcccmCName(ewin), &EwinGetIcccmClass(ewin));
563    if (!EwinGetIcccmCName(ewin) && TryGroup(ewin))
564       ex_icccm_name_class_get(ewin->icccm.group,
565 			      &EwinGetIcccmCName(ewin),
566 			      &EwinGetIcccmClass(ewin));
567 }
568 
569 static void
ICCCM_GetWmCommand(EWin * ewin)570 ICCCM_GetWmCommand(EWin * ewin)
571 {
572    int                 argc;
573    char              **argv, s[4096], *ss;
574 
575    EFREE_NULL(ewin->icccm.wm_command);
576 
577    argc = ex_window_prop_string_list_get(EwinGetClientXwin(ewin),
578 					 ea_i.WM_COMMAND, &argv);
579    if ((argc < 0) && TryGroup(ewin))
580       argc = ex_window_prop_string_list_get(ewin->icccm.group,
581 					    ea_i.WM_COMMAND, &argv);
582 
583    ss = StrlistEncodeEscaped(s, sizeof(s), argv, argc);
584    ewin->icccm.wm_command = Estrdup(ss);
585    StrlistFree(argv, argc);
586 }
587 
588 static void
ICCCM_GetWmClientMachine(EWin * ewin)589 ICCCM_GetWmClientMachine(EWin * ewin)
590 {
591    EFREE_NULL(ewin->icccm.wm_machine);
592 
593    ewin->icccm.wm_machine =
594       ex_window_prop_string_get(EwinGetClientXwin(ewin),
595 				ea_i.WM_CLIENT_MACHINE);
596    if (!ewin->icccm.wm_machine && TryGroup(ewin))
597       ewin->icccm.wm_machine =
598 	 ex_window_prop_string_get(ewin->icccm.group, ea_i.WM_CLIENT_MACHINE);
599 }
600 
601 static void
ICCCM_GetWmIconName(EWin * ewin)602 ICCCM_GetWmIconName(EWin * ewin)
603 {
604    EFREE_NULL(ewin->icccm.wm_icon_name);
605 
606    ewin->icccm.wm_icon_name =
607       ex_window_prop_string_get(EwinGetClientXwin(ewin), ea_i.WM_ICON_NAME);
608    if (!ewin->icccm.wm_icon_name && TryGroup(ewin))
609       ewin->icccm.wm_icon_name =
610 	 ex_window_prop_string_get(ewin->icccm.group, ea_i.WM_ICON_NAME);
611 }
612 
613 static void
ICCCM_GetWmWindowRole(EWin * ewin)614 ICCCM_GetWmWindowRole(EWin * ewin)
615 {
616    EFREE_NULL(ewin->icccm.wm_role);
617    ewin->icccm.wm_role =
618       ex_window_prop_string_get(EwinGetClientXwin(ewin), ea_i.WM_WINDOW_ROLE);
619 }
620 
621 void
ICCCM_GetInfo(EWin * ewin)622 ICCCM_GetInfo(EWin * ewin)
623 {
624    ICCCM_GetWmClass(ewin);
625    ICCCM_GetWmCommand(ewin);
626    ICCCM_GetWmClientMachine(ewin);
627    ICCCM_GetWmIconName(ewin);
628    ICCCM_GetWmWindowRole(ewin);
629 }
630 
631 static void
ICCCM_GetWmHints(EWin * ewin)632 ICCCM_GetWmHints(EWin * ewin)
633 {
634    XWMHints           *hint;
635 
636    hint = XGetWMHints(disp, EwinGetClientXwin(ewin));
637    if (!hint)
638       return;
639 
640    /* I have to make sure the thing i'm docking is a dock app */
641    if ((hint->flags & StateHint) && (hint->initial_state == WithdrawnState))
642      {
643 	if (hint->flags & (StateHint | IconWindowHint | IconPositionHint |
644 			   WindowGroupHint))
645 	  {
646 	     if ((hint->icon_x == 0) && (hint->icon_y == 0)
647 		 && hint->window_group == EwinGetClientXwin(ewin))
648 		ewin->state.docked = 1;
649 	  }
650      }
651 
652    ewin->icccm.need_input =
653       ((hint->flags & InputHint) && (!hint->input)) ? 0 : 1;
654 
655    ewin->icccm.start_iconified =
656       ((hint->flags & StateHint) &&
657        (hint->initial_state == IconicState)) ? 1 : 0;
658 
659    if (hint->flags & IconPixmapHint)
660      {
661 	if (ewin->icccm.icon_pmap != hint->icon_pixmap)
662 	  {
663 	     ewin->icccm.icon_pmap = hint->icon_pixmap;
664 	     EwinChange(ewin, EWIN_CHANGE_ICON_PMAP);
665 	  }
666      }
667    else
668      {
669 	ewin->icccm.icon_pmap = NoXID;
670      }
671 
672    ewin->icccm.icon_mask =
673       (hint->flags & IconMaskHint) ? hint->icon_mask : NoXID;
674 
675    ewin->icccm.icon_win =
676       (hint->flags & IconWindowHint) ? hint->icon_window : NoXID;
677 
678    ewin->icccm.group =
679       (hint->flags & WindowGroupHint) ? hint->window_group : NoXID;
680 
681    if (hint->flags & XUrgencyHint)
682      {
683 	if (!ewin->state.attention)
684 	   EwinChange(ewin, EWIN_CHANGE_ATTENTION);
685 	ewin->icccm.urgency = 1;
686 	ewin->state.attention = 1;
687      }
688    else
689      {
690 	ewin->icccm.urgency = 0;
691      }
692 
693    if (ewin->icccm.group == EwinGetClientXwin(ewin))
694      {
695 	ewin->icccm.is_group_leader = 1;
696      }
697    else
698      {
699 	ewin->icccm.is_group_leader = 0;
700      }
701 
702    XFree(hint);
703 }
704 
705 static void
ICCCM_GetWmProtocols(EWin * ewin)706 ICCCM_GetWmProtocols(EWin * ewin)
707 {
708    EX_Atom             prop[64];	/* More is unlikely */
709    int                 i, num;
710 
711    num = ex_window_prop_atom_get(EwinGetClientXwin(ewin), ea_i.WM_PROTOCOLS,
712 				 prop, 64);
713    if (num < 0)
714       return;
715 
716    ewin->icccm.take_focus = 0;
717    ewin->icccm.delete_window = 0;
718    for (i = 0; i < num; i++)
719      {
720 	if (prop[i] == ea_i.WM_TAKE_FOCUS)
721 	   ewin->icccm.take_focus = 1;
722 	else if (prop[i] == ea_i.WM_DELETE_WINDOW)
723 	   ewin->icccm.delete_window = 1;
724 #if USE_XSYNC
725 	else if (prop[i] == ea_n._NET_WM_SYNC_REQUEST)
726 	  {
727 	     unsigned int        c;
728 
729 	     ewin->ewmh.sync_request_enable = 1;
730 	     ex_window_prop_card32_get(EwinGetClientXwin(ewin),
731 				       ea_n._NET_WM_SYNC_REQUEST_COUNTER,
732 				       &c, 1);
733 	     ewin->ewmh.sync_request_counter = c;
734 	  }
735 #endif
736      }
737 }
738 
739 static void
ICCCM_GetWmTransientFor(EWin * ewin)740 ICCCM_GetWmTransientFor(EWin * ewin)
741 {
742    int                 num;
743    EX_Window           win;
744 
745    num = ex_window_prop_window_get(EwinGetClientXwin(ewin),
746 				   ea_i.WM_TRANSIENT_FOR, &win, 1);
747    if (num > 0)
748      {
749 	ewin->icccm.transient = 1;
750 	ewin->icccm.transient_for = win;
751      }
752    else
753      {
754 	ewin->icccm.transient = 0;
755 	ewin->icccm.transient_for = NoXID;
756      }
757 }
758 
759 static void
ICCCM_GetWmClientLeader(EWin * ewin)760 ICCCM_GetWmClientLeader(EWin * ewin)
761 {
762    int                 num;
763    EX_Window           cleader;
764 
765    num = ex_window_prop_window_get(EwinGetClientXwin(ewin),
766 				   ea_i.WM_CLIENT_LEADER, &cleader, 1);
767    if (num > 0)
768      {
769 	ewin->icccm.client_leader = cleader;
770 	if (!ewin->icccm.group)
771 	   ewin->icccm.group = cleader;
772      }
773 }
774 
775 void
ICCCM_GetHints(EWin * ewin)776 ICCCM_GetHints(EWin * ewin)
777 {
778    ICCCM_GetWmHints(ewin);
779    ICCCM_GetWmProtocols(ewin);
780    ICCCM_GetWmTransientFor(ewin);
781    ICCCM_GetWmClientLeader(ewin);
782 }
783 
784 static void
ICCCM_SetIconSizes(void)785 ICCCM_SetIconSizes(void)
786 {
787    XIconSize          *is;
788 
789    is = XAllocIconSize();
790    is->min_width = 8;
791    is->min_height = 8;
792    is->max_width = 48;
793    is->max_height = 48;
794    is->width_inc = 1;
795    is->height_inc = 1;
796    XSetIconSizes(disp, WinGetXwin(VROOT), is, 1);
797    XFree(is);
798 }
799 
800 /*
801  * Process received window property change
802  */
803 int
ICCCM_ProcessPropertyChange(EWin * ewin,EX_Atom atom_change)804 ICCCM_ProcessPropertyChange(EWin * ewin, EX_Atom atom_change)
805 {
806    if (atom_change == ea_i.WM_NAME)
807      {
808 	ICCCM_GetTitle(ewin);
809 	return 1;
810      }
811 
812    /* ICCCM_GetHints */
813    if (atom_change == ea_i.WM_HINTS)
814      {
815 	ICCCM_GetWmHints(ewin);
816 	return 1;
817      }
818    if (atom_change == ea_i.WM_PROTOCOLS)
819      {
820 	ICCCM_GetWmProtocols(ewin);
821 	return 1;
822      }
823    if (atom_change == ea_i.WM_TRANSIENT_FOR)
824      {
825 	ICCCM_GetWmTransientFor(ewin);
826 	return 1;
827      }
828    if (atom_change == ea_i.WM_CLIENT_LEADER)
829      {
830 	ICCCM_GetWmClientLeader(ewin);
831 	return 1;
832      }
833 
834    /* ICCCM_GetInfo */
835    if (atom_change == ea_i.WM_ICON_NAME)
836      {
837 	ICCCM_GetWmIconName(ewin);
838 	return 1;
839      }
840 #if 1				/* FIXME - Any reason to process these? */
841    if (atom_change == ea_i.WM_CLASS)
842      {
843 	ICCCM_GetWmClass(ewin);
844 	return 1;
845      }
846    if (atom_change == ea_i.WM_COMMAND)
847      {
848 	ICCCM_GetWmCommand(ewin);
849 	return 1;
850      }
851    if (atom_change == ea_i.WM_CLIENT_MACHINE)
852      {
853 	ICCCM_GetWmClientMachine(ewin);
854 	return 1;
855      }
856    if (atom_change == ea_i.WM_WINDOW_ROLE)
857      {
858 	ICCCM_GetWmWindowRole(ewin);
859 	return 1;
860      }
861 #endif
862 
863    if (atom_change == ea_i.WM_COLORMAP_WINDOWS)
864      {
865 	ICCCM_Cmap(ewin);
866 	return 1;
867      }
868 
869    if (atom_change == ea_i.WM_NORMAL_HINTS)
870      {
871 	ICCCM_GetGeoms(ewin);
872 	return 1;
873      }
874 
875    return 0;
876 }
877 
878 #if USE_XSYNC
879 int
EwinSyncRequestSend(EWin * ewin)880 EwinSyncRequestSend(EWin * ewin)
881 {
882    long long           count;
883 
884    if (!ewin->ewmh.sync_request_enable || EServerIsGrabbed())
885       return 0;
886 
887    count = ++ewin->ewmh.sync_request_count;
888 
889    if (count == 0)
890       ewin->ewmh.sync_request_count = ++count;
891    ex_client_message32_send(EwinGetClientXwin(ewin),
892 			    ea_i.WM_PROTOCOLS,
893 			    StructureNotifyMask,
894 			    ea_n._NET_WM_SYNC_REQUEST,
895 			    Mode.events.time,
896 			    (long)(count & 0xffffffff), (long)(count >> 32), 0);
897 
898    return 1;
899 }
900 
901 void
EwinSyncRequestWait(EWin * ewin)902 EwinSyncRequestWait(EWin * ewin)
903 {
904    XSyncWaitCondition  xswc[2];
905    unsigned int        tus;
906 
907    if (!ewin->ewmh.sync_request_enable || EServerIsGrabbed())
908       return;
909 
910    xswc[0].trigger.counter = ewin->ewmh.sync_request_counter;
911    xswc[0].trigger.value_type = XSyncAbsolute;
912    XSyncIntsToValue(&xswc[0].trigger.wait_value,
913 		    ewin->ewmh.sync_request_count & 0xffffffff,
914 		    ewin->ewmh.sync_request_count >> 32);
915    xswc[0].trigger.test_type = XSyncPositiveComparison;
916    XSyncIntsToValue(&xswc[0].event_threshold, 0, 0);
917 
918    xswc[1].trigger.counter = Mode.display.server_time;
919    xswc[1].trigger.value_type = XSyncRelative;
920    XSyncIntsToValue(&xswc[1].trigger.wait_value, 200, 0);	/* 200 ms */
921    xswc[1].trigger.test_type = XSyncPositiveComparison;
922    XSyncIntsToValue(&xswc[1].event_threshold, 0, 0);
923 
924    tus = GetTimeUs();
925    XSyncAwait(disp, xswc, 2);
926    if (EDebug(EDBUG_TYPE_SYNC))
927       Eprintf("%s: t=%#lx c=%llx: Delay=%u us\n", __func__,
928 	      xswc[0].trigger.counter, ewin->ewmh.sync_request_count,
929 	      GetTimeUs() - tus);
930 }
931 #endif /* USE_XSYNC */
932