2 * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
3 * Copyright (C) 2004-2009 Kim Woelders
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:
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.
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.
36 #include <X11/keysym.h>
46 int win_x, win_y, win_w, win_h;
47 int swapcoord_x, swapcoord_y;
52 _NeedServerGrab(int mode)
57 return !Conf.movres.avoid_server_grab;
62 EwinShapeSet(EWin * ewin)
66 ewin->shape_x = EoGetX(ewin);
67 ewin->shape_y = EoGetY(ewin);
69 if (ewin->state.shaded)
71 EwinBorderGetSize(ewin, &bl, &br, &bt, &bb);
72 ewin->shape_w = EoGetW(ewin) - (bl + br);
73 ewin->shape_h = EoGetH(ewin) - (bt + bb);
77 ewin->shape_w = ewin->client.w;
78 ewin->shape_h = ewin->client.h;
83 ActionMoveStart(EWin * ewin, int kbd, int constrained, int nogroup)
88 if (!ewin || ewin->state.inhibit_move)
92 Mode_mr.using_kbd = kbd;
93 Mode_mr.nogroup = nogroup;
95 EventsGetXY(&cx, &cy);
97 SoundPlay(SOUND_MOVE_START);
100 GrabKeyboardSet(EoGetWin(ewin));
102 GrabPointerSet(EoGetWin(ewin), ECSR_ACT_MOVE, 1);
104 Mode.mode = MODE_MOVE_PENDING;
105 Mode.constrained = constrained;
107 Mode_mr.start_x = Mode_mr.cur_x = cx;
108 Mode_mr.start_y = Mode_mr.cur_y = cy;
110 Mode_mr.win_x = Mode_mr.start_x - (EoGetX(ewin) + EoGetX(EoGetDesk(ewin)));
111 Mode_mr.win_y = Mode_mr.start_y - (EoGetY(ewin) + EoGetY(EoGetDesk(ewin)));
114 gwins = ListWinGroupMembersForEwin(ewin, GROUP_ACTION_MOVE, nogroup
115 || Mode.move.swap, &num);
117 if (Conf.movres.mode_move < 0 || Conf.movres.mode_move > 5)
118 Conf.movres.mode_move = 0;
119 Mode_mr.mode = Conf.movres.mode_move;
120 if (num > 1 && Conf.movres.mode_move == 5)
122 Mode_mr.grab_server = _NeedServerGrab(Mode_mr.mode);
124 for (i = 0; i < num; i++)
126 EwinShapeSet(gwins[i]);
127 EwinOpFloatAt(gwins[i], OPSRC_USER, EoGetX(gwins[i]), EoGetY(gwins[i]));
128 if (Mode_mr.mode == 0)
130 ewin->state.moving = 1;
131 EwinUpdateOpacity(gwins[i]);
136 Mode_mr.swapcoord_x = EoGetX(ewin);
137 Mode_mr.swapcoord_y = EoGetY(ewin);
143 ActionMoveEnd(EWin * ewin)
149 if (ewin && ewin != Mode_mr.ewin)
152 GrabKeyboardRelease();
153 GrabPointerRelease();
155 SoundPlay(SOUND_MOVE_STOP);
161 ewin->state.show_coords = 0;
163 gwins = ListWinGroupMembersForEwin(ewin, GROUP_ACTION_MOVE, Mode_mr.nogroup
164 || Mode.move.swap, &num);
166 if (Mode.mode == MODE_MOVE)
168 for (i = 0; i < num; i++)
169 DrawEwinShape(gwins[i], Mode_mr.mode,
170 gwins[i]->shape_x, gwins[i]->shape_y,
171 gwins[i]->client.w, gwins[i]->client.h, 2, i);
173 Mode.mode = MODE_NONE;
175 d2 = DesktopAt(Mode.events.mx, Mode.events.my);
177 for (i = 0; i < num; i++)
180 d1 = EoGetDesk(ewin);
182 EwinOpUnfloatAt(ewin, OPSRC_USER, d2, ewin->shape_x, ewin->shape_y);
184 EwinOpUnfloatAt(ewin, OPSRC_USER, d2,
185 ewin->shape_x - (EoGetX(d2) - EoGetX(d1)),
186 ewin->shape_y - (EoGetY(d2) - EoGetY(d1)));
187 if (Mode_mr.mode == 0)
189 ewin->state.moving = 0;
190 EwinUpdateOpacity(ewin);
199 Mode.mode = MODE_NONE;
201 Mode.place.doing_manual = 0;
203 if (Mode_mr.grab_server)
207 ModulesSignal(ESIGNAL_ANIMATION_RESUME, NULL);
214 ActionMoveSuspend(void)
223 if (Mode.mode == MODE_MOVE_PENDING)
226 /* If non opaque undraw our boxes */
227 if (Mode_mr.grab_server)
229 lst = ListWinGroupMembersForEwin(ewin, GROUP_ACTION_MOVE,
230 Mode_mr.nogroup, &num);
231 for (i = 0; i < num; i++)
234 DrawEwinShape(ewin, Mode_mr.mode, ewin->shape_x,
235 ewin->shape_y, ewin->client.w, ewin->client.h, 3, i);
246 ActionMoveResume(void)
250 int x, y, fl, dx, dy;
256 GrabPointerSet(EoGetWin(ewin), ECSR_ACT_MOVE, 1);
258 fl = (Mode_mr.mode == 5) ? 4 : 0;
259 if (Mode.mode == MODE_MOVE_PENDING)
261 Mode.mode = MODE_MOVE;
262 fl = 0; /* This is the first time we draw it */
265 if (Mode_mr.grab_server)
269 Mode.events.mx - Mode_mr.win_x - EoGetX(EoGetDesk(ewin)) - ewin->shape_x;
271 Mode.events.my - Mode_mr.win_y - EoGetY(EoGetDesk(ewin)) - ewin->shape_y;
273 /* Redraw any windows that were in "move mode" */
274 lst = ListWinGroupMembersForEwin(ewin, GROUP_ACTION_MOVE,
275 Mode_mr.nogroup, &num);
276 for (i = 0; i < num; i++)
280 if (!EoIsFloating(ewin))
283 x = ewin->shape_x + dx;
284 y = ewin->shape_y + dy;
285 DrawEwinShape(ewin, Mode_mr.mode, x, y,
286 ewin->client.w, ewin->client.h, fl, i);
293 #define RD(h, v) (((h) << 8) + (v))
294 #define RD_H(hv) (((hv) >> 8) & 0xff)
295 #define RD_V(hv) (((hv) ) & 0xff)
298 ActionResizeStart(EWin * ewin, int kbd, int hv)
300 int x, y, w, h, ww, hh, cx, cy;
303 if (!ewin || ewin->state.inhibit_resize)
308 EventsGetXY(&cx, &cy);
310 SoundPlay(SOUND_RESIZE_START);
312 if (Conf.movres.mode_resize < 0 || Conf.movres.mode_resize > 4)
313 Conf.movres.mode_resize = 0;
314 Mode_mr.mode = Conf.movres.mode_resize;
315 Mode_mr.using_kbd = kbd;
316 Mode_mr.grab_server = _NeedServerGrab(Mode_mr.mode);
317 if (Mode_mr.grab_server)
320 ModulesSignal(ESIGNAL_ANIMATION_SUSPEND, NULL);
321 /* Run idlers (stacking, border updates, ...) before drawing lines */
324 if (Mode_mr.mode == 0)
326 ewin->state.resizing = 1;
327 EwinUpdateOpacity(ewin);
337 Mode_mr.resize_detail = 0;
338 csr = ECSR_ACT_RESIZE_BR;
341 x = cx - EoGetX(ewin);
342 y = cy - EoGetY(ewin);
343 w = EoGetW(ewin) >> 1;
344 h = EoGetH(ewin) >> 1;
345 ww = EoGetW(ewin) / 6;
346 hh = EoGetH(ewin) / 6;
348 csr = ECSR_ACT_RESIZE;
349 if ((x < w) && (y < h))
351 Mode_mr.resize_detail = RD(1, 1);
352 csr = ECSR_ACT_RESIZE_TL;
354 else if ((x >= w) && (y < h))
356 Mode_mr.resize_detail = RD(2, 1);
357 csr = ECSR_ACT_RESIZE_TR;
359 else if ((x < w) && (y >= h))
361 Mode_mr.resize_detail = RD(1, 2);
362 csr = ECSR_ACT_RESIZE_BL;
364 else if ((x >= w) && (y >= h))
366 Mode_mr.resize_detail = RD(2, 2);
367 csr = ECSR_ACT_RESIZE_BR;
370 /* The following four if statements added on 07/22/04 by Josh Holtrop.
371 * They allow strictly horizontal or vertical resizing when the
372 * cursor is towards the middle of an edge of a window. */
373 if ((abs(x - w) < (w >> 1)) && (y < hh))
375 Mode.mode = MODE_RESIZE_V;
376 Mode_mr.resize_detail = RD(0, 1);
377 csr = ECSR_ACT_RESIZE_V;
379 else if ((abs(x - w) < (w >> 1)) && (y > (EoGetH(ewin) - hh)))
381 Mode.mode = MODE_RESIZE_V;
382 Mode_mr.resize_detail = RD(0, 2);
383 csr = ECSR_ACT_RESIZE_V;
385 else if ((abs(y - h) < (h >> 1)) && (x < ww))
387 Mode.mode = MODE_RESIZE_H;
388 Mode_mr.resize_detail = RD(1, 0);
389 csr = ECSR_ACT_RESIZE_H;
391 else if ((abs(y - h) < (h >> 1)) && (x > (EoGetW(ewin) - ww)))
393 Mode.mode = MODE_RESIZE_H;
394 Mode_mr.resize_detail = RD(2, 0);
395 csr = ECSR_ACT_RESIZE_H;
401 x = cx - EoGetX(ewin);
402 w = EoGetW(ewin) >> 1;
404 Mode_mr.resize_detail = RD(1, 0);
406 Mode_mr.resize_detail = RD(2, 0);
407 csr = ECSR_ACT_RESIZE_H;
412 y = cy - EoGetY(ewin);
413 h = EoGetH(ewin) >> 1;
415 Mode_mr.resize_detail = RD(0, 1);
417 Mode_mr.resize_detail = RD(0, 2);
418 csr = ECSR_ACT_RESIZE_V;
422 Mode_mr.start_x = Mode_mr.cur_x = cx;
423 Mode_mr.start_y = Mode_mr.cur_y = cy;
424 Mode_mr.win_x = EoGetX(ewin);
425 Mode_mr.win_y = EoGetY(ewin);
426 Mode_mr.win_w = ewin->client.w;
427 Mode_mr.win_h = ewin->client.h;
430 GrabKeyboardSet(EoGetWin(ewin));
432 GrabPointerSet(EoGetWin(ewin), csr, 1);
435 ewin->state.show_coords = 1;
436 DrawEwinShape(ewin, Conf.movres.mode_resize, EoGetX(ewin), EoGetY(ewin),
437 ewin->client.w, ewin->client.h, 0, 0);
443 ActionResizeEnd(EWin * ewin)
445 if (ewin && ewin != Mode_mr.ewin)
448 Mode.mode = MODE_NONE;
450 GrabKeyboardRelease();
451 GrabPointerRelease();
453 SoundPlay(SOUND_RESIZE_STOP);
459 ewin->state.show_coords = 0;
460 DrawEwinShape(ewin, Conf.movres.mode_resize, ewin->shape_x, ewin->shape_y,
461 ewin->shape_w, ewin->shape_h, 2, 0);
463 if (Mode_mr.mode == 0)
465 ewin->state.resizing = 0;
466 EwinUpdateOpacity(ewin);
470 if (ewin->state.shaded)
471 EwinOpMove(ewin, OPSRC_USER, ewin->shape_x, ewin->shape_y);
473 EwinOpMoveResize(ewin, OPSRC_USER, ewin->shape_x, ewin->shape_y,
474 ewin->shape_w, ewin->shape_h);
480 if (Mode_mr.grab_server)
483 ModulesSignal(ESIGNAL_ANIMATION_RESUME, NULL);
490 ActionMoveHandleMotion(void)
493 EWin *ewin, **gwins, *ewin1;
496 int screen_snap_dist;
498 int min_dx, max_dx, min_dy, max_dy;
504 EdgeCheckMotion(Mode.events.mx, Mode.events.my);
506 gwins = ListWinGroupMembersForEwin(ewin, GROUP_ACTION_MOVE,
507 Mode_mr.nogroup || Mode.move.swap, &num);
509 if (Mode.mode == MODE_MOVE_PENDING)
511 if (Mode_mr.grab_server)
515 ModulesSignal(ESIGNAL_ANIMATION_SUSPEND, NULL);
518 if (Mode_mr.mode == 0 || num == 1)
519 ewin->state.show_coords = 1;
521 for (i = 0; i < num; i++)
524 DrawEwinShape(ewin1, Mode_mr.mode, EoGetX(ewin1), EoGetY(ewin1),
525 ewin1->client.w, ewin1->client.h, 0, i);
526 if (Conf.movres.mode_move == 0)
529 Mode.mode = MODE_MOVE;
530 dx = Mode.events.mx - Mode_mr.start_x;
531 dy = Mode.events.my - Mode_mr.start_y;
533 else if (Mode.mode == MODE_MOVE)
535 dx = Mode.events.mx - Mode.events.px;
536 dy = Mode.events.my - Mode.events.py;
540 /* It should not be possible to get here. */
551 for (i = 0; i < num; i++)
555 /* make our ewin resist other ewins around the place */
556 SnapEwin(gwins[i], dx, dy, &ndx, &ndy);
557 if ((dx < 0) && (ndx <= 0))
571 if ((dy < 0) && (ndy <= 0))
596 Mode.constrained ? (WinGetW(VROOT) +
597 WinGetH(VROOT)) : Conf.snap.screen_snap_dist;
599 for (i = 0; i < num; i++)
603 /* jump out of snap horizontally */
604 dd = ewin1->req_x - ewin1->shape_x;
608 (((ewin1->shape_x == 0) &&
609 (dd > screen_snap_dist)) ||
610 ((ewin1->shape_x == (WinGetW(VROOT) - EoGetW(ewin1))) &&
611 (dd > screen_snap_dist)) ||
612 ((ewin1->shape_x != 0) &&
613 (ewin1->shape_x != (WinGetW(VROOT) - EoGetW(ewin1)) &&
614 (dd > Conf.snap.edge_snap_dist)))))
617 ndx = ewin1->req_x - ewin1->shape_x + dx;
620 /* jump out of snap vertically */
621 dd = ewin1->req_y - ewin1->shape_y;
625 (((ewin1->shape_y == 0) &&
626 (dd > screen_snap_dist)) ||
627 ((ewin1->shape_y == (WinGetH(VROOT) - EoGetH(ewin1))) &&
628 (dd > screen_snap_dist)) ||
629 ((ewin1->shape_y != 0) &&
630 (ewin1->shape_y != (WinGetH(VROOT) - EoGetH(ewin1)) &&
631 (dd > Conf.snap.edge_snap_dist)))))
634 ndy = ewin1->req_y - ewin1->shape_y + dy;
638 for (i = 0; i < num; i++)
642 /* if its opaque move mode check to see if we have to float */
643 /* the window above all desktops (reparent to root) */
644 if (Mode_mr.mode == 0)
648 dsk = EoGetDesk(ewin1);
649 DetermineEwinFloat(ewin1, ndx, ndy);
650 if (dsk != EoGetDesk(ewin1))
652 ewin1->shape_x += EoGetX(dsk);
653 ewin1->shape_y += EoGetY(dsk);
654 ewin1->req_x += EoGetX(dsk);
655 ewin1->req_y += EoGetY(dsk);
659 /* draw the new position of the window */
660 DrawEwinShape(ewin1, Mode_mr.mode,
661 ewin1->shape_x + ndx, ewin1->shape_y + ndy,
662 ewin1->client.w, ewin1->client.h, 1, i);
664 /* if we didnt jump the window after a resist at the edge */
665 /* reset the requested x to be the prev. requested + delta */
666 /* if we did jump set requested to current shape position */
667 ewin1->req_x = (jumpx) ? ewin1->shape_x : ewin1->req_x + dx;
668 ewin1->req_y = (jumpy) ? ewin1->shape_y : ewin1->req_y + dy;
670 /* swapping of group member locations: */
671 if (Mode.move.swap && GroupsGetSwapmove())
673 EWin **all_gwins, *ewin2;
674 int j, all_gwins_num;
676 all_gwins = ListWinGroupMembersForEwin(ewin, GROUP_ACTION_ANY, 0,
679 for (j = 0; j < all_gwins_num; j++)
681 ewin2 = all_gwins[j];
686 /* check for sufficient overlap and avoid flickering */
687 if (((ewin1->shape_x >= ewin2->shape_x &&
688 ewin1->shape_x <= ewin2->shape_x + EoGetW(ewin2) / 2 &&
690 (ewin1->shape_x <= ewin2->shape_x &&
691 ewin1->shape_x + EoGetW(ewin1) / 2 >= ewin2->shape_x &&
693 ((ewin1->shape_y >= ewin2->shape_y &&
694 ewin1->shape_y <= ewin2->shape_y + EoGetH(ewin2) / 2 &&
696 (EoGetY(ewin1) <= EoGetY(ewin2) &&
697 ewin1->shape_y + EoGetH(ewin1) / 2 >= ewin2->shape_y &&
703 tmp_swapcoord_x = Mode_mr.swapcoord_x;
704 tmp_swapcoord_y = Mode_mr.swapcoord_y;
705 Mode_mr.swapcoord_x = ewin2->shape_x;
706 Mode_mr.swapcoord_y = ewin2->shape_y;
707 EwinOpMove(ewin2, OPSRC_USER,
708 tmp_swapcoord_x, tmp_swapcoord_y);
722 ActionResizeHandleMotion(void)
736 switch (RD_H(Mode_mr.resize_detail))
741 w = Mode_mr.win_w - (Mode.events.mx - Mode_mr.start_x);
742 ICCCM_SizeMatch(ewin, w, h, &w, &h);
743 x = Mode_mr.win_x + (Mode_mr.win_w - w);
746 w = Mode_mr.win_w + (Mode.events.mx - Mode_mr.start_x);
747 ICCCM_SizeMatch(ewin, w, h, &w, &h);
751 switch (RD_V(Mode_mr.resize_detail))
756 h = Mode_mr.win_h - (Mode.events.my - Mode_mr.start_y);
757 ICCCM_SizeMatch(ewin, w, h, &w, &h);
758 y = Mode_mr.win_y + (Mode_mr.win_h - h);
761 h = Mode_mr.win_h + (Mode.events.my - Mode_mr.start_y);
762 ICCCM_SizeMatch(ewin, w, h, &w, &h);
766 DrawEwinShape(ewin, Conf.movres.mode_resize, x, y, w, h, 1, 0);
770 ActionsHandleKey(unsigned int key)
773 int resize, delta, end = 0;
779 resize = Mode.mode == MODE_RESIZE ||
780 Mode.mode == MODE_RESIZE_H || Mode.mode == MODE_RESIZE_V;
782 Mode.events.px = Mode_mr.cur_x;
783 Mode.events.py = Mode_mr.cur_y;
791 Mode_mr.cur_x = Mode_mr.start_x;
792 Mode_mr.cur_y = Mode_mr.start_y;
798 if (!RD_H(Mode_mr.resize_detail))
799 Mode_mr.resize_detail |= RD(1, 0);
800 if (resize && ewin->icccm.w_inc > delta)
801 delta = ewin->icccm.w_inc;
802 Mode_mr.cur_x -= delta;
805 if (!RD_H(Mode_mr.resize_detail))
806 Mode_mr.resize_detail |= RD(2, 0);
807 if (resize && ewin->icccm.w_inc > delta)
808 delta = ewin->icccm.w_inc;
809 Mode_mr.cur_x += delta;
812 if (!RD_V(Mode_mr.resize_detail))
813 Mode_mr.resize_detail |= RD(0, 1);
814 if (resize && ewin->icccm.h_inc > delta)
815 delta = ewin->icccm.h_inc;
816 Mode_mr.cur_y -= delta;
819 if (!RD_V(Mode_mr.resize_detail))
820 Mode_mr.resize_detail |= RD(0, 2);
821 if (resize && ewin->icccm.h_inc > delta)
822 delta = ewin->icccm.h_inc;
823 Mode_mr.cur_y += delta;
827 Mode_mr.using_kbd = 2;
828 Mode.events.mx = Mode_mr.cur_x;
829 Mode.events.my = Mode_mr.cur_y;
833 case MODE_MOVE_PENDING:
835 ActionMoveHandleMotion();
843 ActionResizeHandleMotion();
845 ActionResizeEnd(NULL);
854 ActionsHandleMotion(void)
858 case MODE_MOVE_PENDING:
860 ActionMoveHandleMotion();
866 ActionResizeHandleMotion();
879 case MODE_MOVE_PENDING:
886 ActionResizeEnd(NULL);
898 case MODE_MOVE_PENDING:
908 ActionsEnd(EWin * ewin)
917 ActionResizeEnd(ewin);
918 Mode.action_inhibit = 1;
921 case MODE_MOVE_PENDING:
924 Mode.action_inhibit = 1;
936 SlideEwinTo(EWin * ewin, int fx, int fy, int tx, int ty, int speed, int mode)
938 SlideEwinsTo(&ewin, &fx, &fy, &tx, &ty, 1, speed, mode);
942 SlideEwinsTo(EWin ** ewin, int *fx, int *fy, int *tx, int *ty, int num_wins,
945 int k, x, y, w, h, i;
953 SoundPlay(SOUND_WINDOW_SLIDE);
955 Mode_mr.grab_server = _NeedServerGrab(mode);
956 if (Mode_mr.grab_server)
959 ETimedLoopInit(0, 1024, speed);
960 for (k = 0; k <= 1024;)
962 for (i = 0; i < num_wins; i++)
967 x = ((fx[i] * (1024 - k)) + (tx[i] * k)) >> 10;
968 y = ((fy[i] * (1024 - k)) + (ty[i] * k)) >> 10;
969 w = ewin[i]->client.w;
970 h = ewin[i]->client.h;
972 EoMove(ewin[i], x, y);
974 DrawEwinShape(ewin[i], mode, x, y, w, h, firstlast, i);
977 /* We may loop faster here than originally intended */
978 k = ETimedLoopNext();
981 for (i = 0; i < num_wins; i++)
986 ewin[i]->state.animated = 0;
989 DrawEwinShape(ewin[i], mode, tx[i], ty[i], ewin[i]->client.w,
990 ewin[i]->client.h, 2, i);
991 EwinMove(ewin[i], tx[i], ty[i]);
996 if (Mode_mr.grab_server)
999 SoundPlay(SOUND_WINDOW_SLIDE_END);