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