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