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