Merge branch 'backport'
[dana/openbox.git] / openbox / moveresize.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    moveresize.c for the Openbox window manager
4    Copyright (c) 2006        Mikael Magnusson
5    Copyright (c) 2003-2007   Dana Jansens
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "grab.h"
21 #include "framerender.h"
22 #include "screen.h"
23 #include "client.h"
24 #include "frame.h"
25 #include "openbox.h"
26 #include "resist.h"
27 #include "popup.h"
28 #include "moveresize.h"
29 #include "config.h"
30 #include "event.h"
31 #include "debug.h"
32 #include "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_edge_warp(gint x, gint y);
66 static void cancel_edge_warp();
67 #ifdef SYNC
68 static gboolean sync_timeout_func(gpointer data);
69 #endif
70
71 static void client_dest(ObClient *client, gpointer data)
72 {
73     if (moveresize_client == client)
74         moveresize_end(TRUE);
75 }
76
77 void moveresize_startup(gboolean reconfig)
78 {
79     popup = popup_new();
80     popup_set_text_align(popup, RR_JUSTIFY_CENTER);
81
82     if (!reconfig)
83         client_add_destroy_notify(client_dest, NULL);
84 }
85
86 void moveresize_shutdown(gboolean reconfig)
87 {
88     if (!reconfig) {
89         if (moveresize_in_progress)
90             moveresize_end(FALSE);
91         client_remove_destroy_notify(client_dest);
92     }
93
94     popup_free(popup);
95     popup = NULL;
96 }
97
98 static void popup_coords(ObClient *c, const gchar *format, gint a, gint b)
99 {
100     gchar *text;
101
102     text = g_strdup_printf(format, a, b);
103     if (config_resize_popup_pos == OB_RESIZE_POS_TOP)
104         popup_position(popup, SouthGravity,
105                        c->frame->area.x
106                      + c->frame->area.width/2,
107                        c->frame->area.y - ob_rr_theme->fbwidth);
108     else if (config_resize_popup_pos == OB_RESIZE_POS_CENTER)
109         popup_position(popup, CenterGravity,
110                        c->frame->area.x + c->frame->size.left +
111                        c->area.width / 2,
112                        c->frame->area.y + c->frame->size.top +
113                        c->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
356 static void do_resize(void)
357 {
358     gint x, y, w, h, lw, lh;
359
360     /* see if it is actually going to resize */
361     x = 0;
362     y = 0;
363     w = cur_w;
364     h = cur_h;
365     client_try_configure(moveresize_client, &x, &y, &w, &h,
366                          &lw, &lh, TRUE);
367     if (w == moveresize_client->area.width &&
368         h == moveresize_client->area.height)
369     {
370         return;
371     }
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     /* this would be better with a fixed width font ... XXX can do it better
416        if there are 2 text boxes */
417     if (config_resize_popup_show == 2 || /* == "Always" */
418             (config_resize_popup_show == 1 && /* == "Nonpixel" */
419              moveresize_client->size_inc.width > 1 &&
420              moveresize_client->size_inc.height > 1))
421         popup_coords(moveresize_client, "%d x %d",
422                      moveresize_client->logical_size.width,
423                      moveresize_client->logical_size.height);
424 }
425
426 #ifdef SYNC
427 static gboolean sync_timeout_func(gpointer data)
428 {
429     waiting_for_sync = FALSE; /* we timed out waiting for our sync... */
430     do_resize(); /* ...so let any pending resizes through */
431
432     return FALSE; /* don't repeat */
433 }
434 #endif
435
436 static void calc_resize(gboolean keyboard, gint keydist, gint *dw, gint *dh,
437                         ObDirection dir)
438 {
439     gint resist, x = 0, y = 0, lw, lh, ow, oh, nw, nh;
440     gint trydw, trydh;
441
442     ow = cur_w;
443     oh = cur_h;
444     nw = ow + *dw;
445     nh = oh + *dh;
446
447     if (!keyboard &&
448         (moveresize_client->max_ratio || moveresize_client->min_ratio))
449     {
450         switch (dir) {
451         case OB_DIRECTION_NORTH:
452         case OB_DIRECTION_SOUTH:
453             /* resize the width based on the height */
454             if (moveresize_client->min_ratio) {
455                 if (nh * moveresize_client->min_ratio > nw)
456                     nw = (gint)(nh * moveresize_client->min_ratio);
457             }
458             if (moveresize_client->max_ratio) {
459                 if (nh * moveresize_client->max_ratio < nw)
460                     nw = (gint)(nh * moveresize_client->max_ratio);
461             }
462             break;
463         default:
464             /* resize the height based on the width */
465             if (moveresize_client->min_ratio) {
466                 if (nh * moveresize_client->min_ratio > nw)
467                     nh = (gint)(nw / moveresize_client->min_ratio);
468             }
469             if (moveresize_client->max_ratio) {
470                 if (nh * moveresize_client->max_ratio < nw)
471                     nh = (gint)(nw / moveresize_client->max_ratio);
472             }
473             break;
474         }
475
476         /* see its actual size (apply aspect ratios) */
477         client_try_configure(moveresize_client, &x, &y, &nw, &nh, &lw, &lh,
478                              TRUE);
479         trydw = nw - ow;
480         trydh = nh - oh;
481     }
482
483     /* resist_size_* needs the frame size */
484     nw += moveresize_client->frame->size.left +
485         moveresize_client->frame->size.right;
486     nh += moveresize_client->frame->size.top +
487         moveresize_client->frame->size.bottom;
488
489     if (keyboard) resist = keydist - 1; /* resist for one key press */
490     else resist = config_resist_win;
491     resist_size_windows(moveresize_client, resist, &nw, &nh, dir);
492     if (!keyboard) resist = config_resist_edge;
493     resist_size_monitors(moveresize_client, resist, &nw, &nh, dir);
494
495     nw -= moveresize_client->frame->size.left +
496         moveresize_client->frame->size.right;
497     nh -= moveresize_client->frame->size.top +
498         moveresize_client->frame->size.bottom;
499
500     *dw = nw - ow;
501     *dh = nh - oh;
502
503     /* take aspect ratios into account for resistance */
504     if (!keyboard &&
505         (moveresize_client->max_ratio || moveresize_client->min_ratio))
506     {
507         if (*dh != trydh) { /* got resisted */
508             /* resize the width based on the height */
509             if (moveresize_client->min_ratio) {
510                 if (nh * moveresize_client->min_ratio > nw)
511                     nw = (gint)(nh * moveresize_client->min_ratio);
512             }
513             if (moveresize_client->max_ratio) {
514                 if (nh * moveresize_client->max_ratio < nw)
515                     nw = (gint)(nh * moveresize_client->max_ratio);
516             }
517         }
518         if (*dw != trydw) { /* got resisted */
519             /* resize the height based on the width */
520             if (moveresize_client->min_ratio) {
521                 if (nh * moveresize_client->min_ratio > nw)
522                     nh = (gint)(nw / moveresize_client->min_ratio);
523             }
524             if (moveresize_client->max_ratio) {
525                 if (nh * moveresize_client->max_ratio < nw)
526                     nh = (gint)(nw / moveresize_client->max_ratio);
527             }
528         }
529     }
530
531     /* make sure it's all valid */
532     client_try_configure(moveresize_client, &x, &y, &nw, &nh, &lw, &lh, TRUE);
533
534     *dw = nw - ow;
535     *dh = nh - oh;
536 }
537
538 static gboolean edge_warp_delay_func(gpointer data)
539 {
540     guint d;
541
542     /* only fire every second time. so it's fast the first time, but slower
543        after that */
544     if (edge_warp_odd) {
545         d = screen_find_desktop(screen_desktop, edge_warp_dir, TRUE, FALSE);
546         if (d != screen_desktop) screen_set_desktop(d, TRUE);
547     }
548     edge_warp_odd = !edge_warp_odd;
549
550     return TRUE; /* do repeat ! */
551 }
552
553 static void do_edge_warp(gint x, gint y)
554 {
555     guint i;
556     ObDirection dir;
557
558     if (!config_mouse_screenedgetime) return;
559
560     dir = -1;
561
562     for (i = 0; i < screen_num_monitors; ++i) {
563         Rect *a = screen_physical_area_monitor(i);
564         if (x == RECT_LEFT(*a)) dir = OB_DIRECTION_WEST;
565         if (x == RECT_RIGHT(*a)) dir = OB_DIRECTION_EAST;
566         if (y == RECT_TOP(*a)) dir = OB_DIRECTION_NORTH;
567         if (y == RECT_BOTTOM(*a)) dir = OB_DIRECTION_SOUTH;
568
569         /* try check for xinerama boundaries */
570         if ((x + 1 == RECT_LEFT(*a) || x - 1 == RECT_RIGHT(*a)) &&
571             (dir == OB_DIRECTION_WEST || dir == OB_DIRECTION_EAST))
572         {
573             dir = -1;
574         }
575         if ((y + 1 == RECT_TOP(*a) || y - 1 == RECT_BOTTOM(*a)) &&
576             (dir == OB_DIRECTION_NORTH || dir == OB_DIRECTION_SOUTH))
577         {
578             dir = -1;
579         }
580         g_free(a);
581     }
582
583     if (dir != edge_warp_dir) {
584         cancel_edge_warp();
585         if (dir != (ObDirection)-1) {
586             edge_warp_odd = TRUE; /* switch on the first timeout */
587             obt_main_loop_timeout_add(ob_main_loop,
588                                       config_mouse_screenedgetime * 1000,
589                                       edge_warp_delay_func,
590                                       NULL, NULL, NULL);
591         }
592         edge_warp_dir = dir;
593     }
594 }
595
596 static void cancel_edge_warp(void)
597 {
598     obt_main_loop_timeout_remove(ob_main_loop, edge_warp_delay_func);
599 }
600
601 static void move_with_keys(gint keycode, gint state)
602 {
603     gint dx = 0, dy = 0, ox = cur_x, oy = cur_y;
604     gint opx, px, opy, py;
605     gint dist = 0;
606
607     /* shift means jump to edge */
608     if (state & obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_SHIFT)) {
609         gint x, y;
610         ObDirection dir;
611
612         if (keycode == ob_keycode(OB_KEY_RIGHT))
613             dir = OB_DIRECTION_EAST;
614         else if (keycode == ob_keycode(OB_KEY_LEFT))
615             dir = OB_DIRECTION_WEST;
616         else if (keycode == ob_keycode(OB_KEY_DOWN))
617             dir = OB_DIRECTION_SOUTH;
618         else /* if (keycode == ob_keycode(OB_KEY_UP)) */
619             dir = OB_DIRECTION_NORTH;
620
621         client_find_move_directional(moveresize_client, dir, &x, &y);
622         dx = x - moveresize_client->area.x;
623         dy = y - moveresize_client->area.y;
624     } else {
625         /* control means fine grained */
626         if (state &
627             obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CONTROL))
628         {
629             dist = 1;
630         }
631         else
632             dist = KEY_DIST;
633
634         if (keycode == ob_keycode(OB_KEY_RIGHT))
635             dx = dist;
636         else if (keycode == ob_keycode(OB_KEY_LEFT))
637             dx = -dist;
638         else if (keycode == ob_keycode(OB_KEY_DOWN))
639             dy = dist;
640         else /* if (keycode == ob_keycode(OB_KEY_UP)) */
641             dy = -dist;
642     }
643
644     screen_pointer_pos(&opx, &opy);
645     XWarpPointer(obt_display, None, None, 0, 0, 0, 0, dx, dy);
646     /* steal the motion events this causes */
647     XSync(obt_display, FALSE);
648     {
649         XEvent ce;
650         while (XCheckTypedEvent(obt_display, MotionNotify, &ce));
651     }
652     screen_pointer_pos(&px, &py);
653
654     cur_x += dx;
655     cur_y += dy;
656     do_move(TRUE, dist);
657
658     /* because the cursor moves even though the window does
659        not nessesarily (resistance), this adjusts where the curor
660        thinks it started so that it keeps up with where the window
661        actually is */
662     start_x += (px - opx) - (cur_x - ox);
663     start_y += (py - opy) - (cur_y - oy);
664 }
665
666 static void resize_with_keys(gint keycode, gint state)
667 {
668     gint dw = 0, dh = 0, pdx = 0, pdy = 0, opx, opy, px, py;
669     gint dist = 0, resist = 0;
670     ObDirection dir;
671
672     /* pick the edge if it needs to move */
673     if (keycode == ob_keycode(OB_KEY_RIGHT)) {
674         dir = OB_DIRECTION_EAST;
675         if (key_resize_edge != OB_DIRECTION_WEST &&
676             key_resize_edge != OB_DIRECTION_EAST)
677         {
678             key_resize_edge = OB_DIRECTION_EAST;
679             return;
680         }
681     }
682     if (keycode == ob_keycode(OB_KEY_LEFT)) {
683         dir = OB_DIRECTION_WEST;
684         if (key_resize_edge != OB_DIRECTION_WEST &&
685             key_resize_edge != OB_DIRECTION_EAST)
686         {
687             key_resize_edge = OB_DIRECTION_WEST;
688             return;
689         }
690     }
691     if (keycode == ob_keycode(OB_KEY_UP)) {
692         dir = OB_DIRECTION_NORTH;
693         if (key_resize_edge != OB_DIRECTION_NORTH &&
694             key_resize_edge != OB_DIRECTION_SOUTH)
695         {
696             key_resize_edge = OB_DIRECTION_NORTH;
697             return;
698         }
699     }
700     if (keycode == ob_keycode(OB_KEY_DOWN)) {
701         dir = OB_DIRECTION_SOUTH;
702         if (key_resize_edge != OB_DIRECTION_NORTH &&
703             key_resize_edge != OB_DIRECTION_SOUTH)
704         {
705             key_resize_edge = OB_DIRECTION_SOUTH;
706             return;
707         }
708     }
709
710     /* shift means jump to edge */
711     if (state & obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_SHIFT)) {
712         gint x, y, w, h;
713
714         if (keycode == ob_keycode(OB_KEY_RIGHT))
715             dir = OB_DIRECTION_EAST;
716         else if (keycode == ob_keycode(OB_KEY_LEFT))
717             dir = OB_DIRECTION_WEST;
718         else if (keycode == ob_keycode(OB_KEY_DOWN))
719             dir = OB_DIRECTION_SOUTH;
720         else /* if (keycode == ob_keycode(OB_KEY_UP)) */
721             dir = OB_DIRECTION_NORTH;
722
723         client_find_resize_directional(moveresize_client, key_resize_edge,
724                                        key_resize_edge == dir,
725                                        &x, &y, &w, &h);
726         dw = w - moveresize_client->area.width;
727         dh = h - moveresize_client->area.height;
728     } else {
729         gint distw, disth;
730
731         /* control means fine grained */
732         if (moveresize_client->size_inc.width > 1) {
733             distw = moveresize_client->size_inc.width;
734             resist = 1;
735         }
736         else if (state &
737                  obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CONTROL))
738         {
739             distw = 1;
740             resist = 1;
741         }
742         else {
743             distw = KEY_DIST;
744             resist = KEY_DIST;
745         }
746         if (moveresize_client->size_inc.height > 1) {
747             disth = moveresize_client->size_inc.height;
748             resist = 1;
749         }
750         else if (state &
751                  obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CONTROL))
752         {
753             disth = 1;
754             resist = 1;
755         }
756         else {
757             disth = KEY_DIST;
758             resist = KEY_DIST;
759         }
760
761         if (key_resize_edge == OB_DIRECTION_WEST) {
762             if (dir == OB_DIRECTION_WEST)
763                 dw = (dist = distw);
764             else
765                 dw = -(dist = distw);
766         }
767         else if (key_resize_edge == OB_DIRECTION_EAST) {
768             if (dir == OB_DIRECTION_EAST)
769                 dw = (dist = distw);
770             else
771                 dw = -(dist = distw);
772         }
773         else if (key_resize_edge == OB_DIRECTION_NORTH) {
774             if (dir == OB_DIRECTION_NORTH)
775                 dh = (dist = disth);
776             else
777                 dh = -(dist = disth);
778         }
779         else /*if (key_resize_edge == OB_DIRECTION_SOUTH)*/ {
780             if (dir == OB_DIRECTION_SOUTH)
781                 dh = (dist = disth);
782             else
783                 dh = -(dist = disth);
784         }
785     }
786
787     calc_resize(TRUE, resist, &dw, &dh, dir);
788     if (key_resize_edge == OB_DIRECTION_WEST)
789         cur_x -= dw;
790     else if (key_resize_edge == OB_DIRECTION_NORTH)
791         cur_y -= dh;
792     cur_w += dw;
793     cur_h += dh;
794
795     /* how to move the pointer to keep up with the change */
796     if (key_resize_edge == OB_DIRECTION_WEST)
797         pdx = -dw;
798     else if (key_resize_edge == OB_DIRECTION_EAST)
799         pdx = dw;
800     else if (key_resize_edge == OB_DIRECTION_NORTH)
801         pdy = -dh;
802     else if (key_resize_edge == OB_DIRECTION_SOUTH)
803         pdy = dh;
804
805     screen_pointer_pos(&opx, &opy);
806     XWarpPointer(obt_display, None, None, 0, 0, 0, 0, pdx, pdy);
807     /* steal the motion events this causes */
808     XSync(obt_display, FALSE);
809     {
810         XEvent ce;
811         while (XCheckTypedEvent(obt_display, MotionNotify, &ce));
812     }
813     screen_pointer_pos(&px, &py);
814
815     do_resize();
816
817     /* because the cursor moves even though the window does
818        not nessesarily (resistance), this adjusts where the cursor
819        thinks it started so that it keeps up with where the window
820        actually is */
821     start_x += (px - opx) - dw;
822     start_y += (py - opy) - dh;
823
824 }
825
826 gboolean moveresize_event(XEvent *e)
827 {
828     gboolean used = FALSE;
829
830     if (!moveresize_in_progress) return FALSE;
831
832     if (e->type == ButtonPress) {
833         if (!button) {
834             start_x = e->xbutton.x_root;
835             start_y = e->xbutton.y_root;
836             button = e->xbutton.button; /* this will end it now */
837         }
838         used = e->xbutton.button == button;
839     } else if (e->type == ButtonRelease) {
840         if (!button || e->xbutton.button == button) {
841             moveresize_end(FALSE);
842             used = TRUE;
843         }
844     } else if (e->type == MotionNotify) {
845         if (moving) {
846             cur_x = start_cx + e->xmotion.x_root - start_x;
847             cur_y = start_cy + e->xmotion.y_root - start_y;
848             do_move(FALSE, 0);
849             do_edge_warp(e->xmotion.x_root, e->xmotion.y_root);
850         } else {
851             gint dw, dh;
852             ObDirection dir;
853
854             if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT)) {
855                 dw = -(e->xmotion.x_root - start_x);
856                 dh = -(e->xmotion.y_root - start_y);
857                 dir = OB_DIRECTION_NORTHWEST;
858             } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP)) {
859                 dw = 0;
860                 dh = -(e->xmotion.y_root - start_y);
861                 dir = OB_DIRECTION_NORTH;
862             } else if (corner ==
863                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT)) {
864                 dw = (e->xmotion.x_root - start_x);
865                 dh = -(e->xmotion.y_root - start_y);
866                 dir = OB_DIRECTION_NORTHEAST;
867             } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT)) {
868                 dw = (e->xmotion.x_root - start_x);
869                 dh = 0;
870                 dir = OB_DIRECTION_EAST;
871             } else if (corner ==
872                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT)) {
873                 dw = (e->xmotion.x_root - start_x);
874                 dh = (e->xmotion.y_root - start_y);
875                 dir = OB_DIRECTION_SOUTHEAST;
876             } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOM))
877             {
878                 dw = 0;
879                 dh = (e->xmotion.y_root - start_y);
880                 dir = OB_DIRECTION_SOUTH;
881             } else if (corner ==
882                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT)) {
883                 dw = -(e->xmotion.x_root - start_x);
884                 dh = (e->xmotion.y_root - start_y);
885                 dir = OB_DIRECTION_SOUTHWEST;
886             } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT)) {
887                 dw = -(e->xmotion.x_root - start_x);
888                 dh = 0;
889                 dir = OB_DIRECTION_WEST;
890             } else if (corner ==
891                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD)) {
892                 dw = (e->xmotion.x_root - start_x);
893                 dh = (e->xmotion.y_root - start_y);
894                 dir = OB_DIRECTION_SOUTHEAST;
895             } else
896                 g_assert_not_reached();
897
898             dw -= cur_w - start_cw;
899             dh -= cur_h - start_ch;
900
901             calc_resize(FALSE, 0, &dw, &dh, dir);
902             cur_w += dw;
903             cur_h += dh;
904
905             if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT) ||
906                 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT) ||
907                 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT))
908             {
909                 cur_x -= dw;
910             }
911             if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT) ||
912                 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP) ||
913                 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT))
914             {
915                 cur_y -= dh;
916             }
917
918             do_resize();
919         }
920         used = TRUE;
921     } else if (e->type == KeyPress) {
922         if (e->xkey.keycode == ob_keycode(OB_KEY_ESCAPE)) {
923             moveresize_end(TRUE);
924             used = TRUE;
925         } else if (e->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
926             moveresize_end(FALSE);
927             used = TRUE;
928         } else if (e->xkey.keycode == ob_keycode(OB_KEY_RIGHT) ||
929                    e->xkey.keycode == ob_keycode(OB_KEY_LEFT) ||
930                    e->xkey.keycode == ob_keycode(OB_KEY_DOWN) ||
931                    e->xkey.keycode == ob_keycode(OB_KEY_UP))
932         {
933             if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD)) {
934                 resize_with_keys(e->xkey.keycode, e->xkey.state);
935                 used = TRUE;
936             } else if (corner ==
937                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD))
938             {
939                 move_with_keys(e->xkey.keycode, e->xkey.state);
940                 used = TRUE;
941             }
942         }
943     }
944 #ifdef SYNC
945     else if (e->type == obt_display_extension_sync_basep + XSyncAlarmNotify)
946     {
947         waiting_for_sync = FALSE; /* we got our sync... */
948         do_resize(); /* ...so try resize if there is more change pending */
949         used = TRUE;
950     }
951 #endif
952     return used;
953 }