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 
28 #include "E.h"
29 #include "animation.h"
30 #include "borders.h"
31 #include "desktops.h"
32 #include "emodule.h"
33 #include "eobj.h"
34 #include "events.h"
35 #include "ewins.h"
36 #include "ewin-ops.h"
37 #include "focus.h"
38 #include "groups.h"
39 #include "hints.h"
40 #include "iclass.h"		/* FIXME - Should not be here */
41 #include "screen.h"
42 #include "snaps.h"
43 #include "xwin.h"
44 
45 static const WinOp  winops[] = {
46    {"border", 2, 1, 1, EWIN_OP_BORDER},
47    {"title", 2, 1, 1, EWIN_OP_TITLE},
48 
49    {"focusclick", 0, 1, 1, EWIN_OP_FOCUS_CLICK},	/* Place before "focus" */
50 
51    {"close", 2, 1, 0, EWIN_OP_CLOSE},
52    {"kill", 0, 1, 0, EWIN_OP_KILL},
53    {"iconify", 2, 1, 1, EWIN_OP_ICONIFY},
54    {"alone", 0, 1, 0, EWIN_OP_ALONE},
55    {"opacity", 2, 1, 1, EWIN_OP_OPACITY},
56    {"focused_opacity", 0, 1, 1, EWIN_OP_FOCUSED_OPACITY},
57    {"shadow", 0, 1, 1, EWIN_OP_SHADOW},	/* Place before "shade" */
58    {"shade", 2, 1, 1, EWIN_OP_SHADE},
59    {"stick", 2, 1, 1, EWIN_OP_STICK},
60    {"focus", 2, 1, 0, EWIN_OP_FOCUS},
61 
62    {"desk", 2, 1, 1, EWIN_OP_DESK},
63    {"area", 2, 1, 1, EWIN_OP_AREA},
64    {"move", 2, 1, 1, EWIN_OP_MOVE},
65    {"size", 2, 1, 1, EWIN_OP_SIZE},
66    {"sz", 2, 1, 1, EWIN_OP_SIZE},
67    {"move_relative", 0, 1, 0, EWIN_OP_MOVE_REL},
68    {"mr", 2, 1, 0, EWIN_OP_MOVE_REL},
69    {"resize_relative", 0, 1, 0, EWIN_OP_SIZE_REL},
70    {"sr", 2, 1, 0, EWIN_OP_SIZE_REL},
71 
72    {"toggle_width", 0, 1, 0, EWIN_OP_MAX_WIDTH},
73    {"tw", 2, 1, 0, EWIN_OP_MAX_WIDTH},
74    {"toggle_height", 0, 1, 0, EWIN_OP_MAX_HEIGHT},
75    {"th", 0, 1, 0, EWIN_OP_MAX_HEIGHT},
76    {"toggle_size", 0, 1, 0, EWIN_OP_MAX_SIZE},
77    {"ts", 2, 1, 0, EWIN_OP_MAX_SIZE},
78    {"fullscreen", 2, 1, 1, EWIN_OP_FULLSCREEN},
79    {"zoom", 2, 1, 0, EWIN_OP_ZOOM},
80 
81    {"layer", 2, 1, 1, EWIN_OP_LAYER},
82    {"raise", 2, 1, 0, EWIN_OP_RAISE},
83    {"lower", 2, 1, 0, EWIN_OP_LOWER},
84 
85    {"snap", 0, 1, 0, EWIN_OP_SNAP},
86 
87    {"ignore_arrange", 0, 1, 1, EWIN_OP_IGNORE_ARRANGE},
88    {"never_use_area", 0, 1, 1, EWIN_OP_NEVER_USE_AREA},
89    {"no_button_grabs", 0, 1, 1, EWIN_OP_NO_BUTTON_GRABS},
90    {"skiplists", 4, 1, 1, EWIN_OP_SKIP_LISTS},
91    {"autoshade", 0, 1, 1, EWIN_OP_AUTOSHADE},
92 
93    {"no_app_focus", 0, 1, 1, EWIN_OP_INH_APP_FOCUS},
94    {"no_app_move", 0, 1, 1, EWIN_OP_INH_APP_MOVE},
95    {"no_app_size", 0, 1, 1, EWIN_OP_INH_APP_SIZE},
96    {"no_user_close", 0, 1, 1, EWIN_OP_INH_USER_CLOSE},
97    {"no_user_move", 0, 1, 1, EWIN_OP_INH_USER_MOVE},
98    {"no_user_size", 0, 1, 1, EWIN_OP_INH_USER_SIZE},
99    {"no_wm_focus", 0, 1, 1, EWIN_OP_INH_WM_FOCUS},
100 
101    {"fade", 0, 1, 1, EWIN_OP_FADE},
102    {"no_redir", 4, 1, 1, EWIN_OP_NO_REDIRECT},
103    {"no_argb", 0, 1, 1, EWIN_OP_NO_ARGB},
104 
105    {NULL, 0, 0, 0, EWIN_OP_INVALID}	/* Terminator */
106 };
107 
108 const WinOp        *
EwinOpFind(const char * op)109 EwinOpFind(const char *op)
110 {
111    const WinOp        *wop;
112 
113    wop = winops;
114    for (; wop->name; wop++)
115      {
116 	if (wop->len)
117 	  {
118 	     if (!strncmp(op, wop->name, wop->len))
119 		return wop;
120 	  }
121 	else
122 	  {
123 	     if (!strcmp(op, wop->name))
124 		return wop;
125 	  }
126      }
127 
128    return NULL;
129 }
130 
131 static void
EwinDetermineArea(EWin * ewin)132 EwinDetermineArea(EWin * ewin)
133 {
134    Desk               *dsk;
135    int                 ax, ay;
136 
137    dsk = EoGetDesk(ewin);
138    ewin->vx = dsk->current_area_x * EoGetW(dsk) + EoGetX(ewin);
139    ewin->vy = dsk->current_area_y * EoGetH(dsk) + EoGetY(ewin);
140 
141    if (EwinIsOnScreen(ewin))
142      {
143 	ax = dsk->current_area_x;
144 	ay = dsk->current_area_y;
145      }
146    else
147      {
148 	ax = (ewin->vx + EoGetW(ewin) / 2) / EoGetW(dsk);
149 	ay = (ewin->vy + EoGetH(ewin) / 2) / EoGetH(dsk);
150 	DesksFixArea(&ax, &ay);
151      }
152 
153    if (ax != ewin->area_x || ay != ewin->area_y)
154      {
155 	ewin->area_x = ax;
156 	ewin->area_y = ay;
157 	HintsSetWindowArea(ewin);
158      }
159 }
160 
161 #define MRF_DESK	(1<<0)
162 #define MRF_MOVE	(1<<1)
163 #define MRF_RESIZE	(1<<2)
164 #define MRF_RAISE	(1<<3)
165 #define MRF_FLOAT	(1<<4)
166 #define MRF_UNFLOAT	(1<<5)
167 
168 static void
doEwinMoveResize(EWin * ewin,Desk * dsk,int x,int y,int w,int h,int flags)169 doEwinMoveResize(EWin * ewin, Desk * dsk, int x, int y, int w, int h, int flags)
170 {
171    static int          call_depth = 0;
172    int                 dx, dy, sw, sh, xo, yo;
173    char                move, resize, reparent, raise, floating, configure;
174    EWin              **lst;
175    int                 i, num;
176    Desk               *pdesk;
177    const EImageBorder *pad;
178 
179    if (ewin->state.zoomed)
180       return;
181 
182    if (call_depth > 256)
183       return;
184    call_depth++;
185 
186    if (EDebug(EDBUG_TYPE_MOVERESIZE))
187       Eprintf("%s(%d,%d) %#x f=%x d=%d %d+%d %d*%d %s\n", __func__,
188 	      call_depth, Mode.mode, EwinGetClientXwin(ewin), flags,
189 	      (dsk) ? (int)dsk->num : -1, x, y, w, h, EwinGetTitle(ewin));
190 
191    pdesk = (ewin->o.stacked >= 0) ? EoGetDesk(ewin) : NULL;
192    reparent = move = resize = raise = 0;
193    floating = EoIsFloating(ewin);
194 
195    pad = BorderGetSize(ewin->border);
196 
197    if (flags & MRF_FLOAT)
198      {
199 	if (floating == 0)
200 	  {
201 	     dsk = (!pdesk) ? EoGetDesk(ewin) : pdesk;
202 	     floating = 1;
203 	  }
204 	else if (floating == 1)
205 	  {
206 	     dsk = DeskGet(0);
207 	     floating = 2;
208 	  }
209 	flags |= MRF_RAISE;
210      }
211    else if (flags & MRF_UNFLOAT)
212      {
213 	floating = 0;
214 	flags |= MRF_RAISE;
215      }
216    else if (EoIsSticky(ewin) && !floating)
217      {
218 	dsk = DesksGetCurrent();
219      }
220    else if (!dsk)
221      {
222 	dsk = EoGetDesk(ewin);
223      }
224 
225    if (dsk != pdesk)
226      {
227 	reparent = 1;
228 	flags |= MRF_DESK;
229      }
230 
231    if (Mode.mode == MODE_NONE && !(flags & MRF_NOCHECK_ONSCREEN))
232      {
233 	/* Don't throw windows offscreen */
234 	sw = WinGetW(VROOT);
235 	sh = WinGetH(VROOT);
236 	if (EoIsSticky(ewin))
237 	  {
238 	     xo = yo = 0;
239 	  }
240 	else
241 	  {
242 	     int                 ax, ay;
243 
244 	     DeskGetArea(dsk, &ax, &ay);
245 	     xo = -ax * sw;
246 	     yo = -ay * sh;
247 	     sw *= Conf.desks.areas_nx;
248 	     sh *= Conf.desks.areas_ny;
249 	  }
250 
251 	/* Keep at least 8 pixels (or window dimension) on-screen */
252 	dx = 8;
253 	if (dx > EoGetW(ewin))
254 	   dx = EoGetW(ewin);
255 	dy = 8;
256 	if (dy > EoGetH(ewin))
257 	   dy = EoGetH(ewin);
258 
259 	if (x < xo - EoGetW(ewin) + dx)
260 	   x = xo - EoGetW(ewin) + dx;
261 	else if (x > xo + sw - dx)
262 	   x = xo + sw - dx;
263 	if (y < yo - EoGetH(ewin) + dy)
264 	   y = yo - EoGetH(ewin) + dy;
265 	else if (y > yo + sh - dy)
266 	   y = yo + sh - dy;
267      }
268 
269    if (flags & MRF_RAISE)
270       raise = 1;
271 
272    if (!(flags & MRF_MOVE))
273      {
274 	x = EoGetX(ewin);
275 	y = EoGetY(ewin);
276      }
277 
278    if (!(flags & MRF_RESIZE))
279      {
280 	w = EoGetW(ewin);
281 	h = EoGetH(ewin);
282      }
283    else
284      {
285 	if (w > 32000)
286 	   w = 32000;
287 	if (h > 32000)
288 	   h = 32000;
289 	if (ewin->ops && ewin->ops->Layout)
290 	  {
291 	     ewin->ops->Layout(ewin, &x, &y, &w, &h);
292 	  }
293 	else
294 	  {
295 	     ICCCM_SizeMatch(ewin, w, h, &w, &h);
296 	  }
297 	if (w <= 0)
298 	   w = 1;
299 	if (h <= 0)
300 	   h = 1;
301 
302 	if ((w != ewin->client.w) || (h != ewin->client.h))
303 	   resize = 2;
304 	ewin->client.w = w;
305 	ewin->client.h = h;
306 
307 	if (ewin->state.shaded)
308 	  {
309 	     w = EoGetW(ewin);
310 	     h = EoGetH(ewin);
311 	     if (resize)
312 		EwinBorderMinShadeSize(ewin, &w, &h);
313 	  }
314 	else
315 	  {
316 	     w = ewin->client.w + pad->left + pad->right;
317 	     h = ewin->client.h + pad->top + pad->bottom;
318 	  }
319      }
320 
321    dx = x - EoGetX(ewin);
322    dy = y - EoGetY(ewin);
323    if ((dx != 0) || (dy != 0))
324       move = 1;
325    ewin->client.x = x + pad->left;
326    ewin->client.y = y + pad->top;
327 
328 #if 0
329    Eprintf("repa=%d float=%d raise=%d move=%d resz=%d\n",
330 	   reparent, floating, raise, move, resize);
331 #endif
332    if (EoIsShown(ewin) && (move || reparent))
333       ModulesSignal(ESIGNAL_EWIN_CHANGE, ewin);
334 
335    if (reparent)
336      {
337 	EoReparent(ewin, EoObj(dsk), x, y);
338 	if (flags & MRF_RESIZE)
339 	   EoResize(ewin, w, h);
340      }
341    else
342      {
343 	EoMoveResize(ewin, x, y, w, h);
344      }
345 
346    configure = 0;
347    if (Mode.mode == MODE_NONE || resize || Conf.movres.update_while_moving)
348      {
349 	configure = 1;
350 #if USE_XSYNC
351 	if (Conf.movres.enable_sync_request)
352 	   EwinSyncRequestSend(ewin);
353 #endif
354      }
355 
356    if (flags & MRF_RESIZE)
357      {
358 	if (!ewin->state.shaded)
359 	   EMoveResizeWindow(EwinGetClientConWin(ewin), pad->left, pad->top,
360 			     ewin->client.w, ewin->client.h);
361 #if USE_CONTAINER_WIN
362 	EMoveResizeWindow(EwinGetClientWin(ewin), 0, 0, ewin->client.w,
363 			  ewin->client.h);
364 #endif
365 	EwinBorderCalcSizes(ewin, 0);
366 	if (resize && ewin->state.shaped)
367 	   ewin->update.shape = 1;
368      }
369 
370    EwinPropagateShapes(ewin);
371 
372    /* Clear maximized state on move or resize */
373    if ((move || resize) && !(flags & MRF_KEEP_MAXIMIZED))
374      {
375 	if (ewin->state.maximized_horz || ewin->state.maximized_vert)
376 	  {
377 	     ewin->state.maximized_horz = 0;
378 	     ewin->state.maximized_vert = 0;
379 	     HintsSetWindowState(ewin);
380 	  }
381      }
382 
383    if (raise)
384      {
385 	EoSetFloating(ewin, floating);
386 	EwinRaise(ewin);
387      }
388 
389    if (Mode.mode == MODE_NONE || Conf.movres.update_while_moving)
390       ICCCM_Configure(ewin);
391 
392    if (configure)
393      {
394 #if USE_XSYNC
395 	if (Conf.movres.enable_sync_request)
396 	   EwinSyncRequestWait(ewin);
397 #endif
398      }
399 
400    if (flags & (MRF_DESK | MRF_MOVE | MRF_FLOAT | MRF_UNFLOAT))
401      {
402 	lst = EwinListTransients(ewin, &num, 0);
403 	for (i = 0; i < num; i++)
404 	   doEwinMoveResize(lst[i], dsk, EoGetX(lst[i]) + dx,
405 			    EoGetY(lst[i]) + dy, 0, 0,
406 			    flags & (MRF_DESK | MRF_MOVE |
407 				     MRF_FLOAT | MRF_UNFLOAT |
408 				     MRF_NOCHECK_ONSCREEN |
409 				     MRF_KEEP_MAXIMIZED));
410 	Efree(lst);
411      }
412 
413    EwinDetermineArea(ewin);
414    if (Mode.op_source == OPSRC_USER)
415       EwinSetPlacementGravity(ewin, x, y);
416 
417    if ((flags & (MRF_DESK | MRF_MOVE | MRF_RESIZE)) &&
418        ewin->ops && ewin->ops->MoveResize)
419       ewin->ops->MoveResize(ewin, resize);
420 
421    if (Mode.mode == MODE_NONE)
422      {
423 	SnapshotEwinUpdate(ewin, SNAP_USE_POS | SNAP_USE_SIZE);
424 
425 	if (EoIsShown(ewin))
426 	   ModulesSignal(ESIGNAL_EWIN_CHANGE, ewin);
427      }
428 
429    if (dsk != pdesk)
430      {
431 	HintsSetWindowDesktop(ewin);
432 	SnapshotEwinUpdate(ewin, SNAP_USE_DESK);
433      }
434 
435    call_depth--;
436 }
437 
438 void
EwinMove(EWin * ewin,int x,int y,int flags)439 EwinMove(EWin * ewin, int x, int y, int flags)
440 {
441    doEwinMoveResize(ewin, NULL, x, y, 0, 0,
442 		    MRF_MOVE | (flags & MRF_NOCHECK_ONSCREEN));
443 }
444 
445 void
EwinResize(EWin * ewin,int w,int h,int flags)446 EwinResize(EWin * ewin, int w, int h, int flags)
447 {
448    doEwinMoveResize(ewin, NULL, 0, 0, w, h,
449 		    MRF_RESIZE | (flags & MRF_KEEP_MAXIMIZED));
450 }
451 
452 void
EwinMoveResize(EWin * ewin,int x,int y,int w,int h,int flags)453 EwinMoveResize(EWin * ewin, int x, int y, int w, int h, int flags)
454 {
455    doEwinMoveResize(ewin, NULL, x, y, w, h,
456 		    MRF_MOVE | MRF_RESIZE |
457 		    (flags & (MRF_NOCHECK_ONSCREEN | MRF_KEEP_MAXIMIZED)));
458 }
459 
460 void
EwinMoveResizeWithGravity(EWin * ewin,int x,int y,int w,int h,int grav)461 EwinMoveResizeWithGravity(EWin * ewin, int x, int y, int w, int h, int grav)
462 {
463    EwinGetPosition(ewin, x, y, grav, &x, &y);
464    doEwinMoveResize(ewin, NULL, x, y, w, h, MRF_MOVE | MRF_RESIZE);
465 }
466 
467 void
EwinMoveToDesktop(EWin * ewin,Desk * dsk)468 EwinMoveToDesktop(EWin * ewin, Desk * dsk)
469 {
470    doEwinMoveResize(ewin, dsk, 0, 0, 0, 0, MRF_DESK);
471 }
472 
473 void
EwinMoveToDesktopAt(EWin * ewin,Desk * dsk,int x,int y)474 EwinMoveToDesktopAt(EWin * ewin, Desk * dsk, int x, int y)
475 {
476    doEwinMoveResize(ewin, dsk, x, y, 0, 0, MRF_DESK | MRF_MOVE);
477 }
478 
479 void
EwinOpMove(EWin * ewin,int source,int x,int y)480 EwinOpMove(EWin * ewin, int source, int x, int y)
481 {
482    Mode.op_source = source;
483    EwinMove(ewin, x, y, 0);
484    Mode.op_source = OPSRC_NA;
485 }
486 
487 void
EwinOpResize(EWin * ewin,int source,int w,int h)488 EwinOpResize(EWin * ewin, int source, int w, int h)
489 {
490    Mode.op_source = source;
491    EwinResize(ewin, w, h, 0);
492    Mode.op_source = OPSRC_NA;
493 }
494 
495 void
EwinOpMoveResize(EWin * ewin,int source,int x,int y,int w,int h)496 EwinOpMoveResize(EWin * ewin, int source, int x, int y, int w, int h)
497 {
498    Mode.op_source = source;
499    EwinMoveResize(ewin, x, y, w, h, 0);
500    Mode.op_source = OPSRC_NA;
501 }
502 
503 void
EwinOpMoveToDesktopAt(EWin * ewin,int source,Desk * dsk,int x,int y)504 EwinOpMoveToDesktopAt(EWin * ewin, int source, Desk * dsk, int x, int y)
505 {
506    Mode.op_source = source;
507    EwinMoveToDesktopAt(ewin, dsk, x, y);
508    Mode.op_source = OPSRC_NA;
509 }
510 
511 void
EwinOpFloatAt(EWin * ewin,int source,int x,int y)512 EwinOpFloatAt(EWin * ewin, int source, int x, int y)
513 {
514    Mode.op_source = source;
515    doEwinMoveResize(ewin, EoGetDesk(ewin), x, y, 0, 0, MRF_MOVE | MRF_FLOAT);
516    Mode.op_source = OPSRC_NA;
517 }
518 
519 void
EwinOpUnfloatAt(EWin * ewin,int source,Desk * dsk,int x,int y)520 EwinOpUnfloatAt(EWin * ewin, int source, Desk * dsk, int x, int y)
521 {
522    Mode.op_source = source;
523    doEwinMoveResize(ewin, dsk, x, y, 0, 0, MRF_MOVE | MRF_UNFLOAT);
524    Mode.op_source = OPSRC_NA;
525 }
526 
527 void
EwinIconify(EWin * ewin)528 EwinIconify(EWin * ewin)
529 {
530    static int          call_depth = 0;
531    EWin              **lst, *e;
532    int                 i, num;
533 
534    if (!ewin)
535       return;
536 
537    if (ewin->state.inhibit_iconify)
538       return;
539 
540    if (ewin->state.state != EWIN_STATE_MAPPED)
541       return;
542 
543    if (call_depth > 256)
544       return;
545    call_depth++;
546 
547    Zoom(ewin, 0);
548 
549    /* Save position at which the window was iconified */
550    EwinRememberPositionSet(ewin);
551 
552    if (!EwinIsTransient(ewin))
553       ModulesSignal(ESIGNAL_EWIN_ICONIFY, ewin);
554 
555    ewin->state.iconified = 1;
556    EwinHide(ewin);
557 
558    ICCCM_Iconify(ewin);
559 
560    lst = EwinListTransients(ewin, &num, 0);
561    for (i = 0; i < num; i++)
562      {
563 	e = lst[i];
564 	if (e->state.iconified)
565 	   continue;
566 
567 	EwinIconify(e);
568      }
569 #if ENABLE_GNOME
570    if (lst && call_depth == 1)
571       GNOME_SetClientList();
572 #endif
573    Efree(lst);
574 
575    EwinStateUpdate(ewin);
576    HintsSetWindowState(ewin);
577 
578    call_depth--;
579 }
580 
581 void
EwinAlone(EWin * ewin)582 EwinAlone(EWin * ewin)
583 {
584    EWin               *const *lst, *item;
585    int                 i, num;
586 
587    lst = EwinListGetForDesk(&num, EoGetDesk(ewin));
588 
589    for (i = 0; i < num; i++)
590      {
591 	item = lst[i];
592 
593 	if (item == ewin || EwinIsTransient(item) ||
594 	    item->state.iconified || item->state.donthide ||
595 	    item->area_x != ewin->area_x || item->area_y != ewin->area_y)
596 	   continue;
597 	EwinIconify(item);
598      }
599 }
600 
601 static void
GetOnScreenPos(int x,int y,int w,int h,int * px,int * py)602 GetOnScreenPos(int x, int y, int w, int h, int *px, int *py)
603 {
604    int                 dx, dy;
605 
606    if (x + w > 4 && x <= WinGetW(VROOT) - 4 &&
607        y + h > 4 && y <= WinGetH(VROOT) - 4)
608       goto done;
609 
610    dx = w / 2;
611    dy = h / 2;
612    x = (x + dx) % WinGetW(VROOT);
613    if (x < 0)
614       x += WinGetW(VROOT);
615    x -= dx;
616    y = (y + dy) % WinGetH(VROOT);
617    if (y < 0)
618       y += WinGetH(VROOT);
619    y -= dy;
620 
621  done:
622    *px = x;
623    *py = y;
624 }
625 
626 static void
EwinDeIconify1(EWin * ewin,int dx,int dy)627 EwinDeIconify1(EWin * ewin, int dx, int dy)
628 {
629    static int          call_depth = 0;
630    EWin              **lst, *e;
631    int                 i, num;
632    int                 x, y;
633 
634    if (call_depth > 256)
635       return;
636    call_depth++;
637 
638    if (ewin->state.state != EWIN_STATE_ICONIC || !ewin->state.iconified)
639       return;
640 
641    EwinRememberPositionGet(ewin, DesksGetCurrent(), &x, &y);
642 
643    EwinMoveToDesktopAt(ewin, DesksGetCurrent(), x + dx, y + dy);
644 
645    if (!EwinIsTransient(ewin))
646       ModulesSignal(ESIGNAL_EWIN_DEICONIFY, ewin);
647 
648    ewin->state.iconified = 0;
649    ewin->state.showingdesk = 0;
650 
651    EwinRaise(ewin);
652    EwinShow(ewin);
653    ICCCM_DeIconify(ewin);
654 
655    lst = EwinListTransients(ewin, &num, 0);
656    for (i = 0; i < num; i++)
657      {
658 	e = lst[i];
659 	if (!e->state.iconified)
660 	   continue;
661 
662 	EwinDeIconify1(e, dx, dy);
663      }
664 #if ENABLE_GNOME
665    if (lst && call_depth == 1)
666       GNOME_SetClientList();
667 #endif
668    Efree(lst);
669 
670    EwinStateUpdate(ewin);
671    HintsSetWindowState(ewin);
672 
673    call_depth--;
674 }
675 
676 void
EwinDeIconify(EWin * ewin)677 EwinDeIconify(EWin * ewin)
678 {
679    int                 x, y, ox, oy, dx, dy;
680 
681    EwinRememberPositionGet(ewin, DesksGetCurrent(), &x, &y);
682    ox = x;
683    oy = y;
684 
685    /* If we iconified an offscreen window, get it back on screen */
686    if (!ewin->state.showingdesk)
687       GetOnScreenPos(x, y, EoGetW(ewin), EoGetH(ewin), &x, &y);
688 
689    dx = x - ox;
690    dy = y - oy;
691 
692    EwinDeIconify1(ewin, dx, dy);
693 }
694 
695 static void
EwinUnStick(EWin * ewin)696 EwinUnStick(EWin * ewin)
697 {
698    if (!EoIsSticky(ewin))
699       return;
700 
701    SoundPlay(SOUND_WINDOW_UNSTICK);
702 
703    EoSetSticky(ewin, 0);
704    EwinMoveToDesktopAt(ewin, DesksGetCurrent(), EoGetX(ewin), EoGetY(ewin));
705    EwinBorderUpdateState(ewin);
706    EwinStateUpdate(ewin);
707    HintsSetWindowState(ewin);
708    HintsSetWindowDesktop(ewin);
709    SnapshotEwinUpdate(ewin, SNAP_USE_STICKY);
710 }
711 
712 static void
EwinStick(EWin * ewin)713 EwinStick(EWin * ewin)
714 {
715    int                 x, y, dx, dy;
716 
717    if (EoIsSticky(ewin))
718       return;
719 
720    SoundPlay(SOUND_WINDOW_STICK);
721 
722    /* Avoid "losing" windows made sticky while not in the current viewport */
723    dx = EoGetW(ewin) / 2;
724    dy = EoGetH(ewin) / 2;
725    x = (EoGetX(ewin) + dx) % WinGetW(VROOT);
726    if (x < 0)
727       x += WinGetW(VROOT);
728    x -= dx;
729    y = (EoGetY(ewin) + dy) % WinGetH(VROOT);
730    if (y < 0)
731       y += WinGetH(VROOT);
732    y -= dy;
733 
734    EoSetSticky(ewin, 1);
735    EwinMoveToDesktopAt(ewin, DesksGetCurrent(), x, y);
736    EwinBorderUpdateState(ewin);
737    EwinStateUpdate(ewin);
738    HintsSetWindowState(ewin);
739    HintsSetWindowDesktop(ewin);
740    SnapshotEwinUpdate(ewin, SNAP_USE_STICKY);
741 }
742 
743 #define SHADE_LEFT	0	/* __LEFT */
744 #define SHADE_RIGHT	1	/* __RIGHT */
745 #define SHADE_UP	2	/* __UP/__TOP */
746 #define SHADE_DOWN	3	/* __DOWN/__BOTTOM */
747 
748 void
EwinInstantShade(EWin * ewin,int force)749 EwinInstantShade(EWin * ewin, int force)
750 {
751    int                 x, y, w, h;
752    int                 minw, minh;
753 
754    if (ewin->state.inhibit_shade)
755       return;
756    if (ewin->state.shaded && !force)
757       return;
758 
759    x = EoGetX(ewin);
760    y = EoGetY(ewin);
761    w = EoGetW(ewin);
762    h = EoGetH(ewin);
763 
764    EwinBorderMinShadeSize(ewin, &minw, &minh);
765 
766    switch (BorderGetShadedir(ewin->border))
767      {
768      default:
769      case SHADE_LEFT:
770 	w = minw;
771 	break;
772      case SHADE_RIGHT:
773 	if (!force)
774 	   x = x + w - minw;
775 	w = minw;
776 	break;
777      case SHADE_UP:
778 	h = minh;
779 	break;
780      case SHADE_DOWN:
781 	if (!force)
782 	   y = y + h - minh;
783 	h = minh;
784 	break;
785      }
786 
787    ewin->state.shaded = 2;
788    EoMoveResize(ewin, x, y, w, h);
789 #if USE_CONTAINER_WIN
790    EMoveResizeWindow(ewin->win_container, -30, -30, 1, 1);
791 #else
792    EMoveResizeWindow(EwinGetClientWin(ewin), 100, 100,
793 		     ewin->client.w, ewin->client.h);
794 #endif
795    EwinMoveResize(ewin, x, y, ewin->client.w, ewin->client.h,
796 		  MRF_KEEP_MAXIMIZED);
797 }
798 
799 void
EwinInstantUnShade(EWin * ewin)800 EwinInstantUnShade(EWin * ewin)
801 {
802    XSetWindowAttributes att;
803    int                 x, y, w, h;
804    const EImageBorder *pad;
805 
806    if (ewin->state.zoomed)
807       return;
808    if (!ewin->state.shaded)
809       return;
810 
811    x = EoGetX(ewin);
812    y = EoGetY(ewin);
813 
814    pad = BorderGetSize(ewin->border);
815 
816    switch (BorderGetShadedir(ewin->border))
817      {
818      default:
819 	break;
820      case SHADE_RIGHT:
821 	w = ewin->client.w + pad->left + pad->right;
822 	x = x + EoGetW(ewin) - w;
823 	break;
824      case SHADE_DOWN:
825 	h = ewin->client.h + pad->top + pad->bottom;
826 	y = y + EoGetH(ewin) - h;
827 	break;
828      }
829 
830    /* Reset gravity */
831    att.win_gravity = NorthWestGravity;
832    EChangeWindowAttributes(EwinGetClientWin(ewin), CWWinGravity, &att);
833    /* Force move after gravity change */
834    EWindowSetGeometry(EwinGetClientWin(ewin), -1, -1,
835 		      ewin->client.w, ewin->client.h, 0);
836 
837    ewin->state.shaded = 0;
838    EwinMoveResize(ewin, x, y, ewin->client.w, ewin->client.h,
839 		  MRF_KEEP_MAXIMIZED);
840 }
841 
842 #if USE_CONTAINER_WIN
843 #define _EWIN_ADJUST_SHAPE(ewin, xo, yo) \
844   do { \
845     EShapeSetShape(ewin->win_container, xo, yo, EwinGetClientWin(ewin)); \
846     ewin->update.shape = 1; \
847   } while (0)
848 #else
849 #define _EWIN_ADJUST_SHAPE(ewin, xo, yo) \
850   do { \
851     EShapeUpdate(EwinGetClientWin(ewin)); \
852     ewin->update.shape = 1; \
853   } while (0)
854 #endif
855 
856 typedef struct {
857    int                 x, y, w, h;
858 } _xywh;
859 
860 typedef struct {
861    EWin               *ewin;
862    char                doshade;
863    _xywh               start;
864    _xywh               final;
865    int                 a, b, c;
866 } _ewin_shade_data;
867 
868 static void
_EwinShadeStart(_ewin_shade_data * esd)869 _EwinShadeStart(_ewin_shade_data * esd)
870 {
871    EWin               *ewin = esd->ewin;
872    int                 minw, minh;
873    XSetWindowAttributes att;
874    const EImageBorder *pad;
875 
876    esd->start.x = EoGetX(ewin);
877    esd->start.y = EoGetY(ewin);
878    esd->start.w = EoGetW(ewin);
879    esd->start.h = EoGetH(ewin);
880    esd->final = esd->start;
881    esd->doshade = 1;
882 
883    ewin->state.shading = 1;
884 
885    EwinBorderMinShadeSize(ewin, &minw, &minh);
886 
887    pad = BorderGetSize(ewin->border);
888 
889    switch (BorderGetShadedir(ewin->border))
890      {
891      default:
892      case SHADE_LEFT:
893 	att.win_gravity = EastGravity;
894 	esd->a = esd->start.w;
895 	esd->b = pad->left + pad->right;
896 	esd->final.w = minw;
897 	break;
898      case SHADE_RIGHT:
899 	att.win_gravity = WestGravity;
900 	esd->a = esd->start.w;
901 	esd->b = pad->left + pad->right;
902 	esd->c = esd->start.x + esd->start.w;
903 	esd->final.x = esd->c - minw;
904 	esd->final.w = minw;
905 	break;
906      case SHADE_UP:
907 	att.win_gravity = SouthGravity;
908 	esd->a = esd->start.h;
909 	esd->b = pad->top + pad->bottom;
910 	esd->final.h = minh;
911 	break;
912      case SHADE_DOWN:
913 	att.win_gravity = SouthGravity;
914 	esd->a = esd->start.h;
915 	esd->b = pad->top + pad->bottom;
916 	esd->c = esd->start.y + esd->start.h;
917 	esd->final.y = esd->c - minh;
918 	esd->final.h = minh;
919 	break;
920      }
921 
922    EChangeWindowAttributes(EwinGetClientWin(ewin), CWWinGravity, &att);
923 }
924 
925 static void
_EwinShadeEnd(_ewin_shade_data * esd)926 _EwinShadeEnd(_ewin_shade_data * esd)
927 {
928    EWin               *ewin = esd->ewin;
929 
930    EoMoveResize(ewin, esd->final.x, esd->final.y, esd->final.w, esd->final.h);
931    ewin->state.shaded = 2;
932 #if USE_CONTAINER_WIN
933    EMoveResizeWindow(ewin->win_container, -30, -30, 1, 1);
934 #else
935    EMoveResizeWindow(EwinGetClientWin(ewin), 100, 100,
936 		     ewin->client.w, ewin->client.h);
937    ewin->update.shape = 1;
938 #endif
939    if (ewin->state.shaped)
940       _EWIN_ADJUST_SHAPE(ewin, 0, 0);
941 
942    EwinMoveResize(ewin, EoGetX(ewin), EoGetY(ewin),
943 		  ewin->client.w, ewin->client.h, MRF_KEEP_MAXIMIZED);
944 
945    ewin->state.shading = 0;
946 
947    EwinStateUpdate(ewin);
948    HintsSetWindowState(ewin);
949    SnapshotEwinUpdate(ewin, SNAP_USE_SHADED);
950 }
951 
952 static int          _EwinShadeRun(EObj * eobj, int remaining, void *state);
953 
954 void
EwinShade(EWin * ewin)955 EwinShade(EWin * ewin)
956 {
957    Animator           *an;
958    _ewin_shade_data    esd;
959    int                 duration;
960 
961    if (ewin->state.inhibit_shade)
962       return;
963    if (ewin->state.shaded || ewin->state.shading)
964       return;
965 
966    esd.ewin = ewin;
967    _EwinShadeStart(&esd);
968    if ((Conf.shading.animate) || (ewin->type == EWIN_TYPE_MENU))
969      {
970 	if (Conf.shading.speed < SPEED_MIN)
971 	   Conf.shading.speed = SPEED_MIN;
972 	duration = 1000000 / Conf.shading.speed;
973 	an = AnimatorAdd(&ewin->o, ANIM_SHADE, _EwinShadeRun, duration, 0,
974 			 sizeof(esd), &esd);
975 	AnimatorSetSound(an, SOUND_SHADE, SOUND_NONE);
976      }
977    else
978       _EwinShadeEnd(&esd);
979 }
980 
981 static void
_EwinUnshadeStart(_ewin_shade_data * esd)982 _EwinUnshadeStart(_ewin_shade_data * esd)
983 {
984    EWin               *ewin = esd->ewin;
985    int cow             __UNUSED__, coh __UNUSED__, clx, cly;
986    XSetWindowAttributes att;
987    const EImageBorder *pad;
988 
989    esd->start.x = EoGetX(ewin);
990    esd->start.y = EoGetY(ewin);
991    esd->start.w = EoGetW(ewin);
992    esd->start.h = EoGetH(ewin);
993    esd->final = esd->start;
994    esd->doshade = 0;
995 
996    ewin->state.shading = 1;
997    ewin->state.shaded = 0;
998 
999    cow = ewin->client.w;
1000    coh = ewin->client.h;
1001 
1002    clx = cly = 0;
1003 
1004    pad = BorderGetSize(ewin->border);
1005 
1006    switch (BorderGetShadedir(ewin->border))
1007      {
1008      default:
1009      case SHADE_LEFT:
1010 	att.win_gravity = NorthEastGravity;
1011 	esd->a = pad->left + pad->right;
1012 	esd->b = ewin->client.w + esd->a;
1013 	esd->c = 0;		/* Not used */
1014 	esd->final.w = esd->b;
1015 	cow = 1;
1016 	clx = -ewin->client.w;
1017 	break;
1018      case SHADE_RIGHT:
1019 	att.win_gravity = NorthWestGravity;
1020 	esd->a = pad->left + pad->right;
1021 	esd->b = ewin->client.w + esd->a;
1022 	esd->c = esd->start.x + esd->start.w;	/* NB! w != a is possible */
1023 	esd->final.x = esd->c - esd->b;
1024 	esd->final.w = esd->b;
1025 	cow = 1;
1026 	break;
1027      case SHADE_UP:
1028 	att.win_gravity = SouthEastGravity;
1029 	esd->a = pad->top + pad->bottom;
1030 	esd->b = ewin->client.h + esd->a;
1031 	esd->c = 0;		/* Not used */
1032 	esd->final.h = esd->b;
1033 	coh = 1;
1034 	cly = 1 - ewin->client.h;
1035 	break;
1036      case SHADE_DOWN:
1037 	att.win_gravity = SouthEastGravity;
1038 	esd->a = pad->top + pad->bottom;
1039 	esd->b = ewin->client.h + esd->a;
1040 	esd->c = esd->start.y + esd->start.h;	/* NB! h != a is possible */
1041 	esd->final.y = esd->c - esd->b;
1042 	esd->final.h = esd->b;
1043 	coh = 1;
1044 	cly = 1 - ewin->client.h;
1045 	break;
1046      }
1047 
1048    EChangeWindowAttributes(EwinGetClientWin(ewin), CWWinGravity, &att);
1049    EWindowSync(EwinGetClientWin(ewin));	/* Gravity - recache */
1050 #if USE_CONTAINER_WIN
1051    EMoveResizeWindow(ewin->win_container, pad->left, pad->top, cow, coh);
1052    EMoveResizeWindow(EwinGetClientWin(ewin), clx, cly,
1053 		     ewin->client.w, ewin->client.h);
1054 #else
1055    EMoveResizeWindow(EwinGetClientWin(ewin), pad->left + clx, pad->top + cly,
1056 		     ewin->client.w, ewin->client.h);
1057 #endif
1058 }
1059 
1060 static void
_EwinUnshadeEnd(_ewin_shade_data * esd)1061 _EwinUnshadeEnd(_ewin_shade_data * esd)
1062 {
1063    EWin               *ewin = esd->ewin;
1064    XSetWindowAttributes att;
1065 #if USE_CONTAINER_WIN
1066    const EImageBorder *pad = BorderGetSize(ewin->border);
1067 #endif
1068 
1069    EoMoveResize(ewin, esd->final.x, esd->final.y, esd->final.w, esd->final.h);
1070 
1071    /* Reset gravity */
1072    att.win_gravity = NorthWestGravity;
1073    EChangeWindowAttributes(EwinGetClientWin(ewin), CWWinGravity, &att);
1074 
1075 #if USE_CONTAINER_WIN
1076    EMoveResizeWindow(EwinGetClientWin(ewin), 0, 0,
1077 		     ewin->client.w, ewin->client.h);
1078    EMoveResizeWindow(ewin->win_container, pad->left, pad->top,
1079 		     ewin->client.w, ewin->client.h);
1080 #else
1081    EWindowSync(EwinGetClientWin(ewin));	/* Gravity - recache */
1082 #endif
1083 
1084    if (ewin->state.shaped)
1085       _EWIN_ADJUST_SHAPE(ewin, 0, 0);
1086 
1087    EwinMoveResize(ewin, EoGetX(ewin), EoGetY(ewin),
1088 		  ewin->client.w, ewin->client.h, MRF_KEEP_MAXIMIZED);
1089 
1090    ewin->state.shading = 0;
1091 
1092    EwinStateUpdate(ewin);
1093    HintsSetWindowState(ewin);
1094    SnapshotEwinUpdate(ewin, SNAP_USE_SHADED);
1095 }
1096 
1097 static int
_EwinShadeRun(EObj * eobj,int remaining,void * state)1098 _EwinShadeRun(EObj * eobj, int remaining, void *state)
1099 {
1100    _ewin_shade_data   *esd = (_ewin_shade_data *) state;
1101    EWin               *ewin = (EWin *) eobj;
1102    int                 k, x, y, w, h, ww, hh;
1103    int cow             __UNUSED__, coh __UNUSED__, shx, shy;
1104    const EImageBorder *pad;
1105 
1106    k = 1024 - remaining;
1107 
1108    x = esd->start.x;
1109    y = esd->start.y;
1110    w = esd->start.w;
1111    h = esd->start.h;
1112 
1113    cow = ewin->client.w;
1114    coh = ewin->client.h;
1115 
1116    shx = shy = 0;
1117 
1118    pad = BorderGetSize(ewin->border);
1119 
1120    switch (BorderGetShadedir(ewin->border))
1121      {
1122      default:
1123      case SHADE_LEFT:
1124 	w = ((esd->a * (1024 - k)) + (esd->b * k)) >> 10;
1125 	if (w <= 0)
1126 	   w = 1;
1127 	ww = w - (pad->left + pad->right);
1128 	if (ww <= 0)
1129 	   ww = 1;
1130 	cow = ww;
1131 	shx = ww - ewin->client.w;
1132 	break;
1133      case SHADE_RIGHT:
1134 	w = ((esd->a * (1024 - k)) + (esd->b * k)) >> 10;
1135 	if (w <= 0)
1136 	   w = 1;
1137 	x = esd->c - w;
1138 	ww = w - (pad->left + pad->right);
1139 	if (ww <= 0)
1140 	   ww = 1;
1141 	cow = ww;
1142 	break;
1143      case SHADE_UP:
1144 	h = ((esd->a * (1024 - k)) + (esd->b * k)) >> 10;
1145 	if (h <= 0)
1146 	   h = 1;
1147 	hh = h - (pad->top + pad->bottom);
1148 	if (hh <= 0)
1149 	   hh = 1;
1150 	coh = hh;
1151 	shy = hh - ewin->client.h;
1152 	break;
1153      case SHADE_DOWN:
1154 	h = ((esd->a * (1024 - k)) + (esd->b * k)) >> 10;
1155 	if (h <= 0)
1156 	   h = 1;
1157 	y = esd->c - h;
1158 	hh = h - (pad->top + pad->bottom);
1159 	if (hh <= 0)
1160 	   hh = 1;
1161 	coh = hh;
1162 	shy = hh - ewin->client.h;
1163 	break;
1164      }
1165 #if USE_CONTAINER_WIN
1166    EMoveResizeWindow(ewin->win_container, pad->left, pad->top, cow, coh);
1167 #else
1168    EMoveResizeWindow(EwinGetClientWin(ewin), pad->left + shx, pad->top + shy,
1169 		     ewin->client.w, ewin->client.h);
1170 #endif
1171    if (ewin->state.shaped)
1172       _EWIN_ADJUST_SHAPE(ewin, shx, shy);
1173    EoMoveResize(ewin, x, y, w, h);
1174    EwinBorderCalcSizes(ewin, 1);
1175 
1176    if (remaining == 0)
1177      {
1178 	if (esd->doshade)
1179 	   _EwinShadeEnd(esd);
1180 	else
1181 	   _EwinUnshadeEnd(esd);
1182      }
1183 
1184    return 0;
1185 }
1186 
1187 void
EwinUnShade(EWin * ewin)1188 EwinUnShade(EWin * ewin)
1189 {
1190    Animator           *an;
1191    _ewin_shade_data    esd;
1192    int                 duration;
1193 
1194    if (ewin->state.zoomed)
1195       return;
1196    if (!ewin->state.shaded || ewin->state.shading || ewin->state.iconified)
1197       return;
1198 
1199    esd.ewin = ewin;
1200    _EwinUnshadeStart(&esd);
1201    if ((Conf.shading.animate) || (ewin->type == EWIN_TYPE_MENU))
1202      {
1203 	if (Conf.shading.speed < SPEED_MIN)
1204 	   Conf.shading.speed = SPEED_MIN;
1205 	duration = 1000000 / Conf.shading.speed;
1206 	an = AnimatorAdd(&ewin->o, ANIM_SHADE, _EwinShadeRun, duration, 0,
1207 			 sizeof(esd), &esd);
1208 	AnimatorSetSound(an, SOUND_UNSHADE, SOUND_NONE);
1209      }
1210    else
1211       _EwinUnshadeEnd(&esd);
1212 }
1213 
1214 void
EwinOpFullscreen(EWin * ewin,int source __UNUSED__,int on)1215 EwinOpFullscreen(EWin * ewin, int source __UNUSED__, int on)
1216 {
1217    int                 x, y, w, h, ww, hh;
1218    EWin              **lst;
1219    int                 i, num;
1220    const Border       *b;
1221 
1222    if (ewin->state.fullscreen == (unsigned)on)
1223       return;
1224    if (ewin->state.zoomed)
1225       return;
1226 
1227    if (on)
1228      {
1229 	if (on == 1)
1230 	  {
1231 	     if (ewin->state.inhibit_fullscreeen)
1232 		return;
1233 	     ewin->save_fs.x = EoGetX(ewin);
1234 	     ewin->save_fs.y = EoGetY(ewin);
1235 	     ewin->save_fs.w = ewin->client.w;
1236 	     ewin->save_fs.h = ewin->client.h;
1237 	     ewin->save_fs.layer = EoGetLayer(ewin);
1238 	  }
1239 	if (ewin->state.placed)
1240 	  {
1241 	     x = EoGetX(ewin);
1242 	     y = EoGetY(ewin);
1243 	  }
1244 	else
1245 	  {
1246 	     EventsUpdateXY(&x, &y);
1247 	  }
1248 	ScreenGetAvailableArea(x, y, &x, &y, &w, &h,
1249 			       Conf.place.ignore_struts_fullscreen);
1250 
1251 	ewin->state.fullscreen = 1;
1252 
1253 	/* Fixup if available space doesn't match ICCCM size constraints */
1254 	ICCCM_SizeMatch(ewin, w, h, &ww, &hh);
1255 	if (w == ww && h == hh)
1256 	  {
1257 	     b = BorderFind("BORDERLESS");
1258 	  }
1259 	else
1260 	  {
1261 	     b = BorderCreateFiller(ww, hh, w, h);
1262 	     w = ww;
1263 	     h = hh;
1264 	  }
1265 	EwinBorderSetTo(ewin, b);
1266 
1267 	if (Conf.place.raise_fullscreen)
1268 	  {
1269 	     EoSetLayer(ewin, 8);
1270 	     lst = EwinListTransients(ewin, &num, 0);
1271 	     for (i = 0; i < num; i++)
1272 	       {
1273 		  lst[i]->save_fs.layer = EoGetLayer(lst[i]);
1274 		  EoSetLayer(lst[i], lst[i]->save_fs.layer +
1275 			     EoGetLayer(ewin) - ewin->save_fs.layer);
1276 	       }
1277 	     Efree(lst);
1278 	  }
1279 
1280 	EwinRaise(ewin);
1281 	EwinMoveResize(ewin, x, y, w, h, MRF_KEEP_MAXIMIZED);
1282 	EwinStateUpdate(ewin);
1283      }
1284    else
1285      {
1286 	x = ewin->save_fs.x;
1287 	y = ewin->save_fs.y;
1288 	w = ewin->save_fs.w;
1289 	h = ewin->save_fs.h;
1290 	GetOnScreenPos(x, y, w, h, &x, &y);
1291 	b = ewin->normal_border;
1292 	EwinBorderSetTo(ewin, b);
1293 
1294 	if (Conf.place.raise_fullscreen)
1295 	  {
1296 	     lst = EwinListTransients(ewin, &num, 0);
1297 	     for (i = 0; i < num; i++)
1298 		EoSetLayer(lst[i], lst[i]->save_fs.layer);
1299 	     Efree(lst);
1300 	  }
1301 	EoSetLayer(ewin, ewin->save_fs.layer);
1302 
1303 	ewin->state.fullscreen = 0;
1304 	EwinStateUpdate(ewin);
1305 	EwinRaise(ewin);
1306 	EwinMoveResize(ewin, x, y, w, h, MRF_KEEP_MAXIMIZED);
1307      }
1308 
1309    HintsSetWindowState(ewin);
1310 }
1311 
1312 void
EwinsShowDesktop(int on)1313 EwinsShowDesktop(int on)
1314 {
1315    EWin               *const *lst, *ewin;
1316    int                 i, num;
1317 
1318    lst = EwinListGetForDesk(&num, DesksGetCurrent());
1319 
1320    if (on)
1321      {
1322 	for (i = 0; i < num; i++)
1323 	  {
1324 	     ewin = lst[i];
1325 
1326 	     if (EwinIsTransient(ewin) ||
1327 		 ewin->state.iconified || ewin->state.donthide)
1328 		continue;
1329 
1330 	     ewin->state.showingdesk = 1;
1331 	     EwinIconify(ewin);
1332 	  }
1333      }
1334    else
1335      {
1336 	for (i = num - 1; i >= 0; i--)
1337 	  {
1338 	     ewin = lst[i];
1339 
1340 	     if (!ewin->state.showingdesk)
1341 		continue;
1342 
1343 	     EwinDeIconify(ewin);
1344 	  }
1345      }
1346    Mode.showing_desktop = on;
1347    EWMH_SetShowingDesktop(on);
1348 }
1349 
1350 void
EwinMoveToArea(EWin * ewin,int ax,int ay)1351 EwinMoveToArea(EWin * ewin, int ax, int ay)
1352 {
1353    DesksFixArea(&ax, &ay);
1354    EwinMove(ewin, EoGetX(ewin) + (WinGetW(VROOT) * (ax - ewin->area_x)),
1355 	    EoGetY(ewin) + (WinGetH(VROOT) * (ay - ewin->area_y)), 0);
1356 }
1357 
1358 void
EwinOpActivate(EWin * ewin,int source,int raise)1359 EwinOpActivate(EWin * ewin, int source, int raise)
1360 {
1361    int                 unshade;
1362 
1363    if (source == OPSRC_APP && EwinInhGetApp(ewin, focus))
1364       return;
1365 
1366    unshade = ewin->state.shaded /* && !ewin->state.iconified */ ;
1367 
1368    if (!ewin->state.sliding && !ewin->state.iconified)
1369      {
1370 	/* If somehow lost outside desktop, move it to center */
1371 	if (!EwinIsOnDesktop(ewin))
1372 	   ArrangeEwinCentered(ewin);
1373 	DeskGotoByEwin(ewin, 0);
1374      }
1375    if (raise)
1376       EwinOpRaise(ewin, source);
1377    if (ewin->state.iconified)
1378       EwinOpIconify(ewin, source, 0);
1379    if (unshade)
1380       EwinOpShade(ewin, source, 0);
1381    FocusToEWin(ewin, FOCUS_SET);
1382 }
1383 
1384 void
EwinOpClose(EWin * ewin,int source __UNUSED__)1385 EwinOpClose(EWin * ewin, int source __UNUSED__)
1386 {
1387    EWin              **gwins;
1388    int                 num, i;
1389 
1390    if (!ewin)
1391       return;
1392 
1393    gwins = ListWinGroupMembersForEwin(ewin, GROUP_ACTION_KILL,
1394 				      Mode.nogroup, &num);
1395    for (i = 0; i < num; i++)
1396      {
1397 	ICCCM_Delete(gwins[i]);
1398 	SoundPlay(SOUND_WINDOW_CLOSE);
1399      }
1400    Efree(gwins);
1401 }
1402 
1403 void
EwinOpKill(EWin * ewin,int source __UNUSED__)1404 EwinOpKill(EWin * ewin, int source __UNUSED__)
1405 {
1406    SoundPlay(SOUND_WINDOW_CLOSE);
1407    EwinKill(ewin);
1408 }
1409 
1410 void
EwinOpRaise(EWin * ewin,int source __UNUSED__)1411 EwinOpRaise(EWin * ewin, int source __UNUSED__)
1412 {
1413    EWin              **gwins;
1414    int                 i, num, changed;
1415 
1416    gwins = ListWinGroupMembersForEwin(ewin, GROUP_ACTION_STACKING,
1417 				      Mode.nogroup, &num);
1418    for (i = changed = 0; i < num; i++)
1419       changed += EwinRaise(gwins[i]);
1420    Efree(gwins);
1421    if (changed)
1422       SoundPlay(SOUND_RAISE);
1423 }
1424 
1425 void
EwinOpLower(EWin * ewin,int source __UNUSED__)1426 EwinOpLower(EWin * ewin, int source __UNUSED__)
1427 {
1428    EWin              **gwins;
1429    int                 i, num, changed;
1430 
1431    gwins = ListWinGroupMembersForEwin(ewin, GROUP_ACTION_STACKING,
1432 				      Mode.nogroup, &num);
1433    for (i = changed = 0; i < num; i++)
1434       changed += EwinLower(gwins[i]);
1435    Efree(gwins);
1436    if (changed)
1437       SoundPlay(SOUND_LOWER);
1438 }
1439 
1440 void
EwinOpStick(EWin * ewin,int source __UNUSED__,int on)1441 EwinOpStick(EWin * ewin, int source __UNUSED__, int on)
1442 {
1443    EWin              **gwins;
1444    int                 i, num;
1445 
1446    gwins = ListWinGroupMembersForEwin(ewin, GROUP_ACTION_STICK,
1447 				      Mode.nogroup, &num);
1448    for (i = 0; i < num; i++)
1449      {
1450 	if (on)
1451 	   EwinStick(gwins[i]);
1452 	else
1453 	   EwinUnStick(gwins[i]);
1454      }
1455    Efree(gwins);
1456 }
1457 
1458 void
EwinOpSkipLists(EWin * ewin,int source __UNUSED__,int skip)1459 EwinOpSkipLists(EWin * ewin, int source __UNUSED__, int skip)
1460 {
1461    ewin->props.skip_ext_task = skip;
1462    ewin->props.skip_winlist = skip;
1463    ewin->props.skip_focuslist = skip;
1464 
1465    EwinStateUpdate(ewin);
1466    HintsSetWindowState(ewin);
1467 #if ENABLE_GNOME
1468    GNOME_SetClientList();
1469 #endif
1470    SnapshotEwinUpdate(ewin, SNAP_USE_SKIP_LISTS);
1471 }
1472 
1473 #if 0				/* Unused */
1474 void
1475 EwinOpSkipTask(EWin * ewin, int skip)
1476 {
1477    ewin->props.skip_ext_task = skip;
1478 
1479    EwinStateUpdate(ewin);
1480    HintsSetWindowState(ewin);
1481 #if ENABLE_GNOME
1482    GNOME_SetClientList();
1483 #endif
1484    SnapshotEwinUpdate(ewin, SNAP_USE_SKIP_LISTS);
1485 }
1486 
1487 void
1488 EwinOpSkipFocus(EWin * ewin, int skip)
1489 {
1490    ewin->props.skip_focuslist = skip;
1491    EwinStateUpdate(ewin);
1492    SnapshotEwinUpdate(ewin, SNAP_USE_SKIP_LISTS);
1493 }
1494 
1495 void
1496 EwinOpSkipWinlist(EWin * ewin, int skip)
1497 {
1498    ewin->props.skip_winlist = skip;
1499    EwinStateUpdate(ewin);
1500    SnapshotEwinUpdate(ewin, SNAP_USE_SKIP_LISTS);
1501 }
1502 
1503 void
1504 EwinOpNeverFocus(EWin * ewin, int on)
1505 {
1506    ewin->props.never_focus = on;
1507    EwinStateUpdate(ewin);
1508    SnapshotEwinUpdate(ewin, SNAP_USE_FOCUS_NEVER);
1509 }
1510 #endif
1511 
1512 void
EwinOpIconify(EWin * ewin,int source __UNUSED__,int on)1513 EwinOpIconify(EWin * ewin, int source __UNUSED__, int on)
1514 {
1515    EWin              **gwins;
1516    int                 i, num;
1517 
1518    gwins = ListWinGroupMembersForEwin(ewin, GROUP_ACTION_ICONIFY,
1519 				      Mode.nogroup, &num);
1520    for (i = 0; i < num; i++)
1521      {
1522 	if (on)
1523 	   EwinIconify(gwins[i]);
1524 	else
1525 	   EwinDeIconify(gwins[i]);
1526      }
1527    Efree(gwins);
1528 }
1529 
1530 void
EwinOpShade(EWin * ewin,int source __UNUSED__,int on)1531 EwinOpShade(EWin * ewin, int source __UNUSED__, int on)
1532 {
1533    EWin              **gwins;
1534    int                 i, num;
1535 
1536    gwins = ListWinGroupMembersForEwin(ewin, GROUP_ACTION_SHADE,
1537 				      Mode.nogroup, &num);
1538    for (i = 0; i < num; i++)
1539      {
1540 	if (on)
1541 	   EwinShade(gwins[i]);
1542 	else
1543 	   EwinUnShade(gwins[i]);
1544      }
1545    Efree(gwins);
1546 }
1547 
1548 void
EwinOpSetLayer(EWin * ewin,int source __UNUSED__,int layer)1549 EwinOpSetLayer(EWin * ewin, int source __UNUSED__, int layer)
1550 {
1551    if (EoGetLayer(ewin) > layer)
1552      {
1553 	SoundPlay(SOUND_WINDOW_CHANGE_LAYER_DOWN);
1554      }
1555    else if (EoGetLayer(ewin) < layer)
1556      {
1557 	SoundPlay(SOUND_WINDOW_CHANGE_LAYER_UP);
1558      }
1559    EoSetLayer(ewin, layer);
1560    EwinRaise(ewin);
1561    EwinStateUpdate(ewin);
1562    HintsSetWindowState(ewin);
1563    SnapshotEwinUpdate(ewin, SNAP_USE_LAYER);
1564 }
1565 
1566 void
EwinOpSetBorder(EWin * ewin,int source __UNUSED__,const char * name)1567 EwinOpSetBorder(EWin * ewin, int source __UNUSED__, const char *name)
1568 {
1569    EWin              **gwins;
1570    int                 i, num;
1571    char                has_shaded;
1572    Border             *b;
1573    char                shadechange = 0;
1574 
1575    b = BorderFind(name);
1576    if (!b)
1577       return;
1578 
1579    has_shaded = 0;
1580    gwins = ListWinGroupMembersForEwin(ewin, GROUP_ACTION_SET_WINDOW_BORDER,
1581 				      Mode.nogroup, &num);
1582    for (i = 0; i < num; i++)
1583      {
1584 	if (gwins[i]->state.shaded)
1585 	  {
1586 	     has_shaded = 1;
1587 	     break;
1588 	  }
1589      }
1590    if (has_shaded && !BorderCanShade(b))
1591       return;
1592 
1593    for (i = 0; i < num; i++)
1594      {
1595 	if (b != gwins[i]->border)
1596 	  {
1597 	     SoundPlay(SOUND_WINDOW_BORDER_CHANGE);
1598 	     shadechange = 0;
1599 	     if (gwins[i]->state.shaded)
1600 	       {
1601 		  shadechange = 1;
1602 		  EwinInstantUnShade(gwins[i]);
1603 	       }
1604 	     EwinBorderChange(gwins[i], b, 1);
1605 	     if (shadechange)
1606 		EwinInstantShade(gwins[i], 0);
1607 	  }
1608 	SnapshotEwinUpdate(gwins[i], SNAP_USE_BORDER);
1609      }
1610    Efree(gwins);
1611 }
1612 
1613 void
EwinOpSetOpacity(EWin * ewin,int source __UNUSED__,int opacity)1614 EwinOpSetOpacity(EWin * ewin, int source __UNUSED__, int opacity)
1615 {
1616    unsigned int        op;
1617 
1618    op = OpacityFromPercent(opacity);
1619    ewin->props.opacity = op;
1620    ewin->ewmh.opacity_update = 1;	/* Set opacity on client window */
1621    HintsSetWindowOpacity(ewin);
1622    EwinUpdateOpacity(ewin);
1623    SnapshotEwinUpdate(ewin, SNAP_USE_OPACITY);
1624 }
1625 
1626 void
EwinOpSetFocusedOpacity(EWin * ewin,int source __UNUSED__,int opacity)1627 EwinOpSetFocusedOpacity(EWin * ewin, int source __UNUSED__, int opacity)
1628 {
1629    unsigned int        op;
1630 
1631    op = OpacityFromPercent(opacity);
1632    ewin->props.focused_opacity = op;
1633    EwinUpdateOpacity(ewin);
1634    SnapshotEwinUpdate(ewin, SNAP_USE_OPACITY);
1635 }
1636 
1637 void
EwinOpMoveToDesk(EWin * ewin,int source __UNUSED__,Desk * dsk,int inc)1638 EwinOpMoveToDesk(EWin * ewin, int source __UNUSED__, Desk * dsk, int inc)
1639 {
1640    dsk = DeskGetRelative(dsk, inc);
1641 
1642    EoSetSticky(ewin, 0);
1643    EwinMoveToDesktop(ewin, dsk);
1644    EwinRaise(ewin);
1645    EwinBorderUpdateState(ewin);
1646    EwinStateUpdate(ewin);
1647    HintsSetWindowState(ewin);
1648    HintsSetWindowDesktop(ewin);
1649    SnapshotEwinUpdate(ewin, SNAP_USE_STICKY);
1650 }
1651 
1652 #if 0				/* Unused */
1653 void
1654 EwinMoveToLinearArea(EWin * ewin, int a)
1655 {
1656    int                 ax, ay;
1657 
1658    AreaLinearToXY(a, &ax, &ay);
1659    EwinMoveToArea(ewin, ax, ay);
1660 }
1661 
1662 void
1663 EwinMoveLinearAreaBy(EWin * ewin, int a)
1664 {
1665    EwinMoveToLinearArea(ewin, AreaXYToLinear(ewin->area_x, ewin->area_y) + a);
1666 }
1667 
1668 void
1669 EwinOpMoveToArea(EWin * ewin, int x, int y)
1670 {
1671    EwinMoveToArea(ewin, x, y);
1672    SnapshotEwinUpdate(ewin, SNAP_USE_POS);
1673 }
1674 #endif
1675 
1676 #if 0				/* Not used? */
1677 static int
1678 doMoveWinToLinearArea(EWin * ewin, const char *params)
1679 {
1680    int                 da;
1681 
1682    if (params)
1683      {
1684 	sscanf(params, "%i", &da);
1685 	EwinMoveToLinearArea(ewin, da);
1686      }
1687    SnapshotEwinUpdate(ewin, SNAP_USE_POS);
1688    return 0;
1689 }
1690 
1691 static int
1692 doMoveWinByLinearArea(EWin * ewin, const char *params)
1693 {
1694    EWin               *ewin;
1695    int                 da;
1696 
1697    if (params)
1698      {
1699 	sscanf(params, "%i", &da);
1700 	EwinMoveLinearAreaBy(ewin, da);
1701      }
1702    SnapshotEwinUpdate(ewin, SNAP_USE_POS);
1703    return 0;
1704 }
1705 #endif
1706 
1707 #if 0				/* FIXME - Unused? */
1708 static int
1709 doScrollWindows(EWin * edummy __UNUSED__, const char *params)
1710 {
1711    int                 x, y, num, i;
1712    EWin               *const *lst;
1713 
1714    if (!params)
1715       return 0;
1716 
1717    x = 0;
1718    y = 0;
1719    sscanf(params, "%i %i", &x, &y);
1720 
1721    lst = EwinListGetAll(&num);
1722    for (i = 0; i < num; i++)
1723      {
1724 	if ((lst[i]->desktop == DesksGetCurrent()) && (!lst[i]->sticky) &&
1725 	    (!lst[i]->floating))
1726 	   EwinMove(lst[i], lst[i]->x + x, lst[i]->y + y);
1727      }
1728    return 0;
1729 }
1730 #endif
1731