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