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