allow warping the mouse pointer when switching desktops by bumping into the edge...
[dana/openbox.git] / openbox / moveresize.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    moveresize.c for the Openbox window manager
4    Copyright (c) 2006        Mikael Magnusson
5    Copyright (c) 2003-2007   Dana Jansens
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "grab.h"
21 #include "framerender.h"
22 #include "screen.h"
23 #include "client.h"
24 #include "frame.h"
25 #include "openbox.h"
26 #include "resist.h"
27 #include "popup.h"
28 #include "moveresize.h"
29 #include "config.h"
30 #include "event.h"
31 #include "debug.h"
32 #include "obrender/render.h"
33 #include "obrender/theme.h"
34 #include "obt/display.h"
35 #include "obt/prop.h"
36 #include "obt/keyboard.h"
37
38 #include <X11/Xlib.h>
39 #include <glib.h>
40
41 /* how far windows move and resize with the keyboard arrows */
42 #define KEY_DIST 8
43
44 gboolean moveresize_in_progress = FALSE;
45 ObClient *moveresize_client = NULL;
46 #ifdef SYNC
47 XSyncAlarm moveresize_alarm = None;
48 #endif
49
50 static gboolean moving = FALSE; /* TRUE - moving, FALSE - resizing */
51
52 static gint start_x, start_y, start_cx, start_cy, start_cw, start_ch;
53 static gint cur_x, cur_y, cur_w, cur_h;
54 static guint button;
55 static guint32 corner;
56 static ObDirection edge_warp_dir = -1;
57 static gboolean edge_warp_odd = FALSE;
58 static ObDirection key_resize_edge = -1;
59 #ifdef SYNC
60 static gboolean waiting_for_sync;
61 #endif
62
63 static ObPopup *popup = NULL;
64
65 static void do_move(gboolean keyboard, gint keydist);
66 static void do_resize(void);
67 static void do_edge_warp(gint x, gint y);
68 static void cancel_edge_warp();
69 #ifdef SYNC
70 static gboolean sync_timeout_func(gpointer data);
71 #endif
72
73 static void client_dest(ObClient *client, gpointer data)
74 {
75     if (moveresize_client == client)
76         moveresize_end(TRUE);
77 }
78
79 void moveresize_startup(gboolean reconfig)
80 {
81     popup = popup_new();
82     popup_set_text_align(popup, RR_JUSTIFY_CENTER);
83
84     if (!reconfig)
85         client_add_destroy_notify(client_dest, NULL);
86 }
87
88 void moveresize_shutdown(gboolean reconfig)
89 {
90     if (!reconfig) {
91         if (moveresize_in_progress)
92             moveresize_end(FALSE);
93         client_remove_destroy_notify(client_dest);
94     }
95
96     popup_free(popup);
97     popup = NULL;
98 }
99
100 static void popup_coords(ObClient *c, const gchar *format, gint a, gint b)
101 {
102     gchar *text;
103
104     text = g_strdup_printf(format, a, b);
105     if (config_resize_popup_pos == OB_RESIZE_POS_TOP)
106         popup_position(popup, SouthGravity,
107                        c->frame->area.x
108                      + c->frame->area.width/2,
109                        c->frame->area.y - ob_rr_theme->fbwidth);
110     else if (config_resize_popup_pos == OB_RESIZE_POS_CENTER)
111         popup_position(popup, CenterGravity,
112                        c->frame->area.x + c->frame->area.width / 2,
113                        c->frame->area.y + c->frame->area.height / 2);
114     else /* Fixed */ {
115         const Rect *area = screen_physical_area_active();
116         gint gravity, x, y;
117
118         x = config_resize_popup_fixed.x.pos;
119         if (config_resize_popup_fixed.x.center)
120             x = area->x + area->width/2;
121         else if (config_resize_popup_fixed.x.opposite)
122             x = RECT_RIGHT(*area) - x;
123         else
124             x = area->x + x;
125
126         y = config_resize_popup_fixed.y.pos;
127         if (config_resize_popup_fixed.y.center)
128             y = area->y + area->height/2;
129         else if (config_resize_popup_fixed.y.opposite)
130             y = RECT_RIGHT(*area) - y;
131         else
132             y = area->y + y;
133
134         if (config_resize_popup_fixed.x.center) {
135             if (config_resize_popup_fixed.y.center)
136                 gravity = CenterGravity;
137             else if (config_resize_popup_fixed.y.opposite)
138                 gravity = SouthGravity;
139             else
140                 gravity = NorthGravity;
141         }
142         else if (config_resize_popup_fixed.x.opposite) {
143             if (config_resize_popup_fixed.y.center)
144                 gravity = EastGravity;
145             else if (config_resize_popup_fixed.y.opposite)
146                 gravity = SouthEastGravity;
147             else
148                 gravity = NorthEastGravity;
149         }
150         else {
151             if (config_resize_popup_fixed.y.center)
152                 gravity = WestGravity;
153             else if (config_resize_popup_fixed.y.opposite)
154                 gravity = SouthWestGravity;
155             else
156                 gravity = NorthWestGravity;
157         }
158
159         popup_position(popup, gravity, x, y);
160     }
161     popup_show(popup, text);
162     g_free(text);
163 }
164
165 void moveresize_start(ObClient *c, gint x, gint y, guint b, guint32 cnr)
166 {
167     ObCursor cur;
168     gboolean mv = (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE) ||
169                    cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD));
170     gint up = 1;
171     gint left = 1;
172
173     if (moveresize_in_progress || !c->frame->visible ||
174         !(mv ?
175           (c->functions & OB_CLIENT_FUNC_MOVE) :
176           (c->functions & OB_CLIENT_FUNC_RESIZE)))
177         return;
178
179     if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT)) {
180         cur = OB_CURSOR_NORTHWEST;
181         up = left = -1;
182     }
183     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP)) {
184         cur = OB_CURSOR_NORTH;
185         up = -1;
186     }
187     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT)) {
188         cur = OB_CURSOR_NORTHEAST;
189         up = -1;
190     }
191     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT))
192         cur = OB_CURSOR_EAST;
193     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT))
194         cur = OB_CURSOR_SOUTHEAST;
195     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOM))
196         cur = OB_CURSOR_SOUTH;
197     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT)) {
198         cur = OB_CURSOR_SOUTHWEST;
199         left = -1;
200     }
201     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT)) {
202         cur = OB_CURSOR_WEST;
203         left = -1;
204     }
205     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD))
206         cur = OB_CURSOR_SOUTHEAST;
207     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE))
208         cur = OB_CURSOR_MOVE;
209     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD))
210         cur = OB_CURSOR_MOVE;
211     else
212         g_assert_not_reached();
213
214     /* keep the pointer bounded to the screen for move/resize */
215     if (!grab_pointer(FALSE, TRUE, cur))
216         return;
217     if (!grab_keyboard()) {
218         ungrab_pointer();
219         return;
220     }
221
222     frame_end_iconify_animation(c->frame);
223
224     moving = mv;
225     moveresize_client = c;
226     start_cx = c->area.x;
227     start_cy = c->area.y;
228     start_cw = c->area.width;
229     start_ch = c->area.height;
230     /* these adjustments for the size_inc make resizing a terminal more
231        friendly. you essentially start the resize in the middle of the
232        increment instead of at 0, so you have to move half an increment
233        either way instead of a full increment one and 1 px the other. */
234     start_x = x - (mv ? 0 : left * c->size_inc.width / 2);
235     start_y = y - (mv ? 0 : up * c->size_inc.height / 2);
236     corner = cnr;
237     button = b;
238     key_resize_edge = -1;
239
240     /*
241       have to change start_cx and start_cy if going to do this..
242     if (corner == prop_atoms.net_wm_moveresize_move_keyboard ||
243         corner == prop_atoms.net_wm_moveresize_size_keyboard)
244         XWarpPointer(ob_display, None, c->window, 0, 0, 0, 0,
245                      c->area.width / 2, c->area.height / 2);
246     */
247
248     cur_x = start_cx;
249     cur_y = start_cy;
250     cur_w = start_cw;
251     cur_h = start_ch;
252
253     moveresize_in_progress = TRUE;
254
255 #ifdef SYNC
256     if (config_resize_redraw && !moving && obt_display_extension_sync &&
257         moveresize_client->sync_request && moveresize_client->sync_counter &&
258         !moveresize_client->not_responding)
259     {
260         /* Initialize values for the resize syncing, and create an alarm for
261            the client's xsync counter */
262
263         XSyncValue val;
264         XSyncAlarmAttributes aa;
265
266         /* set the counter to an initial value */
267         XSyncIntToValue(&val, 0);
268         XSyncSetCounter(obt_display, moveresize_client->sync_counter, val);
269
270         /* this will be incremented when we tell the client what we're
271            looking for */
272         moveresize_client->sync_counter_value = 0;
273
274         /* the next sequence we're waiting for with the alarm */
275         XSyncIntToValue(&val, 1);
276
277         /* set an alarm on the counter */
278         aa.trigger.counter = moveresize_client->sync_counter;
279         aa.trigger.wait_value = val;
280         aa.trigger.value_type = XSyncAbsolute;
281         aa.trigger.test_type = XSyncPositiveTransition;
282         aa.events = True;
283         XSyncIntToValue(&aa.delta, 1);
284         moveresize_alarm = XSyncCreateAlarm(obt_display,
285                                             XSyncCACounter |
286                                             XSyncCAValue |
287                                             XSyncCAValueType |
288                                             XSyncCATestType |
289                                             XSyncCADelta |
290                                             XSyncCAEvents,
291                                             &aa);
292
293         waiting_for_sync = FALSE;
294     }
295 #endif
296 }
297
298 void moveresize_end(gboolean cancel)
299 {
300     ungrab_keyboard();
301     ungrab_pointer();
302
303     popup_hide(popup);
304
305     if (!moving) {
306 #ifdef SYNC
307         /* turn off the alarm */
308         if (moveresize_alarm != None) {
309             XSyncDestroyAlarm(obt_display, moveresize_alarm);
310             moveresize_alarm = None;
311         }
312
313         obt_main_loop_timeout_remove(ob_main_loop, sync_timeout_func);
314 #endif
315     }
316
317     /* don't use client_move() here, use the same width/height as
318        we've been using during the move, otherwise we get different results
319        when moving maximized windows between monitors of different sizes !
320     */
321     client_configure(moveresize_client,
322                      (cancel ? start_cx : cur_x),
323                      (cancel ? start_cy : cur_y),
324                      (cancel ? start_cw : cur_w),
325                      (cancel ? start_ch : cur_h),
326                      TRUE, TRUE, FALSE);
327
328     /* dont edge warp after its ended */
329     cancel_edge_warp();
330
331     moveresize_in_progress = FALSE;
332     moveresize_client = NULL;
333 }
334
335 static void do_move(gboolean keyboard, gint keydist)
336 {
337     gint resist;
338
339     if (keyboard) resist = keydist - 1; /* resist for one key press */
340     else resist = config_resist_win;
341     resist_move_windows(moveresize_client, resist, &cur_x, &cur_y);
342     if (!keyboard) resist = config_resist_edge;
343     resist_move_monitors(moveresize_client, resist, &cur_x, &cur_y);
344
345     client_configure(moveresize_client, cur_x, cur_y, cur_w, cur_h,
346                      TRUE, FALSE, FALSE);
347     if (config_resize_popup_show == 2) /* == "Always" */
348         popup_coords(moveresize_client, "%d x %d",
349                      moveresize_client->frame->area.x,
350                      moveresize_client->frame->area.y);
351 }
352
353 static void do_resize(void)
354 {
355     gint x, y, w, h, lw, lh;
356
357     /* see if it is actually going to resize */
358     x = 0;
359     y = 0;
360     w = cur_w;
361     h = cur_h;
362     client_try_configure(moveresize_client, &x, &y, &w, &h,
363                          &lw, &lh, TRUE);
364     if (!(w == moveresize_client->area.width &&
365           h == moveresize_client->area.height))
366     {
367
368 #ifdef SYNC
369         if (config_resize_redraw && obt_display_extension_sync &&
370             moveresize_client->sync_request && moveresize_client->sync_counter &&
371             !moveresize_client->not_responding)
372         {
373             XEvent ce;
374             XSyncValue val;
375
376             /* are we already waiting for the sync counter to catch up? */
377             if (waiting_for_sync)
378                 return;
379
380             /* increment the value we're waiting for */
381             ++moveresize_client->sync_counter_value;
382             XSyncIntToValue(&val, moveresize_client->sync_counter_value);
383
384             /* tell the client what we're waiting for */
385             ce.xclient.type = ClientMessage;
386             ce.xclient.message_type = OBT_PROP_ATOM(WM_PROTOCOLS);
387             ce.xclient.display = obt_display;
388             ce.xclient.window = moveresize_client->window;
389             ce.xclient.format = 32;
390             ce.xclient.data.l[0] = OBT_PROP_ATOM(NET_WM_SYNC_REQUEST);
391             ce.xclient.data.l[1] = event_curtime;
392             ce.xclient.data.l[2] = XSyncValueLow32(val);
393             ce.xclient.data.l[3] = XSyncValueHigh32(val);
394             ce.xclient.data.l[4] = 0l;
395             XSendEvent(obt_display, moveresize_client->window, FALSE,
396                        NoEventMask, &ce);
397
398             waiting_for_sync = TRUE;
399
400             obt_main_loop_timeout_remove(ob_main_loop, sync_timeout_func);
401             obt_main_loop_timeout_add(ob_main_loop, G_USEC_PER_SEC * 2,
402                                       sync_timeout_func,
403                                       NULL, NULL, NULL);
404         }
405 #endif
406
407         client_configure(moveresize_client, cur_x, cur_y, cur_w, cur_h,
408                          TRUE, FALSE, FALSE);
409     }
410
411     /* this would be better with a fixed width font ... XXX can do it better
412        if there are 2 text boxes */
413     if (config_resize_popup_show == 2 || /* == "Always" */
414             (config_resize_popup_show == 1 && /* == "Nonpixel" */
415              moveresize_client->size_inc.width > 1 &&
416              moveresize_client->size_inc.height > 1))
417         popup_coords(moveresize_client, "%d x %d", lw, lh);
418 }
419
420 #ifdef SYNC
421 static gboolean sync_timeout_func(gpointer data)
422 {
423     waiting_for_sync = FALSE; /* we timed out waiting for our sync... */
424     do_resize(); /* ...so let any pending resizes through */
425
426     return FALSE; /* don't repeat */
427 }
428 #endif
429
430 static void calc_resize(gboolean keyboard, gint keydist, gint *dw, gint *dh,
431                         ObDirection dir)
432 {
433     gint resist, x = 0, y = 0, lw, lh, ow, oh, nw, nh;
434     gint trydw, trydh;
435
436     ow = cur_w;
437     oh = cur_h;
438     nw = ow + *dw;
439     nh = oh + *dh;
440
441     if (!keyboard &&
442         (moveresize_client->max_ratio || moveresize_client->min_ratio))
443     {
444         switch (dir) {
445         case OB_DIRECTION_NORTH:
446         case OB_DIRECTION_SOUTH:
447             /* resize the width based on the height */
448             if (moveresize_client->min_ratio) {
449                 if (nh * moveresize_client->min_ratio > nw)
450                     nw = (gint)(nh * moveresize_client->min_ratio);
451             }
452             if (moveresize_client->max_ratio) {
453                 if (nh * moveresize_client->max_ratio < nw)
454                     nw = (gint)(nh * moveresize_client->max_ratio);
455             }
456             break;
457         default:
458             /* resize the height based on the width */
459             if (moveresize_client->min_ratio) {
460                 if (nh * moveresize_client->min_ratio > nw)
461                     nh = (gint)(nw / moveresize_client->min_ratio);
462             }
463             if (moveresize_client->max_ratio) {
464                 if (nh * moveresize_client->max_ratio < nw)
465                     nh = (gint)(nw / moveresize_client->max_ratio);
466             }
467             break;
468         }
469
470         /* see its actual size (apply aspect ratios) */
471         client_try_configure(moveresize_client, &x, &y, &nw, &nh, &lw, &lh,
472                              TRUE);
473         trydw = nw - ow;
474         trydh = nh - oh;
475     }
476
477     /* resist_size_* needs the frame size */
478     nw += moveresize_client->frame->size.left +
479         moveresize_client->frame->size.right;
480     nh += moveresize_client->frame->size.top +
481         moveresize_client->frame->size.bottom;
482
483     if (keyboard) resist = keydist - 1; /* resist for one key press */
484     else resist = config_resist_win;
485     resist_size_windows(moveresize_client, resist, &nw, &nh, dir);
486     if (!keyboard) resist = config_resist_edge;
487     resist_size_monitors(moveresize_client, resist, &nw, &nh, dir);
488
489     nw -= moveresize_client->frame->size.left +
490         moveresize_client->frame->size.right;
491     nh -= moveresize_client->frame->size.top +
492         moveresize_client->frame->size.bottom;
493
494     *dw = nw - ow;
495     *dh = nh - oh;
496
497     /* take aspect ratios into account for resistance */
498     if (!keyboard &&
499         (moveresize_client->max_ratio || moveresize_client->min_ratio))
500     {
501         if (*dh != trydh) { /* got resisted */
502             /* resize the width based on the height */
503             if (moveresize_client->min_ratio) {
504                 if (nh * moveresize_client->min_ratio > nw)
505                     nw = (gint)(nh * moveresize_client->min_ratio);
506             }
507             if (moveresize_client->max_ratio) {
508                 if (nh * moveresize_client->max_ratio < nw)
509                     nw = (gint)(nh * moveresize_client->max_ratio);
510             }
511         }
512         if (*dw != trydw) { /* got resisted */
513             /* resize the height based on the width */
514             if (moveresize_client->min_ratio) {
515                 if (nh * moveresize_client->min_ratio > nw)
516                     nh = (gint)(nw / moveresize_client->min_ratio);
517             }
518             if (moveresize_client->max_ratio) {
519                 if (nh * moveresize_client->max_ratio < nw)
520                     nh = (gint)(nw / moveresize_client->max_ratio);
521             }
522         }
523     }
524
525     /* make sure it's all valid */
526     client_try_configure(moveresize_client, &x, &y, &nw, &nh, &lw, &lh, TRUE);
527
528     *dw = nw - ow;
529     *dh = nh - oh;
530 }
531
532 static void edge_warp_move_ptr(void)
533 {
534     gint x, y;
535     const Rect* a;
536
537     screen_pointer_pos(&x, &y);
538     a = screen_physical_area_all_monitors();
539
540     switch (edge_warp_dir) {
541         case OB_DIRECTION_NORTH:
542             y = a->height - 1;
543             break;
544         case OB_DIRECTION_EAST:
545             x = a->x;
546             break;
547         case OB_DIRECTION_SOUTH:
548             y = a->y;
549             break;
550         case OB_DIRECTION_WEST:
551             x = a->width - 1;
552             break;
553         default:
554         g_assert_not_reached();
555     }
556
557     XWarpPointer(obt_display, 0, obt_root(ob_screen), 0, 0, 0, 0, x, y);
558 }
559
560 static gboolean edge_warp_delay_func(gpointer data)
561 {
562     guint d;
563
564     /* only fire every second time. so it's fast the first time, but slower
565        after that */
566     if (edge_warp_odd) {
567         d = screen_find_desktop(screen_desktop, edge_warp_dir, TRUE, FALSE);
568         if (d != screen_desktop) {
569             if (config_mouse_screenedgewarp) edge_warp_move_ptr();
570             screen_set_desktop(d, TRUE);
571         }
572     }
573     edge_warp_odd = !edge_warp_odd;
574
575     return TRUE; /* do repeat ! */
576 }
577
578 static void do_edge_warp(gint x, gint y)
579 {
580     guint i;
581     ObDirection dir;
582
583     if (!config_mouse_screenedgetime) return;
584
585     dir = -1;
586
587     for (i = 0; i < screen_num_monitors; ++i) {
588         const Rect *a = screen_physical_area_monitor(i);
589         if (x == RECT_LEFT(*a)) dir = OB_DIRECTION_WEST;
590         if (x == RECT_RIGHT(*a)) dir = OB_DIRECTION_EAST;
591         if (y == RECT_TOP(*a)) dir = OB_DIRECTION_NORTH;
592         if (y == RECT_BOTTOM(*a)) dir = OB_DIRECTION_SOUTH;
593
594         /* try check for xinerama boundaries */
595         if ((x + 1 == RECT_LEFT(*a) || x - 1 == RECT_RIGHT(*a)) &&
596             (dir == OB_DIRECTION_WEST || dir == OB_DIRECTION_EAST))
597         {
598             dir = -1;
599         }
600         if ((y + 1 == RECT_TOP(*a) || y - 1 == RECT_BOTTOM(*a)) &&
601             (dir == OB_DIRECTION_NORTH || dir == OB_DIRECTION_SOUTH))
602         {
603             dir = -1;
604         }
605     }
606
607     if (dir != edge_warp_dir) {
608         cancel_edge_warp();
609         if (dir != (ObDirection)-1) {
610             edge_warp_odd = TRUE; /* switch on the first timeout */
611             obt_main_loop_timeout_add(ob_main_loop,
612                                       config_mouse_screenedgetime * 1000,
613                                       edge_warp_delay_func,
614                                       NULL, NULL, NULL);
615         }
616         edge_warp_dir = dir;
617     }
618 }
619
620 static void cancel_edge_warp(void)
621 {
622     obt_main_loop_timeout_remove(ob_main_loop, edge_warp_delay_func);
623 }
624
625 static void move_with_keys(KeySym sym, guint state)
626 {
627     gint dx = 0, dy = 0, ox = cur_x, oy = cur_y;
628     gint opx, px, opy, py;
629     gint dist = 0;
630
631     /* shift means jump to edge */
632     if (state & obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_SHIFT))
633     {
634         gint x, y;
635         ObDirection dir;
636
637         if (sym == XK_Right)
638             dir = OB_DIRECTION_EAST;
639         else if (sym == XK_Left)
640             dir = OB_DIRECTION_WEST;
641         else if (sym == XK_Down)
642             dir = OB_DIRECTION_SOUTH;
643         else /* sym == XK_Up */
644             dir = OB_DIRECTION_NORTH;
645
646         client_find_move_directional(moveresize_client, dir, &x, &y);
647         dx = x - moveresize_client->area.x;
648         dy = y - moveresize_client->area.y;
649     } else {
650         /* control means fine grained */
651         if (state &
652             obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CONTROL))
653         {
654             dist = 1;
655         }
656         else
657             dist = KEY_DIST;
658
659         if (sym == XK_Right)
660             dx = dist;
661         else if (sym == XK_Left)
662             dx = -dist;
663         else if (sym == XK_Down)
664             dy = dist;
665         else /* if (sym == XK_Up) */
666             dy = -dist;
667     }
668
669     screen_pointer_pos(&opx, &opy);
670     XWarpPointer(obt_display, None, None, 0, 0, 0, 0, dx, dy);
671     /* steal the motion events this causes */
672     XSync(obt_display, FALSE);
673     {
674         XEvent ce;
675         while (XCheckTypedEvent(obt_display, MotionNotify, &ce));
676     }
677     screen_pointer_pos(&px, &py);
678
679     cur_x += dx;
680     cur_y += dy;
681     do_move(TRUE, dist);
682
683     /* because the cursor moves even though the window does
684        not nessesarily (resistance), this adjusts where the curor
685        thinks it started so that it keeps up with where the window
686        actually is */
687     start_x += (px - opx) - (cur_x - ox);
688     start_y += (py - opy) - (cur_y - oy);
689 }
690
691 static void resize_with_keys(KeySym sym, guint state)
692 {
693     gint dw = 0, dh = 0, pdx = 0, pdy = 0, opx, opy, px, py;
694     gint resist = 0;
695     ObDirection dir;
696
697     /* pick the edge if it needs to move */
698     if (sym == XK_Right) {
699         dir = OB_DIRECTION_EAST;
700         if (key_resize_edge != OB_DIRECTION_WEST &&
701             key_resize_edge != OB_DIRECTION_EAST)
702         {
703             key_resize_edge = OB_DIRECTION_EAST;
704             return;
705         }
706     } else if (sym == XK_Left) {
707         dir = OB_DIRECTION_WEST;
708         if (key_resize_edge != OB_DIRECTION_WEST &&
709             key_resize_edge != OB_DIRECTION_EAST)
710         {
711             key_resize_edge = OB_DIRECTION_WEST;
712             return;
713         }
714     } else if (sym == XK_Up) {
715         dir = OB_DIRECTION_NORTH;
716         if (key_resize_edge != OB_DIRECTION_NORTH &&
717             key_resize_edge != OB_DIRECTION_SOUTH)
718         {
719             key_resize_edge = OB_DIRECTION_NORTH;
720             return;
721         }
722     } else /* if (sym == XK_Down) */ {
723         dir = OB_DIRECTION_SOUTH;
724         if (key_resize_edge != OB_DIRECTION_NORTH &&
725             key_resize_edge != OB_DIRECTION_SOUTH)
726         {
727             key_resize_edge = OB_DIRECTION_SOUTH;
728             return;
729         }
730     }
731
732     /* shift means jump to edge */
733     if (state & obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_SHIFT))
734     {
735         gint x, y, w, h;
736
737         if (sym == XK_Right)
738             dir = OB_DIRECTION_EAST;
739         else if (sym == XK_Left)
740             dir = OB_DIRECTION_WEST;
741         else if (sym == XK_Down)
742             dir = OB_DIRECTION_SOUTH;
743         else /* if (sym == XK_Up)) */
744             dir = OB_DIRECTION_NORTH;
745
746         client_find_resize_directional(moveresize_client, key_resize_edge,
747                                        key_resize_edge == dir,
748                                        &x, &y, &w, &h);
749         dw = w - moveresize_client->area.width;
750         dh = h - moveresize_client->area.height;
751     } else {
752         gint distw, disth;
753
754         /* control means fine grained */
755         if (moveresize_client->size_inc.width > 1) {
756             distw = moveresize_client->size_inc.width;
757             resist = 1;
758         }
759         else if (state &
760                  obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CONTROL))
761         {
762             distw = 1;
763             resist = 1;
764         }
765         else {
766             distw = KEY_DIST;
767             resist = KEY_DIST;
768         }
769         if (moveresize_client->size_inc.height > 1) {
770             disth = moveresize_client->size_inc.height;
771             resist = 1;
772         }
773         else if (state &
774                  obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CONTROL))
775         {
776             disth = 1;
777             resist = 1;
778         }
779         else {
780             disth = KEY_DIST;
781             resist = KEY_DIST;
782         }
783
784         if (key_resize_edge == OB_DIRECTION_WEST) {
785             if (dir == OB_DIRECTION_WEST)
786                 dw = distw;
787             else
788                 dw = -distw;
789         }
790         else if (key_resize_edge == OB_DIRECTION_EAST) {
791             if (dir == OB_DIRECTION_EAST)
792                 dw = distw;
793             else
794                 dw = -distw;
795         }
796         else if (key_resize_edge == OB_DIRECTION_NORTH) {
797             if (dir == OB_DIRECTION_NORTH)
798                 dh = disth;
799             else
800                 dh = -disth;
801         }
802         else /*if (key_resize_edge == OB_DIRECTION_SOUTH)*/ {
803             if (dir == OB_DIRECTION_SOUTH)
804                 dh = disth;
805             else
806                 dh = -disth;
807         }
808     }
809
810     calc_resize(TRUE, resist, &dw, &dh, dir);
811     if (key_resize_edge == OB_DIRECTION_WEST)
812         cur_x -= dw;
813     else if (key_resize_edge == OB_DIRECTION_NORTH)
814         cur_y -= dh;
815     cur_w += dw;
816     cur_h += dh;
817
818     /* how to move the pointer to keep up with the change */
819     if (key_resize_edge == OB_DIRECTION_WEST)
820         pdx = -dw;
821     else if (key_resize_edge == OB_DIRECTION_EAST)
822         pdx = dw;
823     else if (key_resize_edge == OB_DIRECTION_NORTH)
824         pdy = -dh;
825     else if (key_resize_edge == OB_DIRECTION_SOUTH)
826         pdy = dh;
827
828     screen_pointer_pos(&opx, &opy);
829     XWarpPointer(obt_display, None, None, 0, 0, 0, 0, pdx, pdy);
830     /* steal the motion events this causes */
831     XSync(obt_display, FALSE);
832     {
833         XEvent ce;
834         while (XCheckTypedEvent(obt_display, MotionNotify, &ce));
835     }
836     screen_pointer_pos(&px, &py);
837
838     do_resize();
839
840     /* because the cursor moves even though the window does
841        not nessesarily (resistance), this adjusts where the cursor
842        thinks it started so that it keeps up with where the window
843        actually is */
844     start_x += (px - opx) - dw;
845     start_y += (py - opy) - dh;
846
847 }
848
849 gboolean moveresize_event(XEvent *e)
850 {
851     gboolean used = FALSE;
852
853     if (!moveresize_in_progress) return FALSE;
854
855     if (e->type == ButtonPress) {
856         if (!button) {
857             start_x = e->xbutton.x_root;
858             start_y = e->xbutton.y_root;
859             button = e->xbutton.button; /* this will end it now */
860         }
861         used = e->xbutton.button == button;
862     } else if (e->type == ButtonRelease) {
863         if (!button || e->xbutton.button == button) {
864             moveresize_end(FALSE);
865             used = TRUE;
866         }
867     } else if (e->type == MotionNotify) {
868         if (moving) {
869             cur_x = start_cx + e->xmotion.x_root - start_x;
870             cur_y = start_cy + e->xmotion.y_root - start_y;
871             do_move(FALSE, 0);
872             do_edge_warp(e->xmotion.x_root, e->xmotion.y_root);
873         } else {
874             gint dw, dh;
875             ObDirection dir;
876
877             if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT)) {
878                 dw = -(e->xmotion.x_root - start_x);
879                 dh = -(e->xmotion.y_root - start_y);
880                 dir = OB_DIRECTION_NORTHWEST;
881             } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP)) {
882                 dw = 0;
883                 dh = -(e->xmotion.y_root - start_y);
884                 dir = OB_DIRECTION_NORTH;
885             } else if (corner ==
886                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT)) {
887                 dw = (e->xmotion.x_root - start_x);
888                 dh = -(e->xmotion.y_root - start_y);
889                 dir = OB_DIRECTION_NORTHEAST;
890             } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT)) {
891                 dw = (e->xmotion.x_root - start_x);
892                 dh = 0;
893                 dir = OB_DIRECTION_EAST;
894             } else if (corner ==
895                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT)) {
896                 dw = (e->xmotion.x_root - start_x);
897                 dh = (e->xmotion.y_root - start_y);
898                 dir = OB_DIRECTION_SOUTHEAST;
899             } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOM))
900             {
901                 dw = 0;
902                 dh = (e->xmotion.y_root - start_y);
903                 dir = OB_DIRECTION_SOUTH;
904             } else if (corner ==
905                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT)) {
906                 dw = -(e->xmotion.x_root - start_x);
907                 dh = (e->xmotion.y_root - start_y);
908                 dir = OB_DIRECTION_SOUTHWEST;
909             } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT)) {
910                 dw = -(e->xmotion.x_root - start_x);
911                 dh = 0;
912                 dir = OB_DIRECTION_WEST;
913             } else if (corner ==
914                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD)) {
915                 dw = (e->xmotion.x_root - start_x);
916                 dh = (e->xmotion.y_root - start_y);
917                 dir = OB_DIRECTION_SOUTHEAST;
918             } else
919                 g_assert_not_reached();
920
921             dw -= cur_w - start_cw;
922             dh -= cur_h - start_ch;
923
924             calc_resize(FALSE, 0, &dw, &dh, dir);
925             cur_w += dw;
926             cur_h += dh;
927
928             if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT) ||
929                 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT) ||
930                 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT))
931             {
932                 cur_x -= dw;
933             }
934             if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT) ||
935                 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP) ||
936                 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT))
937             {
938                 cur_y -= dh;
939             }
940
941             do_resize();
942         }
943         used = TRUE;
944     } else if (e->type == KeyPress) {
945         KeySym sym = obt_keyboard_keypress_to_keysym(e);
946
947         if (sym == XK_Escape) {
948             moveresize_end(TRUE);
949             used = TRUE;
950         } else if (sym == XK_Return || sym == XK_KP_Enter) {
951             moveresize_end(FALSE);
952             used = TRUE;
953         } else if (sym == XK_Right || sym == XK_Left ||
954                    sym == XK_Up || sym == XK_Down)
955         {
956             if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD)) {
957                 resize_with_keys(sym, e->xkey.state);
958                 used = TRUE;
959             } else if (corner ==
960                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD))
961             {
962                 move_with_keys(sym, e->xkey.state);
963                 used = TRUE;
964             }
965         }
966     }
967 #ifdef SYNC
968     else if (e->type == obt_display_extension_sync_basep + XSyncAlarmNotify)
969     {
970         waiting_for_sync = FALSE; /* we got our sync... */
971         do_resize(); /* ...so try resize if there is more change pending */
972         used = TRUE;
973     }
974 #endif
975     return used;
976 }