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 #include <X11/extensions/shape.h>
29 
30 #include "E.h"
31 #include "aclass.h"
32 #include "borders.h"
33 #include "cursors.h"
34 #include "desktops.h"
35 #include "emodule.h"
36 #include "eobj.h"
37 #include "events.h"
38 #include "ewins.h"
39 #include "focus.h"
40 #include "grabs.h"
41 #include "groups.h"
42 #include "hints.h"
43 #include "screen.h"
44 #include "slide.h"
45 #include "snaps.h"
46 #include "windowmatch.h"
47 #include "xwin.h"
48 
49 #define EWIN_TOP_EVENT_MASK \
50   (EnterWindowMask | LeaveWindowMask)
51 
52 #define EWIN_CONTAINER_EVENT_MASK \
53   (SubstructureNotifyMask | SubstructureRedirectMask)
54 
55 #define EWIN_CLIENT_EVENT_MASK \
56   (FocusChangeMask | ResizeRedirectMask | \
57    PropertyChangeMask | ColormapChangeMask | VisibilityChangeMask)
58 
59 static void         EwinChangesStart(EWin * ewin);
60 static void         EwinChangesProcess(EWin * ewin);
61 
62 static void         EwinHandleEventsToplevel(Win win, XEvent * ev, void *prm);
63 
64 #if USE_CONTAINER_WIN
65 static void         EwinHandleEventsContainer(Win win, XEvent * ev, void *prm);
66 #endif
67 static void         EwinHandleEventsClient(Win win, XEvent * ev, void *prm);
68 
69 static void         EwinUnmap1(EWin * ewin);
70 static void         EwinUnmap2(EWin * ewin);
71 
72 EX_Window
EwinGetClientXwin(const EWin * ewin)73 EwinGetClientXwin(const EWin * ewin)
74 {
75    Win                 win = EwinGetClientWin(ewin);
76 
77    return (win) ? WinGetXwin(win) : NoXID;
78 }
79 
80 static EWin        *
EwinCreate(int type)81 EwinCreate(int type)
82 {
83    EWin               *ewin;
84 
85    ewin = ECALLOC(EWin, 1);
86 
87    ewin->type = type;
88    ewin->state.state = (Mode.wm.startup) ? EWIN_STATE_STARTUP : EWIN_STATE_NEW;
89 
90    ewin->o.stacked = -1;	/* Not placed on desk yet */
91    EoSetDesk(ewin, DesksGetCurrent());
92    EoSetLayer(ewin, 4);
93    EoSetFade(ewin, 1);
94    EoSetShadow(ewin, 1);
95 
96    ewin->update.shape = 1;
97    ewin->update.border = 1;
98    ewin->save_max.x = ewin->save_max.y = ewin->save_max.w = ewin->save_max.h =
99       -1;
100    ewin->save_fs.x = ewin->save_fs.y = ewin->save_fs.w = ewin->save_fs.h = -1;
101    ewin->save_fs.layer = -1;
102 
103    ewin->icccm.need_input = 1;
104 
105    ewin->icccm.width_min = 0;
106    ewin->icccm.height_min = 0;
107    ewin->icccm.width_max = 65535;
108    ewin->icccm.height_max = 65535;
109    ewin->icccm.base_w = 0;
110    ewin->icccm.base_h = 0;
111    ewin->icccm.w_inc = 1;
112    ewin->icccm.h_inc = 1;
113    ewin->icccm.aspect_min = 0.f;
114    ewin->icccm.aspect_max = 65535.f;
115    ewin->icccm.grav = NorthWestGravity;
116 
117    ewin->area_x = -1;
118    ewin->area_y = -1;
119 
120    ewin->place.gravity = -1;
121 
122    return ewin;
123 }
124 
125 static int
EwinGetAttributes(EWin * ewin,Win win,EX_Window xwin,XWindowAttributes * pxwa)126 EwinGetAttributes(EWin * ewin, Win win, EX_Window xwin,
127 		  XWindowAttributes * pxwa)
128 {
129    XWindowAttributes   xwa;
130 
131    if (!win)
132      {
133 	win = ERegisterWindow(xwin, pxwa);
134 	if (!win)
135 	   return -1;
136      }
137 
138    EGetWindowAttributes(win, &xwa);
139 
140    ewin->client.win = win;
141    ewin->client.x = ewin->save_max.x = ewin->save_fs.x = xwa.x;
142    ewin->client.y = ewin->save_max.y = ewin->save_fs.y = xwa.y;
143    ewin->client.w = ewin->save_max.w = ewin->save_fs.w = xwa.width;
144    ewin->client.h = ewin->save_max.h = ewin->save_fs.h = xwa.height;
145    ewin->client.bw = xwa.border_width;
146 
147    if (EDebug(EDBUG_TYPE_SNAPS))
148       Eprintf("Snap get attr  %#x: %4d+%4d %4dx%4d: %s\n",
149 	      EwinGetClientXwin(ewin), ewin->client.x, ewin->client.y,
150 	      ewin->client.w, ewin->client.h, EwinGetTitle(ewin));
151 
152    return 0;
153 }
154 
155 static void
EwinGetHints(EWin * ewin)156 EwinGetHints(EWin * ewin)
157 {
158    if (EDebug(EDBUG_TYPE_EWINS))
159       Eprintf("%s %#x\n", __func__, EwinGetClientXwin(ewin));
160 
161    ICCCM_GetTitle(ewin);
162    ICCCM_GetHints(ewin);
163    ICCCM_GetGeoms(ewin);
164    MWM_GetHints(ewin, 0);
165    ICCCM_GetInfo(ewin);		/* NB! Need group info first */
166    HintsGetWindowHints(ewin);
167 }
168 
169 static void
EwinHintsInferProps(EWin * ewin)170 EwinHintsInferProps(EWin * ewin)
171 {
172    if (ewin->ewmh.type.b.desktop)
173      {
174 	EoSetLayer(ewin, 0);
175 	if (!ewin->state.identified)
176 	   EoSetSticky(ewin, 1);
177 	ewin->props.focusclick = 1;
178 	ewin->props.skip_focuslist = 1;
179 	EwinInhSetUser(ewin, move, 1);
180 	EwinInhSetUser(ewin, size, 1);
181 	ewin->props.donthide = 1;
182 	ewin->props.no_border = 1;
183      }
184    if (ewin->ewmh.type.b.dock)
185      {
186 	ewin->props.skip_ext_task = 1;
187 	ewin->props.skip_winlist = 1;
188 	ewin->props.skip_focuslist = 1;
189 	if (!ewin->state.identified)
190 	   EoSetSticky(ewin, 1);
191 	ewin->props.donthide = 1;
192      }
193    if (ewin->ewmh.type.b.utility)
194      {
195 	/* Epplets hit this */
196 	ewin->props.skip_ext_task = 1;
197 	ewin->props.skip_winlist = 1;
198 	ewin->props.skip_focuslist = 1;
199 	ewin->props.never_use_area = 1;
200 	ewin->props.donthide = 1;
201      }
202 }
203 
204 static void
EwinManage(EWin * ewin)205 EwinManage(EWin * ewin)
206 {
207    XSetWindowAttributes att;
208    Win                 frame;
209    int                 type;
210 
211    /* There seems to be a shape related problem when window dimensions
212     * are >= 32768. Also, it looks like it is not possible(?) to create
213     * pixmaps with dimensions >= 32768.
214     * So, limit to 32000 leaving room for borders. */
215    if (ewin->client.w <= 0)
216       ewin->client.w = 100;
217    else if (ewin->client.w > 32000)
218       ewin->client.w = 32000;
219    if (ewin->client.h <= 0)
220       ewin->client.h = 100;
221    else if (ewin->client.h > 32000)
222       ewin->client.h = 32000;
223 
224    if (ewin->state.docked)
225       ewin->inh_wm.b.border = 1;
226 
227    ewin->serial = NextRequest(disp);
228 
229    frame = EoGetWin(ewin);
230    if (!frame)
231      {
232 	type = (ewin->props.no_argb) ? WIN_TYPE_NO_ARGB : WIN_TYPE_CLIENT;
233 	frame =
234 	   ECreateObjectWindow(VROOT, ewin->client.x, ewin->client.y,
235 			       ewin->client.w, ewin->client.h, 0, type,
236 			       EwinGetClientWin(ewin));
237 	EoInit(ewin, EOBJ_TYPE_EWIN, frame, ewin->client.x, ewin->client.y,
238 	       ewin->client.w, ewin->client.h, 1, NULL);
239 	EventCallbackRegister(EoGetWin(ewin), EwinHandleEventsToplevel, ewin);
240 
241 #if USE_CONTAINER_WIN
242 	ewin->win_container =
243 	   ECreateWindow(frame, 0, 0, ewin->client.w, ewin->client.h, 0);
244 	EventCallbackRegister(ewin->win_container, EwinHandleEventsContainer,
245 			      ewin);
246 #endif
247 	EventCallbackRegister(EwinGetClientWin(ewin), EwinHandleEventsClient,
248 			      ewin);
249 
250 	EobjListFocusAdd(&ewin->o, 1);
251 	EobjListOrderAdd(&ewin->o);
252      }
253 
254 #if USE_CONTAINER_WIN
255    att.event_mask = EWIN_CONTAINER_EVENT_MASK;
256    att.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask;
257    EChangeWindowAttributes(ewin->win_container,
258 			   CWEventMask | CWDontPropagate, &att);
259    EMapWindow(ewin->win_container);
260 #endif
261 
262 #if USE_CONTAINER_WIN
263    att.event_mask = EWIN_TOP_EVENT_MASK;
264 #else
265    att.event_mask = EWIN_TOP_EVENT_MASK | EWIN_CONTAINER_EVENT_MASK;
266 #endif
267    att.do_not_propagate_mask =
268       KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask |
269       PointerMotionMask;
270 #if USE_XI2
271    EChangeWindowAttributes(EoGetWin(ewin), CWDontPropagate, &att);
272    ESelectInput(EoGetWin(ewin), att.event_mask);
273 #else
274    EChangeWindowAttributes(EoGetWin(ewin), CWEventMask | CWDontPropagate, &att);
275 #endif
276 
277    ewin->client.event_mask = EWIN_CLIENT_EVENT_MASK;
278    ESelectInput(EwinGetClientWin(ewin), ewin->client.event_mask);
279 
280    if (EDebug(EDBUG_TYPE_EWINS))
281       Eprintf("%s %#x frame=%#x cont=%#x st=%d\n", __func__,
282 	      EwinGetClientXwin(ewin), EoGetXwin(ewin),
283 	      EwinGetContainerXwin(ewin), ewin->state.state);
284 
285    if (!EwinIsInternal(ewin))
286      {
287 	XShapeSelectInput(disp, EwinGetClientXwin(ewin), ShapeNotifyMask);
288 	ESetWindowBorderWidth(EwinGetClientWin(ewin), 0);
289 	ewin->client.bw = 0;
290      }
291 
292    ICCCM_AdoptStart(ewin);
293 
294 #if USE_CONTAINER_WIN
295    /* We must reparent after getting original window position */
296    EReparentWindow(EwinGetClientWin(ewin), ewin->win_container, 0, 0);
297 #else
298    EReparentWindow(EwinGetClientWin(ewin), frame, 0, 0);
299 #endif
300 
301    /* Something (e.g. a match) may have changed the window size */
302    EResizeWindow(EwinGetClientWin(ewin), ewin->client.w, ewin->client.h);
303 
304    EwinUpdateShapeInfo(ewin);
305 
306    ModulesSignal(ESIGNAL_EWIN_CREATE, ewin);
307 }
308 
309 /*
310  * Derive frame window geometry from client window properties
311  */
312 static void
EwinSetGeometry(EWin * ewin)313 EwinSetGeometry(EWin * ewin)
314 {
315    int                 x, y;
316    int                 grav;
317    const EImageBorder *pad;
318 
319    grav = (ewin->state.identified) ? StaticGravity : ewin->icccm.grav;
320 
321    EwinGetPosition(ewin, ewin->client.x, ewin->client.y, grav, &x, &y);
322 
323    pad = BorderGetSize(ewin->border);
324 
325    ewin->client.x = x + pad->left;
326    ewin->client.y = y + pad->top;
327 
328    EoMoveResize(ewin, x, y,
329 		ewin->client.w + pad->left + pad->right,
330 		ewin->client.h + pad->top + pad->bottom);
331 }
332 
333 static void
EwinConfigure(EWin * ewin)334 EwinConfigure(EWin * ewin)
335 {
336    EwinStateUpdate(ewin);
337 
338    if (!EwinIsInternal(ewin) && Mode.wm.startup)
339       EHintsGetInfo(ewin);	/* E restart hints */
340    EwinHintsInferProps(ewin);
341    SnapshotEwinApply(ewin);	/* Apply saved settings */
342 
343    if (ewin->save_fs.layer < 0)
344       ewin->save_fs.layer = EoGetLayer(ewin);
345 
346    EwinStateUpdate(ewin);	/* Update after snaps etc. */
347 
348    ICCCM_Adopt(ewin);
349 
350    EwinBorderSelect(ewin);	/* Select border before calculating geometry */
351    EwinSetGeometry(ewin);	/* Calculate window geometry before border parts */
352    EwinBorderSetTo(ewin, NULL);
353 
354    EwinStateUpdate(ewin);	/* Update after border configuration */
355 
356    if (!ewin->props.no_button_grabs)
357       GrabButtonGrabs(EoGetWin(ewin));
358 
359    if (ewin->state.shaded)
360       EwinInstantShade(ewin, 1);
361 
362    EwinUpdateOpacity(ewin);
363 
364    if (ewin->state.no_border && EoGetWin(ewin)->argb)
365       EoSetShadow(ewin, 0);
366 
367    HintsSetWindowState(ewin);
368    HintsSetWindowOpacity(ewin);
369 
370    HintsSetClientList();
371 
372    if (EDebug(EDBUG_TYPE_EWINS))
373       Eprintf("%s %#x st=%d: %s\n", __func__, EwinGetClientXwin(ewin),
374 	      ewin->state.state, EwinGetTitle(ewin));
375 }
376 
377 static void
EwinCleanup(EWin * ewin)378 EwinCleanup(EWin * ewin)
379 {
380    EwinBorderDetach(ewin);
381 }
382 
383 static void
EwinDestroy(EWin * ewin)384 EwinDestroy(EWin * ewin)
385 {
386    EWin              **lst;
387    int                 i, num;
388 
389    if (!ewin)
390       return;
391 
392    EwinUnmap1(ewin);
393 
394    if (EoIsMapped(ewin))
395      {
396 	EoUnmap(ewin);
397 	EwinUnmap2(ewin);
398      }
399 
400    if (EDebug(EDBUG_TYPE_EWINS))
401       Eprintf("%s %#x st=%d: %s\n", __func__, EwinGetClientXwin(ewin),
402 	      ewin->state.state, EwinGetTitle(ewin));
403 
404    EventCallbackUnregister(EoGetWin(ewin), EwinHandleEventsToplevel, ewin);
405 #if USE_CONTAINER_WIN
406    EventCallbackUnregister(ewin->win_container, EwinHandleEventsContainer,
407 			   ewin);
408 #endif
409    EventCallbackUnregister(EwinGetClientWin(ewin), EwinHandleEventsClient,
410 			   ewin);
411    if (!EwinIsInternal(ewin))
412       EUnregisterWindow(EwinGetClientWin(ewin));
413 
414    SnapshotEwinUnmatch(ewin);
415 
416    ModulesSignal(ESIGNAL_EWIN_DESTROY, ewin);
417 
418    lst = EwinListTransientFor(ewin, &num);
419    for (i = 0; i < num; i++)
420      {
421 	lst[i]->icccm.transient_count--;
422 	if (lst[i]->icccm.transient_count < 0)	/* Paranoia? */
423 	   lst[i]->icccm.transient_count = 0;
424      }
425    Efree(lst);
426 
427    EwinCleanup(ewin);
428    EobjListOrderDel(&ewin->o);
429    EobjListFocusDel(&ewin->o);
430    EoFini(ewin);
431 
432    HintsSetClientList();
433 
434    Efree(ewin->icccm.wm_icon_name);
435    Efree(ewin->icccm.wm_role);
436    Efree(ewin->icccm.wm_command);
437    Efree(ewin->icccm.wm_machine);
438    Efree(ewin->ewmh.wm_name);
439    Efree(ewin->ewmh.wm_icon_name);
440    Efree(ewin->ewmh.wm_icon);
441    Efree(ewin->bits);
442    Efree(ewin->session_id);
443    PmapMaskFree(&ewin->mini_pmm);
444    GroupsEwinRemove(ewin);
445 
446    Efree(ewin);
447 }
448 
449 void
DetermineEwinFloat(EWin * ewin,int dx,int dy)450 DetermineEwinFloat(EWin * ewin, int dx, int dy)
451 {
452    char                dofloat = 0;
453    int                 x, y, w, h, xd, yd;
454    Desk               *dsk;
455 
456    dsk = EoGetDesk(ewin);
457    x = EoGetX(ewin);
458    y = EoGetY(ewin);
459    w = EoGetW(ewin);
460    h = EoGetH(ewin);
461 
462    xd = EoGetX(dsk);
463    yd = EoGetY(dsk);
464 
465    if ((dsk->num != 0) && (EoIsFloating(ewin) < 2) &&
466        ((xd != 0) || (yd != 0) || (DesksGetCurrent() != dsk)))
467      {
468 	switch (Conf.desks.dragdir)
469 	  {
470 	  case 0:
471 	     if (((x + dx < 0) ||
472 		  ((x + dx + w <= WinGetW(VROOT)) &&
473 		   (DesktopAt(xd + x + dx + w - 1, yd) != dsk))))
474 		dofloat = 1;
475 	     break;
476 	  case 1:
477 	     if (((x + dx + w > WinGetW(VROOT)) ||
478 		  ((x + dx >= 0) && (DesktopAt(xd + x + dx, yd) != dsk))))
479 		dofloat = 1;
480 	     break;
481 	  case 2:
482 	     if (((y + dy < 0) ||
483 		  ((y + dy + h <= WinGetH(VROOT)) &&
484 		   (DesktopAt(xd, yd + y + dy + h - 1) != dsk))))
485 		dofloat = 1;
486 	     break;
487 	  case 3:
488 	     if (((y + dy + h > WinGetH(VROOT)) ||
489 		  ((y + dy >= 0) && (DesktopAt(xd, yd + y + dy) != dsk))))
490 		dofloat = 1;
491 	     break;
492 	  }
493 
494 	if (dofloat)
495 	   EwinOpFloatAt(ewin, OPSRC_USER, x + xd, y + yd);
496      }
497 }
498 
499 EWin               *
GetEwinPointerInClient(void)500 GetEwinPointerInClient(void)
501 {
502    int                 px, py;
503    EWin               *const *lst, *ewin;
504    int                 i, num;
505    Desk               *dsk;
506 
507    dsk = DesktopAt(Mode.events.cx, Mode.events.cy);
508    EQueryPointer(EoGetWin(dsk), &px, &py, NULL, NULL);
509 
510    lst = EwinListGetForDesk(&num, dsk);
511    for (i = 0; i < num; i++)
512      {
513 	int                 x, y, w, h;
514 
515 	ewin = lst[i];
516 	x = EoGetX(ewin);
517 	y = EoGetY(ewin);
518 	w = EoGetW(ewin);
519 	h = EoGetH(ewin);
520 	if ((px >= x) && (py >= y) && (px < (x + w)) && (py < (y + h)) &&
521 	    EoIsMapped(ewin))
522 	   return ewin;
523      }
524 
525    return NULL;
526 }
527 
528 EWin               *
GetFocusEwin(void)529 GetFocusEwin(void)
530 {
531    return Mode.focuswin;
532 }
533 
534 EWin               *
GetContextEwin(void)535 GetContextEwin(void)
536 {
537    EWin               *ewin;
538 
539    ewin = Mode.context_ewin;
540    if (ewin)
541       goto done;
542 
543    ewin = NULL;
544 
545  done:
546 #if 0
547    Eprintf("%s %#x %s\n", __func__, EwinGetClientXwin(ewin),
548 	   EwinGetTitle(ewin));
549 #endif
550    return ewin;
551 }
552 
553 void
SetContextEwin(EWin * ewin)554 SetContextEwin(EWin * ewin)
555 {
556    if (ewin && ewin->type == EWIN_TYPE_MENU)
557       return;
558 #if 0
559    Eprintf("%s %#x %s\n", __func__, EwinGetClientXwin(ewin),
560 	   EwinGetTitle(ewin));
561 #endif
562    Mode.context_ewin = ewin;
563 }
564 
565 /*
566  * Derive frame window position from client window and border properties
567  */
568 void
EwinGetPosition(const EWin * ewin,int x,int y,int grav,int * px,int * py)569 EwinGetPosition(const EWin * ewin, int x, int y, int grav, int *px, int *py)
570 {
571    int                 bw, bd_lr, bd_tb;
572    const EImageBorder *pad;
573 
574    bw = ewin->client.bw;
575 
576    pad = BorderGetSize(ewin->border);
577    bd_lr = pad->left + pad->right;
578    bd_tb = pad->top + pad->bottom;
579 
580    if (grav == 0)
581       grav = ewin->icccm.grav;
582 
583    switch (grav)
584      {
585      case NorthWestGravity:
586      case WestGravity:
587      case SouthWestGravity:
588 	x -= bw;
589 	break;
590      case NorthGravity:
591      case CenterGravity:
592      case SouthGravity:
593 	x -= bd_lr / 2;
594 	break;
595      case NorthEastGravity:
596      case EastGravity:
597      case SouthEastGravity:
598 	x -= bd_lr - bw;
599 	break;
600      case StaticGravity:
601 	x -= pad->left;
602 	break;
603      default:
604 	break;
605      }
606 
607    switch (grav)
608      {
609      case NorthWestGravity:
610      case NorthGravity:
611      case NorthEastGravity:
612 	y -= bw;
613 	break;
614      case WestGravity:
615      case CenterGravity:
616      case EastGravity:
617 	y -= bd_tb / 2;
618 	break;
619      case SouthWestGravity:
620      case SouthGravity:
621      case SouthEastGravity:
622 	y -= bd_tb - bw;
623 	break;
624      case StaticGravity:
625 	y -= pad->top;
626 	break;
627      default:
628 	break;
629      }
630 
631    *px = x;
632    *py = y;
633 }
634 
635 /*
636  * Keep resizing on-screen window on-screen
637  */
638 static void
EwinKeepOnScreen(const EWin * ewin,int wn,int hn,int * px,int * py)639 EwinKeepOnScreen(const EWin * ewin, int wn, int hn, int *px, int *py)
640 {
641    int                 x = *px, y = *py, w, h;
642    int                 sx, sy, sw, sh, xy;
643 
644    w = EoGetW(ewin);
645    h = EoGetH(ewin);
646 
647    ScreenGetAvailableArea(x, y, &sx, &sy, &sw, &sh, Conf.place.ignore_struts);
648 
649    /* Quit if not on-screen to begin with */
650    if (x < sx || x + w > sx + sw || y < sy || y + h > sy + sh)
651       return;
652 
653    /* Attempt to keep on-screen */
654    xy = sx + sw - (w - ewin->client.w + wn);
655    if (x > xy)
656       x = xy;
657    if (x < sx)
658       x = sx;
659    xy = sy + sh - (h - ewin->client.h + hn);
660    if (y > xy)
661       y = xy;
662    if (y < sy)
663       y = sy;
664 
665    *px = x;
666    *py = y;
667 }
668 
669 void
EwinUpdateShapeInfo(EWin * ewin)670 EwinUpdateShapeInfo(EWin * ewin)
671 {
672    ewin->state.shaped =
673 #if USE_CONTAINER_WIN
674       EShapeSetShape(ewin->win_container, 0, 0, EwinGetClientWin(ewin));
675 #else
676       EShapeUpdate(EwinGetClientWin(ewin));
677 #endif
678 
679    if (EDebug(EX_EVENT_SHAPE_NOTIFY))
680       Eprintf("%s %#x cont=%#x shaped=%d\n", __func__,
681 	      EwinGetClientXwin(ewin), EwinGetContainerXwin(ewin),
682 	      ewin->state.shaped);
683 }
684 
685 void
EwinPropagateShapes(EWin * ewin)686 EwinPropagateShapes(EWin * ewin)
687 {
688    if (!EoIsShown(ewin))
689       return;
690 
691    if (ewin->state.docked)
692       return;
693 
694    if (!ewin->update.shape)
695       return;
696 
697    if (EDebug(EX_EVENT_SHAPE_NOTIFY))
698       Eprintf("%s %#x frame=%#x shaped=%d\n", __func__,
699 	      EwinGetClientXwin(ewin), EoGetXwin(ewin), ewin->state.shaped);
700 
701    EoShapeUpdate(ewin, 1);
702    ewin->update.shape = 0;
703 }
704 
705 void
EwinStateUpdate(EWin * ewin)706 EwinStateUpdate(EWin * ewin)
707 {
708    int                 fs_zo;
709 
710    fs_zo = ewin->state.fullscreen || ewin->state.zoomed;
711 
712    ewin->state.inhibit_actions = ewin->props.no_actions;
713    ewin->state.inhibit_focus =
714       !(ewin->icccm.need_input || ewin->icccm.take_focus) ||
715       EwinInhGetWM(ewin, focus) || ewin->state.iconified;
716 
717    ewin->state.inhibit_move = EwinInhGetUser(ewin, move) || fs_zo;
718    ewin->state.inhibit_resize = ewin->state.iconified || ewin->state.shaded ||
719       (ewin->props.no_resize_h && ewin->props.no_resize_v) ||
720       EwinInhGetUser(ewin, size) || fs_zo;
721    ewin->state.inhibit_iconify = EwinInhGetWM(ewin, iconify);
722    ewin->state.inhibit_shade = ewin->state.no_border ||
723       ewin->state.iconified || fs_zo;
724    ewin->state.inhibit_stick = 0;
725    ewin->state.inhibit_max_hor = ewin->state.inhibit_resize ||
726       ewin->props.no_resize_h || fs_zo;
727    ewin->state.inhibit_max_ver = ewin->state.inhibit_resize ||
728       ewin->props.no_resize_v || fs_zo;
729    ewin->state.inhibit_fullscreeen =
730       ewin->state.inhibit_move || ewin->state.inhibit_resize;
731    ewin->state.inhibit_change_desk = ewin->state.iconified;
732    ewin->state.inhibit_close = EwinInhGetApp(ewin, close) ||
733       EwinInhGetUser(ewin, close);
734 
735    ewin->state.donthide = ewin->props.donthide ||
736       ewin->props.skip_ext_task || ewin->props.skip_winlist ||
737       ewin->props.skip_focuslist;
738 
739    SnapshotEwinUpdate(ewin, SNAP_USE_FLAGS);
740 }
741 
742 static void
AddToFamily(EWin * ewin,EX_Window xwin,XWindowAttributes * pxwa,int startup)743 AddToFamily(EWin * ewin, EX_Window xwin, XWindowAttributes * pxwa, int startup)
744 {
745    XWindowAttributes   attr;
746    EWin               *ewin2;
747    EWin              **lst;
748    int                 i, k, num, fx, fy, x, y;
749    char                doslide, manplace;
750    Desk               *dsk;
751 
752    EGrabServer();
753 
754    if (!pxwa)
755      {
756 	pxwa = &attr;
757 	if (!EXGetWindowAttributes(xwin, &attr))
758 	   goto done;
759      }
760 #ifndef __cplusplus
761 #define c_class class
762 #endif
763    if (pxwa->c_class != InputOutput)
764       goto done;
765 
766    if (ewin)
767       EwinCleanup(ewin);
768    else
769       ewin = EwinCreate(EWIN_TYPE_NORMAL);
770    if (!ewin)
771       goto done;
772 
773    if (EwinGetAttributes(ewin, NULL, xwin, pxwa))
774      {
775 	if (EDebug(EDBUG_TYPE_EWINS))
776 	   Eprintf("Window is gone %#x\n", xwin);
777 	/* We got here by MapRequest. DestroyNotify should follow. */
778 	goto done;
779      }
780 
781    EwinGetHints(ewin);
782    WindowMatchEwinOps(ewin);	/* Window matches */
783    EwinManage(ewin);
784    EwinConfigure(ewin);
785 
786    if (startup)
787       ewin->state.placed = 1;
788 
789    /* if it hasn't been planted on a desktop - assign it the current desktop */
790    dsk = EoGetDesk(ewin);
791 
792    /* if is an afterstep/windowmaker dock app - dock it */
793    if (Conf.dock.enable && ewin->state.docked)
794       DockIt(ewin);
795 
796    ewin2 = NULL;
797    if (ewin->icccm.transient)
798      {
799 	if (ewin->icccm.transient_for == NoXID ||
800 	    ewin->icccm.transient_for == WinGetXwin(VROOT))
801 	  {
802 	     /* Group transient */
803 	     ewin->icccm.transient_for = WinGetXwin(VROOT);
804 #if 0				/* Maybe? */
805 	     ewin->layer++;
806 #endif
807 	     /* Don't treat this as a normal transient */
808 	     ewin->icccm.transient = -1;
809 	  }
810 	else if (ewin->icccm.transient_for == EwinGetClientXwin(ewin))
811 	  {
812 	     /* Some apps actually do this. Why? */
813 	     ewin->icccm.transient = 0;
814 	  }
815 	else
816 	  {
817 	     /* Regular transient */
818 	  }
819 
820 	if (ewin->icccm.transient)
821 	  {
822 	     /* Tag the parent window if this is a transient */
823 	     lst = EwinListTransientFor(ewin, &num);
824 	     for (i = 0; i < num; i++)
825 	       {
826 		  lst[i]->icccm.transient_count++;
827 		  if (EoGetLayer(ewin) < EoGetLayer(lst[i]))
828 		     EoSetLayer(ewin, EoGetLayer(lst[i]));
829 	       }
830 	     if (lst)
831 	       {
832 		  ewin2 = lst[0];
833 		  EoSetSticky(ewin, EoIsSticky(lst[0]));
834 		  Efree(lst);
835 	       }
836 	     else
837 	       {
838 		  /* No parents? - not a transient */
839 		  ewin->icccm.transient = 0;
840 	       }
841 	  }
842      }
843 
844    x = EoGetX(ewin);
845    y = EoGetY(ewin);
846 
847    doslide = manplace = 0;
848    if (Mode.place.enable_features > 0 && !ewin->state.snapstarted)
849      {
850 	/* if set for borderless then dont slide it in */
851 	if (Conf.place.slidein &&
852 	    !ewin->state.no_border && dsk == DesksGetCurrent())
853 	   doslide = 1;
854 
855 	if (Conf.place.manual && !Mode.place.doing_manual &&
856 	    !ewin->state.placed && !ewin->icccm.transient)
857 	   manplace = 1;
858      }
859 
860    if (ewin->icccm.transient && Conf.focus.transientsfollowleader)
861      {
862 	/* Place transient on desk where transient-for or
863 	 * group leader or other member is */
864 
865 	/* ewin2 is the transient-for ewin (if any) */
866 	if (!ewin2)
867 	   ewin2 = EwinFindGroupMember(ewin);
868 
869 	if (ewin2)
870 	  {
871 	     dsk = EoGetDesk(ewin2);	/* The destination desk */
872 	     if (!Mode.wm.startup && Conf.focus.switchfortransientmap &&
873 		 !ewin->state.iconified)
874 	       {
875 		  /* Goto desk/area where transient-for or group resides */
876 		  DeskGotoByEwin(ewin2, 1);
877 	       }
878 	  }
879      }
880 
881    if (ewin->state.fullscreen)
882      {
883 	EwinOpFullscreen(ewin, OPSRC_WM, 2);
884 	ewin->state.placed = 1;
885 	doslide = manplace = 0;
886 	x = EoGetX(ewin);
887 	y = EoGetY(ewin);
888      }
889    else if (!ewin->state.identified &&
890 	    (ewin->state.maximized_horz || ewin->state.maximized_vert))
891      {
892 	int                 hor, ver;
893 
894 	/* New client requested maximisation */
895 	hor = ewin->state.maximized_horz;
896 	ver = ewin->state.maximized_vert;
897 	ewin->state.maximized_horz = ewin->state.maximized_vert = 0;
898 	MaxSizeHV(ewin, "absolute", hor, ver, SLIDE_FOCUS | SLIDE_SOUND);
899 	/* Set old state to current maximized one */
900 	ewin->save_max.x = EoGetX(ewin);
901 	ewin->save_max.y = EoGetY(ewin);
902 	ewin->save_max.w = ewin->client.w;
903 	ewin->save_max.h = ewin->client.h;
904 	ewin->state.placed = 0;
905      }
906    else
907      {
908 	EwinResize(ewin, ewin->client.w, ewin->client.h, 0);
909      }
910 
911    /* if the window asked to be iconified at the start */
912    if (ewin->icccm.start_iconified)
913      {
914 	EwinMoveToDesktopAt(ewin, dsk, x, y);
915 	ewin->state.state = EWIN_STATE_MAPPED;
916 	EwinIconify(ewin);
917 	ewin->state.state = EWIN_STATE_ICONIC;
918 	goto done;
919      }
920 
921    if (manplace && GrabPointerSet(VROOT, ECSR_GRAB, 0) != GrabSuccess)
922       manplace = 0;
923 
924    /* if it hasn't been placed yet.... find a spot for it */
925    if ((!ewin->state.placed) && (!manplace))
926      {
927 	int                 cx, cy;
928 
929 	/* Place the window below the mouse pointer */
930 	if (Conf.place.manual_mouse_pointer)
931 	  {
932 	     /* if the loser has manual placement on and the app asks to be on */
933 	     /*  a desktop, then send E to that desktop so the user can place */
934 	     /* the window there */
935 	     DeskGoto(dsk);
936 
937 	     EventsUpdateXY(&cx, &cy);
938 
939 	     /* try to center the window on the mouse pointer */
940 	     ArrangeEwinCenteredOn(ewin, cx, cy, 0, 0, &x, &y);
941 	  }
942 	else if (ewin->icccm.transient)
943 	  {
944 	     /* Center unplaced transients on parent (or root) */
945 	     Win                 parent;
946 
947 	     ewin2 = NULL;
948 	     if (EwinGetTransientFor(ewin) != NoXID)
949 		ewin2 = EwinFindByClient(EwinGetTransientFor(ewin));
950 	     parent = (ewin2) ? EoGetWin(ewin2) : VROOT;
951 
952 	     ArrangeEwinCenteredOn(ewin, WinGetX(parent), WinGetY(parent),
953 				   WinGetW(parent), WinGetH(parent), &x, &y);
954 	  }
955 	else if (ewin->ewmh.type.b.dialog)
956 	  {
957 	     /* Center unplaced (non-transient) dialogs on root */
958 	     ArrangeEwinCenteredXY(ewin, &x, &y);
959 	  }
960 	else
961 	  {
962 	     ArrangeEwinXY(ewin, &x, &y);
963 	  }
964 	ewin->state.placed = 1;
965      }
966 
967    /* if we should slide it in and are not currently in the middle of a slide */
968    if ((manplace) && (!ewin->state.placed))
969      {
970 	int                 cx, cy;
971 
972 	/* if the loser has manual placement on and the app asks to be on */
973 	/*  a desktop, then send E to that desktop so the user can place */
974 	/* the window there */
975 	DeskGoto(dsk);
976 
977 	EventsUpdateXY(&cx, &cy);
978 
979 	ewin->state.placed = 1;
980 	x = cx - 8;
981 	y = cy - 8;
982 	GrabPointerSet(VROOT, ECSR_GRAB, 0);
983 	EoSetFloating(ewin, 1);	/* Causes reparenting to root */
984 	EwinOpFloatAt(ewin, OPSRC_USER, x, y);
985 	EwinShow(ewin);
986 	Mode.place.doing_manual = 1;
987 	MoveResizeMoveStart(ewin, 0, 0, 0);
988      }
989    else if (doslide)
990      {
991 	k = rand() % 4;
992 	if (k == 0)
993 	  {
994 	     fx = (rand() % (WinGetW(VROOT))) - EoGetW(ewin);
995 	     fy = -EoGetH(ewin);
996 	  }
997 	else if (k == 1)
998 	  {
999 	     fx = (rand() % (WinGetW(VROOT)));
1000 	     fy = WinGetH(VROOT);
1001 	  }
1002 	else if (k == 2)
1003 	  {
1004 	     fx = -EoGetW(ewin);
1005 	     fy = (rand() % (WinGetH(VROOT)));
1006 	  }
1007 	else
1008 	  {
1009 	     fx = WinGetW(VROOT);
1010 	     fy = (rand() % (WinGetH(VROOT))) - EoGetH(ewin);
1011 	  }
1012 
1013 	EwinMoveToDesktopAt(ewin, dsk, fx, fy);
1014 	EwinShow(ewin);
1015 	ewin->req_x = x;
1016 	ewin->req_y = y;
1017 
1018 	EwinSlideTo(ewin, EoGetX(ewin), EoGetY(ewin), ewin->req_x, ewin->req_y,
1019 		    Conf.place.slidespeedmap, Conf.place.slidemode,
1020 		    SLIDE_FOCUS | SLIDE_WARP | SLIDE_SOUND);
1021      }
1022    else
1023      {
1024 	EwinMoveToDesktopAt(ewin, dsk, x, y);
1025 	EwinShow(ewin);
1026      }
1027 
1028  done:
1029    EUngrabServer();
1030 }
1031 
1032 EWin               *
AddInternalToFamily(Win win,const char * bname,int type,const EWinOps * ops,void * ptr)1033 AddInternalToFamily(Win win, const char *bname, int type,
1034 		    const EWinOps * ops, void *ptr)
1035 {
1036    EWin               *ewin;
1037 
1038    EGrabServer();
1039 
1040    ewin = EwinCreate(type);
1041    if (!ewin)
1042       goto done;
1043 
1044    ewin->props.donthide = 1;
1045    EwinGetAttributes(ewin, win, NoXID, NULL);
1046    WindowMatchEwinOps(ewin);	/* Window matches */
1047    EwinManage(ewin);
1048 
1049    ewin->data = ptr;
1050    ewin->ops = ops;
1051    if (ops && ops->Init)
1052       ops->Init(ewin);		/* Type specific initialisation */
1053 
1054    if (bname)
1055       ewin->border = BorderFind(bname);
1056 
1057    EwinConfigure(ewin);
1058 
1059 #if 0
1060    Eprintf("Desk=%d, layer=%d, sticky=%d, floating=%d\n",
1061 	   EoGetDeskNum(ewin), EoGetLayer(ewin), EoIsSticky(ewin),
1062 	   EoIsFloating(ewin));
1063 #endif
1064 
1065  done:
1066    EUngrabServer();
1067 
1068    return ewin;
1069 }
1070 
1071 static void
EwinUnmap1(EWin * ewin)1072 EwinUnmap1(EWin * ewin)
1073 {
1074    /* The client may have been unmapped but the frame is not yet */
1075 
1076    Zoom(ewin, 0);
1077 
1078    MoveResizeEnd(ewin);
1079    DrawEwinShapeEnd(ewin);
1080 }
1081 
1082 static void
EwinUnmap2(EWin * ewin)1083 EwinUnmap2(EWin * ewin)
1084 {
1085    /* The frame has been unmapped */
1086 
1087    FocusToEWin(ewin, FOCUS_EWIN_UNMAP);
1088    if (ewin == Mode.mouse_over_ewin)
1089       Mode.mouse_over_ewin = NULL;
1090    if (ewin == Mode.context_ewin)
1091       Mode.context_ewin = NULL;
1092 
1093    ModulesSignal(ESIGNAL_EWIN_UNMAP, ewin);
1094 }
1095 
1096 static void
EwinWithdraw(EWin * ewin,Win to)1097 EwinWithdraw(EWin * ewin, Win to)
1098 {
1099    int                 x, y;
1100    EX_Window           xwin;
1101 
1102    /* Only external clients should go here */
1103 
1104    if (EDebug(EDBUG_TYPE_EWINS))
1105       Eprintf("%s %#x st=%d: %s\n", __func__, EwinGetClientXwin(ewin),
1106 	      ewin->state.state, EwinGetTitle(ewin));
1107 
1108    EGrabServer();
1109 
1110    xwin = EXWindowGetParent(EwinGetClientXwin(ewin));
1111 
1112    /* Window may be destroyed after unmap but DestroyNotify not received yet */
1113    if (xwin == NoXID)
1114       goto done;
1115 
1116    ESelectInput(EwinGetClientWin(ewin), NoEventMask);
1117    XShapeSelectInput(disp, EwinGetClientXwin(ewin), NoEventMask);
1118 
1119    if (xwin == EwinGetContainerXwin(ewin))
1120      {
1121 	const EImageBorder *pad = BorderGetSize(ewin->border);
1122 
1123 	/* Park the client window on the new root */
1124 	x = ewin->client.x;
1125 	y = ewin->client.y;
1126 	ETranslateCoordinates(EwinGetClientWin(ewin), VROOT,
1127 			      -pad->left, -pad->top, &x, &y, NULL);
1128 	EReparentWindow(EwinGetClientWin(ewin), to, x, y);
1129 	HintsDelWindowHints(ewin);
1130      }
1131    ICCCM_Withdraw(ewin);
1132 
1133    ESync(0);
1134 
1135  done:
1136    EUngrabServer();
1137 }
1138 
1139 static void
EwinEventMapRequest(EWin * ewin,XEvent * ev)1140 EwinEventMapRequest(EWin * ewin, XEvent * ev)
1141 {
1142    EX_Window           xwin;
1143 
1144    xwin = ev->xmaprequest.window;
1145 
1146    if (ewin)
1147      {
1148 	if (ewin->state.state == EWIN_STATE_ICONIC)
1149 	   EwinDeIconify(ewin);
1150 	else if (ewin->state.state == EWIN_STATE_WITHDRAWN)
1151 	   AddToFamily(ewin, xwin, NULL, 0);
1152 	else
1153 	  {
1154 	     if (EDebug(EDBUG_TYPE_EWINS))
1155 		Eprintf("%s: Already managing %s %#x\n", __func__, "A",
1156 			EwinGetClientXwin(ewin));
1157 	     EReparentWindow(EwinGetClientWin(ewin), EwinGetContainerWin(ewin),
1158 			     0, 0);
1159 	  }
1160      }
1161    else
1162      {
1163 	/* Check if we are already managing it */
1164 	ewin = EwinFindByClient(xwin);
1165 
1166 	/* Some clients MapRequest more than once ?!? */
1167 	if (ewin)
1168 	  {
1169 	     if (EDebug(EDBUG_TYPE_EWINS))
1170 		Eprintf("%s: Already managing %s %#x\n", __func__, "B",
1171 			EwinGetClientXwin(ewin));
1172 	     EReparentWindow(EwinGetClientWin(ewin), EwinGetContainerWin(ewin),
1173 			     0, 0);
1174 	     EwinShow(ewin);
1175 	  }
1176 	else
1177 	   AddToFamily(NULL, xwin, NULL, 0);
1178      }
1179 }
1180 
1181 static void
EwinEventDestroy(EWin * ewin)1182 EwinEventDestroy(EWin * ewin)
1183 {
1184    if (EDebug(EDBUG_TYPE_EWINS))
1185       Eprintf("%s %#x st=%d: %s\n", __func__, EwinGetClientXwin(ewin),
1186 	      ewin->state.state, EwinGetTitle(ewin));
1187 
1188    EwinDestroy(ewin);
1189 }
1190 
1191 static void
EwinEventReparent(EWin * ewin,XEvent * ev)1192 EwinEventReparent(EWin * ewin, XEvent * ev)
1193 {
1194    EX_Window           parent;
1195 
1196    EGrabServer();
1197 
1198    parent = EoIsGone(ewin) ? NoXID : ev->xreparent.parent;
1199 
1200    if (EDebug(EDBUG_TYPE_EWINS))
1201       Eprintf("%s %#x st=%d parent=%#x: %s\n", __func__,
1202 	      EwinGetClientXwin(ewin), ewin->state.state, parent,
1203 	      EwinGetTitle(ewin));
1204 
1205    if (parent != EwinGetContainerXwin(ewin))
1206       EwinDestroy(ewin);
1207 
1208    EUngrabServer();
1209 }
1210 
1211 static void
EwinEventMap(EWin * ewin,XEvent * ev)1212 EwinEventMap(EWin * ewin, XEvent * ev)
1213 {
1214    int                 old_state;
1215 
1216    /* Catch clients setting OR without proper withdrawal (just unmap/map) */
1217    if (ev->xmap.override_redirect)
1218       return;
1219 
1220    old_state = ewin->state.state;
1221    ewin->state.state = EWIN_STATE_MAPPED;
1222 
1223    if (EDebug(EDBUG_TYPE_EWINS))
1224       Eprintf("%s %#x st=%d: %s\n", __func__, EwinGetClientXwin(ewin),
1225 	      ewin->state.state, EwinGetTitle(ewin));
1226 
1227    /* If first time we may want to focus it (unless during startup) */
1228    if (old_state == EWIN_STATE_NEW)
1229       FocusToEWin(ewin, FOCUS_EWIN_NEW);
1230    else
1231       FocusToEWin(ewin, FOCUS_SET);
1232 
1233    ModulesSignal(ESIGNAL_EWIN_CHANGE, ewin);
1234 }
1235 
1236 static void
EwinEventUnmap(EWin * ewin,XEvent * ev)1237 EwinEventUnmap(EWin * ewin, XEvent * ev)
1238 {
1239    if (EDebug(EDBUG_TYPE_EWINS))
1240       Eprintf("%s %#x st=%d: %s\n", __func__, EwinGetClientXwin(ewin),
1241 	      ewin->state.state, EwinGetTitle(ewin));
1242 
1243    if (ewin->state.state == EWIN_STATE_STARTUP ||
1244        ewin->state.state == EWIN_STATE_NEW)
1245      {
1246 #if 0
1247 	/* We get here after reparenting to container and occasionally in
1248 	 * other(?) situations */
1249 	Eprintf("%s %#x: Ignoring bogus Unmap event\n", __func__,
1250 		EwinGetClientXwin(ewin));
1251 #endif
1252 	return;
1253      }
1254 
1255    /* Ignore synthetic events */
1256    if (ev->xany.send_event)
1257       return;
1258 
1259    if (ewin->state.state == EWIN_STATE_WITHDRAWN)
1260       return;
1261 
1262    if (ewin->state.iconified)
1263       ewin->state.state = EWIN_STATE_ICONIC;
1264    else
1265       ewin->state.state = EWIN_STATE_WITHDRAWN;
1266 
1267    EwinUnmap1(ewin);
1268    EWindowSetMapped(EwinGetClientWin(ewin), 0);
1269    EoUnmap(ewin);
1270    EwinUnmap2(ewin);
1271 
1272    if (ewin->state.state == EWIN_STATE_ICONIC)
1273       return;
1274 
1275    if (EwinIsInternal(ewin))
1276      {
1277 #if 1				/* FIXME - Remove? */
1278 	/* We should never get here */
1279 	Eprintf("FIXME: This cannot happen (%s)\n", EoGetName(ewin));
1280 #endif
1281 	return;
1282      }
1283 
1284    if (EoIsGone(ewin))
1285       return;
1286 
1287    EwinWithdraw(ewin, VROOT);
1288 }
1289 
1290 static void
EwinEventConfigureRequest(EWin * ewin,XEvent * ev)1291 EwinEventConfigureRequest(EWin * ewin, XEvent * ev)
1292 {
1293    EX_Window           winrel;
1294    EWin               *ewin2;
1295    int                 x = 0, y = 0, w = 0, h = 0;
1296    XWindowChanges      xwc;
1297 
1298    if (ewin)
1299      {
1300 	x = EoGetX(ewin);
1301 	y = EoGetY(ewin);
1302 	w = ewin->client.w;
1303 	h = ewin->client.h;
1304 	winrel = 0;
1305 	/* This is shady - some clients send root coords, some use the
1306 	 * ICCCM ones sent by us */
1307 	if ((ev->xconfigurerequest.value_mask & (CWX | CWY)) &&
1308 	    !EwinInhGetApp(ewin, move))
1309 	  {
1310 #if 0				/* FIXME - ??? */
1311 	     if (ev->xconfigurerequest.value_mask & CWX)
1312 		x = ev->xconfigurerequest.x;
1313 	     if (ev->xconfigurerequest.value_mask & CWY)
1314 		y = ev->xconfigurerequest.y;
1315 #else
1316 	     if (ev->xconfigurerequest.value_mask & CWX)
1317 		x = ev->xconfigurerequest.x -
1318 		   (Mode.wm.win_x + EoGetX(EoGetDesk(ewin)));
1319 	     if (ev->xconfigurerequest.value_mask & CWY)
1320 		y = ev->xconfigurerequest.y -
1321 		   (Mode.wm.win_y + EoGetY(EoGetDesk(ewin)));
1322 #endif
1323 	     /* Correct position taking gravity into account */
1324 	     EwinGetPosition(ewin, x, y, 0, &x, &y);
1325 	  }
1326 	if ((ev->xconfigurerequest.value_mask & (CWWidth | CWHeight)) &&
1327 	    !EwinInhGetApp(ewin, size))
1328 	  {
1329 	     if (ev->xconfigurerequest.value_mask & CWWidth)
1330 		w = ev->xconfigurerequest.width;
1331 	     if (ev->xconfigurerequest.value_mask & CWHeight)
1332 		h = ev->xconfigurerequest.height;
1333 	     if ((ev->xconfigurerequest.value_mask & (CWX | CWY)) == 0)
1334 	       {
1335 		  /* Resizing only */
1336 		  EwinKeepOnScreen(ewin, w, h, &x, &y);
1337 	       }
1338 	  }
1339 	if (ev->xconfigurerequest.value_mask & CWSibling)
1340 	   winrel = ev->xconfigurerequest.above;
1341 	if (ev->xconfigurerequest.value_mask & CWStackMode)
1342 	  {
1343 	     ewin2 = EwinFindByClient(winrel);
1344 	     if (ewin2)
1345 		winrel = EoGetXwin(ewin2);
1346 	     xwc.sibling = winrel;
1347 	     xwc.stack_mode = ev->xconfigurerequest.detail;
1348 	     if (Mode.mode == MODE_NONE)
1349 	       {
1350 		  if (xwc.stack_mode == Above)
1351 		     EwinRaise(ewin);
1352 		  else if (xwc.stack_mode == Below)
1353 		     EwinLower(ewin);
1354 	       }
1355 	  }
1356 
1357 	EwinMoveResize(ewin, x, y, w, h, MRF_NOCHECK_ONSCREEN);
1358 	ReZoom(ewin);
1359      }
1360    else
1361      {
1362 	xwc.x = ev->xconfigurerequest.x;
1363 	xwc.y = ev->xconfigurerequest.y;
1364 	xwc.width = ev->xconfigurerequest.width;
1365 	xwc.height = ev->xconfigurerequest.height;
1366 	xwc.border_width = ev->xconfigurerequest.border_width;
1367 	xwc.sibling = ev->xconfigurerequest.above;
1368 	xwc.stack_mode = ev->xconfigurerequest.detail;
1369 	XConfigureWindow(disp, ev->xconfigurerequest.window,
1370 			 ev->xconfigurerequest.value_mask, &xwc);
1371      }
1372 }
1373 
1374 static void
EwinEventResizeRequest(EWin * ewin,XEvent * ev)1375 EwinEventResizeRequest(EWin * ewin, XEvent * ev)
1376 {
1377    if (ewin)
1378      {
1379 	EwinResize(ewin, ev->xresizerequest.width, ev->xresizerequest.height,
1380 		   0);
1381 	ReZoom(ewin);
1382      }
1383    else
1384      {
1385 	XResizeWindow(disp, ev->xresizerequest.window,
1386 		      ev->xresizerequest.width, ev->xresizerequest.height);
1387      }
1388 }
1389 
1390 static void
EwinEventCirculateRequest(EWin * ewin,XEvent * ev)1391 EwinEventCirculateRequest(EWin * ewin, XEvent * ev)
1392 {
1393    if (ewin)
1394      {
1395 	if (ev->xcirculaterequest.place == PlaceOnTop)
1396 	   EwinRaise(ewin);
1397 	else
1398 	   EwinLower(ewin);
1399      }
1400    else
1401      {
1402 	if (ev->xcirculaterequest.place == PlaceOnTop)
1403 	   XRaiseWindow(disp, ev->xcirculaterequest.window);
1404 	else
1405 	   XLowerWindow(disp, ev->xcirculaterequest.window);
1406      }
1407 }
1408 
1409 static void
EwinEventPropertyNotify(EWin * ewin,XEvent * ev)1410 EwinEventPropertyNotify(EWin * ewin, XEvent * ev)
1411 {
1412    if (EwinIsInternal(ewin))
1413       return;
1414 
1415    EGrabServer();
1416    EwinChangesStart(ewin);
1417 
1418    HintsProcessPropertyChange(ewin, ev);
1419    EwinStateUpdate(ewin);
1420 
1421    EwinChangesProcess(ewin);
1422    EUngrabServer();
1423 }
1424 
1425 static void
EwinEventShapeChange(EWin * ewin,XEvent * ev)1426 EwinEventShapeChange(EWin * ewin, XEvent * ev)
1427 {
1428    XShapeEvent        *se = (XShapeEvent *) ev;
1429 
1430    if (EDebug(EX_EVENT_SHAPE_NOTIFY))
1431       Eprintf("%s %#x %s: state.shaped=%d ev->shaped=%d\n", __func__,
1432 	      EwinGetClientXwin(ewin), EoGetName(ewin), ewin->state.shaped,
1433 	      se->shaped);
1434    if (!se->shaped && !ewin->state.shaped)
1435       return;
1436    EwinUpdateShapeInfo(ewin);
1437    ewin->update.shape = 1;
1438    EwinPropagateShapes(ewin);
1439 }
1440 
1441 static void
EwinEventVisibility(EWin * ewin,int state)1442 EwinEventVisibility(EWin * ewin, int state)
1443 {
1444    ewin->state.visibility = state;
1445 }
1446 
1447 void
EwinReparent(EWin * ewin,Win parent)1448 EwinReparent(EWin * ewin, Win parent)
1449 {
1450    EwinWithdraw(ewin, parent);
1451 }
1452 
1453 int
EwinRaise(EWin * ewin)1454 EwinRaise(EWin * ewin)
1455 {
1456    static int          call_depth = 0;
1457    EWin              **lst;
1458    int                 i, num, numt;
1459 
1460    if (call_depth > 256)
1461       return 0;
1462    call_depth++;
1463 
1464    num = EoRaise(ewin);
1465 
1466    if (EDebug(EDBUG_TYPE_RAISELOWER))
1467       Eprintf("%s(%d) %#x %s n=%d\n", __func__, call_depth,
1468 	      EwinGetClientXwin(ewin), EwinGetTitle(ewin), num);
1469 
1470    if (num == 0)		/* Quit if stacking is unchanged */
1471       goto done;
1472 
1473    lst = EwinListTransients(ewin, &numt, 1);
1474    for (i = 0; i < numt; i++)
1475       EwinRaise(lst[i]);
1476    Efree(lst);
1477 
1478    if (call_depth == 1)
1479      {
1480 	ModulesSignal(ESIGNAL_EWIN_CHANGE, ewin);
1481 	ClickGrabsUpdate();
1482      }
1483 
1484  done:
1485    call_depth--;
1486 
1487    return num;
1488 }
1489 
1490 int
EwinLower(EWin * ewin)1491 EwinLower(EWin * ewin)
1492 {
1493    static int          call_depth = 0;
1494    EWin              **lst;
1495    int                 i, num, numt;
1496 
1497    if (call_depth > 256)
1498       return 0;
1499    call_depth++;
1500 
1501    num = EoLower(ewin);
1502 
1503    if (EDebug(EDBUG_TYPE_RAISELOWER))
1504       Eprintf("%s(%d) %#x %s n=%d\n", __func__, call_depth,
1505 	      EwinGetClientXwin(ewin), EwinGetTitle(ewin), num);
1506 
1507    if (num == 0)		/* Quit if stacking is unchanged */
1508       goto done;
1509 
1510    lst = EwinListTransientFor(ewin, &numt);
1511    for (i = 0; i < numt; i++)
1512       EwinLower(lst[i]);
1513    Efree(lst);
1514 
1515    if (call_depth == 1)
1516      {
1517 	ModulesSignal(ESIGNAL_EWIN_CHANGE, ewin);
1518 	ClickGrabsUpdate();
1519      }
1520 
1521  done:
1522    call_depth--;
1523 
1524    return num;
1525 }
1526 
1527 void
EwinShow(EWin * ewin)1528 EwinShow(EWin * ewin)
1529 {
1530    if (EoIsShown(ewin))
1531       return;
1532 
1533    if (EwinGetClientWin(ewin))
1534      {
1535 	EMapWindow(EwinGetClientWin(ewin));
1536      }
1537 
1538    if (ewin->update.shape)
1539      {
1540 	ewin->o.shown = 1;
1541 	EwinPropagateShapes(ewin);
1542 	ewin->o.shown = 0;
1543      }
1544 
1545    EoMap(ewin, 0);
1546 
1547    EwinStateUpdate(ewin);
1548 
1549    if (ewin->place.gravity < 0)
1550       EwinSetPlacementGravity(ewin, EoGetX(ewin), EoGetY(ewin));
1551 }
1552 
1553 void
EwinHide(EWin * ewin)1554 EwinHide(EWin * ewin)
1555 {
1556    if (!EwinIsInternal(ewin) && (!EoIsShown(ewin) || !EoIsMapped(ewin)))
1557       return;
1558 
1559    EwinUnmap1(ewin);
1560 
1561    EUnmapWindow(EwinGetClientWin(ewin));
1562    EoUnmap(ewin);
1563 
1564    EwinUnmap2(ewin);
1565 
1566    EwinStateUpdate(ewin);
1567 
1568    if (!EwinIsInternal(ewin) || ewin->state.iconified)
1569       return;
1570 
1571    if (EwinGetClientWin(ewin))
1572      {
1573 	ESelectInput(EwinGetClientWin(ewin), NoEventMask);
1574 	XShapeSelectInput(disp, EwinGetClientXwin(ewin), NoEventMask);
1575      }
1576 
1577    if (ewin->ops && ewin->ops->Close)
1578       ewin->ops->Close(ewin);
1579 
1580    EwinDestroy(ewin);
1581 }
1582 
1583 void
EwinKill(EWin * ewin)1584 EwinKill(EWin * ewin)
1585 {
1586    if (EwinIsInternal(ewin))
1587       return;
1588 
1589    XKillClient(disp, EwinGetClientXwin(ewin));
1590 
1591 #if 0				/* Wait for unmap/destroy for now */
1592    EwinUnmap1(ewin);
1593    EoUnmap(ewin);
1594    EwinUnmap2(ewin);
1595 
1596    EwinDestroy(ewin);
1597 #endif
1598 }
1599 
1600 void
EwinSetTitle(EWin * ewin,const char * title)1601 EwinSetTitle(EWin * ewin, const char *title)
1602 {
1603    HintsSetWindowName(EwinGetClientWin(ewin), title);
1604 
1605    EFREE_DUP(ewin->o.icccm.wm_name, title);
1606    EFREE_DUP(ewin->ewmh.wm_name, title);
1607 }
1608 
1609 void
EwinSetClass(EWin * ewin,const char * name,const char * clss)1610 EwinSetClass(EWin * ewin, const char *name, const char *clss)
1611 {
1612    HintsSetWindowClass(EwinGetClientWin(ewin), name, clss);
1613 
1614    EFREE_DUP(ewin->o.icccm.wm_res_name, name);
1615    EFREE_DUP(ewin->o.icccm.wm_res_class, clss);
1616 }
1617 
1618 const char         *
EwinGetTitle(const EWin * ewin)1619 EwinGetTitle(const EWin * ewin)
1620 {
1621    const char         *name;
1622 
1623    if (!ewin)
1624       return NULL;
1625    name = ewin->ewmh.wm_name;
1626    if (name)
1627       goto done;
1628    name = EwinGetIcccmName(ewin);
1629    if (name)
1630       goto done;
1631 
1632  done:
1633    return (name && name[0]) ? name : NULL;
1634 }
1635 
1636 #if 0				/* Unused */
1637 const char         *
1638 EwinGetIconName(const EWin * ewin)
1639 {
1640    const char         *name;
1641 
1642    name = ewin->ewmh.wm_icon_name;
1643    if (name)
1644       goto done;
1645    name = ewin->icccm.wm_icon_name;
1646    if (name)
1647       goto done;
1648 
1649    return EwinGetTitle(ewin);
1650 
1651  done:
1652    return (name && strlen(name)) ? name : NULL;
1653 }
1654 #endif
1655 
1656 const char         *
EwinBorderGetName(const EWin * ewin)1657 EwinBorderGetName(const EWin * ewin)
1658 {
1659    return (ewin->border) ? BorderGetName(ewin->border) : "?";
1660 }
1661 
1662 void
EwinBorderGetSize(const EWin * ewin,int * bl,int * br,int * bt,int * bb)1663 EwinBorderGetSize(const EWin * ewin, int *bl, int *br, int *bt, int *bb)
1664 {
1665    const EImageBorder *pad = BorderGetSize(ewin->border);
1666 
1667    if (!pad)
1668      {
1669 	*bl = *br = *bt = *bb = 0;
1670 	return;
1671      }
1672 
1673    *bl = pad->left;
1674    *br = pad->right;
1675    *bt = pad->top;
1676    *bb = pad->bottom;
1677 }
1678 
1679 void
EwinBorderUpdateState(EWin * ewin)1680 EwinBorderUpdateState(EWin * ewin)
1681 {
1682    EwinBorderDraw(ewin, 0, 0);
1683 }
1684 
1685 int
EwinIsOnScreen(const EWin * ewin)1686 EwinIsOnScreen(const EWin * ewin)
1687 {
1688    int                 x, y, w, h;
1689 
1690    if (EoIsSticky(ewin))
1691       return 1;
1692    if (EoGetDesk(ewin) != DesksGetCurrent())
1693       return 0;
1694 
1695    x = EoGetX(ewin);
1696    y = EoGetY(ewin);
1697    w = EoGetW(ewin);
1698    h = EoGetH(ewin);
1699 
1700    if (x + w <= 0 || x >= WinGetW(VROOT) || y + h <= 0 || y >= WinGetH(VROOT))
1701       return 0;
1702 
1703    return 1;
1704 }
1705 
1706 int
EwinIsOnDesktop(const EWin * ewin)1707 EwinIsOnDesktop(const EWin * ewin)
1708 {
1709    int                 xd, yd, wd, hd;
1710 
1711    wd = WinGetW(VROOT);
1712    hd = WinGetH(VROOT);
1713    if (EoIsSticky(ewin))
1714      {
1715 	xd = yd = 0;
1716      }
1717    else
1718      {
1719 	int                 ax, ay;
1720 
1721 	DeskGetArea(EoGetDesk(ewin), &ax, &ay);
1722 	xd = -ax * wd;
1723 	yd = -ay * hd;
1724 	wd *= Conf.desks.areas_nx;
1725 	hd *= Conf.desks.areas_ny;
1726      }
1727 
1728    return
1729       EoGetX(ewin) + EoGetW(ewin) - 8 >= xd && EoGetX(ewin) + 8 <= xd + wd &&
1730       EoGetY(ewin) + EoGetH(ewin) - 8 >= yd && EoGetY(ewin) + 8 <= yd + hd;
1731 }
1732 
1733 /*
1734  * Save current position in absolute viewport coordinates
1735  */
1736 void
EwinRememberPositionSet(EWin * ewin)1737 EwinRememberPositionSet(EWin * ewin)
1738 {
1739    int                 ax, ay;
1740 
1741    ewin->req_x = EoGetX(ewin);
1742    ewin->req_y = EoGetY(ewin);
1743    if (!EoIsSticky(ewin))
1744      {
1745 	DeskGetArea(EoGetDesk(ewin), &ax, &ay);
1746 	ewin->req_x += ax * WinGetW(VROOT);
1747 	ewin->req_y += ay * WinGetH(VROOT);
1748      }
1749 }
1750 
1751 /*
1752  * Get saved position in relative viewport coordinates
1753  */
1754 void
EwinRememberPositionGet(EWin * ewin,Desk * dsk,int * px,int * py)1755 EwinRememberPositionGet(EWin * ewin, Desk * dsk, int *px, int *py)
1756 {
1757    int                 x, y, ax, ay;
1758 
1759    x = ewin->req_x;
1760    y = ewin->req_y;
1761    if (!EoIsSticky(ewin))
1762      {
1763 	DeskGetArea(dsk, &ax, &ay);
1764 	x -= ax * WinGetW(VROOT);
1765 	y -= ay * WinGetH(VROOT);
1766      }
1767 
1768    *px = x;
1769    *py = y;
1770 }
1771 
1772 /*
1773  * Set placement gravity
1774  */
1775 void
EwinSetPlacementGravity(EWin * ewin,int x,int y)1776 EwinSetPlacementGravity(EWin * ewin, int x, int y)
1777 {
1778    int                 w, h, ax, ay, wd, hd;
1779    Desk               *dsk;
1780 
1781    dsk = EoGetDesk(ewin);
1782    wd = EoGetW(dsk);
1783    hd = EoGetH(dsk);
1784    DeskGetArea(dsk, &ax, &ay);
1785 
1786    w = EoGetW(ewin);
1787    h = EoGetH(ewin);
1788 
1789    /* Get relative area */
1790    ewin->place.ax = ewin->area_x;
1791    ewin->place.ay = ewin->area_y;
1792    ax = ewin->place.ax - ax;
1793    ay = ewin->place.ay - ay;
1794 
1795    x -= ax * wd;
1796    y -= ay * hd;
1797 
1798    if (x <= (wd - w) / 2)
1799      {
1800 	if (y <= (hd - h) / 2)
1801 	  {
1802 	     ewin->place.gravity = EWIN_GRAVITY_NW;
1803 	     ewin->place.gx = x;
1804 	     ewin->place.gy = y;
1805 	  }
1806 	else
1807 	  {
1808 	     ewin->place.gravity = EWIN_GRAVITY_SW;
1809 	     ewin->place.gx = x;
1810 	     ewin->place.gy = hd - (y + h);
1811 	  }
1812      }
1813    else
1814      {
1815 	if (y <= (hd - h) / 2)
1816 	  {
1817 	     ewin->place.gravity = EWIN_GRAVITY_NE;
1818 	     ewin->place.gx = wd - (x + w);
1819 	     ewin->place.gy = y;
1820 	  }
1821 	else
1822 	  {
1823 	     ewin->place.gravity = EWIN_GRAVITY_SE;
1824 	     ewin->place.gx = wd - (x + w);
1825 	     ewin->place.gy = hd - (y + h);
1826 	  }
1827      }
1828 
1829 #if 0				/* Debug */
1830    Eprintf("Set gravity %d,%d %d,%d %d %d,%d %d,%d: %s\n", ax, ay, x, y,
1831 	   ewin->place.gravity, ewin->place.ax, ewin->place.ay,
1832 	   ewin->place.gx, ewin->place.gy, EwinGetTitle(ewin));
1833 #endif
1834 }
1835 
1836 void
EwinReposition(EWin * ewin)1837 EwinReposition(EWin * ewin)
1838 {
1839    int                 wdo, hdo, wdn, hdn;
1840    int                 x, y, w, h, ax, ay, xn, yn;
1841 
1842    wdo = Mode.screen.w_old;
1843    hdo = Mode.screen.h_old;
1844    wdn = WinGetW(VROOT);
1845    hdn = WinGetH(VROOT);
1846 
1847    x = EoGetX(ewin);
1848    y = EoGetY(ewin);
1849    w = EoGetW(ewin);
1850    h = EoGetH(ewin);
1851 
1852    /* Get relative area */
1853    if (EoIsSticky(ewin))
1854      {
1855 	ax = ay = 0;
1856      }
1857    else
1858      {
1859 	DeskGetArea(EoGetDesk(ewin), &ax, &ay);
1860 	ax = ewin->place.ax - ax;
1861 	ay = ewin->place.ay - ay;
1862      }
1863 
1864    x -= ax * wdo;
1865    y -= ay * hdo;
1866 
1867    /* Reposition to same distance from screen edges determined by
1868     * placement gravity.
1869     * Fall back to left/top if this causes left/top to go offscreen */
1870    switch (ewin->place.gravity)
1871      {
1872      default:
1873      case EWIN_GRAVITY_NW:
1874      case EWIN_GRAVITY_SW:
1875 	xn = ewin->place.gx;
1876 	break;
1877      case EWIN_GRAVITY_NE:
1878      case EWIN_GRAVITY_SE:
1879 	xn = wdn - w - ewin->place.gx;
1880 	break;
1881      }
1882    if (x > 0 && xn < 0)
1883       xn = x;
1884 
1885    switch (ewin->place.gravity)
1886      {
1887      default:
1888      case EWIN_GRAVITY_NW:
1889      case EWIN_GRAVITY_NE:
1890 	yn = ewin->place.gy;
1891 	break;
1892      case EWIN_GRAVITY_SW:
1893      case EWIN_GRAVITY_SE:
1894 	yn = hdn - h - ewin->place.gy;
1895 	break;
1896      }
1897    if (y > 0 && yn < 0)
1898       yn = y;
1899 
1900 #if 0				/* Debug */
1901    Eprintf("Reposition %d,%d -> %d,%d: %s\n", x, y, xn, yn, EwinGetTitle(ewin));
1902 #endif
1903 
1904    xn += ax * wdn;
1905    yn += ay * hdn;
1906 
1907    EwinMove(ewin, xn, yn, 0);
1908 }
1909 
1910 void
EwinWarpTo(EWin * ewin,int force)1911 EwinWarpTo(EWin * ewin, int force)
1912 {
1913    if (!force && ewin == Mode.mouse_over_ewin)
1914       return;
1915 
1916    if (ewin->state.iconified)
1917       return;
1918 
1919    EWarpPointer(EoGetWin(ewin), EoGetW(ewin) / 2, EoGetH(ewin) / 2);
1920    Mode.mouse_over_ewin = ewin;
1921 }
1922 
1923 typedef union {
1924    unsigned int        all;
1925    struct {
1926       unsigned char       rsvd;
1927       unsigned char       inh_app;
1928       unsigned char       inh_user;
1929       unsigned char       inh_wm;
1930    } f;
1931 } EWinMiscFlags;
1932 
1933 typedef union {
1934    unsigned int        all;
1935    struct {
1936       unsigned            nua:1;
1937       unsigned            ctf:1;
1938       unsigned            nbg:1;
1939       unsigned            autoshade:1;
1940       unsigned            ia:1;
1941       unsigned:           3;
1942       unsigned:           8;
1943       unsigned:           8;
1944       unsigned            no_fade:1;
1945       unsigned            no_shadow:1;
1946       unsigned:           6;
1947    } f;
1948 } EWinMiscFlags2;
1949 
1950 void
EwinFlagsEncode(const EWin * ewin,unsigned int * flags)1951 EwinFlagsEncode(const EWin * ewin, unsigned int *flags)
1952 {
1953    EWinMiscFlags       fm;
1954    EWinMiscFlags2      fm2;
1955 
1956    fm.all = 0;
1957    fm.f.inh_app = ewin->inh_app.all;
1958    fm.f.inh_user = ewin->inh_user.all;
1959    fm.f.inh_wm = ewin->inh_wm.all;
1960 
1961    fm2.all = 0;
1962    fm2.f.ia = ewin->props.ignorearrange;
1963    fm2.f.nua = ewin->props.never_use_area;
1964    fm2.f.ctf = ewin->props.focusclick;
1965    fm2.f.nbg = ewin->props.no_button_grabs;
1966    fm2.f.autoshade = ewin->props.autoshade;
1967 #if USE_COMPOSITE
1968    fm2.f.no_fade = !EoGetFade(ewin);
1969    fm2.f.no_shadow = !EoGetShadow(ewin);
1970 #endif
1971 
1972    flags[0] = fm.all;
1973    flags[1] = fm2.all;
1974 }
1975 
1976 void
EwinFlagsDecode(EWin * ewin,const unsigned int * flags)1977 EwinFlagsDecode(EWin * ewin, const unsigned int *flags)
1978 {
1979    EWinMiscFlags       fm;
1980    EWinMiscFlags2      fm2;
1981 
1982    fm.all = flags[0];
1983    ewin->inh_app.all = fm.f.inh_app;
1984    ewin->inh_user.all = fm.f.inh_user;
1985    ewin->inh_wm.all = fm.f.inh_wm;
1986 
1987    fm2.all = flags[1];
1988    ewin->props.ignorearrange = fm2.f.ia;
1989    ewin->props.never_use_area = fm2.f.nua;
1990    ewin->props.focusclick = fm2.f.ctf;
1991    ewin->props.no_button_grabs = fm2.f.nbg;
1992    ewin->props.autoshade = fm2.f.autoshade;
1993 #if USE_COMPOSITE
1994    EoSetFade(ewin, !fm2.f.no_fade);
1995    EoSetShadow(ewin, !fm2.f.no_shadow);
1996 #endif
1997 }
1998 
1999 void
EwinUpdateOpacity(EWin * ewin)2000 EwinUpdateOpacity(EWin * ewin)
2001 {
2002    unsigned int        opacity;
2003 
2004    opacity = 0;
2005    if (ewin->state.moving || ewin->state.resizing)
2006       opacity = OpacityFromPercent(Conf.opacity.movres);
2007    else if (ewin->state.active)
2008       opacity = ewin->props.focused_opacity;
2009    if (opacity == 0)
2010       opacity = ewin->props.opacity;
2011    if (opacity == 0)
2012       opacity = ewin->state.active ?
2013 	 OpacityFromPercent(Conf.opacity.focused) :
2014 	 OpacityFromPercent(Conf.opacity.unfocused);
2015    if (opacity == 0)
2016       opacity = 0xffffffff;	/* Fallback */
2017 
2018    EoChangeOpacity(ewin, opacity);
2019 }
2020 
2021 /*
2022  * Change requests
2023  */
2024 static struct {
2025    unsigned int        flags;
2026    Desk               *desk;
2027 } EWinChanges;
2028 
2029 void
EwinChange(EWin * ewin __UNUSED__,unsigned int flag)2030 EwinChange(EWin * ewin __UNUSED__, unsigned int flag)
2031 {
2032    EWinChanges.flags |= flag;
2033 }
2034 
2035 static void
EwinChangesStart(EWin * ewin)2036 EwinChangesStart(EWin * ewin)
2037 {
2038    EWinChanges.flags = 0;
2039    EWinChanges.desk = EoGetDesk(ewin);
2040 }
2041 
2042 static void
EwinChangesProcess(EWin * ewin)2043 EwinChangesProcess(EWin * ewin)
2044 {
2045    if (!EWinChanges.flags)
2046       return;
2047 
2048    if (EWinChanges.flags & EWIN_CHANGE_NAME)
2049      {
2050 	EwinBorderUpdateInfo(ewin);
2051 	EwinBorderCalcSizes(ewin, 1);
2052      }
2053 
2054    if (EWinChanges.flags & EWIN_CHANGE_DESKTOP)
2055      {
2056 	Desk               *desk, *pdesk;
2057 
2058 	desk = EoGetDesk(ewin);
2059 	pdesk = EWinChanges.desk;
2060 	if (desk != pdesk && !EoIsSticky(ewin))
2061 	  {
2062 	     EoSetDesk(ewin, pdesk);
2063 	     EwinMoveToDesktop(ewin, desk);
2064 	  }
2065      }
2066 
2067    if (EWinChanges.flags & EWIN_CHANGE_ICON_PMAP)
2068      {
2069 	ModulesSignal(ESIGNAL_EWIN_CHANGE_ICON, ewin);
2070      }
2071 
2072    if (EWinChanges.flags & EWIN_CHANGE_OPACITY)
2073      {
2074 	EwinUpdateOpacity(ewin);
2075 	HintsSetWindowOpacity(ewin);
2076 	SnapshotEwinUpdate(ewin, SNAP_USE_OPACITY);
2077      }
2078 
2079    if (EWinChanges.flags & EWIN_CHANGE_ATTENTION)
2080      {
2081 	HintsSetWindowState(ewin);
2082      }
2083 
2084    EWinChanges.flags = 0;
2085 }
2086 
2087 EWin              **
EwinListTransients(const EWin * ewin,int * num,int group)2088 EwinListTransients(const EWin * ewin, int *num, int group)
2089 {
2090    EWin               *const *ewins, **lst, *ew;
2091    int                 i, j, n;
2092 
2093    j = 0;
2094    lst = NULL;
2095 
2096    if (EwinGetTransientCount(ewin) <= 0)
2097       goto done;
2098 
2099    ewins = EwinListGetAll(&n);
2100 
2101    /* Find regular transients */
2102    for (i = 0; i < n; i++)
2103      {
2104 	ew = ewins[i];
2105 
2106 	/* Skip self-reference */
2107 	if (ew == ewin)
2108 	   continue;
2109 
2110 	if (EwinGetTransientFor(ew) == EwinGetClientXwin(ewin))
2111 	  {
2112 	     lst = EREALLOC(EWin *, lst, j + 1);
2113 	     lst[j++] = ew;
2114 	  }
2115      }
2116 
2117    if (!group)
2118       goto done;
2119 
2120    /* Group transients (if ewin is not a transient) */
2121    if (EwinIsTransient(ewin))
2122       goto done;
2123 
2124    for (i = 0; i < n; i++)
2125      {
2126 	ew = ewins[i];
2127 
2128 	/* Skip self-reference */
2129 	if (ew == ewin)
2130 	   continue;
2131 
2132 	if (EwinGetTransientFor(ew) == WinGetXwin(VROOT) &&
2133 	    EwinGetWindowGroup(ew) == EwinGetWindowGroup(ewin))
2134 	  {
2135 	     lst = EREALLOC(EWin *, lst, j + 1);
2136 	     lst[j++] = ew;
2137 	  }
2138      }
2139 
2140  done:
2141    *num = j;
2142    return lst;
2143 }
2144 
2145 EWin              **
EwinListTransientFor(const EWin * ewin,int * num)2146 EwinListTransientFor(const EWin * ewin, int *num)
2147 {
2148    EWin               *const *ewins, **lst, *ew;
2149    int                 i, j, n;
2150 
2151    j = 0;
2152    lst = NULL;
2153 
2154    if (!EwinIsTransient(ewin))
2155       goto done;
2156 
2157    ewins = EwinListGetAll(&n);
2158    for (i = 0; i < n; i++)
2159      {
2160 	ew = ewins[i];
2161 
2162 	/* Skip self-reference */
2163 	if (ew == ewin)
2164 	   continue;
2165 
2166 	/* Regular parent or if root trans, top level group members */
2167 	if ((EwinGetTransientFor(ewin) == EwinGetClientXwin(ew)) ||
2168 	    (!EwinIsTransient(ew) &&
2169 	     EwinGetTransientFor(ewin) == WinGetXwin(VROOT) &&
2170 	     EwinGetWindowGroup(ew) == EwinGetWindowGroup(ewin)))
2171 	  {
2172 	     lst = EREALLOC(EWin *, lst, j + 1);
2173 	     lst[j++] = ew;
2174 	  }
2175      }
2176 
2177  done:
2178    *num = j;
2179    return lst;
2180 }
2181 
2182 static void
EwinsTouch(Desk * dsk)2183 EwinsTouch(Desk * dsk)
2184 {
2185    int                 i, num;
2186    EWin               *const *lst, *ewin;
2187 
2188    if (!dsk)
2189       lst = EwinListGetAll(&num);
2190    else
2191       lst = EwinListGetForDesk(&num, dsk);
2192 
2193    for (i = num - 1; i >= 0; i--)
2194      {
2195 	ewin = lst[i];
2196 	if (EoIsMapped(ewin) && EwinIsOnScreen(ewin))
2197 	   EwinMove(ewin, EoGetX(ewin), EoGetY(ewin), 0);
2198      }
2199 }
2200 
2201 static void
EwinsReposition(void)2202 EwinsReposition(void)
2203 {
2204    int                 i, num;
2205    EWin               *const *lst;
2206 
2207    lst = EwinListGetAll(&num);
2208    for (i = num - 1; i >= 0; i--)
2209       EwinReposition(lst[i]);
2210 }
2211 
2212 void
EwinsMoveStickyToDesk(Desk * dsk)2213 EwinsMoveStickyToDesk(Desk * dsk)
2214 {
2215    EWin               *const *lst, *ewin;
2216    int                 i, num;
2217 
2218    lst = EwinListStackGet(&num);
2219    for (i = 0; i < num; i++)
2220      {
2221 	ewin = lst[num - 1 - i];
2222 	if (!EoIsSticky(ewin) && !EoIsFloating(ewin))
2223 	   continue;
2224 	if (EwinIsTransientChild(ewin))
2225 	   continue;
2226 
2227 	EwinMoveToDesktop(ewin, dsk);
2228      }
2229 }
2230 
2231 void
EwinsManage(void)2232 EwinsManage(void)
2233 {
2234    Window             *xwins, par, rt;
2235    EX_Window           xwin;
2236    XWindowAttributes   attr;
2237    unsigned int        i, num;
2238 
2239 #ifdef USE_EXT_INIT_WIN
2240    xwin = ExtInitWinGet();
2241    if (xwin)
2242       XRaiseWindow(disp, xwin);
2243 #endif
2244 
2245    xwins = NULL;
2246    num = 0;
2247    XQueryTree(disp, WinGetXwin(VROOT), &rt, &par, &xwins, &num);
2248    if (!xwins)
2249       return;
2250 
2251    EGrabServer();
2252 
2253    for (i = 0; i < num; i++)
2254      {
2255 	xwin = xwins[i];
2256 
2257 	/* Skip if already "known" */
2258 	if (EobjListStackFind(xwin))
2259 	   continue;
2260 
2261 	if (!EXGetWindowAttributes(xwin, &attr))
2262 	   continue;
2263 
2264 	if (attr.map_state == IsUnmapped)
2265 	   continue;
2266 
2267 	if (attr.override_redirect)
2268 	   EobjRegisterOR(xwin, &attr, 1);
2269 	else
2270 	   AddToFamily(NULL, xwin, &attr, 1);
2271      }
2272 
2273    EUngrabServer();
2274 
2275    XFree(xwins);
2276 }
2277 
2278 void
EwinsSetFree(void)2279 EwinsSetFree(void)
2280 {
2281    int                 i, num;
2282    EWin               *const *lst, *ewin;
2283 
2284    if (EDebug(EDBUG_TYPE_SESSION))
2285       Eprintf("%s\n", __func__);
2286 
2287    EHintsSetInfoOnAll();
2288 
2289    lst = EwinListStackGet(&num);
2290    for (i = num - 1; i >= 0; i--)
2291      {
2292 	ewin = lst[i];
2293 	if (EwinIsInternal(ewin))
2294 	   continue;
2295 
2296 	if (ewin->state.iconified)
2297 	   ICCCM_DeIconify(ewin);
2298 
2299 	/* This makes E determine the client window stacking at exit */
2300 	EwinInstantUnShade(ewin);
2301 	if (Mode.wm.window)
2302 	   ICCCM_Delete(ewin);
2303 	else
2304 	   EReparentWindow(EwinGetClientWin(ewin), RROOT,
2305 			   ewin->client.x, ewin->client.y);
2306      }
2307 }
2308 
2309 /*
2310  * Event handlers
2311  */
2312 
2313 #define DEBUG_EWIN_EVENTS 0
2314 
2315 static int
ActionsCheck(const char * which,EWin * ewin,XEvent * ev)2316 ActionsCheck(const char *which, EWin * ewin, XEvent * ev)
2317 {
2318    ActionClass        *ac;
2319 
2320    ac = ActionclassFind(which);
2321    if (!ac)
2322       return 0;
2323 
2324    if (ev->type == ButtonPress)
2325      {
2326 	GrabPointerSet(EoGetWin(ewin), ECSR_GRAB, 0);
2327 	FocusToEWin(ewin, FOCUS_CLICK);
2328      }
2329    else if (ev->type == ButtonRelease)
2330      {
2331 	GrabPointerRelease();
2332      }
2333 
2334    return ActionclassEvent(ac, ev, ewin);
2335 }
2336 
2337 static EWin        *
_EwinEventEwinCheck(const char * txt,XEvent * ev,EWin * ewin)2338 _EwinEventEwinCheck(const char *txt, XEvent * ev, EWin * ewin)
2339 {
2340    int                 ser_diff;
2341 
2342    ser_diff = (int)(ev->xany.serial - ewin->serial);
2343    if (ser_diff < 0 && ser_diff > -1000)
2344      {
2345 	Eprintf("%s: %#lx: Ignore obsolete event %d\n", txt,
2346 		ev->xany.window, ev->type);
2347 	return NULL;
2348      }
2349 
2350    ewin->serial = ev->xany.serial;
2351 
2352    return ewin;
2353 }
2354 
2355 static EWin        *
_EwinEventEwinFind(XEvent * ev,EX_Window xwin)2356 _EwinEventEwinFind(XEvent * ev, EX_Window xwin)
2357 {
2358    EWin               *ewin;
2359 
2360    ewin = EwinFindByClient(xwin);
2361    if (!ewin)
2362       return ewin;
2363 
2364    return _EwinEventEwinCheck("root", ev, ewin);
2365 }
2366 
2367 static int
EwinHandleContainerEvents(EWin * ewin,XEvent * ev)2368 EwinHandleContainerEvents(EWin * ewin, XEvent * ev)
2369 {
2370    EX_Window           xwin = EwinGetClientXwin(ewin);
2371 
2372    switch (ev->type)
2373      {
2374      case MapRequest:
2375 	if (ev->xmaprequest.window != xwin)
2376 	   break;
2377 	EwinEventMapRequest(ewin, ev);
2378 	break;
2379      case ConfigureRequest:
2380 	if (ev->xconfigurerequest.window != xwin)
2381 	   break;
2382 	EwinEventConfigureRequest(ewin, ev);
2383 	break;
2384      case ResizeRequest:
2385 	if (ev->xresizerequest.window != xwin)
2386 	   break;
2387 	EwinEventResizeRequest(ewin, ev);
2388 	break;
2389      case CirculateRequest:
2390 	if (ev->xcirculaterequest.window != xwin)
2391 	   break;
2392 	EwinEventCirculateRequest(ewin, ev);
2393 	break;
2394 
2395      case DestroyNotify:
2396 	if (ev->xdestroywindow.window != xwin)
2397 	   break;
2398 	EwinEventDestroy(ewin);
2399 	break;
2400 
2401      case EX_EVENT_UNMAP_GONE:
2402 	if (ev->xunmap.window != xwin)
2403 	   break;
2404 	EoSetGone(ewin);
2405 	goto do_unmap;
2406      case UnmapNotify:
2407 	if (ev->xunmap.window != xwin)
2408 	   break;
2409       do_unmap:
2410 	EwinEventUnmap(ewin, ev);
2411 	break;
2412 
2413      case MapNotify:
2414 	if (ev->xmap.window != xwin)
2415 	   break;
2416 	EwinEventMap(ewin, ev);
2417 	break;
2418 
2419      case EX_EVENT_REPARENT_GONE:
2420 	if (ev->xreparent.window != xwin)
2421 	   break;
2422 	EoSetGone(ewin);
2423 	goto do_reparent;
2424      case ReparentNotify:
2425 	if (ev->xreparent.window != xwin)
2426 	   break;
2427       do_reparent:
2428 	EwinEventReparent(ewin, ev);
2429 	break;
2430 
2431      case EX_EVENT_MAP_GONE:
2432      case GravityNotify:
2433      case ConfigureNotify:
2434 	break;
2435 
2436      default:
2437 	return 0;		/* Not handled */
2438      }
2439 
2440    return 1;			/* Handled */
2441 }
2442 
2443 static void
EwinHandleEventsToplevel(Win win __UNUSED__,XEvent * ev,void * prm)2444 EwinHandleEventsToplevel(Win win __UNUSED__, XEvent * ev, void *prm)
2445 {
2446    EWin               *ewin = (EWin *) prm;
2447 
2448    if (!_EwinEventEwinCheck("frm", ev, ewin))
2449       return;
2450 
2451    switch (ev->type)
2452      {
2453      case ButtonPress:
2454 	ActionsCheck("BUTTONBINDINGS", ewin, ev);
2455 	break;
2456      case ButtonRelease:
2457 	ActionsCheck("BUTTONBINDINGS", ewin, ev);
2458 	break;
2459 
2460      case EnterNotify:
2461 	FocusHandleEnter(ewin, ev);
2462 	break;
2463      case LeaveNotify:
2464 	FocusHandleLeave(ewin, ev);
2465 	break;
2466 
2467      default:
2468 	if (EwinHandleContainerEvents(ewin, ev))
2469 	   break;
2470 #if DEBUG_EWIN_EVENTS
2471 	Eprintf("%s: type=%2d win=%#lx: %s\n", __func__,
2472 		ev->type, EwinGetClientXwin(ewin), EwinGetTitle(ewin));
2473 #endif
2474 	break;
2475      }
2476 }
2477 
2478 #if USE_CONTAINER_WIN
2479 static void
EwinHandleEventsContainer(Win win __UNUSED__,XEvent * ev,void * prm)2480 EwinHandleEventsContainer(Win win __UNUSED__, XEvent * ev, void *prm)
2481 {
2482    EWin               *ewin = (EWin *) prm;
2483 
2484    if (ev->type == ButtonPress)
2485      {
2486 	FocusHandleClick(ewin, EwinGetClientConWin(ewin));
2487 	return;
2488      }
2489 
2490    if (!_EwinEventEwinCheck("cont", ev, ewin))
2491       return;
2492 
2493    switch (ev->type)
2494      {
2495      default:
2496 	if (EwinHandleContainerEvents(ewin, ev))
2497 	   break;
2498 #if DEBUG_EWIN_EVENTS
2499 	Eprintf("%s: type=%2d win=%#x: %s\n", __func__,
2500 		ev->type, EwinGetClientXwin(ewin), EwinGetTitle(ewin));
2501 #endif
2502 	break;
2503      }
2504 }
2505 #endif
2506 
2507 static void
EwinHandleEventsClient(Win win __UNUSED__,XEvent * ev,void * prm)2508 EwinHandleEventsClient(Win win __UNUSED__, XEvent * ev, void *prm)
2509 {
2510    EWin               *ewin = (EWin *) prm;
2511 
2512 #if !USE_CONTAINER_WIN
2513    if (ev->type == ButtonPress)
2514      {
2515 	FocusHandleClick(ewin, EwinGetClientConWin(ewin));
2516 	return;
2517      }
2518 #endif
2519 
2520    if (!_EwinEventEwinCheck("cli", ev, ewin))
2521       return;
2522 
2523    switch (ev->type)
2524      {
2525      case FocusIn:
2526      case FocusOut:
2527 	if (ev->xfocus.detail == NotifyInferior)
2528 	   break;
2529 	if (BorderGetAclass(ewin->border))
2530 	   ActionclassEvent(BorderGetAclass(ewin->border), ev, ewin);
2531 	FocusHandleChange(ewin, ev);
2532 	break;
2533 
2534      case ConfigureNotify:
2535      case GravityNotify:
2536 	break;
2537 
2538      case VisibilityNotify:
2539 	EwinEventVisibility(ewin, ev->xvisibility.state);
2540 	break;
2541 
2542      case PropertyNotify:
2543 	EwinEventPropertyNotify(ewin, ev);
2544 	break;
2545 
2546      case ClientMessage:
2547 	HintsProcessClientClientMessage(ewin, &(ev->xclient));
2548 	break;
2549 
2550      case EX_EVENT_SHAPE_NOTIFY:
2551 	EwinEventShapeChange(ewin, ev);
2552 	break;
2553 
2554      default:
2555 #if DEBUG_EWIN_EVENTS
2556 	Eprintf("%s: type=%2d win=%#x: %s\n", __func__,
2557 		ev->type, EwinGetClientXwin(ewin), EwinGetTitle(ewin));
2558 #endif
2559 	break;
2560      }
2561 }
2562 
2563 static void
EwinHandleEventsRoot(Win win __UNUSED__,XEvent * ev,void * prm __UNUSED__)2564 EwinHandleEventsRoot(Win win __UNUSED__, XEvent * ev, void *prm __UNUSED__)
2565 {
2566    EWin               *ewin;
2567 
2568    switch (ev->type)
2569      {
2570      case MapRequest:
2571 	EwinEventMapRequest(NULL, ev);
2572 	break;
2573      case ConfigureRequest:
2574 #if 0
2575 	Eprintf("%s ConfigureRequest %#x\n", __func__,
2576 		ev->xconfigurerequest.window);
2577 #endif
2578 	ewin = EwinFindByClient(ev->xconfigurerequest.window);
2579 	EwinEventConfigureRequest(ewin, ev);
2580 	break;
2581      case ResizeRequest:
2582 #if 0
2583 	Eprintf("%s ResizeRequest %#x\n", __func__, ev->xresizerequest.window);
2584 #endif
2585 	ewin = EwinFindByClient(ev->xresizerequest.window);
2586 	EwinEventResizeRequest(ewin, ev);
2587 	break;
2588      case CirculateRequest:
2589 #if 0
2590 	Eprintf("%s CirculateRequest %#x\n", __func__,
2591 		ev->xcirculaterequest.window);
2592 #endif
2593 	EwinEventCirculateRequest(NULL, ev);
2594 	break;
2595 
2596      case UnmapNotify:
2597      case EX_EVENT_UNMAP_GONE:
2598 	/* Catch clients unmapped after MapRequest but before being reparented */
2599 	ewin = _EwinEventEwinFind(ev, ev->xunmap.window);
2600 	if (!ewin)
2601 	   break;
2602 	if (ev->type == EX_EVENT_UNMAP_GONE)
2603 	   EoSetGone(ewin);
2604 	EwinEventUnmap(ewin, ev);
2605 	break;
2606 
2607      case DestroyNotify:
2608 	/* Catch clients destroyed after MapRequest but before being reparented */
2609 	ewin = _EwinEventEwinFind(ev, ev->xdestroywindow.window);
2610 	if (!ewin)
2611 	   break;
2612 	EwinEventDestroy(ewin);
2613 	break;
2614 
2615      case ReparentNotify:
2616      case EX_EVENT_REPARENT_GONE:
2617 	ewin = _EwinEventEwinFind(ev, ev->xreparent.window);
2618 	if (!ewin)
2619 	   break;
2620 	if (ev->type == EX_EVENT_REPARENT_GONE)
2621 	   EoSetGone(ewin);
2622 	EwinEventReparent(ewin, ev);
2623 	break;
2624 
2625      case ClientMessage:
2626 	HintsProcessRootClientMessage(&(ev->xclient));
2627 	break;
2628 
2629      default:
2630 #if 0
2631 	Eprintf("%s: type=%2d win=%#x\n", __func__, ev->type, ev->xany.window);
2632 #endif
2633 	break;
2634      }
2635 }
2636 
2637 static void
EwinsInit(void)2638 EwinsInit(void)
2639 {
2640    EventCallbackRegister(VROOT, EwinHandleEventsRoot, NULL);
2641 }
2642 
2643 /*
2644  * Ewins module
2645  * This is the WM.
2646  */
2647 
2648 static void
EwinsSighan(int sig,void * prm)2649 EwinsSighan(int sig, void *prm)
2650 {
2651    switch (sig)
2652      {
2653      case ESIGNAL_INIT:
2654 	EwinsInit();
2655 	break;
2656 #if 0
2657      case ESIGNAL_START:
2658 	EwinsManage();
2659 	break;
2660 #endif
2661      case ESIGNAL_DESK_RESIZE:
2662 	EwinsReposition();
2663 	break;
2664      case ESIGNAL_THEME_TRANS_CHANGE:
2665 	EwinsTouch(DesksGetCurrent());
2666 	break;
2667      case ESIGNAL_BACKGROUND_CHANGE:
2668 	EwinsTouch((Desk *) prm);
2669 	break;
2670      }
2671 }
2672 
2673 /*
2674  * Module descriptor
2675  */
2676 extern const EModule ModEwins;
2677 
2678 const EModule       ModEwins = {
2679    "ewins", NULL,
2680    EwinsSighan,
2681    {0, NULL},
2682    {0, NULL}
2683 };
2684