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