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