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