1 /*
2  * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
3  * Copyright (C) 2004-2020 Kim Woelders
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to
7  * deal in the Software without restriction, including without limitation the
8  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9  * sell copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies of the Software, its documentation and marketing & publicity
14  * materials, and acknowledgment shall be given in the documentation, materials
15  * and software packages that this Software was used.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 #include "config.h"
25 
26 #include <X11/Xlib.h>
27 #include <X11/keysym.h>
28 
29 #include "E.h"
30 #include "borders.h"
31 #include "cursors.h"
32 #include "desktops.h"
33 #include "emodule.h"
34 #include "ewins.h"
35 #include "focus.h"
36 #include "grabs.h"
37 #include "groups.h"
38 #include "hints.h"
39 #include "timers.h"
40 #include "xwin.h"
41 
42 static struct {
43    Win                 events;
44    EWin               *ewin;
45    char                mode;
46    char                using_kbd;
47    char                nogroup;
48    char                grab_server;
49    int                 start_x, start_y;
50    int                 cur_x, cur_y;
51    int                 win_x, win_y, win_w, win_h;
52    int                 swapcoord_x, swapcoord_y;
53    int                 resize_detail;
54 } Mode_mr;
55 
56 static void         _MoveResizeInit(void);
57 
58 void
EwinShapeSet(EWin * ewin)59 EwinShapeSet(EWin * ewin)
60 {
61    int                 bl, br, bt, bb;
62 
63    ewin->shape_x = EoGetX(ewin);
64    ewin->shape_y = EoGetY(ewin);
65 
66    if (ewin->state.shaded)
67      {
68 	EwinBorderGetSize(ewin, &bl, &br, &bt, &bb);
69 	ewin->shape_w = EoGetW(ewin) - (bl + br);
70 	ewin->shape_h = EoGetH(ewin) - (bt + bb);
71      }
72    else
73      {
74 	ewin->shape_w = ewin->client.w;
75 	ewin->shape_h = ewin->client.h;
76      }
77 }
78 
79 void
MoveResizeMoveStart(EWin * ewin,int kbd,int constrained,int nogroup)80 MoveResizeMoveStart(EWin * ewin, int kbd, int constrained, int nogroup)
81 {
82    EWin              **gwins;
83    int                 i, num, cx, cy;
84 
85    if (!ewin || ewin->state.inhibit_move)
86       return;
87 
88    _MoveResizeInit();
89 
90    Mode_mr.ewin = ewin;
91    Mode_mr.using_kbd = kbd;
92    Mode_mr.nogroup = nogroup;
93 
94    cx = Mode.events.cx;
95    cy = Mode.events.cy;
96 
97    SoundPlay(SOUND_MOVE_START);
98 
99    Mode.mode = MODE_MOVE_PENDING;
100    Mode.constrained = constrained;
101 
102    Mode_mr.start_x = Mode_mr.cur_x = cx;
103    Mode_mr.start_y = Mode_mr.cur_y = cy;
104 
105    Mode_mr.win_x = Mode_mr.start_x - (EoGetX(ewin) + EoGetX(EoGetDesk(ewin)));
106    Mode_mr.win_y = Mode_mr.start_y - (EoGetY(ewin) + EoGetY(EoGetDesk(ewin)));
107 
108    EwinRaise(ewin);
109    gwins = ListWinGroupMembersForEwin(ewin, GROUP_ACTION_MOVE, nogroup
110 				      || Mode.move.swap, &num);
111 
112    Conf.movres.mode_move = MoveResizeModeValidateMove(Conf.movres.mode_move);
113    Mode_mr.mode = Conf.movres.mode_move;
114    Mode_mr.grab_server = DrawEwinShapeNeedsGrab(Mode_mr.mode);
115 
116    for (i = 0; i < num; i++)
117      {
118 	EwinShapeSet(gwins[i]);
119 	EwinOpFloatAt(gwins[i], OPSRC_USER, EoGetX(gwins[i]), EoGetY(gwins[i]));
120 	if ((Mode_mr.mode == MR_OPAQUE) || (Mode_mr.mode == MR_TECH_OPAQUE))
121 	  {
122 	     ewin->state.moving = 1;
123 	     EwinUpdateOpacity(gwins[i]);
124 	  }
125      }
126    Efree(gwins);
127 
128    if (kbd)
129       GrabKeyboardSet(Mode_mr.events);
130    else
131       GrabPointerSet(Mode_mr.events, ECSR_ACT_MOVE, 1);
132 
133    Mode_mr.swapcoord_x = EoGetX(ewin);
134    Mode_mr.swapcoord_y = EoGetY(ewin);
135 }
136 
137 static void
_MoveResizeMoveEnd(EWin * ewin)138 _MoveResizeMoveEnd(EWin * ewin)
139 {
140    EWin              **gwins;
141    int                 num, i;
142    Desk               *d1, *d2;
143 
144    if (ewin && ewin != Mode_mr.ewin)
145       return;
146 
147    GrabKeyboardRelease();
148    GrabPointerRelease();
149 
150    SoundPlay(SOUND_MOVE_STOP);
151 
152    ewin = Mode_mr.ewin;
153    if (!ewin)
154       goto done;
155 
156    ewin->state.show_coords = 0;
157 
158    gwins = ListWinGroupMembersForEwin(ewin, GROUP_ACTION_MOVE, Mode_mr.nogroup
159 				      || Mode.move.swap, &num);
160 
161    if (Mode.mode == MODE_MOVE)
162      {
163 	for (i = 0; i < num; i++)
164 	   DrawEwinShape(gwins[i], Mode_mr.mode,
165 			 gwins[i]->shape_x, gwins[i]->shape_y,
166 			 gwins[i]->client.w, gwins[i]->client.h, 2);
167      }
168    Mode.mode = MODE_NONE;
169 
170    d2 = DesktopAt(Mode.events.mx, Mode.events.my);
171 
172    for (i = 0; i < num; i++)
173      {
174 	ewin = gwins[i];
175 	d1 = EoGetDesk(ewin);
176 	if (d2 == d1)
177 	   EwinOpUnfloatAt(ewin, OPSRC_USER, d2, ewin->shape_x, ewin->shape_y);
178 	else
179 	   EwinOpUnfloatAt(ewin, OPSRC_USER, d2,
180 			   ewin->shape_x - (EoGetX(d2) - EoGetX(d1)),
181 			   ewin->shape_y - (EoGetY(d2) - EoGetY(d1)));
182 	if ((Mode_mr.mode == MR_OPAQUE) || (Mode_mr.mode == MR_TECH_OPAQUE))
183 	  {
184 	     ewin->state.moving = 0;
185 	     EwinUpdateOpacity(ewin);
186 	  }
187      }
188 
189    Efree(gwins);
190 
191    ESync(ESYNC_MOVRES);
192 
193  done:
194    Mode.mode = MODE_NONE;
195    Mode.move.swap = 0;
196    Mode.place.doing_manual = 0;
197    Mode_mr.ewin = NULL;
198 
199    if (Mode_mr.grab_server)
200      {
201 	FocusEnable(1);
202 	EUngrabServer();
203 	ModulesSignal(ESIGNAL_ANIMATION_RESUME, NULL);
204      }
205 }
206 
207 static void
_MoveResizeMoveSuspend(void)208 _MoveResizeMoveSuspend(void)
209 {
210    EWin               *ewin, **lst;
211    int                 i, num;
212 
213    ewin = Mode_mr.ewin;
214    if (!ewin)
215       return;
216 
217    if (Mode.mode == MODE_MOVE_PENDING)
218       return;
219 
220    /* If non opaque undraw our boxes */
221    if (Mode_mr.grab_server)
222      {
223 	lst = ListWinGroupMembersForEwin(ewin, GROUP_ACTION_MOVE,
224 					 Mode_mr.nogroup, &num);
225 	for (i = 0; i < num; i++)
226 	  {
227 	     ewin = lst[i];
228 	     DrawEwinShape(ewin, Mode_mr.mode, ewin->shape_x,
229 			   ewin->shape_y, ewin->client.w, ewin->client.h, 3);
230 	  }
231 	Efree(lst);
232 
233 	EUngrabServer();
234      }
235 }
236 
237 static void
_MoveResizeMoveResume(void)238 _MoveResizeMoveResume(void)
239 {
240    EWin               *ewin, **lst;
241    int                 i, num;
242    int                 x, y, dx, dy;
243 
244    ewin = Mode_mr.ewin;
245    if (!ewin)
246       return;
247 
248    GrabPointerSet(Mode_mr.events, ECSR_ACT_MOVE, 1);
249 
250    if (Mode.mode == MODE_MOVE_PENDING)
251       Mode.mode = MODE_MOVE;
252 
253    if (Mode_mr.grab_server)
254       EGrabServer();
255 
256    dx =
257       Mode.events.mx - Mode_mr.win_x - EoGetX(EoGetDesk(ewin)) - ewin->shape_x;
258    dy =
259       Mode.events.my - Mode_mr.win_y - EoGetY(EoGetDesk(ewin)) - ewin->shape_y;
260 
261    /* Redraw any windows that were in "move mode" */
262    lst = ListWinGroupMembersForEwin(ewin, GROUP_ACTION_MOVE,
263 				    Mode_mr.nogroup, &num);
264    for (i = 0; i < num; i++)
265      {
266 	ewin = lst[i];
267 
268 	if (!EoIsFloating(ewin))
269 	   continue;
270 
271 	x = ewin->shape_x + dx;
272 	y = ewin->shape_y + dy;
273 	DrawEwinShape(ewin, Mode_mr.mode, x, y,
274 		      ewin->client.w, ewin->client.h, 0);
275      }
276    Efree(lst);
277 }
278 
279 #define RD(h, v) (((h) << 8) + (v))
280 #define RD_H(hv) (((hv) >> 8) & 0xff)
281 #define RD_V(hv) (((hv)     ) & 0xff)
282 
283 void
MoveResizeResizeStart(EWin * ewin,int kbd,int hv)284 MoveResizeResizeStart(EWin * ewin, int kbd, int hv)
285 {
286    int                 x, y, w, h, ww, hh, cx, cy;
287    unsigned int        csr;
288 
289    if (!ewin || ewin->state.inhibit_resize)
290       return;
291 
292    _MoveResizeInit();
293 
294    Mode_mr.ewin = ewin;
295 
296    cx = Mode.events.cx;
297    cy = Mode.events.cy;
298 
299    SoundPlay(SOUND_RESIZE_START);
300 
301    Conf.movres.mode_resize =
302       MoveResizeModeValidateResize(Conf.movres.mode_resize);
303    Mode_mr.mode = Conf.movres.mode_resize;
304    Mode_mr.using_kbd = kbd;
305    Mode_mr.grab_server = DrawEwinShapeNeedsGrab(Mode_mr.mode);
306    if (Mode_mr.grab_server)
307      {
308 	EGrabServer();
309 	ModulesSignal(ESIGNAL_ANIMATION_SUSPEND, NULL);
310 	/* Run idlers (stacking, border updates, ...) before drawing lines */
311 	IdlersRun();
312      }
313    if ((Mode_mr.mode == MR_OPAQUE) || (Mode_mr.mode == MR_TECH_OPAQUE))
314      {
315 	ewin->state.resizing = 1;
316 	EwinUpdateOpacity(ewin);
317      }
318 
319    switch (hv)
320      {
321      default:
322      case MODE_RESIZE:
323 	Mode.mode = hv;
324 	if (kbd)
325 	  {
326 	     Mode_mr.resize_detail = 0;
327 	     csr = ECSR_ACT_RESIZE_BR;
328 	     break;
329 	  }
330 	x = cx - EoGetX(ewin);
331 	y = cy - EoGetY(ewin);
332 	w = EoGetW(ewin) >> 1;
333 	h = EoGetH(ewin) >> 1;
334 	ww = EoGetW(ewin) / 6;
335 	hh = EoGetH(ewin) / 6;
336 
337 	csr = ECSR_ACT_RESIZE;
338 	if ((x < w) && (y < h))
339 	  {
340 	     Mode_mr.resize_detail = RD(1, 1);
341 	     csr = ECSR_ACT_RESIZE_TL;
342 	  }
343 	else if ((x >= w) && (y < h))
344 	  {
345 	     Mode_mr.resize_detail = RD(2, 1);
346 	     csr = ECSR_ACT_RESIZE_TR;
347 	  }
348 	else if ((x < w) && (y >= h))
349 	  {
350 	     Mode_mr.resize_detail = RD(1, 2);
351 	     csr = ECSR_ACT_RESIZE_BL;
352 	  }
353 	else if ((x >= w) && (y >= h))
354 	  {
355 	     Mode_mr.resize_detail = RD(2, 2);
356 	     csr = ECSR_ACT_RESIZE_BR;
357 	  }
358 
359 	/* The following four if statements added on 07/22/04 by Josh Holtrop.
360 	 * They allow strictly horizontal or vertical resizing when the
361 	 * cursor is towards the middle of an edge of a window. */
362 	if ((abs(x - w) < (w >> 1)) && (y < hh))
363 	  {
364 	     Mode.mode = MODE_RESIZE_V;
365 	     Mode_mr.resize_detail = RD(0, 1);
366 	     csr = ECSR_ACT_RESIZE_V;
367 	  }
368 	else if ((abs(x - w) < (w >> 1)) && (y > (EoGetH(ewin) - hh)))
369 	  {
370 	     Mode.mode = MODE_RESIZE_V;
371 	     Mode_mr.resize_detail = RD(0, 2);
372 	     csr = ECSR_ACT_RESIZE_V;
373 	  }
374 	else if ((abs(y - h) < (h >> 1)) && (x < ww))
375 	  {
376 	     Mode.mode = MODE_RESIZE_H;
377 	     Mode_mr.resize_detail = RD(1, 0);
378 	     csr = ECSR_ACT_RESIZE_H;
379 	  }
380 	else if ((abs(y - h) < (h >> 1)) && (x > (EoGetW(ewin) - ww)))
381 	  {
382 	     Mode.mode = MODE_RESIZE_H;
383 	     Mode_mr.resize_detail = RD(2, 0);
384 	     csr = ECSR_ACT_RESIZE_H;
385 	  }
386 	break;
387 
388      case MODE_RESIZE_H:
389 	Mode.mode = hv;
390 	x = cx - EoGetX(ewin);
391 	w = EoGetW(ewin) >> 1;
392 	if (x < w)
393 	   Mode_mr.resize_detail = RD(1, 0);
394 	else
395 	   Mode_mr.resize_detail = RD(2, 0);
396 	csr = ECSR_ACT_RESIZE_H;
397 	break;
398 
399      case MODE_RESIZE_V:
400 	Mode.mode = hv;
401 	y = cy - EoGetY(ewin);
402 	h = EoGetH(ewin) >> 1;
403 	if (y < h)
404 	   Mode_mr.resize_detail = RD(0, 1);
405 	else
406 	   Mode_mr.resize_detail = RD(0, 2);
407 	csr = ECSR_ACT_RESIZE_V;
408 	break;
409      }
410 
411    Mode_mr.start_x = Mode_mr.cur_x = cx;
412    Mode_mr.start_y = Mode_mr.cur_y = cy;
413    Mode_mr.win_x = EoGetX(ewin);
414    Mode_mr.win_y = EoGetY(ewin);
415    Mode_mr.win_w = ewin->client.w;
416    Mode_mr.win_h = ewin->client.h;
417 
418    if (kbd)
419       GrabKeyboardSet(Mode_mr.events);
420    else
421       GrabPointerSet(Mode_mr.events, csr, 1);
422 
423    EwinShapeSet(ewin);
424    ewin->state.show_coords = 1;
425    DrawEwinShape(ewin, Conf.movres.mode_resize, EoGetX(ewin), EoGetY(ewin),
426 		 ewin->client.w, ewin->client.h, 0);
427 }
428 
429 static void
_MoveResizeResizeEnd(EWin * ewin)430 _MoveResizeResizeEnd(EWin * ewin)
431 {
432    if (ewin && ewin != Mode_mr.ewin)
433       return;
434 
435    Mode.mode = MODE_NONE;
436 
437    GrabKeyboardRelease();
438    GrabPointerRelease();
439 
440    SoundPlay(SOUND_RESIZE_STOP);
441 
442    ewin = Mode_mr.ewin;
443    if (!ewin)
444       goto done;
445 
446    ewin->state.show_coords = 0;
447    DrawEwinShape(ewin, Conf.movres.mode_resize, ewin->shape_x, ewin->shape_y,
448 		 ewin->shape_w, ewin->shape_h, 2);
449 
450    if ((Mode_mr.mode == MR_OPAQUE) || (Mode_mr.mode == MR_TECH_OPAQUE))
451      {
452 	ewin->state.resizing = 0;
453 	EwinUpdateOpacity(ewin);
454      }
455    else
456      {
457 	if (ewin->state.shaded)
458 	   EwinOpMove(ewin, OPSRC_USER, ewin->shape_x, ewin->shape_y);
459 	else
460 	   EwinOpMoveResize(ewin, OPSRC_USER, ewin->shape_x, ewin->shape_y,
461 			    ewin->shape_w, ewin->shape_h);
462      }
463 
464    ESync(ESYNC_MOVRES);
465 
466  done:
467    Mode_mr.ewin = NULL;
468    if (Mode_mr.grab_server)
469      {
470 	EUngrabServer();
471 	ModulesSignal(ESIGNAL_ANIMATION_RESUME, NULL);
472      }
473 }
474 
475 static void
_MoveResizeMoveHandleMotion(void)476 _MoveResizeMoveHandleMotion(void)
477 {
478    int                 dx, dy, dd;
479    EWin               *ewin, **gwins, *ewin1;
480    int                 i, num;
481    int                 ndx, ndy;
482    int                 screen_snap_dist;
483    char                jumpx, jumpy;
484    int                 min_dx, max_dx, min_dy, max_dy;
485 
486    ewin = Mode_mr.ewin;
487    if (!ewin)
488       return;
489 
490    EdgeCheckMotion(Mode.events.mx, Mode.events.my);
491 
492    gwins = ListWinGroupMembersForEwin(ewin, GROUP_ACTION_MOVE,
493 				      Mode_mr.nogroup || Mode.move.swap, &num);
494 
495    if (Mode.mode == MODE_MOVE_PENDING)
496      {
497 	Mode.mode = MODE_MOVE;
498 	if (Mode_mr.grab_server)
499 	  {
500 	     EGrabServer();
501 	     FocusEnable(0);
502 	     ModulesSignal(ESIGNAL_ANIMATION_SUSPEND, NULL);
503 	  }
504 
505 	if (Mode_mr.mode == MR_OPAQUE || num == 1)
506 	   ewin->state.show_coords = 1;
507 
508 	for (i = 0; i < num; i++)
509 	  {
510 	     ewin1 = gwins[i];
511 	     DrawEwinShape(ewin1, Mode_mr.mode, EoGetX(ewin1), EoGetY(ewin1),
512 			   ewin1->client.w, ewin1->client.h, 0);
513 	     if (Conf.movres.mode_move == MR_OPAQUE)
514 		Mode_mr.mode = MR_OPAQUE;
515 	  }
516 	dx = Mode.events.mx - Mode_mr.start_x;
517 	dy = Mode.events.my - Mode_mr.start_y;
518      }
519    else if (Mode.mode == MODE_MOVE)
520      {
521 	dx = Mode.events.mx - Mode.events.px;
522 	dy = Mode.events.my - Mode.events.py;
523      }
524    else
525      {
526 	/* It should not be possible to get here. */
527 	goto done;
528      }
529 
530    jumpx = 0;
531    jumpy = 0;
532    min_dx = dx;
533    min_dy = dy;
534    max_dx = dx;
535    max_dy = dy;
536 
537    for (i = 0; i < num; i++)
538      {
539 	ndx = dx;
540 	ndy = dy;
541 	/* make our ewin resist other ewins around the place */
542 	SnapEwin(gwins[i], dx, dy, &ndx, &ndy);
543 	if ((dx < 0) && (ndx <= 0))
544 	  {
545 	     if (ndx > min_dx)
546 		min_dx = ndx;
547 	     if (ndx < max_dx)
548 		max_dx = ndx;
549 	  }
550 	else if (ndx >= 0)
551 	  {
552 	     if (ndx < min_dx)
553 		min_dx = ndx;
554 	     if (ndx > max_dx)
555 		max_dx = ndx;
556 	  }
557 	if ((dy < 0) && (ndy <= 0))
558 	  {
559 	     if (ndy > min_dy)
560 		min_dy = ndy;
561 	     if (ndy < max_dy)
562 		max_dy = ndy;
563 	  }
564 	else if (ndy >= 0)
565 	  {
566 	     if (ndy < min_dy)
567 		min_dy = ndy;
568 	     if (ndy > max_dy)
569 		max_dy = ndy;
570 	  }
571      }
572    if (min_dx == dx)
573       ndx = max_dx;
574    else
575       ndx = min_dx;
576    if (min_dy == dy)
577       ndy = max_dy;
578    else
579       ndy = min_dy;
580 
581    screen_snap_dist =
582       Mode.constrained ? (WinGetW(VROOT) +
583 			  WinGetH(VROOT)) : Conf.snap.screen_snap_dist;
584 
585    for (i = 0; i < num; i++)
586      {
587 	ewin1 = gwins[i];
588 
589 	/* jump out of snap horizontally */
590 	dd = ewin1->req_x - ewin1->shape_x;
591 	if (dd < 0)
592 	   dd = -dd;
593 	if ((ndx != dx) &&
594 	    (((ewin1->shape_x == 0) &&
595 	      (dd > screen_snap_dist)) ||
596 	     ((ewin1->shape_x == (WinGetW(VROOT) - EoGetW(ewin1))) &&
597 	      (dd > screen_snap_dist)) ||
598 	     ((ewin1->shape_x != 0) &&
599 	      (ewin1->shape_x != (WinGetW(VROOT) - EoGetW(ewin1)) &&
600 	       (dd > Conf.snap.edge_snap_dist)))))
601 	  {
602 	     jumpx = 1;
603 	     ndx = ewin1->req_x - ewin1->shape_x + dx;
604 	  }
605 
606 	/* jump out of snap vertically */
607 	dd = ewin1->req_y - ewin1->shape_y;
608 	if (dd < 0)
609 	   dd = -dd;
610 	if ((ndy != dy) &&
611 	    (((ewin1->shape_y == 0) &&
612 	      (dd > screen_snap_dist)) ||
613 	     ((ewin1->shape_y == (WinGetH(VROOT) - EoGetH(ewin1))) &&
614 	      (dd > screen_snap_dist)) ||
615 	     ((ewin1->shape_y != 0) &&
616 	      (ewin1->shape_y != (WinGetH(VROOT) - EoGetH(ewin1)) &&
617 	       (dd > Conf.snap.edge_snap_dist)))))
618 	  {
619 	     jumpy = 1;
620 	     ndy = ewin1->req_y - ewin1->shape_y + dy;
621 	  }
622      }
623 
624    for (i = 0; i < num; i++)
625      {
626 	ewin1 = gwins[i];
627 
628 	/* if its opaque move mode check to see if we have to float */
629 	/* the window above all desktops (reparent to root) */
630 	if (Mode_mr.mode == MR_OPAQUE)
631 	  {
632 	     Desk               *dsk;
633 
634 	     dsk = EoGetDesk(ewin1);
635 	     DetermineEwinFloat(ewin1, ndx, ndy);
636 	     if (dsk != EoGetDesk(ewin1))
637 	       {
638 		  ewin1->shape_x += EoGetX(dsk);
639 		  ewin1->shape_y += EoGetY(dsk);
640 		  ewin1->req_x += EoGetX(dsk);
641 		  ewin1->req_y += EoGetY(dsk);
642 	       }
643 	  }
644 
645 	/* draw the new position of the window */
646 	DrawEwinShape(ewin1, Mode_mr.mode,
647 		      ewin1->shape_x + ndx, ewin1->shape_y + ndy,
648 		      ewin1->client.w, ewin1->client.h, 1);
649 
650 	/* if we didnt jump the window after a resist at the edge */
651 	/* reset the requested x to be the prev. requested + delta */
652 	/* if we did jump set requested to current shape position */
653 	ewin1->req_x = (jumpx) ? ewin1->shape_x : ewin1->req_x + dx;
654 	ewin1->req_y = (jumpy) ? ewin1->shape_y : ewin1->req_y + dy;
655 
656 	/* swapping of group member locations: */
657 	if (Mode.move.swap && GroupsGetSwapmove())
658 	  {
659 	     EWin              **all_gwins, *ewin2;
660 	     int                 j, all_gwins_num;
661 
662 	     all_gwins = ListWinGroupMembersForEwin(ewin, GROUP_ACTION_ANY, 0,
663 						    &all_gwins_num);
664 
665 	     for (j = 0; j < all_gwins_num; j++)
666 	       {
667 		  ewin2 = all_gwins[j];
668 
669 		  if (ewin1 == ewin2)
670 		     continue;
671 
672 		  /* check for sufficient overlap and avoid flickering */
673 		  if (((ewin1->shape_x >= ewin2->shape_x &&
674 			ewin1->shape_x <= ewin2->shape_x + EoGetW(ewin2) / 2 &&
675 			dx <= 0) ||
676 		       (ewin1->shape_x <= ewin2->shape_x &&
677 			ewin1->shape_x + EoGetW(ewin1) / 2 >= ewin2->shape_x &&
678 			dx >= 0)) &&
679 		      ((ewin1->shape_y >= ewin2->shape_y &&
680 			ewin1->shape_y <= ewin2->shape_y + EoGetH(ewin2) / 2 &&
681 			dy <= 0) ||
682 		       (EoGetY(ewin1) <= EoGetY(ewin2) &&
683 			ewin1->shape_y + EoGetH(ewin1) / 2 >= ewin2->shape_y &&
684 			dy >= 0)))
685 		    {
686 		       int                 tmp_swapcoord_x;
687 		       int                 tmp_swapcoord_y;
688 
689 		       tmp_swapcoord_x = Mode_mr.swapcoord_x;
690 		       tmp_swapcoord_y = Mode_mr.swapcoord_y;
691 		       Mode_mr.swapcoord_x = ewin2->shape_x;
692 		       Mode_mr.swapcoord_y = ewin2->shape_y;
693 		       EwinOpMove(ewin2, OPSRC_USER,
694 				  tmp_swapcoord_x, tmp_swapcoord_y);
695 		       break;
696 		    }
697 	       }
698 
699 	     Efree(all_gwins);
700 	  }
701      }
702 
703  done:
704    Efree(gwins);
705 }
706 
707 static void
_MoveResizeResizeHandleMotion(void)708 _MoveResizeResizeHandleMotion(void)
709 {
710    int                 x, y, w, h;
711    EWin               *ewin;
712 
713    ewin = Mode_mr.ewin;
714    if (!ewin)
715       return;
716 
717    w = ewin->client.w;
718    h = ewin->client.h;
719    x = ewin->shape_x;
720    y = ewin->shape_y;
721 
722    switch (RD_H(Mode_mr.resize_detail))
723      {
724      default:
725 	break;
726      case 1:			/* Left */
727 	w = Mode_mr.win_w - (Mode.events.mx - Mode_mr.start_x);
728 	ICCCM_SizeMatch(ewin, w, h, &w, &h);
729 	x = Mode_mr.win_x + (Mode_mr.win_w - w);
730 	break;
731      case 2:			/* Right */
732 	w = Mode_mr.win_w + (Mode.events.mx - Mode_mr.start_x);
733 	ICCCM_SizeMatch(ewin, w, h, &w, &h);
734 	break;
735      }
736 
737    switch (RD_V(Mode_mr.resize_detail))
738      {
739      default:
740 	break;
741      case 1:			/* Top */
742 	h = Mode_mr.win_h - (Mode.events.my - Mode_mr.start_y);
743 	ICCCM_SizeMatch(ewin, w, h, &w, &h);
744 	y = Mode_mr.win_y + (Mode_mr.win_h - h);
745 	break;
746      case 2:			/* Bottom */
747 	h = Mode_mr.win_h + (Mode.events.my - Mode_mr.start_y);
748 	ICCCM_SizeMatch(ewin, w, h, &w, &h);
749 	break;
750      }
751 
752    DrawEwinShape(ewin, Conf.movres.mode_resize, x, y, w, h, 1);
753 }
754 
755 static void
_MoveResizeHandleKey(unsigned int key)756 _MoveResizeHandleKey(unsigned int key)
757 {
758    EWin               *ewin;
759    int                 resize, delta, end = 0;
760 
761    ewin = Mode_mr.ewin;
762    if (!ewin)
763       return;
764 
765    resize = Mode.mode == MODE_RESIZE ||
766       Mode.mode == MODE_RESIZE_H || Mode.mode == MODE_RESIZE_V;
767 
768    Mode.events.px = Mode_mr.cur_x;
769    Mode.events.py = Mode_mr.cur_y;
770    delta = 5;
771 
772    switch (key)
773      {
774      default:
775 	return;
776 
777      case XK_Escape:
778 	Mode_mr.cur_x = Mode_mr.start_x;
779 	Mode_mr.cur_y = Mode_mr.start_y;
780 	/* FALLTHROUGH */
781      case XK_Return:
782 	end = 1;
783 	break;
784 
785      case XK_Left:
786 	if (!RD_H(Mode_mr.resize_detail))
787 	   Mode_mr.resize_detail |= RD(1, 0);
788 	if (resize && ewin->icccm.w_inc > delta)
789 	   delta = ewin->icccm.w_inc;
790 	Mode_mr.cur_x -= delta;
791 	break;
792      case XK_Right:
793 	if (!RD_H(Mode_mr.resize_detail))
794 	   Mode_mr.resize_detail |= RD(2, 0);
795 	if (resize && ewin->icccm.w_inc > delta)
796 	   delta = ewin->icccm.w_inc;
797 	Mode_mr.cur_x += delta;
798 	break;
799      case XK_Up:
800 	if (!RD_V(Mode_mr.resize_detail))
801 	   Mode_mr.resize_detail |= RD(0, 1);
802 	if (resize && ewin->icccm.h_inc > delta)
803 	   delta = ewin->icccm.h_inc;
804 	Mode_mr.cur_y -= delta;
805 	break;
806      case XK_Down:
807 	if (!RD_V(Mode_mr.resize_detail))
808 	   Mode_mr.resize_detail |= RD(0, 2);
809 	if (resize && ewin->icccm.h_inc > delta)
810 	   delta = ewin->icccm.h_inc;
811 	Mode_mr.cur_y += delta;
812 	break;
813      }
814 
815    Mode_mr.using_kbd = 2;
816    Mode.events.mx = Mode_mr.cur_x;
817    Mode.events.my = Mode_mr.cur_y;
818 
819    switch (Mode.mode)
820      {
821      case MODE_MOVE_PENDING:
822      case MODE_MOVE:
823 	_MoveResizeMoveHandleMotion();
824 	if (end)
825 	   _MoveResizeMoveEnd(NULL);
826 	break;
827 
828      case MODE_RESIZE:
829      case MODE_RESIZE_H:
830      case MODE_RESIZE_V:
831 	_MoveResizeResizeHandleMotion();
832 	if (end)
833 	   _MoveResizeResizeEnd(NULL);
834 	break;
835 
836      default:
837 	break;
838      }
839 }
840 
841 static void
_MoveResizeHandleMotion(void)842 _MoveResizeHandleMotion(void)
843 {
844    switch (Mode.mode)
845      {
846      case MODE_MOVE_PENDING:
847      case MODE_MOVE:
848 	_MoveResizeMoveHandleMotion();
849 	break;
850 
851      case MODE_RESIZE:
852      case MODE_RESIZE_H:
853      case MODE_RESIZE_V:
854 	_MoveResizeResizeHandleMotion();
855 	break;
856 
857      default:
858 	break;
859      }
860 }
861 
862 void
MoveResizeSuspend(void)863 MoveResizeSuspend(void)
864 {
865    switch (Mode.mode)
866      {
867      case MODE_MOVE_PENDING:
868      case MODE_MOVE:
869 	_MoveResizeMoveSuspend();
870 	break;
871      }
872 }
873 
874 void
MoveResizeResume(void)875 MoveResizeResume(void)
876 {
877    switch (Mode.mode)
878      {
879      case MODE_MOVE_PENDING:
880      case MODE_MOVE:
881 	_MoveResizeMoveResume();
882 	break;
883      }
884 }
885 
886 void
MoveResizeEnd(EWin * ewin)887 MoveResizeEnd(EWin * ewin)
888 {
889    switch (Mode.mode)
890      {
891      case MODE_RESIZE:
892      case MODE_RESIZE_H:
893      case MODE_RESIZE_V:
894 	_MoveResizeResizeEnd(ewin);
895 	break;
896 
897      case MODE_MOVE_PENDING:
898      case MODE_MOVE:
899 	_MoveResizeMoveEnd(ewin);
900 	break;
901      }
902 }
903 
904 static void
_MoveResizeEventHandler(Win win __UNUSED__,XEvent * ev,void * prm __UNUSED__)905 _MoveResizeEventHandler(Win win __UNUSED__, XEvent * ev, void *prm __UNUSED__)
906 {
907    EWin               *ewin;
908 
909 #if 0
910    Eprintf("%s: type=%2d win=%#lx\n", __func__, ev->type, ev->xany.window);
911 #endif
912    switch (ev->type)
913      {
914      default:
915 	break;
916      case KeyPress:
917 	_MoveResizeHandleKey(XLookupKeysym(&ev->xkey, 0));
918 	break;
919 #if 0
920      case ButtonPress:
921 	break;
922 #endif
923      case ButtonRelease:
924 	ewin = Mode_mr.ewin;
925 	if (!ewin)
926 	   break;
927 	MoveResizeEnd(ewin);
928 	BorderCheckState(ewin, ev);
929 	break;
930      case MotionNotify:
931 	_MoveResizeHandleMotion();
932 	break;
933      }
934 }
935 
936 static void
_MoveResizeInit(void)937 _MoveResizeInit(void)
938 {
939    if (Mode_mr.events)
940       return;
941    Mode_mr.events = ECreateEventWindow(VROOT, 0, 0, 1, 1);
942    EMapWindow(Mode_mr.events);
943    EventCallbackRegister(Mode_mr.events, _MoveResizeEventHandler, NULL);
944 }
945