Merge branch 'backport' into work
[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 "render/render.h"
33 #include "render/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 *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         g_free(area);
162     }
163     popup_show(popup, text);
164     g_free(text);
165 }
166
167 void moveresize_start(ObClient *c, gint x, gint y, guint b, guint32 cnr)
168 {
169     ObCursor cur;
170     gboolean mv = (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE) ||
171                    cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD));
172     gint up = 1;
173     gint left = 1;
174
175     if (moveresize_in_progress || !c->frame->visible ||
176         !(mv ?
177           (c->functions & OB_CLIENT_FUNC_MOVE) :
178           (c->functions & OB_CLIENT_FUNC_RESIZE)))
179         return;
180
181     if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT)) {
182         cur = OB_CURSOR_NORTHWEST;
183         up = left = -1;
184     }
185     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP)) {
186         cur = OB_CURSOR_NORTH;
187         up = -1;
188     }
189     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT)) {
190         cur = OB_CURSOR_NORTHEAST;
191         up = -1;
192     }
193     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT))
194         cur = OB_CURSOR_EAST;
195     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT))
196         cur = OB_CURSOR_SOUTHEAST;
197     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOM))
198         cur = OB_CURSOR_SOUTH;
199     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT)) {
200         cur = OB_CURSOR_SOUTHWEST;
201         left = -1;
202     }
203     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT)) {
204         cur = OB_CURSOR_WEST;
205         left = -1;
206     }
207     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD))
208         cur = OB_CURSOR_SOUTHEAST;
209     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE))
210         cur = OB_CURSOR_MOVE;
211     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD))
212         cur = OB_CURSOR_MOVE;
213     else
214         g_assert_not_reached();
215
216     /* keep the pointer bounded to the screen for move/resize */
217     if (!grab_pointer(FALSE, TRUE, cur))
218         return;
219     if (!grab_keyboard()) {
220         ungrab_pointer();
221         return;
222     }
223
224     frame_end_iconify_animation(c->frame);
225
226     moving = mv;
227     moveresize_client = c;
228     start_cx = c->area.x;
229     start_cy = c->area.y;
230     start_cw = c->area.width;
231     start_ch = c->area.height;
232     /* these adjustments for the size_inc make resizing a terminal more
233        friendly. you essentially start the resize in the middle of the
234        increment instead of at 0, so you have to move half an increment
235        either way instead of a full increment one and 1 px the other. */
236     start_x = x - (mv ? 0 : left * c->size_inc.width / 2);
237     start_y = y - (mv ? 0 : up * c->size_inc.height / 2);
238     corner = cnr;
239     button = b;
240     key_resize_edge = -1;
241
242     /*
243       have to change start_cx and start_cy if going to do this..
244     if (corner == prop_atoms.net_wm_moveresize_move_keyboard ||
245         corner == prop_atoms.net_wm_moveresize_size_keyboard)
246         XWarpPointer(ob_display, None, c->window, 0, 0, 0, 0,
247                      c->area.width / 2, c->area.height / 2);
248     */
249
250     cur_x = start_cx;
251     cur_y = start_cy;
252     cur_w = start_cw;
253     cur_h = start_ch;
254
255     moveresize_in_progress = TRUE;
256
257 #ifdef SYNC
258     if (config_resize_redraw && !moving && obt_display_extension_sync &&
259         moveresize_client->sync_request && moveresize_client->sync_counter &&
260         !moveresize_client->not_responding)
261     {
262         /* Initialize values for the resize syncing, and create an alarm for
263            the client's xsync counter */
264
265         XSyncValue val;
266         XSyncAlarmAttributes aa;
267
268         /* set the counter to an initial value */
269         XSyncIntToValue(&val, 0);
270         XSyncSetCounter(obt_display, moveresize_client->sync_counter, val);
271
272         /* this will be incremented when we tell the client what we're
273            looking for */
274         moveresize_client->sync_counter_value = 0;
275
276         /* the next sequence we're waiting for with the alarm */
277         XSyncIntToValue(&val, 1);
278
279         /* set an alarm on the counter */
280         aa.trigger.counter = moveresize_client->sync_counter;
281         aa.trigger.wait_value = val;
282         aa.trigger.value_type = XSyncAbsolute;
283         aa.trigger.test_type = XSyncPositiveTransition;
284         aa.events = True;
285         XSyncIntToValue(&aa.delta, 1);
286         moveresize_alarm = XSyncCreateAlarm(obt_display,
287                                             XSyncCACounter |
288                                             XSyncCAValue |
289                                             XSyncCAValueType |
290                                             XSyncCATestType |
291                                             XSyncCADelta |
292                                             XSyncCAEvents,
293                                             &aa);
294
295         waiting_for_sync = FALSE;
296     }
297 #endif
298 }
299
300 void moveresize_end(gboolean cancel)
301 {
302     ungrab_keyboard();
303     ungrab_pointer();
304
305     popup_hide(popup);
306
307     if (moving) {
308         client_move(moveresize_client,
309                     (cancel ? start_cx : cur_x),
310                     (cancel ? start_cy : cur_y));
311     } else {
312 #ifdef SYNC
313         /* turn off the alarm */
314         if (moveresize_alarm != None) {
315             XSyncDestroyAlarm(obt_display, moveresize_alarm);
316             moveresize_alarm = None;
317         }
318
319         obt_main_loop_timeout_remove(ob_main_loop, sync_timeout_func);
320 #endif
321
322         client_configure(moveresize_client,
323                          (cancel ? start_cx : cur_x),
324                          (cancel ? start_cy : cur_y),
325                          (cancel ? start_cw : cur_w),
326                          (cancel ? start_ch : cur_h),
327                          TRUE, TRUE, FALSE);
328     }
329
330     /* dont edge warp after its ended */
331     cancel_edge_warp();
332
333     moveresize_in_progress = FALSE;
334     moveresize_client = NULL;
335 }
336
337 static void do_move(gboolean keyboard, gint keydist)
338 {
339     gint resist;
340
341     if (keyboard) resist = keydist - 1; /* resist for one key press */
342     else resist = config_resist_win;
343     resist_move_windows(moveresize_client, resist, &cur_x, &cur_y);
344     if (!keyboard) resist = config_resist_edge;
345     resist_move_monitors(moveresize_client, resist, &cur_x, &cur_y);
346
347     client_configure(moveresize_client, cur_x, cur_y, cur_w, cur_h,
348                      TRUE, FALSE, FALSE);
349     if (config_resize_popup_show == 2) /* == "Always" */
350         popup_coords(moveresize_client, "%d x %d",
351                      moveresize_client->frame->area.x,
352                      moveresize_client->frame->area.y);
353 }
354
355 static void do_resize(void)
356 {
357     gint x, y, w, h, lw, lh;
358
359     /* see if it is actually going to resize */
360     x = 0;
361     y = 0;
362     w = cur_w;
363     h = cur_h;
364     client_try_configure(moveresize_client, &x, &y, &w, &h,
365                          &lw, &lh, TRUE);
366     if (!(w == moveresize_client->area.width &&
367           h == moveresize_client->area.height))
368     {
369
370 #ifdef SYNC
371         if (config_resize_redraw && obt_display_extension_sync &&
372             moveresize_client->sync_request && moveresize_client->sync_counter &&
373             !moveresize_client->not_responding)
374         {
375             XEvent ce;
376             XSyncValue val;
377
378             /* are we already waiting for the sync counter to catch up? */
379             if (waiting_for_sync)
380                 return;
381
382             /* increment the value we're waiting for */
383             ++moveresize_client->sync_counter_value;
384             XSyncIntToValue(&val, moveresize_client->sync_counter_value);
385
386             /* tell the client what we're waiting for */
387             ce.xclient.type = ClientMessage;
388             ce.xclient.message_type = OBT_PROP_ATOM(WM_PROTOCOLS);
389             ce.xclient.display = obt_display;
390             ce.xclient.window = moveresize_client->window;
391             ce.xclient.format = 32;
392             ce.xclient.data.l[0] = OBT_PROP_ATOM(NET_WM_SYNC_REQUEST);
393             ce.xclient.data.l[1] = event_curtime;
394             ce.xclient.data.l[2] = XSyncValueLow32(val);
395             ce.xclient.data.l[3] = XSyncValueHigh32(val);
396             ce.xclient.data.l[4] = 0l;
397             XSendEvent(obt_display, moveresize_client->window, FALSE,
398                        NoEventMask, &ce);
399
400             waiting_for_sync = TRUE;
401
402             obt_main_loop_timeout_remove(ob_main_loop, sync_timeout_func);
403             obt_main_loop_timeout_add(ob_main_loop, G_USEC_PER_SEC * 2,
404                                       sync_timeout_func,
405                                       NULL, NULL, NULL);
406         }
407 #endif
408
409         client_configure(moveresize_client, cur_x, cur_y, cur_w, cur_h,
410                          TRUE, FALSE, FALSE);
411     }
412
413     /* this would be better with a fixed width font ... XXX can do it better
414        if there are 2 text boxes */
415     if (config_resize_popup_show == 2 || /* == "Always" */
416             (config_resize_popup_show == 1 && /* == "Nonpixel" */
417              moveresize_client->size_inc.width > 1 &&
418              moveresize_client->size_inc.height > 1))
419         popup_coords(moveresize_client, "%d x %d", lw, lh);
420 }
421
422 #ifdef SYNC
423 static gboolean sync_timeout_func(gpointer data)
424 {
425     waiting_for_sync = FALSE; /* we timed out waiting for our sync... */
426     do_resize(); /* ...so let any pending resizes through */
427
428     return FALSE; /* don't repeat */
429 }
430 #endif
431
432 static void calc_resize(gboolean keyboard, gint keydist, gint *dw, gint *dh,
433                         ObDirection dir)
434 {
435     gint resist, x = 0, y = 0, lw, lh, ow, oh, nw, nh;
436     gint trydw, trydh;
437
438     ow = cur_w;
439     oh = cur_h;
440     nw = ow + *dw;
441     nh = oh + *dh;
442
443     if (!keyboard &&
444         (moveresize_client->max_ratio || moveresize_client->min_ratio))
445     {
446         switch (dir) {
447         case OB_DIRECTION_NORTH:
448         case OB_DIRECTION_SOUTH:
449             /* resize the width based on the height */
450             if (moveresize_client->min_ratio) {
451                 if (nh * moveresize_client->min_ratio > nw)
452                     nw = (gint)(nh * moveresize_client->min_ratio);
453             }
454             if (moveresize_client->max_ratio) {
455                 if (nh * moveresize_client->max_ratio < nw)
456                     nw = (gint)(nh * moveresize_client->max_ratio);
457             }
458             break;
459         default:
460             /* resize the height based on the width */
461             if (moveresize_client->min_ratio) {
462                 if (nh * moveresize_client->min_ratio > nw)
463                     nh = (gint)(nw / moveresize_client->min_ratio);
464             }
465             if (moveresize_client->max_ratio) {
466                 if (nh * moveresize_client->max_ratio < nw)
467                     nh = (gint)(nw / moveresize_client->max_ratio);
468             }
469             break;
470         }
471
472         /* see its actual size (apply aspect ratios) */
473         client_try_configure(moveresize_client, &x, &y, &nw, &nh, &lw, &lh,
474                              TRUE);
475         trydw = nw - ow;
476         trydh = nh - oh;
477     }
478
479     /* resist_size_* needs the frame size */
480     nw += moveresize_client->frame->size.left +
481         moveresize_client->frame->size.right;
482     nh += moveresize_client->frame->size.top +
483         moveresize_client->frame->size.bottom;
484
485     if (keyboard) resist = keydist - 1; /* resist for one key press */
486     else resist = config_resist_win;
487     resist_size_windows(moveresize_client, resist, &nw, &nh, dir);
488     if (!keyboard) resist = config_resist_edge;
489     resist_size_monitors(moveresize_client, resist, &nw, &nh, dir);
490
491     nw -= moveresize_client->frame->size.left +
492         moveresize_client->frame->size.right;
493     nh -= moveresize_client->frame->size.top +
494         moveresize_client->frame->size.bottom;
495
496     *dw = nw - ow;
497     *dh = nh - oh;
498
499     /* take aspect ratios into account for resistance */
500     if (!keyboard &&
501         (moveresize_client->max_ratio || moveresize_client->min_ratio))
502     {
503         if (*dh != trydh) { /* got resisted */
504             /* resize the width based on the height */
505             if (moveresize_client->min_ratio) {
506                 if (nh * moveresize_client->min_ratio > nw)
507                     nw = (gint)(nh * moveresize_client->min_ratio);
508             }
509             if (moveresize_client->max_ratio) {
510                 if (nh * moveresize_client->max_ratio < nw)
511                     nw = (gint)(nh * moveresize_client->max_ratio);
512             }
513         }
514         if (*dw != trydw) { /* got resisted */
515             /* resize the height based on the width */
516             if (moveresize_client->min_ratio) {
517                 if (nh * moveresize_client->min_ratio > nw)
518                     nh = (gint)(nw / moveresize_client->min_ratio);
519             }
520             if (moveresize_client->max_ratio) {
521                 if (nh * moveresize_client->max_ratio < nw)
522                     nh = (gint)(nw / moveresize_client->max_ratio);
523             }
524         }
525     }
526
527     /* make sure it's all valid */
528     client_try_configure(moveresize_client, &x, &y, &nw, &nh, &lw, &lh, TRUE);
529
530     *dw = nw - ow;
531     *dh = nh - oh;
532 }
533
534 static gboolean edge_warp_delay_func(gpointer data)
535 {
536     guint d;
537
538     /* only fire every second time. so it's fast the first time, but slower
539        after that */
540     if (edge_warp_odd) {
541         d = screen_find_desktop(screen_desktop, edge_warp_dir, TRUE, FALSE);
542         if (d != screen_desktop) screen_set_desktop(d, TRUE);
543     }
544     edge_warp_odd = !edge_warp_odd;
545
546     return TRUE; /* do repeat ! */
547 }
548
549 static void do_edge_warp(gint x, gint y)
550 {
551     guint i;
552     ObDirection dir;
553
554     if (!config_mouse_screenedgetime) return;
555
556     dir = -1;
557
558     for (i = 0; i < screen_num_monitors; ++i) {
559         Rect *a = screen_physical_area_monitor(i);
560         if (x == RECT_LEFT(*a)) dir = OB_DIRECTION_WEST;
561         if (x == RECT_RIGHT(*a)) dir = OB_DIRECTION_EAST;
562         if (y == RECT_TOP(*a)) dir = OB_DIRECTION_NORTH;
563         if (y == RECT_BOTTOM(*a)) dir = OB_DIRECTION_SOUTH;
564
565         /* try check for xinerama boundaries */
566         if ((x + 1 == RECT_LEFT(*a) || x - 1 == RECT_RIGHT(*a)) &&
567             (dir == OB_DIRECTION_WEST || dir == OB_DIRECTION_EAST))
568         {
569             dir = -1;
570         }
571         if ((y + 1 == RECT_TOP(*a) || y - 1 == RECT_BOTTOM(*a)) &&
572             (dir == OB_DIRECTION_NORTH || dir == OB_DIRECTION_SOUTH))
573         {
574             dir = -1;
575         }
576         g_free(a);
577     }
578
579     if (dir != edge_warp_dir) {
580         cancel_edge_warp();
581         if (dir != (ObDirection)-1) {
582             edge_warp_odd = TRUE; /* switch on the first timeout */
583             obt_main_loop_timeout_add(ob_main_loop,
584                                       config_mouse_screenedgetime * 1000,
585                                       edge_warp_delay_func,
586                                       NULL, NULL, NULL);
587         }
588         edge_warp_dir = dir;
589     }
590 }
591
592 static void cancel_edge_warp(void)
593 {
594     obt_main_loop_timeout_remove(ob_main_loop, edge_warp_delay_func);
595 }
596
597 static void move_with_keys(gint keycode, gint state)
598 {
599     gint dx = 0, dy = 0, ox = cur_x, oy = cur_y;
600     gint opx, px, opy, py;
601     gint dist = 0;
602
603     /* shift means jump to edge */
604     if (state & obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_SHIFT)) {
605         gint x, y;
606         ObDirection dir;
607
608         if (keycode == ob_keycode(OB_KEY_RIGHT))
609             dir = OB_DIRECTION_EAST;
610         else if (keycode == ob_keycode(OB_KEY_LEFT))
611             dir = OB_DIRECTION_WEST;
612         else if (keycode == ob_keycode(OB_KEY_DOWN))
613             dir = OB_DIRECTION_SOUTH;
614         else /* if (keycode == ob_keycode(OB_KEY_UP)) */
615             dir = OB_DIRECTION_NORTH;
616
617         client_find_move_directional(moveresize_client, dir, &x, &y);
618         dx = x - moveresize_client->area.x;
619         dy = y - moveresize_client->area.y;
620     } else {
621         /* control means fine grained */
622         if (state &
623             obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CONTROL))
624         {
625             dist = 1;
626         }
627         else
628             dist = KEY_DIST;
629
630         if (keycode == ob_keycode(OB_KEY_RIGHT))
631             dx = dist;
632         else if (keycode == ob_keycode(OB_KEY_LEFT))
633             dx = -dist;
634         else if (keycode == ob_keycode(OB_KEY_DOWN))
635             dy = dist;
636         else /* if (keycode == ob_keycode(OB_KEY_UP)) */
637             dy = -dist;
638     }
639
640     screen_pointer_pos(&opx, &opy);
641     XWarpPointer(obt_display, None, None, 0, 0, 0, 0, dx, dy);
642     /* steal the motion events this causes */
643     XSync(obt_display, FALSE);
644     {
645         XEvent ce;
646         while (XCheckTypedEvent(obt_display, MotionNotify, &ce));
647     }
648     screen_pointer_pos(&px, &py);
649
650     cur_x += dx;
651     cur_y += dy;
652     do_move(TRUE, dist);
653
654     /* because the cursor moves even though the window does
655        not nessesarily (resistance), this adjusts where the curor
656        thinks it started so that it keeps up with where the window
657        actually is */
658     start_x += (px - opx) - (cur_x - ox);
659     start_y += (py - opy) - (cur_y - oy);
660 }
661
662 static void resize_with_keys(gint keycode, gint state)
663 {
664     gint dw = 0, dh = 0, pdx = 0, pdy = 0, opx, opy, px, py;
665     gint dist = 0, resist = 0;
666     ObDirection dir;
667
668     /* pick the edge if it needs to move */
669     if (keycode == ob_keycode(OB_KEY_RIGHT)) {
670         dir = OB_DIRECTION_EAST;
671         if (key_resize_edge != OB_DIRECTION_WEST &&
672             key_resize_edge != OB_DIRECTION_EAST)
673         {
674             key_resize_edge = OB_DIRECTION_EAST;
675             return;
676         }
677     }
678     if (keycode == ob_keycode(OB_KEY_LEFT)) {
679         dir = OB_DIRECTION_WEST;
680         if (key_resize_edge != OB_DIRECTION_WEST &&
681             key_resize_edge != OB_DIRECTION_EAST)
682         {
683             key_resize_edge = OB_DIRECTION_WEST;
684             return;
685         }
686     }
687     if (keycode == ob_keycode(OB_KEY_UP)) {
688         dir = OB_DIRECTION_NORTH;
689         if (key_resize_edge != OB_DIRECTION_NORTH &&
690             key_resize_edge != OB_DIRECTION_SOUTH)
691         {
692             key_resize_edge = OB_DIRECTION_NORTH;
693             return;
694         }
695     }
696     if (keycode == ob_keycode(OB_KEY_DOWN)) {
697         dir = OB_DIRECTION_SOUTH;
698         if (key_resize_edge != OB_DIRECTION_NORTH &&
699             key_resize_edge != OB_DIRECTION_SOUTH)
700         {
701             key_resize_edge = OB_DIRECTION_SOUTH;
702             return;
703         }
704     }
705
706     /* shift means jump to edge */
707     if (state & obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_SHIFT)) {
708         gint x, y, w, h;
709
710         if (keycode == ob_keycode(OB_KEY_RIGHT))
711             dir = OB_DIRECTION_EAST;
712         else if (keycode == ob_keycode(OB_KEY_LEFT))
713             dir = OB_DIRECTION_WEST;
714         else if (keycode == ob_keycode(OB_KEY_DOWN))
715             dir = OB_DIRECTION_SOUTH;
716         else /* if (keycode == ob_keycode(OB_KEY_UP)) */
717             dir = OB_DIRECTION_NORTH;
718
719         client_find_resize_directional(moveresize_client, key_resize_edge,
720                                        key_resize_edge == dir,
721                                        &x, &y, &w, &h);
722         dw = w - moveresize_client->area.width;
723         dh = h - moveresize_client->area.height;
724     } else {
725         gint distw, disth;
726
727         /* control means fine grained */
728         if (moveresize_client->size_inc.width > 1) {
729             distw = moveresize_client->size_inc.width;
730             resist = 1;
731         }
732         else if (state &
733                  obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CONTROL))
734         {
735             distw = 1;
736             resist = 1;
737         }
738         else {
739             distw = KEY_DIST;
740             resist = KEY_DIST;
741         }
742         if (moveresize_client->size_inc.height > 1) {
743             disth = moveresize_client->size_inc.height;
744             resist = 1;
745         }
746         else if (state &
747                  obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CONTROL))
748         {
749             disth = 1;
750             resist = 1;
751         }
752         else {
753             disth = KEY_DIST;
754             resist = KEY_DIST;
755         }
756
757         if (key_resize_edge == OB_DIRECTION_WEST) {
758             if (dir == OB_DIRECTION_WEST)
759                 dw = (dist = distw);
760             else
761                 dw = -(dist = distw);
762         }
763         else if (key_resize_edge == OB_DIRECTION_EAST) {
764             if (dir == OB_DIRECTION_EAST)
765                 dw = (dist = distw);
766             else
767                 dw = -(dist = distw);
768         }
769         else if (key_resize_edge == OB_DIRECTION_NORTH) {
770             if (dir == OB_DIRECTION_NORTH)
771                 dh = (dist = disth);
772             else
773                 dh = -(dist = disth);
774         }
775         else /*if (key_resize_edge == OB_DIRECTION_SOUTH)*/ {
776             if (dir == OB_DIRECTION_SOUTH)
777                 dh = (dist = disth);
778             else
779                 dh = -(dist = disth);
780         }
781     }
782
783     calc_resize(TRUE, resist, &dw, &dh, dir);
784     if (key_resize_edge == OB_DIRECTION_WEST)
785         cur_x -= dw;
786     else if (key_resize_edge == OB_DIRECTION_NORTH)
787         cur_y -= dh;
788     cur_w += dw;
789     cur_h += dh;
790
791     /* how to move the pointer to keep up with the change */
792     if (key_resize_edge == OB_DIRECTION_WEST)
793         pdx = -dw;
794     else if (key_resize_edge == OB_DIRECTION_EAST)
795         pdx = dw;
796     else if (key_resize_edge == OB_DIRECTION_NORTH)
797         pdy = -dh;
798     else if (key_resize_edge == OB_DIRECTION_SOUTH)
799         pdy = dh;
800
801     screen_pointer_pos(&opx, &opy);
802     XWarpPointer(obt_display, None, None, 0, 0, 0, 0, pdx, pdy);
803     /* steal the motion events this causes */
804     XSync(obt_display, FALSE);
805     {
806         XEvent ce;
807         while (XCheckTypedEvent(obt_display, MotionNotify, &ce));
808     }
809     screen_pointer_pos(&px, &py);
810
811     do_resize();
812
813     /* because the cursor moves even though the window does
814        not nessesarily (resistance), this adjusts where the cursor
815        thinks it started so that it keeps up with where the window
816        actually is */
817     start_x += (px - opx) - dw;
818     start_y += (py - opy) - dh;
819
820 }
821
822 gboolean moveresize_event(XEvent *e)
823 {
824     gboolean used = FALSE;
825
826     if (!moveresize_in_progress) return FALSE;
827
828     if (e->type == ButtonPress) {
829         if (!button) {
830             start_x = e->xbutton.x_root;
831             start_y = e->xbutton.y_root;
832             button = e->xbutton.button; /* this will end it now */
833         }
834         used = e->xbutton.button == button;
835     } else if (e->type == ButtonRelease) {
836         if (!button || e->xbutton.button == button) {
837             moveresize_end(FALSE);
838             used = TRUE;
839         }
840     } else if (e->type == MotionNotify) {
841         if (moving) {
842             cur_x = start_cx + e->xmotion.x_root - start_x;
843             cur_y = start_cy + e->xmotion.y_root - start_y;
844             do_move(FALSE, 0);
845             do_edge_warp(e->xmotion.x_root, e->xmotion.y_root);
846         } else {
847             gint dw, dh;
848             ObDirection dir;
849
850             if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT)) {
851                 dw = -(e->xmotion.x_root - start_x);
852                 dh = -(e->xmotion.y_root - start_y);
853                 dir = OB_DIRECTION_NORTHWEST;
854             } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP)) {
855                 dw = 0;
856                 dh = -(e->xmotion.y_root - start_y);
857                 dir = OB_DIRECTION_NORTH;
858             } else if (corner ==
859                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT)) {
860                 dw = (e->xmotion.x_root - start_x);
861                 dh = -(e->xmotion.y_root - start_y);
862                 dir = OB_DIRECTION_NORTHEAST;
863             } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT)) {
864                 dw = (e->xmotion.x_root - start_x);
865                 dh = 0;
866                 dir = OB_DIRECTION_EAST;
867             } else if (corner ==
868                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT)) {
869                 dw = (e->xmotion.x_root - start_x);
870                 dh = (e->xmotion.y_root - start_y);
871                 dir = OB_DIRECTION_SOUTHEAST;
872             } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOM))
873             {
874                 dw = 0;
875                 dh = (e->xmotion.y_root - start_y);
876                 dir = OB_DIRECTION_SOUTH;
877             } else if (corner ==
878                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT)) {
879                 dw = -(e->xmotion.x_root - start_x);
880                 dh = (e->xmotion.y_root - start_y);
881                 dir = OB_DIRECTION_SOUTHWEST;
882             } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT)) {
883                 dw = -(e->xmotion.x_root - start_x);
884                 dh = 0;
885                 dir = OB_DIRECTION_WEST;
886             } else if (corner ==
887                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD)) {
888                 dw = (e->xmotion.x_root - start_x);
889                 dh = (e->xmotion.y_root - start_y);
890                 dir = OB_DIRECTION_SOUTHEAST;
891             } else
892                 g_assert_not_reached();
893
894             dw -= cur_w - start_cw;
895             dh -= cur_h - start_ch;
896
897             calc_resize(FALSE, 0, &dw, &dh, dir);
898             cur_w += dw;
899             cur_h += dh;
900
901             if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT) ||
902                 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT) ||
903                 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT))
904             {
905                 cur_x -= dw;
906             }
907             if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT) ||
908                 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP) ||
909                 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT))
910             {
911                 cur_y -= dh;
912             }
913
914             do_resize();
915         }
916         used = TRUE;
917     } else if (e->type == KeyPress) {
918         if (e->xkey.keycode == ob_keycode(OB_KEY_ESCAPE)) {
919             moveresize_end(TRUE);
920             used = TRUE;
921         } else if (e->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
922             moveresize_end(FALSE);
923             used = TRUE;
924         } else if (e->xkey.keycode == ob_keycode(OB_KEY_RIGHT) ||
925                    e->xkey.keycode == ob_keycode(OB_KEY_LEFT) ||
926                    e->xkey.keycode == ob_keycode(OB_KEY_DOWN) ||
927                    e->xkey.keycode == ob_keycode(OB_KEY_UP))
928         {
929             if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD)) {
930                 resize_with_keys(e->xkey.keycode, e->xkey.state);
931                 used = TRUE;
932             } else if (corner ==
933                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD))
934             {
935                 move_with_keys(e->xkey.keycode, e->xkey.state);
936                 used = TRUE;
937             }
938         }
939     }
940 #ifdef SYNC
941     else if (e->type == obt_display_extension_sync_basep + XSyncAlarmNotify)
942     {
943         waiting_for_sync = FALSE; /* we got our sync... */
944         do_resize(); /* ...so try resize if there is more change pending */
945         used = TRUE;
946     }
947 #endif
948     return used;
949 }