Don't edge-warp with the cursor based on a monitor that does not contain the pointer...
[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 "client.h"
24 #include "focus.h"
25 #include "frame.h"
26 #include "openbox.h"
27 #include "resist.h"
28 #include "popup.h"
29 #include "moveresize.h"
30 #include "config.h"
31 #include "event.h"
32 #include "debug.h"
33 #include "obrender/render.h"
34 #include "obrender/theme.h"
35 #include "obt/display.h"
36 #include "obt/xqueue.h"
37 #include "obt/prop.h"
38 #include "obt/keyboard.h"
39
40 #include <X11/Xlib.h>
41 #include <glib.h>
42
43 /* how far windows move and resize with the keyboard arrows */
44 #define KEY_DIST 8
45 #define SYNC_TIMEOUTS 4
46
47 gboolean moveresize_in_progress = FALSE;
48 ObClient *moveresize_client = NULL;
49 #ifdef SYNC
50 XSyncAlarm moveresize_alarm = None;
51 #endif
52
53 static gboolean moving = FALSE; /* TRUE - moving, FALSE - resizing */
54
55 /* starting geometry for the window being moved/resized, so it can be
56    restored */
57 static gint start_x, start_y, start_cx, start_cy, start_cw, start_ch;
58 static gboolean was_max_horz, was_max_vert;
59 static Rect pre_max_area;
60 static gint cur_x, cur_y, cur_w, cur_h;
61 static guint button;
62 static guint32 corner;
63 static ObDirection edge_warp_dir = -1;
64 static gboolean edge_warp_odd = FALSE;
65 static guint edge_warp_timer = 0;
66 static ObDirection key_resize_edge = -1;
67 static guint waiting_for_sync;
68 #ifdef SYNC
69 static guint sync_timer = 0;
70 #endif
71
72 static ObPopup *popup = NULL;
73
74 static void do_move(gboolean keyboard, gint keydist);
75 static void do_resize(void);
76 static void do_edge_warp(gint x, gint y);
77 static void cancel_edge_warp();
78 #ifdef SYNC
79 static gboolean sync_timeout_func(gpointer data);
80 #endif
81
82 static void client_dest(ObClient *client, gpointer data)
83 {
84     if (moveresize_client == client)
85         moveresize_end(TRUE);
86 }
87
88 void moveresize_startup(gboolean reconfig)
89 {
90     popup = popup_new();
91     popup_set_text_align(popup, RR_JUSTIFY_CENTER);
92
93     if (!reconfig)
94         client_add_destroy_notify(client_dest, NULL);
95 }
96
97 void moveresize_shutdown(gboolean reconfig)
98 {
99     if (!reconfig) {
100         if (moveresize_in_progress)
101             moveresize_end(FALSE);
102         client_remove_destroy_notify(client_dest);
103     }
104
105     popup_free(popup);
106     popup = NULL;
107 }
108
109 static void popup_coords(ObClient *c, const gchar *format, gint a, gint b)
110 {
111     gchar *text;
112
113     text = g_strdup_printf(format, a, b);
114     if (config_resize_popup_pos == OB_RESIZE_POS_TOP)
115         popup_position(popup, SouthGravity,
116                        c->frame->area.x
117                      + c->frame->area.width/2,
118                        c->frame->area.y - ob_rr_theme->fbwidth);
119     else if (config_resize_popup_pos == OB_RESIZE_POS_CENTER)
120         popup_position(popup, CenterGravity,
121                        c->frame->area.x + c->frame->area.width / 2,
122                        c->frame->area.y + c->frame->area.height / 2);
123     else /* Fixed */ {
124         const Rect *area = screen_physical_area_active();
125         gint gravity, x, y;
126
127         x = config_resize_popup_fixed.x.pos;
128         if (config_resize_popup_fixed.x.center)
129             x = area->x + area->width/2;
130         else if (config_resize_popup_fixed.x.opposite)
131             x = RECT_RIGHT(*area) - x;
132         else
133             x = area->x + x;
134
135         y = config_resize_popup_fixed.y.pos;
136         if (config_resize_popup_fixed.y.center)
137             y = area->y + area->height/2;
138         else if (config_resize_popup_fixed.y.opposite)
139             y = RECT_RIGHT(*area) - y;
140         else
141             y = area->y + y;
142
143         if (config_resize_popup_fixed.x.center) {
144             if (config_resize_popup_fixed.y.center)
145                 gravity = CenterGravity;
146             else if (config_resize_popup_fixed.y.opposite)
147                 gravity = SouthGravity;
148             else
149                 gravity = NorthGravity;
150         }
151         else if (config_resize_popup_fixed.x.opposite) {
152             if (config_resize_popup_fixed.y.center)
153                 gravity = EastGravity;
154             else if (config_resize_popup_fixed.y.opposite)
155                 gravity = SouthEastGravity;
156             else
157                 gravity = NorthEastGravity;
158         }
159         else {
160             if (config_resize_popup_fixed.y.center)
161                 gravity = WestGravity;
162             else if (config_resize_popup_fixed.y.opposite)
163                 gravity = SouthWestGravity;
164             else
165                 gravity = NorthWestGravity;
166         }
167
168         popup_position(popup, gravity, x, y);
169     }
170     popup_show(popup, text);
171     g_free(text);
172 }
173
174 void moveresize_start(ObClient *c, gint x, gint y, guint b, guint32 cnr)
175 {
176     ObCursor cur;
177     gboolean mv = (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE) ||
178                    cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD));
179     gint up = 1;
180     gint left = 1;
181
182     if (moveresize_in_progress || !c->frame->visible ||
183         !(mv ?
184           (c->functions & OB_CLIENT_FUNC_MOVE) :
185           (c->functions & OB_CLIENT_FUNC_RESIZE)))
186         return;
187
188     if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT)) {
189         cur = OB_CURSOR_NORTHWEST;
190         up = left = -1;
191     }
192     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP)) {
193         cur = OB_CURSOR_NORTH;
194         up = -1;
195     }
196     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT)) {
197         cur = OB_CURSOR_NORTHEAST;
198         up = -1;
199     }
200     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT))
201         cur = OB_CURSOR_EAST;
202     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT))
203         cur = OB_CURSOR_SOUTHEAST;
204     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOM))
205         cur = OB_CURSOR_SOUTH;
206     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT)) {
207         cur = OB_CURSOR_SOUTHWEST;
208         left = -1;
209     }
210     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT)) {
211         cur = OB_CURSOR_WEST;
212         left = -1;
213     }
214     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD))
215         cur = OB_CURSOR_SOUTHEAST;
216     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE))
217         cur = OB_CURSOR_MOVE;
218     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD))
219         cur = OB_CURSOR_MOVE;
220     else
221         g_assert_not_reached();
222
223     /* keep the pointer bounded to the screen for move/resize */
224     if (!grab_pointer(FALSE, TRUE, cur))
225         return;
226     if (!grab_keyboard()) {
227         ungrab_pointer();
228         return;
229     }
230
231     frame_end_iconify_animation(c->frame);
232
233     moving = mv;
234     moveresize_client = c;
235     start_cx = c->area.x;
236     start_cy = c->area.y;
237     start_cw = c->area.width;
238     start_ch = c->area.height;
239     /* these adjustments for the size_inc make resizing a terminal more
240        friendly. you essentially start the resize in the middle of the
241        increment instead of at 0, so you have to move half an increment
242        either way instead of a full increment one and 1 px the other. */
243     start_x = x - (mv ? 0 : left * c->size_inc.width / 2);
244     start_y = y - (mv ? 0 : up * c->size_inc.height / 2);
245     corner = cnr;
246     button = b;
247     key_resize_edge = -1;
248
249     /* default to not putting max back on cancel */
250     was_max_horz = was_max_vert = FALSE;
251
252     /*
253       have to change start_cx and start_cy if going to do this..
254     if (corner == prop_atoms.net_wm_moveresize_move_keyboard ||
255         corner == prop_atoms.net_wm_moveresize_size_keyboard)
256         XWarpPointer(ob_display, None, c->window, 0, 0, 0, 0,
257                      c->area.width / 2, c->area.height / 2);
258     */
259
260     cur_x = start_cx;
261     cur_y = start_cy;
262     cur_w = start_cw;
263     cur_h = start_ch;
264
265     moveresize_in_progress = TRUE;
266     waiting_for_sync = 0;
267
268 #ifdef SYNC
269     if (config_resize_redraw && !moving && obt_display_extension_sync &&
270         moveresize_client->sync_request && moveresize_client->sync_counter &&
271         !moveresize_client->not_responding)
272     {
273         /* Initialize values for the resize syncing, and create an alarm for
274            the client's xsync counter */
275
276         XSyncValue val;
277         XSyncAlarmAttributes aa;
278
279         /* set the counter to an initial value */
280         XSyncIntToValue(&val, 0);
281         XSyncSetCounter(obt_display, moveresize_client->sync_counter, val);
282
283         /* this will be incremented when we tell the client what we're
284            looking for */
285         moveresize_client->sync_counter_value = 0;
286
287         /* the next sequence we're waiting for with the alarm */
288         XSyncIntToValue(&val, 1);
289
290         /* set an alarm on the counter */
291         aa.trigger.counter = moveresize_client->sync_counter;
292         aa.trigger.wait_value = val;
293         aa.trigger.value_type = XSyncAbsolute;
294         aa.trigger.test_type = XSyncPositiveTransition;
295         aa.events = True;
296         XSyncIntToValue(&aa.delta, 1);
297         moveresize_alarm = XSyncCreateAlarm(obt_display,
298                                             XSyncCACounter |
299                                             XSyncCAValue |
300                                             XSyncCAValueType |
301                                             XSyncCATestType |
302                                             XSyncCADelta |
303                                             XSyncCAEvents,
304                                             &aa);
305     }
306 #endif
307 }
308
309 void moveresize_end(gboolean cancel)
310 {
311     ungrab_keyboard();
312     ungrab_pointer();
313
314     popup_hide(popup);
315
316     if (!moving) {
317 #ifdef SYNC
318         /* turn off the alarm */
319         if (moveresize_alarm != None) {
320             XSyncDestroyAlarm(obt_display, moveresize_alarm);
321             moveresize_alarm = None;
322         }
323
324         if (sync_timer) g_source_remove(sync_timer);
325         sync_timer = 0;
326 #endif
327     }
328
329     /* don't use client_move() here, use the same width/height as
330        we've been using during the move, otherwise we get different results
331        when moving maximized windows between monitors of different sizes !
332     */
333     client_configure(moveresize_client,
334                      (cancel ? start_cx : cur_x),
335                      (cancel ? start_cy : cur_y),
336                      (cancel ? start_cw : cur_w),
337                      (cancel ? start_ch : cur_h),
338                      TRUE, TRUE, FALSE);
339
340     /* restore the client's maximized state. do this after putting the window
341        back in its original spot to minimize visible flicker */
342     if (cancel && (was_max_horz || was_max_vert)) {
343         const gboolean h = moveresize_client->max_horz;
344         const gboolean v = moveresize_client->max_vert;
345
346         client_maximize(moveresize_client, TRUE,
347                         was_max_horz && was_max_vert ? 0 :
348                         (was_max_horz ? 1 : 2));
349
350         /* replace the premax values with the ones we had saved if
351            the client doesn't have any already set */
352         if (was_max_horz && !h) {
353             moveresize_client->pre_max_area.x = pre_max_area.x;
354             moveresize_client->pre_max_area.width = pre_max_area.width;
355         }
356         if (was_max_vert && !v) {
357             moveresize_client->pre_max_area.y = pre_max_area.y;
358             moveresize_client->pre_max_area.height = pre_max_area.height;
359         }
360     }
361
362     /* dont edge warp after its ended */
363     cancel_edge_warp();
364
365     moveresize_in_progress = FALSE;
366     moveresize_client = NULL;
367 }
368
369 static void do_move(gboolean keyboard, gint keydist)
370 {
371     gint resist;
372
373     if (keyboard) resist = keydist - 1; /* resist for one key press */
374     else resist = config_resist_win;
375     resist_move_windows(moveresize_client, resist, &cur_x, &cur_y);
376     if (!keyboard) resist = config_resist_edge;
377     resist_move_monitors(moveresize_client, resist, &cur_x, &cur_y);
378
379     client_configure(moveresize_client, cur_x, cur_y, cur_w, cur_h,
380                      TRUE, FALSE, FALSE);
381     if (config_resize_popup_show == 2) /* == "Always" */
382         popup_coords(moveresize_client, "%d x %d",
383                      moveresize_client->frame->area.x,
384                      moveresize_client->frame->area.y);
385 }
386
387 static void do_resize(void)
388 {
389     gint x, y, w, h, lw, lh;
390
391     /* see if it is actually going to resize
392        USE cur_x AND cur_y HERE !  Otherwise the try_configure won't know
393        what struts to use !!
394      */
395     x = cur_x;
396     y = cur_y;
397     w = cur_w;
398     h = cur_h;
399     client_try_configure(moveresize_client, &x, &y, &w, &h,
400                          &lw, &lh, TRUE);
401     if (!(w == moveresize_client->area.width &&
402           h == moveresize_client->area.height) &&
403         /* if waiting_for_sync == 0, then we aren't waiting.
404            if it is > SYNC_TIMEOUTS, then we have timed out
405            that many times already, so forget about waiting more */
406         (waiting_for_sync == 0 || waiting_for_sync > SYNC_TIMEOUTS))
407     {
408 #ifdef SYNC
409         if (config_resize_redraw && obt_display_extension_sync &&
410             /* don't send another sync when one is pending */
411             waiting_for_sync == 0 &&
412             moveresize_client->sync_request &&
413             moveresize_client->sync_counter &&
414             !moveresize_client->not_responding)
415         {
416             XEvent ce;
417             XSyncValue val;
418
419             /* increment the value we're waiting for */
420             ++moveresize_client->sync_counter_value;
421             XSyncIntToValue(&val, moveresize_client->sync_counter_value);
422
423             /* tell the client what we're waiting for */
424             ce.xclient.type = ClientMessage;
425             ce.xclient.message_type = OBT_PROP_ATOM(WM_PROTOCOLS);
426             ce.xclient.display = obt_display;
427             ce.xclient.window = moveresize_client->window;
428             ce.xclient.format = 32;
429             ce.xclient.data.l[0] = OBT_PROP_ATOM(NET_WM_SYNC_REQUEST);
430             ce.xclient.data.l[1] = event_time();
431             ce.xclient.data.l[2] = XSyncValueLow32(val);
432             ce.xclient.data.l[3] = XSyncValueHigh32(val);
433             ce.xclient.data.l[4] = 0l;
434             XSendEvent(obt_display, moveresize_client->window, FALSE,
435                        NoEventMask, &ce);
436
437             waiting_for_sync = 1;
438
439             if (sync_timer) g_source_remove(sync_timer);
440             sync_timer = g_timeout_add(2000, sync_timeout_func, NULL);
441         }
442 #endif
443
444         /* force a ConfigureNotify, it is part of the spec for SYNC resizing
445            and MUST follow the sync counter notification */
446         client_configure(moveresize_client, cur_x, cur_y, cur_w, cur_h,
447                          TRUE, FALSE, TRUE);
448     }
449
450     /* this would be better with a fixed width font ... XXX can do it better
451        if there are 2 text boxes */
452     if (config_resize_popup_show == 2 || /* == "Always" */
453             (config_resize_popup_show == 1 && /* == "Nonpixel" */
454              moveresize_client->size_inc.width > 1 &&
455              moveresize_client->size_inc.height > 1))
456         popup_coords(moveresize_client, "%d x %d", lw, lh);
457 }
458
459 #ifdef SYNC
460 static gboolean sync_timeout_func(gpointer data)
461 {
462     ++waiting_for_sync; /* we timed out waiting for our sync... */
463     do_resize(); /* ...so let any pending resizes through */
464
465     if (waiting_for_sync > SYNC_TIMEOUTS) {
466         sync_timer = 0;
467         return FALSE; /* don't repeat */
468     }
469     else
470         return TRUE; /* keep waiting */
471 }
472 #endif
473
474 static void calc_resize(gboolean keyboard, gint keydist, gint *dw, gint *dh,
475                         ObDirection dir)
476 {
477     gint resist, x = 0, y = 0, lw, lh, ow, oh, nw, nh;
478     gint trydw, trydh;
479
480     ow = cur_w;
481     oh = cur_h;
482     nw = ow + *dw;
483     nh = oh + *dh;
484
485     if (!keyboard &&
486         (moveresize_client->max_ratio || moveresize_client->min_ratio))
487     {
488         switch (dir) {
489         case OB_DIRECTION_NORTH:
490         case OB_DIRECTION_SOUTH:
491             /* resize the width based on the height */
492             if (moveresize_client->min_ratio) {
493                 if (nh * moveresize_client->min_ratio > nw)
494                     nw = (gint)(nh * moveresize_client->min_ratio);
495             }
496             if (moveresize_client->max_ratio) {
497                 if (nh * moveresize_client->max_ratio < nw)
498                     nw = (gint)(nh * moveresize_client->max_ratio);
499             }
500             break;
501         default:
502             /* resize the height based on the width */
503             if (moveresize_client->min_ratio) {
504                 if (nh * moveresize_client->min_ratio > nw)
505                     nh = (gint)(nw / moveresize_client->min_ratio);
506             }
507             if (moveresize_client->max_ratio) {
508                 if (nh * moveresize_client->max_ratio < nw)
509                     nh = (gint)(nw / moveresize_client->max_ratio);
510             }
511             break;
512         }
513
514         /* see its actual size (apply aspect ratios) */
515         client_try_configure(moveresize_client, &x, &y, &nw, &nh, &lw, &lh,
516                              TRUE);
517         trydw = nw - ow;
518         trydh = nh - oh;
519     }
520
521     /* resist_size_* needs the frame size */
522     nw += moveresize_client->frame->size.left +
523         moveresize_client->frame->size.right;
524     nh += moveresize_client->frame->size.top +
525         moveresize_client->frame->size.bottom;
526
527     if (keyboard) resist = keydist - 1; /* resist for one key press */
528     else resist = config_resist_win;
529     resist_size_windows(moveresize_client, resist, &nw, &nh, dir);
530     if (!keyboard) resist = config_resist_edge;
531     resist_size_monitors(moveresize_client, resist, &nw, &nh, dir);
532
533     nw -= moveresize_client->frame->size.left +
534         moveresize_client->frame->size.right;
535     nh -= moveresize_client->frame->size.top +
536         moveresize_client->frame->size.bottom;
537
538     *dw = nw - ow;
539     *dh = nh - oh;
540
541     /* take aspect ratios into account for resistance */
542     if (!keyboard &&
543         (moveresize_client->max_ratio || moveresize_client->min_ratio))
544     {
545         if (*dh != trydh) { /* got resisted */
546             /* resize the width based on the height */
547             if (moveresize_client->min_ratio) {
548                 if (nh * moveresize_client->min_ratio > nw)
549                     nw = (gint)(nh * moveresize_client->min_ratio);
550             }
551             if (moveresize_client->max_ratio) {
552                 if (nh * moveresize_client->max_ratio < nw)
553                     nw = (gint)(nh * moveresize_client->max_ratio);
554             }
555         }
556         if (*dw != trydw) { /* got resisted */
557             /* resize the height based on the width */
558             if (moveresize_client->min_ratio) {
559                 if (nh * moveresize_client->min_ratio > nw)
560                     nh = (gint)(nw / moveresize_client->min_ratio);
561             }
562             if (moveresize_client->max_ratio) {
563                 if (nh * moveresize_client->max_ratio < nw)
564                     nh = (gint)(nw / moveresize_client->max_ratio);
565             }
566         }
567     }
568
569     /* make sure it's all valid */
570     client_try_configure(moveresize_client, &x, &y, &nw, &nh, &lw, &lh, TRUE);
571
572     *dw = nw - ow;
573     *dh = nh - oh;
574 }
575
576 static void edge_warp_move_ptr(void)
577 {
578     gint x, y;
579     const Rect* a;
580
581     screen_pointer_pos(&x, &y);
582     a = screen_physical_area_all_monitors();
583
584     switch (edge_warp_dir) {
585         case OB_DIRECTION_NORTH:
586             y = a->height - 1;
587             break;
588         case OB_DIRECTION_EAST:
589             x = a->x;
590             break;
591         case OB_DIRECTION_SOUTH:
592             y = a->y;
593             break;
594         case OB_DIRECTION_WEST:
595             x = a->width - 1;
596             break;
597         default:
598         g_assert_not_reached();
599     }
600
601     XWarpPointer(obt_display, 0, obt_root(ob_screen), 0, 0, 0, 0, x, y);
602 }
603
604 static gboolean edge_warp_delay_func(gpointer data)
605 {
606     guint d;
607
608     /* only fire every second time. so it's fast the first time, but slower
609        after that */
610     if (edge_warp_odd) {
611         d = screen_find_desktop(screen_desktop, edge_warp_dir, TRUE, FALSE);
612         if (d != screen_desktop) {
613             if (config_mouse_screenedgewarp) edge_warp_move_ptr();
614             screen_set_desktop(d, TRUE);
615         }
616     }
617     edge_warp_odd = !edge_warp_odd;
618
619     return TRUE; /* do repeat ! */
620 }
621
622 static void do_edge_warp(gint x, gint y)
623 {
624     guint i;
625     ObDirection dir;
626
627     if (!config_mouse_screenedgetime) return;
628
629     dir = -1;
630
631     for (i = 0; i < screen_num_monitors; ++i) {
632         const Rect *a = screen_physical_area_monitor(i);
633
634         if (!RECT_CONTAINS(*a, x, y))
635             continue;
636
637         if (x == RECT_LEFT(*a)) dir = OB_DIRECTION_WEST;
638         if (x == RECT_RIGHT(*a)) dir = OB_DIRECTION_EAST;
639         if (y == RECT_TOP(*a)) dir = OB_DIRECTION_NORTH;
640         if (y == RECT_BOTTOM(*a)) dir = OB_DIRECTION_SOUTH;
641
642         /* try check for xinerama boundaries */
643         if ((x + 1 == RECT_LEFT(*a) || x - 1 == RECT_RIGHT(*a)) &&
644             (dir == OB_DIRECTION_WEST || dir == OB_DIRECTION_EAST))
645         {
646             dir = -1;
647         }
648         if ((y + 1 == RECT_TOP(*a) || y - 1 == RECT_BOTTOM(*a)) &&
649             (dir == OB_DIRECTION_NORTH || dir == OB_DIRECTION_SOUTH))
650         {
651             dir = -1;
652         }
653     }
654
655     if (dir != edge_warp_dir) {
656         cancel_edge_warp();
657         if (dir != (ObDirection)-1) {
658             edge_warp_odd = TRUE; /* switch on the first timeout */
659             edge_warp_timer = g_timeout_add(config_mouse_screenedgetime,
660                                             edge_warp_delay_func, NULL);
661         }
662         edge_warp_dir = dir;
663     }
664 }
665
666 static void cancel_edge_warp(void)
667 {
668     if (edge_warp_timer) g_source_remove(edge_warp_timer);
669     edge_warp_timer = 0;
670 }
671
672 static void move_with_keys(KeySym sym, guint state)
673 {
674     gint dx = 0, dy = 0, ox = cur_x, oy = cur_y;
675     gint opx, px, opy, py;
676     gint dist = 0;
677
678     /* shift means jump to edge */
679     if (state & obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_SHIFT))
680     {
681         gint x, y;
682         ObDirection dir;
683
684         if (sym == XK_Right)
685             dir = OB_DIRECTION_EAST;
686         else if (sym == XK_Left)
687             dir = OB_DIRECTION_WEST;
688         else if (sym == XK_Down)
689             dir = OB_DIRECTION_SOUTH;
690         else /* sym == XK_Up */
691             dir = OB_DIRECTION_NORTH;
692
693         client_find_move_directional(moveresize_client, dir, &x, &y);
694         dx = x - moveresize_client->area.x;
695         dy = y - moveresize_client->area.y;
696     } else {
697         /* control means fine grained */
698         if (state &
699             obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CONTROL))
700         {
701             dist = 1;
702         }
703         else
704             dist = KEY_DIST;
705
706         if (sym == XK_Right)
707             dx = dist;
708         else if (sym == XK_Left)
709             dx = -dist;
710         else if (sym == XK_Down)
711             dy = dist;
712         else /* if (sym == XK_Up) */
713             dy = -dist;
714     }
715
716     screen_pointer_pos(&opx, &opy);
717     XWarpPointer(obt_display, None, None, 0, 0, 0, 0, dx, dy);
718     /* steal the motion events this causes */
719     XSync(obt_display, FALSE);
720     {
721         XEvent ce;
722         while (xqueue_remove_local(&ce, xqueue_match_type,
723                                    GINT_TO_POINTER(MotionNotify)));
724     }
725     screen_pointer_pos(&px, &py);
726
727     cur_x += dx;
728     cur_y += dy;
729     do_move(TRUE, dist);
730
731     /* because the cursor moves even though the window does
732        not nessesarily (resistance), this adjusts where the curor
733        thinks it started so that it keeps up with where the window
734        actually is */
735     start_x += (px - opx) - (cur_x - ox);
736     start_y += (py - opy) - (cur_y - oy);
737 }
738
739 static void resize_with_keys(KeySym sym, guint state)
740 {
741     gint dw = 0, dh = 0, pdx = 0, pdy = 0, opx, opy, px, py;
742     gint resist = 0;
743     ObDirection dir;
744
745     /* pick the edge if it needs to move */
746     if (sym == XK_Right) {
747         dir = OB_DIRECTION_EAST;
748         if (key_resize_edge != OB_DIRECTION_WEST &&
749             key_resize_edge != OB_DIRECTION_EAST)
750         {
751             key_resize_edge = OB_DIRECTION_EAST;
752             return;
753         }
754     } else if (sym == XK_Left) {
755         dir = OB_DIRECTION_WEST;
756         if (key_resize_edge != OB_DIRECTION_WEST &&
757             key_resize_edge != OB_DIRECTION_EAST)
758         {
759             key_resize_edge = OB_DIRECTION_WEST;
760             return;
761         }
762     } else if (sym == XK_Up) {
763         dir = OB_DIRECTION_NORTH;
764         if (key_resize_edge != OB_DIRECTION_NORTH &&
765             key_resize_edge != OB_DIRECTION_SOUTH)
766         {
767             key_resize_edge = OB_DIRECTION_NORTH;
768             return;
769         }
770     } else /* if (sym == XK_Down) */ {
771         dir = OB_DIRECTION_SOUTH;
772         if (key_resize_edge != OB_DIRECTION_NORTH &&
773             key_resize_edge != OB_DIRECTION_SOUTH)
774         {
775             key_resize_edge = OB_DIRECTION_SOUTH;
776             return;
777         }
778     }
779
780     /* shift means jump to edge */
781     if (state & obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_SHIFT))
782     {
783         gint x, y, w, h;
784
785         if (sym == XK_Right)
786             dir = OB_DIRECTION_EAST;
787         else if (sym == XK_Left)
788             dir = OB_DIRECTION_WEST;
789         else if (sym == XK_Down)
790             dir = OB_DIRECTION_SOUTH;
791         else /* if (sym == XK_Up)) */
792             dir = OB_DIRECTION_NORTH;
793
794         client_find_resize_directional(moveresize_client, key_resize_edge,
795                                        key_resize_edge == dir,
796                                        &x, &y, &w, &h);
797         dw = w - moveresize_client->area.width;
798         dh = h - moveresize_client->area.height;
799     } else {
800         gint distw, disth;
801
802         /* control means fine grained */
803         if (moveresize_client->size_inc.width > 1) {
804             distw = moveresize_client->size_inc.width;
805             resist = 1;
806         }
807         else if (state &
808                  obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CONTROL))
809         {
810             distw = 1;
811             resist = 1;
812         }
813         else {
814             distw = KEY_DIST;
815             resist = KEY_DIST;
816         }
817         if (moveresize_client->size_inc.height > 1) {
818             disth = moveresize_client->size_inc.height;
819             resist = 1;
820         }
821         else if (state &
822                  obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CONTROL))
823         {
824             disth = 1;
825             resist = 1;
826         }
827         else {
828             disth = KEY_DIST;
829             resist = KEY_DIST;
830         }
831
832         if (key_resize_edge == OB_DIRECTION_WEST) {
833             if (dir == OB_DIRECTION_WEST)
834                 dw = distw;
835             else
836                 dw = -distw;
837         }
838         else if (key_resize_edge == OB_DIRECTION_EAST) {
839             if (dir == OB_DIRECTION_EAST)
840                 dw = distw;
841             else
842                 dw = -distw;
843         }
844         else if (key_resize_edge == OB_DIRECTION_NORTH) {
845             if (dir == OB_DIRECTION_NORTH)
846                 dh = disth;
847             else
848                 dh = -disth;
849         }
850         else /*if (key_resize_edge == OB_DIRECTION_SOUTH)*/ {
851             if (dir == OB_DIRECTION_SOUTH)
852                 dh = disth;
853             else
854                 dh = -disth;
855         }
856     }
857
858     if (moveresize_client->max_horz &&
859         (key_resize_edge == OB_DIRECTION_WEST ||
860          key_resize_edge == OB_DIRECTION_EAST))
861     {
862         /* unmax horz */
863         was_max_horz = TRUE;
864         pre_max_area.x = moveresize_client->pre_max_area.x;
865         pre_max_area.width = moveresize_client->pre_max_area.width;
866
867         moveresize_client->pre_max_area.x = cur_x;
868         moveresize_client->pre_max_area.width = cur_w;
869         client_maximize(moveresize_client, FALSE, 1);
870     }
871     else if (moveresize_client->max_vert &&
872              (key_resize_edge == OB_DIRECTION_NORTH ||
873               key_resize_edge == OB_DIRECTION_SOUTH))
874     {
875         /* unmax vert */
876         was_max_vert = TRUE;
877         pre_max_area.y = moveresize_client->pre_max_area.y;
878         pre_max_area.height = moveresize_client->pre_max_area.height;
879
880         moveresize_client->pre_max_area.y = cur_y;
881         moveresize_client->pre_max_area.height = cur_h;
882         client_maximize(moveresize_client, FALSE, 2);
883     }
884
885     calc_resize(TRUE, resist, &dw, &dh, dir);
886     if (key_resize_edge == OB_DIRECTION_WEST)
887         cur_x -= dw;
888     else if (key_resize_edge == OB_DIRECTION_NORTH)
889         cur_y -= dh;
890     cur_w += dw;
891     cur_h += dh;
892
893     /* how to move the pointer to keep up with the change */
894     if (key_resize_edge == OB_DIRECTION_WEST)
895         pdx = -dw;
896     else if (key_resize_edge == OB_DIRECTION_EAST)
897         pdx = dw;
898     else if (key_resize_edge == OB_DIRECTION_NORTH)
899         pdy = -dh;
900     else if (key_resize_edge == OB_DIRECTION_SOUTH)
901         pdy = dh;
902
903     screen_pointer_pos(&opx, &opy);
904     XWarpPointer(obt_display, None, None, 0, 0, 0, 0, pdx, pdy);
905     /* steal the motion events this causes */
906     XSync(obt_display, FALSE);
907     {
908         XEvent ce;
909         while (xqueue_remove_local(&ce, xqueue_match_type,
910                                    GINT_TO_POINTER(MotionNotify)));
911     }
912     screen_pointer_pos(&px, &py);
913
914     do_resize();
915
916     /* because the cursor moves even though the window does
917        not nessesarily (resistance), this adjusts where the cursor
918        thinks it started so that it keeps up with where the window
919        actually is */
920     start_x += (px - opx) - dw;
921     start_y += (py - opy) - dh;
922
923 }
924
925 gboolean moveresize_event(XEvent *e)
926 {
927     gboolean used = FALSE;
928
929     if (!moveresize_in_progress) return FALSE;
930
931     if (e->type == ButtonPress) {
932         if (!button) {
933             start_x = e->xbutton.x_root;
934             start_y = e->xbutton.y_root;
935             button = e->xbutton.button; /* this will end it now */
936         }
937         used = e->xbutton.button == button;
938     } else if (e->type == ButtonRelease) {
939         if (!button || e->xbutton.button == button) {
940             moveresize_end(FALSE);
941             used = TRUE;
942         }
943     } else if (e->type == MotionNotify) {
944         if (moving) {
945             cur_x = start_cx + e->xmotion.x_root - start_x;
946             cur_y = start_cy + e->xmotion.y_root - start_y;
947             do_move(FALSE, 0);
948             do_edge_warp(e->xmotion.x_root, e->xmotion.y_root);
949         } else {
950             gint dw, dh;
951             ObDirection dir;
952
953             if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT)) {
954                 dw = -(e->xmotion.x_root - start_x);
955                 dh = -(e->xmotion.y_root - start_y);
956                 dir = OB_DIRECTION_NORTHWEST;
957             } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP)) {
958                 dw = 0;
959                 dh = -(e->xmotion.y_root - start_y);
960                 dir = OB_DIRECTION_NORTH;
961             } else if (corner ==
962                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT)) {
963                 dw = (e->xmotion.x_root - start_x);
964                 dh = -(e->xmotion.y_root - start_y);
965                 dir = OB_DIRECTION_NORTHEAST;
966             } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT)) {
967                 dw = (e->xmotion.x_root - start_x);
968                 dh = 0;
969                 dir = OB_DIRECTION_EAST;
970             } else if (corner ==
971                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT)) {
972                 dw = (e->xmotion.x_root - start_x);
973                 dh = (e->xmotion.y_root - start_y);
974                 dir = OB_DIRECTION_SOUTHEAST;
975             } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOM))
976             {
977                 dw = 0;
978                 dh = (e->xmotion.y_root - start_y);
979                 dir = OB_DIRECTION_SOUTH;
980             } else if (corner ==
981                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT)) {
982                 dw = -(e->xmotion.x_root - start_x);
983                 dh = (e->xmotion.y_root - start_y);
984                 dir = OB_DIRECTION_SOUTHWEST;
985             } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT)) {
986                 dw = -(e->xmotion.x_root - start_x);
987                 dh = 0;
988                 dir = OB_DIRECTION_WEST;
989             } else if (corner ==
990                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD)) {
991                 dw = (e->xmotion.x_root - start_x);
992                 dh = (e->xmotion.y_root - start_y);
993                 dir = OB_DIRECTION_SOUTHEAST;
994             } else
995                 g_assert_not_reached();
996
997             /* override the client's max state if desired */
998             if (ABS(dw) >= config_resist_edge) {
999                 if (moveresize_client->max_horz) {
1000                     /* unmax horz */
1001                     was_max_horz = TRUE;
1002                     pre_max_area.x = moveresize_client->pre_max_area.x;
1003                     pre_max_area.width = moveresize_client->pre_max_area.width;
1004
1005                     moveresize_client->pre_max_area.x = cur_x;
1006                     moveresize_client->pre_max_area.width = cur_w;
1007                     client_maximize(moveresize_client, FALSE, 1);
1008                 }
1009             }
1010             else if (was_max_horz && !moveresize_client->max_horz) {
1011                 /* remax horz and put the premax back */
1012                 client_maximize(moveresize_client, TRUE, 1);
1013                 moveresize_client->pre_max_area.x = pre_max_area.x;
1014                 moveresize_client->pre_max_area.width = pre_max_area.width;
1015             }
1016
1017             if (ABS(dh) >= config_resist_edge) {
1018                 if (moveresize_client->max_vert) {
1019                     /* unmax vert */
1020                     was_max_vert = TRUE;
1021                     pre_max_area.y = moveresize_client->pre_max_area.y;
1022                     pre_max_area.height =
1023                         moveresize_client->pre_max_area.height;
1024
1025                     moveresize_client->pre_max_area.y = cur_y;
1026                     moveresize_client->pre_max_area.height = cur_h;
1027                     client_maximize(moveresize_client, FALSE, 2);
1028                 }
1029             }
1030             else if (was_max_vert && !moveresize_client->max_vert) {
1031                 /* remax vert and put the premax back */
1032                 client_maximize(moveresize_client, TRUE, 2);
1033                 moveresize_client->pre_max_area.y = pre_max_area.y;
1034                 moveresize_client->pre_max_area.height = pre_max_area.height;
1035             }
1036
1037             dw -= cur_w - start_cw;
1038             dh -= cur_h - start_ch;
1039
1040             calc_resize(FALSE, 0, &dw, &dh, dir);
1041             cur_w += dw;
1042             cur_h += dh;
1043
1044             if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT) ||
1045                 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT) ||
1046                 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT))
1047             {
1048                 cur_x -= dw;
1049             }
1050             if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT) ||
1051                 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP) ||
1052                 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT))
1053             {
1054                 cur_y -= dh;
1055             }
1056
1057             do_resize();
1058         }
1059         used = TRUE;
1060     } else if (e->type == KeyPress) {
1061         KeySym sym = obt_keyboard_keypress_to_keysym(e);
1062
1063         if (sym == XK_Escape) {
1064             moveresize_end(TRUE);
1065             used = TRUE;
1066         } else if (sym == XK_Return || sym == XK_KP_Enter) {
1067             moveresize_end(FALSE);
1068             used = TRUE;
1069         } else if (sym == XK_Right || sym == XK_Left ||
1070                    sym == XK_Up || sym == XK_Down)
1071         {
1072             if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD)) {
1073                 resize_with_keys(sym, e->xkey.state);
1074                 used = TRUE;
1075             } else if (corner ==
1076                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD))
1077             {
1078                 move_with_keys(sym, e->xkey.state);
1079                 used = TRUE;
1080             }
1081         }
1082     }
1083 #ifdef SYNC
1084     else if (e->type == obt_display_extension_sync_basep + XSyncAlarmNotify)
1085     {
1086         waiting_for_sync = 0; /* we got our sync... */
1087         do_resize(); /* ...so try resize if there is more change pending */
1088         used = TRUE;
1089     }
1090 #endif
1091
1092     if (used && moveresize_client == focus_client)
1093         event_update_user_time();
1094
1095     return used;
1096 }