set the frame's initial size to something invalid so the extents hint will be
[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 "obrender/render.h"
33 #include "obrender/theme.h"
34 #include "obt/display.h"
35 #include "obt/xqueue.h"
36 #include "obt/prop.h"
37 #include "obt/keyboard.h"
38
39 #include <X11/Xlib.h>
40 #include <glib.h>
41
42 /* how far windows move and resize with the keyboard arrows */
43 #define KEY_DIST 8
44 #define SYNC_TIMEOUTS 4
45
46 gboolean moveresize_in_progress = FALSE;
47 ObClient *moveresize_client = NULL;
48 #ifdef SYNC
49 XSyncAlarm moveresize_alarm = None;
50 #endif
51
52 static gboolean moving = FALSE; /* TRUE - moving, FALSE - resizing */
53
54 static gint start_x, start_y, start_cx, start_cy, start_cw, start_ch;
55 static gint cur_x, cur_y, cur_w, cur_h;
56 static guint button;
57 static guint32 corner;
58 static ObDirection edge_warp_dir = -1;
59 static gboolean edge_warp_odd = FALSE;
60 static ObDirection key_resize_edge = -1;
61 #ifdef SYNC
62 static guint waiting_for_sync;
63 #endif
64
65 static ObPopup *popup = NULL;
66
67 static void do_move(gboolean keyboard, gint keydist);
68 static void do_resize(void);
69 static void do_edge_warp(gint x, gint y);
70 static void cancel_edge_warp();
71 #ifdef SYNC
72 static gboolean sync_timeout_func(gpointer data);
73 #endif
74
75 static void client_dest(ObClient *client, gpointer data)
76 {
77     if (moveresize_client == client)
78         moveresize_end(TRUE);
79 }
80
81 void moveresize_startup(gboolean reconfig)
82 {
83     popup = popup_new();
84     popup_set_text_align(popup, RR_JUSTIFY_CENTER);
85
86     if (!reconfig)
87         client_add_destroy_notify(client_dest, NULL);
88 }
89
90 void moveresize_shutdown(gboolean reconfig)
91 {
92     if (!reconfig) {
93         if (moveresize_in_progress)
94             moveresize_end(FALSE);
95         client_remove_destroy_notify(client_dest);
96     }
97
98     popup_free(popup);
99     popup = NULL;
100 }
101
102 static void popup_coords(ObClient *c, const gchar *format, gint a, gint b)
103 {
104     gchar *text;
105
106     text = g_strdup_printf(format, a, b);
107     if (config_resize_popup_pos == OB_RESIZE_POS_TOP)
108         popup_position(popup, SouthGravity,
109                        c->frame->area.x
110                      + c->frame->area.width/2,
111                        c->frame->area.y - ob_rr_theme->fbwidth);
112     else if (config_resize_popup_pos == OB_RESIZE_POS_CENTER)
113         popup_position(popup, CenterGravity,
114                        c->frame->area.x + c->frame->area.width / 2,
115                        c->frame->area.y + c->frame->area.height / 2);
116     else /* Fixed */ {
117         const Rect *area = screen_physical_area_active();
118         gint gravity, x, y;
119
120         x = config_resize_popup_fixed.x.pos;
121         if (config_resize_popup_fixed.x.center)
122             x = area->x + area->width/2;
123         else if (config_resize_popup_fixed.x.opposite)
124             x = RECT_RIGHT(*area) - x;
125         else
126             x = area->x + x;
127
128         y = config_resize_popup_fixed.y.pos;
129         if (config_resize_popup_fixed.y.center)
130             y = area->y + area->height/2;
131         else if (config_resize_popup_fixed.y.opposite)
132             y = RECT_RIGHT(*area) - y;
133         else
134             y = area->y + y;
135
136         if (config_resize_popup_fixed.x.center) {
137             if (config_resize_popup_fixed.y.center)
138                 gravity = CenterGravity;
139             else if (config_resize_popup_fixed.y.opposite)
140                 gravity = SouthGravity;
141             else
142                 gravity = NorthGravity;
143         }
144         else if (config_resize_popup_fixed.x.opposite) {
145             if (config_resize_popup_fixed.y.center)
146                 gravity = EastGravity;
147             else if (config_resize_popup_fixed.y.opposite)
148                 gravity = SouthEastGravity;
149             else
150                 gravity = NorthEastGravity;
151         }
152         else {
153             if (config_resize_popup_fixed.y.center)
154                 gravity = WestGravity;
155             else if (config_resize_popup_fixed.y.opposite)
156                 gravity = SouthWestGravity;
157             else
158                 gravity = NorthWestGravity;
159         }
160
161         popup_position(popup, gravity, x, y);
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 = 0;
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 #ifdef SYNC
309         /* turn off the alarm */
310         if (moveresize_alarm != None) {
311             XSyncDestroyAlarm(obt_display, moveresize_alarm);
312             moveresize_alarm = None;
313         }
314
315         obt_main_loop_timeout_remove(ob_main_loop, sync_timeout_func);
316 #endif
317     }
318
319     /* don't use client_move() here, use the same width/height as
320        we've been using during the move, otherwise we get different results
321        when moving maximized windows between monitors of different sizes !
322     */
323     client_configure(moveresize_client,
324                      (cancel ? start_cx : cur_x),
325                      (cancel ? start_cy : cur_y),
326                      (cancel ? start_cw : cur_w),
327                      (cancel ? start_ch : cur_h),
328                      TRUE, TRUE, FALSE);
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        USE cur_x AND cur_y HERE !  Otherwise the try_configure won't know
361        what struts to use !!
362      */
363     x = cur_x;
364     y = cur_y;
365     w = cur_w;
366     h = cur_h;
367     client_try_configure(moveresize_client, &x, &y, &w, &h,
368                          &lw, &lh, TRUE);
369     if (!(w == moveresize_client->area.width &&
370           h == moveresize_client->area.height) &&
371         /* if waiting_for_sync == 0, then we aren't waiting.
372            if it is > SYNC_TIMEOUTS, then we have timed out
373            that many times already, so forget about waiting more */
374         (waiting_for_sync == 0 || waiting_for_sync > SYNC_TIMEOUTS))
375     {
376 #ifdef SYNC
377         if (config_resize_redraw && obt_display_extension_sync &&
378             /* don't send another sync when one is pending */
379             waiting_for_sync == 0 &&
380             moveresize_client->sync_request &&
381             moveresize_client->sync_counter &&
382             !moveresize_client->not_responding)
383         {
384             XEvent ce;
385             XSyncValue val;
386
387             /* increment the value we're waiting for */
388             ++moveresize_client->sync_counter_value;
389             XSyncIntToValue(&val, moveresize_client->sync_counter_value);
390
391             /* tell the client what we're waiting for */
392             ce.xclient.type = ClientMessage;
393             ce.xclient.message_type = OBT_PROP_ATOM(WM_PROTOCOLS);
394             ce.xclient.display = obt_display;
395             ce.xclient.window = moveresize_client->window;
396             ce.xclient.format = 32;
397             ce.xclient.data.l[0] = OBT_PROP_ATOM(NET_WM_SYNC_REQUEST);
398             ce.xclient.data.l[1] = event_time();
399             ce.xclient.data.l[2] = XSyncValueLow32(val);
400             ce.xclient.data.l[3] = XSyncValueHigh32(val);
401             ce.xclient.data.l[4] = 0l;
402             XSendEvent(obt_display, moveresize_client->window, FALSE,
403                        NoEventMask, &ce);
404
405             waiting_for_sync = 1;
406
407             obt_main_loop_timeout_remove(ob_main_loop, sync_timeout_func);
408             obt_main_loop_timeout_add(ob_main_loop, G_USEC_PER_SEC * 2,
409                                       sync_timeout_func,
410                                       NULL, NULL, NULL);
411         }
412 #endif
413
414         /* force a ConfigureNotify, it is part of the spec for SYNC resizing
415            and MUST follow the sync counter notification */
416         client_configure(moveresize_client, cur_x, cur_y, cur_w, cur_h,
417                          TRUE, FALSE, TRUE);
418     }
419
420     /* this would be better with a fixed width font ... XXX can do it better
421        if there are 2 text boxes */
422     if (config_resize_popup_show == 2 || /* == "Always" */
423             (config_resize_popup_show == 1 && /* == "Nonpixel" */
424              moveresize_client->size_inc.width > 1 &&
425              moveresize_client->size_inc.height > 1))
426         popup_coords(moveresize_client, "%d x %d", lw, lh);
427 }
428
429 #ifdef SYNC
430 static gboolean sync_timeout_func(gpointer data)
431 {
432     ++waiting_for_sync; /* we timed out waiting for our sync... */
433     do_resize(); /* ...so let any pending resizes through */
434
435     if (waiting_for_sync > SYNC_TIMEOUTS)
436         return FALSE; /* don't repeat */
437     else
438         return TRUE; /* keep waiting */
439 }
440 #endif
441
442 static void calc_resize(gboolean keyboard, gint keydist, gint *dw, gint *dh,
443                         ObDirection dir)
444 {
445     gint resist, x = 0, y = 0, lw, lh, ow, oh, nw, nh;
446     gint trydw, trydh;
447
448     ow = cur_w;
449     oh = cur_h;
450     nw = ow + *dw;
451     nh = oh + *dh;
452
453     if (!keyboard &&
454         (moveresize_client->max_ratio || moveresize_client->min_ratio))
455     {
456         switch (dir) {
457         case OB_DIRECTION_NORTH:
458         case OB_DIRECTION_SOUTH:
459             /* resize the width based on the height */
460             if (moveresize_client->min_ratio) {
461                 if (nh * moveresize_client->min_ratio > nw)
462                     nw = (gint)(nh * moveresize_client->min_ratio);
463             }
464             if (moveresize_client->max_ratio) {
465                 if (nh * moveresize_client->max_ratio < nw)
466                     nw = (gint)(nh * moveresize_client->max_ratio);
467             }
468             break;
469         default:
470             /* resize the height based on the width */
471             if (moveresize_client->min_ratio) {
472                 if (nh * moveresize_client->min_ratio > nw)
473                     nh = (gint)(nw / moveresize_client->min_ratio);
474             }
475             if (moveresize_client->max_ratio) {
476                 if (nh * moveresize_client->max_ratio < nw)
477                     nh = (gint)(nw / moveresize_client->max_ratio);
478             }
479             break;
480         }
481
482         /* see its actual size (apply aspect ratios) */
483         client_try_configure(moveresize_client, &x, &y, &nw, &nh, &lw, &lh,
484                              TRUE);
485         trydw = nw - ow;
486         trydh = nh - oh;
487     }
488
489     /* resist_size_* needs the frame size */
490     nw += moveresize_client->frame->size.left +
491         moveresize_client->frame->size.right;
492     nh += moveresize_client->frame->size.top +
493         moveresize_client->frame->size.bottom;
494
495     if (keyboard) resist = keydist - 1; /* resist for one key press */
496     else resist = config_resist_win;
497     resist_size_windows(moveresize_client, resist, &nw, &nh, dir);
498     if (!keyboard) resist = config_resist_edge;
499     resist_size_monitors(moveresize_client, resist, &nw, &nh, dir);
500
501     nw -= moveresize_client->frame->size.left +
502         moveresize_client->frame->size.right;
503     nh -= moveresize_client->frame->size.top +
504         moveresize_client->frame->size.bottom;
505
506     *dw = nw - ow;
507     *dh = nh - oh;
508
509     /* take aspect ratios into account for resistance */
510     if (!keyboard &&
511         (moveresize_client->max_ratio || moveresize_client->min_ratio))
512     {
513         if (*dh != trydh) { /* got resisted */
514             /* resize the width based on the height */
515             if (moveresize_client->min_ratio) {
516                 if (nh * moveresize_client->min_ratio > nw)
517                     nw = (gint)(nh * moveresize_client->min_ratio);
518             }
519             if (moveresize_client->max_ratio) {
520                 if (nh * moveresize_client->max_ratio < nw)
521                     nw = (gint)(nh * moveresize_client->max_ratio);
522             }
523         }
524         if (*dw != trydw) { /* got resisted */
525             /* resize the height based on the width */
526             if (moveresize_client->min_ratio) {
527                 if (nh * moveresize_client->min_ratio > nw)
528                     nh = (gint)(nw / moveresize_client->min_ratio);
529             }
530             if (moveresize_client->max_ratio) {
531                 if (nh * moveresize_client->max_ratio < nw)
532                     nh = (gint)(nw / moveresize_client->max_ratio);
533             }
534         }
535     }
536
537     /* make sure it's all valid */
538     client_try_configure(moveresize_client, &x, &y, &nw, &nh, &lw, &lh, TRUE);
539
540     *dw = nw - ow;
541     *dh = nh - oh;
542 }
543
544 static void edge_warp_move_ptr(void)
545 {
546     gint x, y;
547     const Rect* a;
548
549     screen_pointer_pos(&x, &y);
550     a = screen_physical_area_all_monitors();
551
552     switch (edge_warp_dir) {
553         case OB_DIRECTION_NORTH:
554             y = a->height - 1;
555             break;
556         case OB_DIRECTION_EAST:
557             x = a->x;
558             break;
559         case OB_DIRECTION_SOUTH:
560             y = a->y;
561             break;
562         case OB_DIRECTION_WEST:
563             x = a->width - 1;
564             break;
565         default:
566         g_assert_not_reached();
567     }
568
569     XWarpPointer(obt_display, 0, obt_root(ob_screen), 0, 0, 0, 0, x, y);
570 }
571
572 static gboolean edge_warp_delay_func(gpointer data)
573 {
574     guint d;
575
576     /* only fire every second time. so it's fast the first time, but slower
577        after that */
578     if (edge_warp_odd) {
579         d = screen_find_desktop(screen_desktop, edge_warp_dir, TRUE, FALSE);
580         if (d != screen_desktop) {
581             if (config_mouse_screenedgewarp) edge_warp_move_ptr();
582             screen_set_desktop(d, TRUE);
583         }
584     }
585     edge_warp_odd = !edge_warp_odd;
586
587     return TRUE; /* do repeat ! */
588 }
589
590 static void do_edge_warp(gint x, gint y)
591 {
592     guint i;
593     ObDirection dir;
594
595     if (!config_mouse_screenedgetime) return;
596
597     dir = -1;
598
599     for (i = 0; i < screen_num_monitors; ++i) {
600         const Rect *a = screen_physical_area_monitor(i);
601         if (x == RECT_LEFT(*a)) dir = OB_DIRECTION_WEST;
602         if (x == RECT_RIGHT(*a)) dir = OB_DIRECTION_EAST;
603         if (y == RECT_TOP(*a)) dir = OB_DIRECTION_NORTH;
604         if (y == RECT_BOTTOM(*a)) dir = OB_DIRECTION_SOUTH;
605
606         /* try check for xinerama boundaries */
607         if ((x + 1 == RECT_LEFT(*a) || x - 1 == RECT_RIGHT(*a)) &&
608             (dir == OB_DIRECTION_WEST || dir == OB_DIRECTION_EAST))
609         {
610             dir = -1;
611         }
612         if ((y + 1 == RECT_TOP(*a) || y - 1 == RECT_BOTTOM(*a)) &&
613             (dir == OB_DIRECTION_NORTH || dir == OB_DIRECTION_SOUTH))
614         {
615             dir = -1;
616         }
617     }
618
619     if (dir != edge_warp_dir) {
620         cancel_edge_warp();
621         if (dir != (ObDirection)-1) {
622             edge_warp_odd = TRUE; /* switch on the first timeout */
623             obt_main_loop_timeout_add(ob_main_loop,
624                                       config_mouse_screenedgetime * 1000,
625                                       edge_warp_delay_func,
626                                       NULL, NULL, NULL);
627         }
628         edge_warp_dir = dir;
629     }
630 }
631
632 static void cancel_edge_warp(void)
633 {
634     obt_main_loop_timeout_remove(ob_main_loop, edge_warp_delay_func);
635 }
636
637 static void move_with_keys(KeySym sym, guint state)
638 {
639     gint dx = 0, dy = 0, ox = cur_x, oy = cur_y;
640     gint opx, px, opy, py;
641     gint dist = 0;
642
643     /* shift means jump to edge */
644     if (state & obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_SHIFT))
645     {
646         gint x, y;
647         ObDirection dir;
648
649         if (sym == XK_Right)
650             dir = OB_DIRECTION_EAST;
651         else if (sym == XK_Left)
652             dir = OB_DIRECTION_WEST;
653         else if (sym == XK_Down)
654             dir = OB_DIRECTION_SOUTH;
655         else /* sym == XK_Up */
656             dir = OB_DIRECTION_NORTH;
657
658         client_find_move_directional(moveresize_client, dir, &x, &y);
659         dx = x - moveresize_client->area.x;
660         dy = y - moveresize_client->area.y;
661     } else {
662         /* control means fine grained */
663         if (state &
664             obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CONTROL))
665         {
666             dist = 1;
667         }
668         else
669             dist = KEY_DIST;
670
671         if (sym == XK_Right)
672             dx = dist;
673         else if (sym == XK_Left)
674             dx = -dist;
675         else if (sym == XK_Down)
676             dy = dist;
677         else /* if (sym == XK_Up) */
678             dy = -dist;
679     }
680
681     screen_pointer_pos(&opx, &opy);
682     XWarpPointer(obt_display, None, None, 0, 0, 0, 0, dx, dy);
683     /* steal the motion events this causes */
684     XSync(obt_display, FALSE);
685     {
686         XEvent ce;
687         while (xqueue_remove_local(&ce, xqueue_match_type,
688                                    GINT_TO_POINTER(MotionNotify)));
689     }
690     screen_pointer_pos(&px, &py);
691
692     cur_x += dx;
693     cur_y += dy;
694     do_move(TRUE, dist);
695
696     /* because the cursor moves even though the window does
697        not nessesarily (resistance), this adjusts where the curor
698        thinks it started so that it keeps up with where the window
699        actually is */
700     start_x += (px - opx) - (cur_x - ox);
701     start_y += (py - opy) - (cur_y - oy);
702 }
703
704 static void resize_with_keys(KeySym sym, guint state)
705 {
706     gint dw = 0, dh = 0, pdx = 0, pdy = 0, opx, opy, px, py;
707     gint resist = 0;
708     ObDirection dir;
709
710     /* pick the edge if it needs to move */
711     if (sym == XK_Right) {
712         dir = OB_DIRECTION_EAST;
713         if (key_resize_edge != OB_DIRECTION_WEST &&
714             key_resize_edge != OB_DIRECTION_EAST)
715         {
716             key_resize_edge = OB_DIRECTION_EAST;
717             return;
718         }
719     } else if (sym == XK_Left) {
720         dir = OB_DIRECTION_WEST;
721         if (key_resize_edge != OB_DIRECTION_WEST &&
722             key_resize_edge != OB_DIRECTION_EAST)
723         {
724             key_resize_edge = OB_DIRECTION_WEST;
725             return;
726         }
727     } else if (sym == XK_Up) {
728         dir = OB_DIRECTION_NORTH;
729         if (key_resize_edge != OB_DIRECTION_NORTH &&
730             key_resize_edge != OB_DIRECTION_SOUTH)
731         {
732             key_resize_edge = OB_DIRECTION_NORTH;
733             return;
734         }
735     } else /* if (sym == XK_Down) */ {
736         dir = OB_DIRECTION_SOUTH;
737         if (key_resize_edge != OB_DIRECTION_NORTH &&
738             key_resize_edge != OB_DIRECTION_SOUTH)
739         {
740             key_resize_edge = OB_DIRECTION_SOUTH;
741             return;
742         }
743     }
744
745     /* shift means jump to edge */
746     if (state & obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_SHIFT))
747     {
748         gint x, y, w, h;
749
750         if (sym == XK_Right)
751             dir = OB_DIRECTION_EAST;
752         else if (sym == XK_Left)
753             dir = OB_DIRECTION_WEST;
754         else if (sym == XK_Down)
755             dir = OB_DIRECTION_SOUTH;
756         else /* if (sym == XK_Up)) */
757             dir = OB_DIRECTION_NORTH;
758
759         client_find_resize_directional(moveresize_client, key_resize_edge,
760                                        key_resize_edge == dir,
761                                        &x, &y, &w, &h);
762         dw = w - moveresize_client->area.width;
763         dh = h - moveresize_client->area.height;
764     } else {
765         gint distw, disth;
766
767         /* control means fine grained */
768         if (moveresize_client->size_inc.width > 1) {
769             distw = moveresize_client->size_inc.width;
770             resist = 1;
771         }
772         else if (state &
773                  obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CONTROL))
774         {
775             distw = 1;
776             resist = 1;
777         }
778         else {
779             distw = KEY_DIST;
780             resist = KEY_DIST;
781         }
782         if (moveresize_client->size_inc.height > 1) {
783             disth = moveresize_client->size_inc.height;
784             resist = 1;
785         }
786         else if (state &
787                  obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CONTROL))
788         {
789             disth = 1;
790             resist = 1;
791         }
792         else {
793             disth = KEY_DIST;
794             resist = KEY_DIST;
795         }
796
797         if (key_resize_edge == OB_DIRECTION_WEST) {
798             if (dir == OB_DIRECTION_WEST)
799                 dw = distw;
800             else
801                 dw = -distw;
802         }
803         else if (key_resize_edge == OB_DIRECTION_EAST) {
804             if (dir == OB_DIRECTION_EAST)
805                 dw = distw;
806             else
807                 dw = -distw;
808         }
809         else if (key_resize_edge == OB_DIRECTION_NORTH) {
810             if (dir == OB_DIRECTION_NORTH)
811                 dh = disth;
812             else
813                 dh = -disth;
814         }
815         else /*if (key_resize_edge == OB_DIRECTION_SOUTH)*/ {
816             if (dir == OB_DIRECTION_SOUTH)
817                 dh = disth;
818             else
819                 dh = -disth;
820         }
821     }
822
823     calc_resize(TRUE, resist, &dw, &dh, dir);
824     if (key_resize_edge == OB_DIRECTION_WEST)
825         cur_x -= dw;
826     else if (key_resize_edge == OB_DIRECTION_NORTH)
827         cur_y -= dh;
828     cur_w += dw;
829     cur_h += dh;
830
831     /* how to move the pointer to keep up with the change */
832     if (key_resize_edge == OB_DIRECTION_WEST)
833         pdx = -dw;
834     else if (key_resize_edge == OB_DIRECTION_EAST)
835         pdx = dw;
836     else if (key_resize_edge == OB_DIRECTION_NORTH)
837         pdy = -dh;
838     else if (key_resize_edge == OB_DIRECTION_SOUTH)
839         pdy = dh;
840
841     screen_pointer_pos(&opx, &opy);
842     XWarpPointer(obt_display, None, None, 0, 0, 0, 0, pdx, pdy);
843     /* steal the motion events this causes */
844     XSync(obt_display, FALSE);
845     {
846         XEvent ce;
847         while (xqueue_remove_local(&ce, xqueue_match_type,
848                                    GINT_TO_POINTER(MotionNotify)));
849     }
850     screen_pointer_pos(&px, &py);
851
852     do_resize();
853
854     /* because the cursor moves even though the window does
855        not nessesarily (resistance), this adjusts where the cursor
856        thinks it started so that it keeps up with where the window
857        actually is */
858     start_x += (px - opx) - dw;
859     start_y += (py - opy) - dh;
860
861 }
862
863 gboolean moveresize_event(XEvent *e)
864 {
865     gboolean used = FALSE;
866
867     if (!moveresize_in_progress) return FALSE;
868
869     if (e->type == ButtonPress) {
870         if (!button) {
871             start_x = e->xbutton.x_root;
872             start_y = e->xbutton.y_root;
873             button = e->xbutton.button; /* this will end it now */
874         }
875         used = e->xbutton.button == button;
876     } else if (e->type == ButtonRelease) {
877         if (!button || e->xbutton.button == button) {
878             moveresize_end(FALSE);
879             used = TRUE;
880         }
881     } else if (e->type == MotionNotify) {
882         if (moving) {
883             cur_x = start_cx + e->xmotion.x_root - start_x;
884             cur_y = start_cy + e->xmotion.y_root - start_y;
885             do_move(FALSE, 0);
886             do_edge_warp(e->xmotion.x_root, e->xmotion.y_root);
887         } else {
888             gint dw, dh;
889             ObDirection dir;
890
891             if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT)) {
892                 dw = -(e->xmotion.x_root - start_x);
893                 dh = -(e->xmotion.y_root - start_y);
894                 dir = OB_DIRECTION_NORTHWEST;
895             } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP)) {
896                 dw = 0;
897                 dh = -(e->xmotion.y_root - start_y);
898                 dir = OB_DIRECTION_NORTH;
899             } else if (corner ==
900                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT)) {
901                 dw = (e->xmotion.x_root - start_x);
902                 dh = -(e->xmotion.y_root - start_y);
903                 dir = OB_DIRECTION_NORTHEAST;
904             } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT)) {
905                 dw = (e->xmotion.x_root - start_x);
906                 dh = 0;
907                 dir = OB_DIRECTION_EAST;
908             } else if (corner ==
909                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT)) {
910                 dw = (e->xmotion.x_root - start_x);
911                 dh = (e->xmotion.y_root - start_y);
912                 dir = OB_DIRECTION_SOUTHEAST;
913             } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOM))
914             {
915                 dw = 0;
916                 dh = (e->xmotion.y_root - start_y);
917                 dir = OB_DIRECTION_SOUTH;
918             } else if (corner ==
919                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT)) {
920                 dw = -(e->xmotion.x_root - start_x);
921                 dh = (e->xmotion.y_root - start_y);
922                 dir = OB_DIRECTION_SOUTHWEST;
923             } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT)) {
924                 dw = -(e->xmotion.x_root - start_x);
925                 dh = 0;
926                 dir = OB_DIRECTION_WEST;
927             } else if (corner ==
928                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD)) {
929                 dw = (e->xmotion.x_root - start_x);
930                 dh = (e->xmotion.y_root - start_y);
931                 dir = OB_DIRECTION_SOUTHEAST;
932             } else
933                 g_assert_not_reached();
934
935             dw -= cur_w - start_cw;
936             dh -= cur_h - start_ch;
937
938             calc_resize(FALSE, 0, &dw, &dh, dir);
939             cur_w += dw;
940             cur_h += dh;
941
942             if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT) ||
943                 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT) ||
944                 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT))
945             {
946                 cur_x -= dw;
947             }
948             if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT) ||
949                 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP) ||
950                 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT))
951             {
952                 cur_y -= dh;
953             }
954
955             do_resize();
956         }
957         used = TRUE;
958     } else if (e->type == KeyPress) {
959         KeySym sym = obt_keyboard_keypress_to_keysym(e);
960
961         if (sym == XK_Escape) {
962             moveresize_end(TRUE);
963             used = TRUE;
964         } else if (sym == XK_Return || sym == XK_KP_Enter) {
965             moveresize_end(FALSE);
966             used = TRUE;
967         } else if (sym == XK_Right || sym == XK_Left ||
968                    sym == XK_Up || sym == XK_Down)
969         {
970             if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD)) {
971                 resize_with_keys(sym, e->xkey.state);
972                 used = TRUE;
973             } else if (corner ==
974                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD))
975             {
976                 move_with_keys(sym, e->xkey.state);
977                 used = TRUE;
978             }
979         }
980     }
981 #ifdef SYNC
982     else if (e->type == obt_display_extension_sync_basep + XSyncAlarmNotify)
983     {
984         waiting_for_sync = 0; /* we got our sync... */
985         do_resize(); /* ...so try resize if there is more change pending */
986         used = TRUE;
987     }
988 #endif
989     return used;
990 }