make keypad enter do the same thing as return
[mikachu/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         Rect const *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 gboolean edge_warp_delay_func(gpointer data)
533 {
534     guint d;
535
536     /* only fire every second time. so it's fast the first time, but slower
537        after that */
538     if (edge_warp_odd) {
539         d = screen_find_desktop(screen_desktop, edge_warp_dir, TRUE, FALSE);
540         if (d != screen_desktop) screen_set_desktop(d, TRUE);
541     }
542     edge_warp_odd = !edge_warp_odd;
543
544     return TRUE; /* do repeat ! */
545 }
546
547 static void do_edge_warp(gint x, gint y)
548 {
549     guint i;
550     ObDirection dir;
551
552     if (!config_mouse_screenedgetime) return;
553
554     dir = -1;
555
556     for (i = 0; i < screen_num_monitors; ++i) {
557         Rect const *a = screen_physical_area_monitor(i);
558         if (x == RECT_LEFT(*a)) dir = OB_DIRECTION_WEST;
559         if (x == RECT_RIGHT(*a)) dir = OB_DIRECTION_EAST;
560         if (y == RECT_TOP(*a)) dir = OB_DIRECTION_NORTH;
561         if (y == RECT_BOTTOM(*a)) dir = OB_DIRECTION_SOUTH;
562
563         /* try check for xinerama boundaries */
564         if ((x + 1 == RECT_LEFT(*a) || x - 1 == RECT_RIGHT(*a)) &&
565             (dir == OB_DIRECTION_WEST || dir == OB_DIRECTION_EAST))
566         {
567             dir = -1;
568         }
569         if ((y + 1 == RECT_TOP(*a) || y - 1 == RECT_BOTTOM(*a)) &&
570             (dir == OB_DIRECTION_NORTH || dir == OB_DIRECTION_SOUTH))
571         {
572             dir = -1;
573         }
574     }
575
576     if (dir != edge_warp_dir) {
577         cancel_edge_warp();
578         if (dir != (ObDirection)-1) {
579             edge_warp_odd = TRUE; /* switch on the first timeout */
580             obt_main_loop_timeout_add(ob_main_loop,
581                                       config_mouse_screenedgetime * 1000,
582                                       edge_warp_delay_func,
583                                       NULL, NULL, NULL);
584         }
585         edge_warp_dir = dir;
586     }
587 }
588
589 static void cancel_edge_warp(void)
590 {
591     obt_main_loop_timeout_remove(ob_main_loop, edge_warp_delay_func);
592 }
593
594 static void move_with_keys(KeySym sym, guint state)
595 {
596     gint dx = 0, dy = 0, ox = cur_x, oy = cur_y;
597     gint opx, px, opy, py;
598     gint dist = 0;
599
600     /* shift means jump to edge */
601     if (state & obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_SHIFT))
602     {
603         gint x, y;
604         ObDirection dir;
605
606         if (sym == XK_Right)
607             dir = OB_DIRECTION_EAST;
608         else if (sym == XK_Left)
609             dir = OB_DIRECTION_WEST;
610         else if (sym == XK_Down)
611             dir = OB_DIRECTION_SOUTH;
612         else /* sym == XK_Up */
613             dir = OB_DIRECTION_NORTH;
614
615         client_find_move_directional(moveresize_client, dir, &x, &y);
616         dx = x - moveresize_client->area.x;
617         dy = y - moveresize_client->area.y;
618     } else {
619         /* control means fine grained */
620         if (state &
621             obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CONTROL))
622         {
623             dist = 1;
624         }
625         else
626             dist = KEY_DIST;
627
628         if (sym == XK_Right)
629             dx = dist;
630         else if (sym == XK_Left)
631             dx = -dist;
632         else if (sym == XK_Down)
633             dy = dist;
634         else /* if (sym == XK_Up) */
635             dy = -dist;
636     }
637
638     screen_pointer_pos(&opx, &opy);
639     XWarpPointer(obt_display, None, None, 0, 0, 0, 0, dx, dy);
640     /* steal the motion events this causes */
641     XSync(obt_display, FALSE);
642     {
643         XEvent ce;
644         while (XCheckTypedEvent(obt_display, MotionNotify, &ce));
645     }
646     screen_pointer_pos(&px, &py);
647
648     cur_x += dx;
649     cur_y += dy;
650     do_move(TRUE, dist);
651
652     /* because the cursor moves even though the window does
653        not nessesarily (resistance), this adjusts where the curor
654        thinks it started so that it keeps up with where the window
655        actually is */
656     start_x += (px - opx) - (cur_x - ox);
657     start_y += (py - opy) - (cur_y - oy);
658 }
659
660 static void resize_with_keys(KeySym sym, guint state)
661 {
662     gint dw = 0, dh = 0, pdx = 0, pdy = 0, opx, opy, px, py;
663     gint resist = 0;
664     ObDirection dir;
665
666     /* pick the edge if it needs to move */
667     if (sym == XK_Right) {
668         dir = OB_DIRECTION_EAST;
669         if (key_resize_edge != OB_DIRECTION_WEST &&
670             key_resize_edge != OB_DIRECTION_EAST)
671         {
672             key_resize_edge = OB_DIRECTION_EAST;
673             return;
674         }
675     } else if (sym == XK_Left) {
676         dir = OB_DIRECTION_WEST;
677         if (key_resize_edge != OB_DIRECTION_WEST &&
678             key_resize_edge != OB_DIRECTION_EAST)
679         {
680             key_resize_edge = OB_DIRECTION_WEST;
681             return;
682         }
683     } else if (sym == XK_Up) {
684         dir = OB_DIRECTION_NORTH;
685         if (key_resize_edge != OB_DIRECTION_NORTH &&
686             key_resize_edge != OB_DIRECTION_SOUTH)
687         {
688             key_resize_edge = OB_DIRECTION_NORTH;
689             return;
690         }
691     } else /* if (sym == XK_Down) */ {
692         dir = OB_DIRECTION_SOUTH;
693         if (key_resize_edge != OB_DIRECTION_NORTH &&
694             key_resize_edge != OB_DIRECTION_SOUTH)
695         {
696             key_resize_edge = OB_DIRECTION_SOUTH;
697             return;
698         }
699     }
700
701     /* shift means jump to edge */
702     if (state & obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_SHIFT))
703     {
704         gint x, y, w, h;
705
706         if (sym == XK_Right)
707             dir = OB_DIRECTION_EAST;
708         else if (sym == XK_Left)
709             dir = OB_DIRECTION_WEST;
710         else if (sym == XK_Down)
711             dir = OB_DIRECTION_SOUTH;
712         else /* if (sym == XK_Up)) */
713             dir = OB_DIRECTION_NORTH;
714
715         client_find_resize_directional(moveresize_client, key_resize_edge,
716                                        key_resize_edge == dir,
717                                        &x, &y, &w, &h);
718         dw = w - moveresize_client->area.width;
719         dh = h - moveresize_client->area.height;
720     } else {
721         gint distw, disth;
722
723         /* control means fine grained */
724         if (moveresize_client->size_inc.width > 1) {
725             distw = moveresize_client->size_inc.width;
726             resist = 1;
727         }
728         else if (state &
729                  obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CONTROL))
730         {
731             distw = 1;
732             resist = 1;
733         }
734         else {
735             distw = KEY_DIST;
736             resist = KEY_DIST;
737         }
738         if (moveresize_client->size_inc.height > 1) {
739             disth = moveresize_client->size_inc.height;
740             resist = 1;
741         }
742         else if (state &
743                  obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CONTROL))
744         {
745             disth = 1;
746             resist = 1;
747         }
748         else {
749             disth = KEY_DIST;
750             resist = KEY_DIST;
751         }
752
753         if (key_resize_edge == OB_DIRECTION_WEST) {
754             if (dir == OB_DIRECTION_WEST)
755                 dw = distw;
756             else
757                 dw = -distw;
758         }
759         else if (key_resize_edge == OB_DIRECTION_EAST) {
760             if (dir == OB_DIRECTION_EAST)
761                 dw = distw;
762             else
763                 dw = -distw;
764         }
765         else if (key_resize_edge == OB_DIRECTION_NORTH) {
766             if (dir == OB_DIRECTION_NORTH)
767                 dh = disth;
768             else
769                 dh = -disth;
770         }
771         else /*if (key_resize_edge == OB_DIRECTION_SOUTH)*/ {
772             if (dir == OB_DIRECTION_SOUTH)
773                 dh = disth;
774             else
775                 dh = -disth;
776         }
777     }
778
779     calc_resize(TRUE, resist, &dw, &dh, dir);
780     if (key_resize_edge == OB_DIRECTION_WEST)
781         cur_x -= dw;
782     else if (key_resize_edge == OB_DIRECTION_NORTH)
783         cur_y -= dh;
784     cur_w += dw;
785     cur_h += dh;
786
787     /* how to move the pointer to keep up with the change */
788     if (key_resize_edge == OB_DIRECTION_WEST)
789         pdx = -dw;
790     else if (key_resize_edge == OB_DIRECTION_EAST)
791         pdx = dw;
792     else if (key_resize_edge == OB_DIRECTION_NORTH)
793         pdy = -dh;
794     else if (key_resize_edge == OB_DIRECTION_SOUTH)
795         pdy = dh;
796
797     screen_pointer_pos(&opx, &opy);
798     XWarpPointer(obt_display, None, None, 0, 0, 0, 0, pdx, pdy);
799     /* steal the motion events this causes */
800     XSync(obt_display, FALSE);
801     {
802         XEvent ce;
803         while (XCheckTypedEvent(obt_display, MotionNotify, &ce));
804     }
805     screen_pointer_pos(&px, &py);
806
807     do_resize();
808
809     /* because the cursor moves even though the window does
810        not nessesarily (resistance), this adjusts where the cursor
811        thinks it started so that it keeps up with where the window
812        actually is */
813     start_x += (px - opx) - dw;
814     start_y += (py - opy) - dh;
815
816 }
817
818 gboolean moveresize_event(XEvent *e)
819 {
820     gboolean used = FALSE;
821
822     if (!moveresize_in_progress) return FALSE;
823
824     if (e->type == ButtonPress) {
825         if (!button) {
826             start_x = e->xbutton.x_root;
827             start_y = e->xbutton.y_root;
828             button = e->xbutton.button; /* this will end it now */
829         }
830         used = e->xbutton.button == button;
831     } else if (e->type == ButtonRelease) {
832         if (!button || e->xbutton.button == button) {
833             moveresize_end(FALSE);
834             used = TRUE;
835         }
836     } else if (e->type == MotionNotify) {
837         if (moving) {
838             cur_x = start_cx + e->xmotion.x_root - start_x;
839             cur_y = start_cy + e->xmotion.y_root - start_y;
840             do_move(FALSE, 0);
841             do_edge_warp(e->xmotion.x_root, e->xmotion.y_root);
842         } else {
843             gint dw, dh;
844             ObDirection dir;
845
846             if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT)) {
847                 dw = -(e->xmotion.x_root - start_x);
848                 dh = -(e->xmotion.y_root - start_y);
849                 dir = OB_DIRECTION_NORTHWEST;
850             } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP)) {
851                 dw = 0;
852                 dh = -(e->xmotion.y_root - start_y);
853                 dir = OB_DIRECTION_NORTH;
854             } else if (corner ==
855                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT)) {
856                 dw = (e->xmotion.x_root - start_x);
857                 dh = -(e->xmotion.y_root - start_y);
858                 dir = OB_DIRECTION_NORTHEAST;
859             } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT)) {
860                 dw = (e->xmotion.x_root - start_x);
861                 dh = 0;
862                 dir = OB_DIRECTION_EAST;
863             } else if (corner ==
864                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT)) {
865                 dw = (e->xmotion.x_root - start_x);
866                 dh = (e->xmotion.y_root - start_y);
867                 dir = OB_DIRECTION_SOUTHEAST;
868             } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOM))
869             {
870                 dw = 0;
871                 dh = (e->xmotion.y_root - start_y);
872                 dir = OB_DIRECTION_SOUTH;
873             } else if (corner ==
874                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT)) {
875                 dw = -(e->xmotion.x_root - start_x);
876                 dh = (e->xmotion.y_root - start_y);
877                 dir = OB_DIRECTION_SOUTHWEST;
878             } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT)) {
879                 dw = -(e->xmotion.x_root - start_x);
880                 dh = 0;
881                 dir = OB_DIRECTION_WEST;
882             } else if (corner ==
883                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD)) {
884                 dw = (e->xmotion.x_root - start_x);
885                 dh = (e->xmotion.y_root - start_y);
886                 dir = OB_DIRECTION_SOUTHEAST;
887             } else
888                 g_assert_not_reached();
889
890             dw -= cur_w - start_cw;
891             dh -= cur_h - start_ch;
892
893             calc_resize(FALSE, 0, &dw, &dh, dir);
894             cur_w += dw;
895             cur_h += dh;
896
897             if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT) ||
898                 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT) ||
899                 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT))
900             {
901                 cur_x -= dw;
902             }
903             if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT) ||
904                 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP) ||
905                 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT))
906             {
907                 cur_y -= dh;
908             }
909
910             do_resize();
911         }
912         used = TRUE;
913     } else if (e->type == KeyPress) {
914         KeySym sym = obt_keyboard_keypress_to_keysym(e);
915
916         if (sym == XK_Escape) {
917             moveresize_end(TRUE);
918             used = TRUE;
919         } else if (sym == XK_Return || sym == XK_KP_Enter) {
920             moveresize_end(FALSE);
921             used = TRUE;
922         } else if (sym == XK_Right || sym == XK_Left ||
923                    sym == XK_Up || sym == XK_Down)
924         {
925             if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD)) {
926                 resize_with_keys(sym, e->xkey.state);
927                 used = TRUE;
928             } else if (corner ==
929                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD))
930             {
931                 move_with_keys(sym, e->xkey.state);
932                 used = TRUE;
933             }
934         }
935     }
936 #ifdef SYNC
937     else if (e->type == obt_display_extension_sync_basep + XSyncAlarmNotify)
938     {
939         waiting_for_sync = FALSE; /* we got our sync... */
940         do_resize(); /* ...so try resize if there is more change pending */
941         used = TRUE;
942     }
943 #endif
944     return used;
945 }