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