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