make warping on screen edge keep firing if you don't move the mouse away from the...
[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 "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(FALSE);
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()
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         if (dir == (ObDirection)-1)
530             cancel_edge_warp();
531         else {
532             edge_warp_odd = TRUE; /* switch on the first timeout */
533             ob_main_loop_timeout_add(ob_main_loop,
534                                      config_mouse_screenedgetime * 1000,
535                                      edge_warp_delay_func,
536                                      NULL, NULL, NULL);
537         }
538         edge_warp_dir = dir;
539     }
540 }
541
542 static void cancel_edge_warp()
543 {
544     ob_main_loop_timeout_remove(ob_main_loop, edge_warp_delay_func);
545 }
546
547 static void move_with_keys(gint keycode, gint state)
548 {
549     gint dx = 0, dy = 0, ox = cur_x, oy = cur_y;
550     gint opx, px, opy, py;
551     gint dist = 0;
552
553     /* shift means jump to edge */
554     if (state & modkeys_key_to_mask(OB_MODKEY_KEY_SHIFT)) {
555         gint x, y;
556         ObDirection dir;
557
558         if (keycode == ob_keycode(OB_KEY_RIGHT))
559             dir = OB_DIRECTION_EAST;
560         else if (keycode == ob_keycode(OB_KEY_LEFT))
561             dir = OB_DIRECTION_WEST;
562         else if (keycode == ob_keycode(OB_KEY_DOWN))
563             dir = OB_DIRECTION_SOUTH;
564         else /* if (keycode == ob_keycode(OB_KEY_UP)) */
565             dir = OB_DIRECTION_NORTH;
566
567         client_find_move_directional(moveresize_client, dir, &x, &y);
568         dx = x - moveresize_client->area.x;
569         dy = y - moveresize_client->area.y;
570     } else {
571         /* control means fine grained */
572         if (state & modkeys_key_to_mask(OB_MODKEY_KEY_CONTROL))
573             dist = 1;
574         else
575             dist = KEY_DIST;
576
577         if (keycode == ob_keycode(OB_KEY_RIGHT))
578             dx = dist;
579         else if (keycode == ob_keycode(OB_KEY_LEFT))
580             dx = -dist;
581         else if (keycode == ob_keycode(OB_KEY_DOWN))
582             dy = dist;
583         else /* if (keycode == ob_keycode(OB_KEY_UP)) */
584             dy = -dist;
585     }
586
587     screen_pointer_pos(&opx, &opy);
588     XWarpPointer(ob_display, None, None, 0, 0, 0, 0, dx, dy);
589     /* steal the motion events this causes */
590     XSync(ob_display, FALSE);
591     {
592         XEvent ce;
593         while (XCheckTypedEvent(ob_display, MotionNotify, &ce));
594     }
595     screen_pointer_pos(&px, &py);
596
597     cur_x += dx;
598     cur_y += dy;
599     do_move(TRUE, dist);
600
601     /* because the cursor moves even though the window does
602        not nessesarily (resistance), this adjusts where the curor
603        thinks it started so that it keeps up with where the window
604        actually is */
605     start_x += (px - opx) - (cur_x - ox);
606     start_y += (py - opy) - (cur_y - oy);
607 }
608
609 static void resize_with_keys(gint keycode, gint state)
610 {
611     gint dw = 0, dh = 0, pdx = 0, pdy = 0, opx, opy, px, py;
612     gint dist = 0, resist = 0;
613     ObDirection dir;
614
615     /* pick the edge if it needs to move */
616     if (keycode == ob_keycode(OB_KEY_RIGHT)) {
617         dir = OB_DIRECTION_EAST;
618         if (key_resize_edge != OB_DIRECTION_WEST &&
619             key_resize_edge != OB_DIRECTION_EAST)
620         {
621             key_resize_edge = OB_DIRECTION_EAST;
622             return;
623         }
624     }
625     if (keycode == ob_keycode(OB_KEY_LEFT)) {
626         dir = OB_DIRECTION_WEST;
627         if (key_resize_edge != OB_DIRECTION_WEST &&
628             key_resize_edge != OB_DIRECTION_EAST)
629         {
630             key_resize_edge = OB_DIRECTION_WEST;
631             return;
632         }
633     }
634     if (keycode == ob_keycode(OB_KEY_UP)) {
635         dir = OB_DIRECTION_NORTH;
636         if (key_resize_edge != OB_DIRECTION_NORTH &&
637             key_resize_edge != OB_DIRECTION_SOUTH)
638         {
639             key_resize_edge = OB_DIRECTION_NORTH;
640             return;
641         }
642     }
643     if (keycode == ob_keycode(OB_KEY_DOWN)) {
644         dir = OB_DIRECTION_SOUTH;
645         if (key_resize_edge != OB_DIRECTION_NORTH &&
646             key_resize_edge != OB_DIRECTION_SOUTH)
647         {
648             key_resize_edge = OB_DIRECTION_SOUTH;
649             return;
650         }
651     }
652
653     /* shift means jump to edge */
654     if (state & modkeys_key_to_mask(OB_MODKEY_KEY_SHIFT)) {
655         gint x, y, w, h;
656
657         if (keycode == ob_keycode(OB_KEY_RIGHT))
658             dir = OB_DIRECTION_EAST;
659         else if (keycode == ob_keycode(OB_KEY_LEFT))
660             dir = OB_DIRECTION_WEST;
661         else if (keycode == ob_keycode(OB_KEY_DOWN))
662             dir = OB_DIRECTION_SOUTH;
663         else /* if (keycode == ob_keycode(OB_KEY_UP)) */
664             dir = OB_DIRECTION_NORTH;
665
666         client_find_resize_directional(moveresize_client, key_resize_edge,
667                                        key_resize_edge == dir,
668                                        &x, &y, &w, &h);
669         dw = w - moveresize_client->area.width;
670         dh = h - moveresize_client->area.height;
671     } else {
672         gint distw, disth;
673
674         /* control means fine grained */
675         if (moveresize_client->size_inc.width > 1) {
676             distw = moveresize_client->size_inc.width;
677             resist = 1;
678         }
679         else if (state & modkeys_key_to_mask(OB_MODKEY_KEY_CONTROL)) {
680             distw = 1;
681             resist = 1;
682         }
683         else {
684             distw = KEY_DIST;
685             resist = KEY_DIST;
686         }
687         if (moveresize_client->size_inc.height > 1) {
688             disth = moveresize_client->size_inc.height;
689             resist = 1;
690         }
691         else if (state & modkeys_key_to_mask(OB_MODKEY_KEY_CONTROL)) {
692             disth = 1;
693             resist = 1;
694         }
695         else {
696             disth = KEY_DIST;
697             resist = KEY_DIST;
698         }
699
700         if (key_resize_edge == OB_DIRECTION_WEST) {
701             if (dir == OB_DIRECTION_WEST)
702                 dw = (dist = distw);
703             else
704                 dw = -(dist = distw);
705         }
706         else if (key_resize_edge == OB_DIRECTION_EAST) {
707             if (dir == OB_DIRECTION_EAST)
708                 dw = (dist = distw);
709             else
710                 dw = -(dist = distw);
711         }
712         else if (key_resize_edge == OB_DIRECTION_NORTH) {
713             if (dir == OB_DIRECTION_NORTH)
714                 dh = (dist = disth);
715             else
716                 dh = -(dist = disth);
717         }
718         else /*if (key_resize_edge == OB_DIRECTION_SOUTH)*/ {
719             if (dir == OB_DIRECTION_SOUTH)
720                 dh = (dist = disth);
721             else
722                 dh = -(dist = disth);
723         }
724     }
725
726     calc_resize(TRUE, resist, &dw, &dh, dir);
727     if (key_resize_edge == OB_DIRECTION_WEST)
728         cur_x -= dw;
729     else if (key_resize_edge == OB_DIRECTION_NORTH)
730         cur_y -= dh;
731     cur_w += dw;
732     cur_h += dh;
733
734     /* how to move the pointer to keep up with the change */
735     if (key_resize_edge == OB_DIRECTION_WEST)
736         pdx = -dw;
737     else if (key_resize_edge == OB_DIRECTION_EAST)
738         pdx = dw;
739     else if (key_resize_edge == OB_DIRECTION_NORTH)
740         pdy = -dh;
741     else if (key_resize_edge == OB_DIRECTION_SOUTH)
742         pdy = dh;
743
744     screen_pointer_pos(&opx, &opy);
745     XWarpPointer(ob_display, None, None, 0, 0, 0, 0, pdx, pdy);
746     /* steal the motion events this causes */
747     XSync(ob_display, FALSE);
748     {
749         XEvent ce;
750         while (XCheckTypedEvent(ob_display, MotionNotify, &ce));
751     }
752     screen_pointer_pos(&px, &py);
753
754     do_resize();
755
756     /* because the cursor moves even though the window does
757        not nessesarily (resistance), this adjusts where the cursor
758        thinks it started so that it keeps up with where the window
759        actually is */
760     start_x += (px - opx) - dw;
761     start_y += (py - opy) - dh;
762
763 }
764
765 gboolean moveresize_event(XEvent *e)
766 {
767     gboolean used = FALSE;
768
769     if (!moveresize_in_progress) return FALSE;
770
771     if (e->type == ButtonPress) {
772         if (!button) {
773             start_x = e->xbutton.x_root;
774             start_y = e->xbutton.y_root;
775             button = e->xbutton.button; /* this will end it now */
776         }
777         used = e->xbutton.button == button;
778     } else if (e->type == ButtonRelease) {
779         if (!button || e->xbutton.button == button) {
780             moveresize_end(FALSE);
781             used = TRUE;
782         }
783     } else if (e->type == MotionNotify) {
784         if (moving) {
785             cur_x = start_cx + e->xmotion.x_root - start_x;
786             cur_y = start_cy + e->xmotion.y_root - start_y;
787             do_move(FALSE, 0);
788             do_edge_warp(e->xmotion.x_root, e->xmotion.y_root);
789         } else {
790             gint dw, dh;
791             ObDirection dir;
792
793             if (corner == prop_atoms.net_wm_moveresize_size_topleft) {
794                 dw = -(e->xmotion.x_root - start_x);
795                 dh = -(e->xmotion.y_root - start_y);
796                 dir = OB_DIRECTION_NORTHWEST;
797             } else if (corner == prop_atoms.net_wm_moveresize_size_top) {
798                 dw = 0;
799                 dh = -(e->xmotion.y_root - start_y);
800                 dir = OB_DIRECTION_NORTH;
801             } else if (corner == prop_atoms.net_wm_moveresize_size_topright) {
802                 dw = (e->xmotion.x_root - start_x);
803                 dh = -(e->xmotion.y_root - start_y);
804                 dir = OB_DIRECTION_NORTHEAST;
805             } else if (corner == prop_atoms.net_wm_moveresize_size_right) {
806                 dw = (e->xmotion.x_root - start_x);
807                 dh = 0;
808                 dir = OB_DIRECTION_EAST;
809             } else if (corner ==
810                        prop_atoms.net_wm_moveresize_size_bottomright) {
811                 dw = (e->xmotion.x_root - start_x);
812                 dh = (e->xmotion.y_root - start_y);
813                 dir = OB_DIRECTION_SOUTHEAST;
814             } else if (corner == prop_atoms.net_wm_moveresize_size_bottom) {
815                 dw = 0;
816                 dh = (e->xmotion.y_root - start_y);
817                 dir = OB_DIRECTION_SOUTH;
818             } else if (corner ==
819                        prop_atoms.net_wm_moveresize_size_bottomleft) {
820                 dw = -(e->xmotion.x_root - start_x);
821                 dh = (e->xmotion.y_root - start_y);
822                 dir = OB_DIRECTION_SOUTHWEST;
823             } else if (corner == prop_atoms.net_wm_moveresize_size_left) {
824                 dw = -(e->xmotion.x_root - start_x);
825                 dh = 0;
826                 dir = OB_DIRECTION_WEST;
827             } else if (corner == prop_atoms.net_wm_moveresize_size_keyboard) {
828                 dw = (e->xmotion.x_root - start_x);
829                 dh = (e->xmotion.y_root - start_y);
830                 dir = OB_DIRECTION_SOUTHEAST;
831             } else
832                 g_assert_not_reached();
833
834             dw -= cur_w - start_cw;
835             dh -= cur_h - start_ch;
836
837             calc_resize(FALSE, 0, &dw, &dh, dir);
838             cur_w += dw;
839             cur_h += dh;
840
841             if (corner == prop_atoms.net_wm_moveresize_size_topleft ||
842                 corner == prop_atoms.net_wm_moveresize_size_left ||
843                 corner == prop_atoms.net_wm_moveresize_size_bottomleft)
844             {
845                 cur_x -= dw;
846             }
847             if (corner == prop_atoms.net_wm_moveresize_size_topleft ||
848                 corner == prop_atoms.net_wm_moveresize_size_top ||
849                 corner == prop_atoms.net_wm_moveresize_size_topright)
850             {
851                 cur_y -= dh;
852             }
853
854             do_resize();
855         }
856         used = TRUE;
857     } else if (e->type == KeyPress) {
858         if (e->xkey.keycode == ob_keycode(OB_KEY_ESCAPE)) {
859             moveresize_end(TRUE);
860             used = TRUE;
861         } else if (e->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
862             moveresize_end(FALSE);
863             used = TRUE;
864         } else if (e->xkey.keycode == ob_keycode(OB_KEY_RIGHT) ||
865                    e->xkey.keycode == ob_keycode(OB_KEY_LEFT) ||
866                    e->xkey.keycode == ob_keycode(OB_KEY_DOWN) ||
867                    e->xkey.keycode == ob_keycode(OB_KEY_UP))
868         {
869             if (corner == prop_atoms.net_wm_moveresize_size_keyboard) {
870                 resize_with_keys(e->xkey.keycode, e->xkey.state);
871                 used = TRUE;
872             } else if (corner == prop_atoms.net_wm_moveresize_move_keyboard) {
873                 move_with_keys(e->xkey.keycode, e->xkey.state);
874                 used = TRUE;
875             }
876         }
877     }
878 #ifdef SYNC
879     else if (e->type == extensions_sync_event_basep + XSyncAlarmNotify)
880     {
881         waiting_for_sync = FALSE; /* we got our sync... */
882         do_resize(); /* ...so try resize if there is more change pending */
883         used = TRUE;
884     }
885 #endif
886     return used;
887 }