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