Merge branch 'backport' into work
[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 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         return;
370     }
371
372 #ifdef SYNC
373     if (config_resize_redraw && obt_display_extension_sync &&
374         moveresize_client->sync_request && moveresize_client->sync_counter &&
375         !moveresize_client->not_responding)
376     {
377         XEvent ce;
378         XSyncValue val;
379
380         /* are we already waiting for the sync counter to catch up? */
381         if (waiting_for_sync)
382             return;
383
384         /* increment the value we're waiting for */
385         ++moveresize_client->sync_counter_value;
386         XSyncIntToValue(&val, moveresize_client->sync_counter_value);
387
388         /* tell the client what we're waiting for */
389         ce.xclient.type = ClientMessage;
390         ce.xclient.message_type = OBT_PROP_ATOM(WM_PROTOCOLS);
391         ce.xclient.display = obt_display;
392         ce.xclient.window = moveresize_client->window;
393         ce.xclient.format = 32;
394         ce.xclient.data.l[0] = OBT_PROP_ATOM(NET_WM_SYNC_REQUEST);
395         ce.xclient.data.l[1] = event_curtime;
396         ce.xclient.data.l[2] = XSyncValueLow32(val);
397         ce.xclient.data.l[3] = XSyncValueHigh32(val);
398         ce.xclient.data.l[4] = 0l;
399         XSendEvent(obt_display, moveresize_client->window, FALSE,
400                    NoEventMask, &ce);
401
402         waiting_for_sync = TRUE;
403
404         obt_main_loop_timeout_remove(ob_main_loop, sync_timeout_func);
405         obt_main_loop_timeout_add(ob_main_loop, G_USEC_PER_SEC * 2,
406                                   sync_timeout_func,
407                                   NULL, NULL, NULL);
408     }
409 #endif
410
411     client_configure(moveresize_client, cur_x, cur_y, cur_w, cur_h,
412                      TRUE, FALSE, FALSE);
413
414     /* this would be better with a fixed width font ... XXX can do it better
415        if there are 2 text boxes */
416     if (config_resize_popup_show == 2 || /* == "Always" */
417             (config_resize_popup_show == 1 && /* == "Nonpixel" */
418              moveresize_client->size_inc.width > 1 &&
419              moveresize_client->size_inc.height > 1))
420         popup_coords(moveresize_client, "%d x %d",
421                      moveresize_client->logical_size.width,
422                      moveresize_client->logical_size.height);
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 }