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