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