]> icculus.org git repositories - dana/openbox.git/blob - openbox/moveresize.c
Fix the EXTENTS reported on window frames.
[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 "frame.h"
25 #include "openbox.h"
26 #include "resist.h"
27 #include "popup.h"
28 #include "moveresize.h"
29 #include "config.h"
30 #include "event.h"
31 #include "debug.h"
32 #include "obrender/render.h"
33 #include "obrender/theme.h"
34 #include "obt/display.h"
35 #include "obt/xqueue.h"
36 #include "obt/prop.h"
37 #include "obt/keyboard.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 #define SYNC_TIMEOUTS 4
45
46 gboolean moveresize_in_progress = FALSE;
47 ObClient *moveresize_client = NULL;
48 #ifdef SYNC
49 XSyncAlarm moveresize_alarm = None;
50 #endif
51
52 static gboolean moving = FALSE; /* TRUE - moving, FALSE - resizing */
53
54 /* starting geometry for the window being moved/resized, so it can be
55    restored */
56 static gint start_x, start_y, start_cx, start_cy, start_cw, start_ch;
57 static gboolean was_max_horz, was_max_vert;
58 static Rect pre_max_area;
59 static gint cur_x, cur_y, cur_w, cur_h;
60 static guint button;
61 static guint32 corner;
62 static ObDirection edge_warp_dir = -1;
63 static gboolean edge_warp_odd = FALSE;
64 static guint edge_warp_timer = 0;
65 static ObDirection key_resize_edge = -1;
66 #ifdef SYNC
67 static guint waiting_for_sync;
68 static guint sync_timer = 0;
69 #endif
70
71 static ObPopup *popup = NULL;
72
73 static void do_move(gboolean keyboard, gint keydist);
74 static void do_resize(void);
75 static void do_edge_warp(gint x, gint y);
76 static void cancel_edge_warp();
77 #ifdef SYNC
78 static gboolean sync_timeout_func(gpointer data);
79 #endif
80
81 static void client_dest(ObClient *client, gpointer data)
82 {
83     if (moveresize_client == client)
84         moveresize_end(TRUE);
85 }
86
87 void moveresize_startup(gboolean reconfig)
88 {
89     popup = popup_new();
90     popup_set_text_align(popup, RR_JUSTIFY_CENTER);
91
92     if (!reconfig)
93         client_add_destroy_notify(client_dest, NULL);
94 }
95
96 void moveresize_shutdown(gboolean reconfig)
97 {
98     if (!reconfig) {
99         if (moveresize_in_progress)
100             moveresize_end(FALSE);
101         client_remove_destroy_notify(client_dest);
102     }
103
104     popup_free(popup);
105     popup = NULL;
106 }
107
108 static void popup_coords(ObClient *c, const gchar *format, gint a, gint b)
109 {
110     gchar *text;
111
112     text = g_strdup_printf(format, a, b);
113     if (config_resize_popup_pos == OB_RESIZE_POS_TOP)
114         popup_position(popup, SouthGravity,
115                        c->frame->area.x
116                      + c->frame->area.width/2,
117                        c->frame->area.y - ob_rr_theme->fbwidth);
118     else if (config_resize_popup_pos == OB_RESIZE_POS_CENTER)
119         popup_position(popup, CenterGravity,
120                        c->frame->area.x + c->frame->area.width / 2,
121                        c->frame->area.y + c->frame->area.height / 2);
122     else /* Fixed */ {
123         const Rect *area = screen_physical_area_active();
124         gint gravity, x, y;
125
126         x = config_resize_popup_fixed.x.pos;
127         if (config_resize_popup_fixed.x.center)
128             x = area->x + area->width/2;
129         else if (config_resize_popup_fixed.x.opposite)
130             x = RECT_RIGHT(*area) - x;
131         else
132             x = area->x + x;
133
134         y = config_resize_popup_fixed.y.pos;
135         if (config_resize_popup_fixed.y.center)
136             y = area->y + area->height/2;
137         else if (config_resize_popup_fixed.y.opposite)
138             y = RECT_RIGHT(*area) - y;
139         else
140             y = area->y + y;
141
142         if (config_resize_popup_fixed.x.center) {
143             if (config_resize_popup_fixed.y.center)
144                 gravity = CenterGravity;
145             else if (config_resize_popup_fixed.y.opposite)
146                 gravity = SouthGravity;
147             else
148                 gravity = NorthGravity;
149         }
150         else if (config_resize_popup_fixed.x.opposite) {
151             if (config_resize_popup_fixed.y.center)
152                 gravity = EastGravity;
153             else if (config_resize_popup_fixed.y.opposite)
154                 gravity = SouthEastGravity;
155             else
156                 gravity = NorthEastGravity;
157         }
158         else {
159             if (config_resize_popup_fixed.y.center)
160                 gravity = WestGravity;
161             else if (config_resize_popup_fixed.y.opposite)
162                 gravity = SouthWestGravity;
163             else
164                 gravity = NorthWestGravity;
165         }
166
167         popup_position(popup, gravity, x, y);
168     }
169     popup_show(popup, text);
170     g_free(text);
171 }
172
173 void moveresize_start(ObClient *c, gint x, gint y, guint b, guint32 cnr)
174 {
175     ObCursor cur;
176     gboolean mv = (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE) ||
177                    cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD));
178     gint up = 1;
179     gint left = 1;
180
181     if (moveresize_in_progress || !c->frame->visible ||
182         !(mv ?
183           (c->functions & OB_CLIENT_FUNC_MOVE) :
184           (c->functions & OB_CLIENT_FUNC_RESIZE)))
185         return;
186
187     if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT)) {
188         cur = OB_CURSOR_NORTHWEST;
189         up = left = -1;
190     }
191     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP)) {
192         cur = OB_CURSOR_NORTH;
193         up = -1;
194     }
195     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT)) {
196         cur = OB_CURSOR_NORTHEAST;
197         up = -1;
198     }
199     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT))
200         cur = OB_CURSOR_EAST;
201     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT))
202         cur = OB_CURSOR_SOUTHEAST;
203     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOM))
204         cur = OB_CURSOR_SOUTH;
205     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT)) {
206         cur = OB_CURSOR_SOUTHWEST;
207         left = -1;
208     }
209     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT)) {
210         cur = OB_CURSOR_WEST;
211         left = -1;
212     }
213     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD))
214         cur = OB_CURSOR_SOUTHEAST;
215     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE))
216         cur = OB_CURSOR_MOVE;
217     else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD))
218         cur = OB_CURSOR_MOVE;
219     else
220         g_assert_not_reached();
221
222     /* keep the pointer bounded to the screen for move/resize */
223     if (!grab_pointer(FALSE, TRUE, cur))
224         return;
225     if (!grab_keyboard()) {
226         ungrab_pointer();
227         return;
228     }
229
230     frame_end_iconify_animation(c->frame);
231
232     moving = mv;
233     moveresize_client = c;
234     start_cx = c->area.x;
235     start_cy = c->area.y;
236     start_cw = c->area.width;
237     start_ch = c->area.height;
238     /* these adjustments for the size_inc make resizing a terminal more
239        friendly. you essentially start the resize in the middle of the
240        increment instead of at 0, so you have to move half an increment
241        either way instead of a full increment one and 1 px the other. */
242     start_x = x - (mv ? 0 : left * c->size_inc.width / 2);
243     start_y = y - (mv ? 0 : up * c->size_inc.height / 2);
244     corner = cnr;
245     button = b;
246     key_resize_edge = -1;
247
248     /* default to not putting max back on cancel */
249     was_max_horz = was_max_vert = FALSE;
250
251     /*
252       have to change start_cx and start_cy if going to do this..
253     if (corner == prop_atoms.net_wm_moveresize_move_keyboard ||
254         corner == prop_atoms.net_wm_moveresize_size_keyboard)
255         XWarpPointer(ob_display, None, c->window, 0, 0, 0, 0,
256                      c->area.width / 2, c->area.height / 2);
257     */
258
259     cur_x = start_cx;
260     cur_y = start_cy;
261     cur_w = start_cw;
262     cur_h = start_ch;
263
264     moveresize_in_progress = TRUE;
265
266 #ifdef SYNC
267     if (config_resize_redraw && !moving && obt_display_extension_sync &&
268         moveresize_client->sync_request && moveresize_client->sync_counter &&
269         !moveresize_client->not_responding)
270     {
271         /* Initialize values for the resize syncing, and create an alarm for
272            the client's xsync counter */
273
274         XSyncValue val;
275         XSyncAlarmAttributes aa;
276
277         /* set the counter to an initial value */
278         XSyncIntToValue(&val, 0);
279         XSyncSetCounter(obt_display, moveresize_client->sync_counter, val);
280
281         /* this will be incremented when we tell the client what we're
282            looking for */
283         moveresize_client->sync_counter_value = 0;
284
285         /* the next sequence we're waiting for with the alarm */
286         XSyncIntToValue(&val, 1);
287
288         /* set an alarm on the counter */
289         aa.trigger.counter = moveresize_client->sync_counter;
290         aa.trigger.wait_value = val;
291         aa.trigger.value_type = XSyncAbsolute;
292         aa.trigger.test_type = XSyncPositiveTransition;
293         aa.events = True;
294         XSyncIntToValue(&aa.delta, 1);
295         moveresize_alarm = XSyncCreateAlarm(obt_display,
296                                             XSyncCACounter |
297                                             XSyncCAValue |
298                                             XSyncCAValueType |
299                                             XSyncCATestType |
300                                             XSyncCADelta |
301                                             XSyncCAEvents,
302                                             &aa);
303
304         waiting_for_sync = 0;
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         if (x == RECT_LEFT(*a)) dir = OB_DIRECTION_WEST;
634         if (x == RECT_RIGHT(*a)) dir = OB_DIRECTION_EAST;
635         if (y == RECT_TOP(*a)) dir = OB_DIRECTION_NORTH;
636         if (y == RECT_BOTTOM(*a)) dir = OB_DIRECTION_SOUTH;
637
638         /* try check for xinerama boundaries */
639         if ((x + 1 == RECT_LEFT(*a) || x - 1 == RECT_RIGHT(*a)) &&
640             (dir == OB_DIRECTION_WEST || dir == OB_DIRECTION_EAST))
641         {
642             dir = -1;
643         }
644         if ((y + 1 == RECT_TOP(*a) || y - 1 == RECT_BOTTOM(*a)) &&
645             (dir == OB_DIRECTION_NORTH || dir == OB_DIRECTION_SOUTH))
646         {
647             dir = -1;
648         }
649     }
650
651     if (dir != edge_warp_dir) {
652         cancel_edge_warp();
653         if (dir != (ObDirection)-1) {
654             edge_warp_odd = TRUE; /* switch on the first timeout */
655             edge_warp_timer = g_timeout_add(config_mouse_screenedgetime,
656                                             edge_warp_delay_func, NULL);
657         }
658         edge_warp_dir = dir;
659     }
660 }
661
662 static void cancel_edge_warp(void)
663 {
664     if (edge_warp_timer) g_source_remove(edge_warp_timer);
665     edge_warp_timer = 0;
666 }
667
668 static void move_with_keys(KeySym sym, guint state)
669 {
670     gint dx = 0, dy = 0, ox = cur_x, oy = cur_y;
671     gint opx, px, opy, py;
672     gint dist = 0;
673
674     /* shift means jump to edge */
675     if (state & obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_SHIFT))
676     {
677         gint x, y;
678         ObDirection dir;
679
680         if (sym == XK_Right)
681             dir = OB_DIRECTION_EAST;
682         else if (sym == XK_Left)
683             dir = OB_DIRECTION_WEST;
684         else if (sym == XK_Down)
685             dir = OB_DIRECTION_SOUTH;
686         else /* sym == XK_Up */
687             dir = OB_DIRECTION_NORTH;
688
689         client_find_move_directional(moveresize_client, dir, &x, &y);
690         dx = x - moveresize_client->area.x;
691         dy = y - moveresize_client->area.y;
692     } else {
693         /* control means fine grained */
694         if (state &
695             obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CONTROL))
696         {
697             dist = 1;
698         }
699         else
700             dist = KEY_DIST;
701
702         if (sym == XK_Right)
703             dx = dist;
704         else if (sym == XK_Left)
705             dx = -dist;
706         else if (sym == XK_Down)
707             dy = dist;
708         else /* if (sym == XK_Up) */
709             dy = -dist;
710     }
711
712     screen_pointer_pos(&opx, &opy);
713     XWarpPointer(obt_display, None, None, 0, 0, 0, 0, dx, dy);
714     /* steal the motion events this causes */
715     XSync(obt_display, FALSE);
716     {
717         XEvent ce;
718         while (xqueue_remove_local(&ce, xqueue_match_type,
719                                    GINT_TO_POINTER(MotionNotify)));
720     }
721     screen_pointer_pos(&px, &py);
722
723     cur_x += dx;
724     cur_y += dy;
725     do_move(TRUE, dist);
726
727     /* because the cursor moves even though the window does
728        not nessesarily (resistance), this adjusts where the curor
729        thinks it started so that it keeps up with where the window
730        actually is */
731     start_x += (px - opx) - (cur_x - ox);
732     start_y += (py - opy) - (cur_y - oy);
733 }
734
735 static void resize_with_keys(KeySym sym, guint state)
736 {
737     gint dw = 0, dh = 0, pdx = 0, pdy = 0, opx, opy, px, py;
738     gint resist = 0;
739     ObDirection dir;
740
741     /* pick the edge if it needs to move */
742     if (sym == XK_Right) {
743         dir = OB_DIRECTION_EAST;
744         if (key_resize_edge != OB_DIRECTION_WEST &&
745             key_resize_edge != OB_DIRECTION_EAST)
746         {
747             key_resize_edge = OB_DIRECTION_EAST;
748             return;
749         }
750     } else if (sym == XK_Left) {
751         dir = OB_DIRECTION_WEST;
752         if (key_resize_edge != OB_DIRECTION_WEST &&
753             key_resize_edge != OB_DIRECTION_EAST)
754         {
755             key_resize_edge = OB_DIRECTION_WEST;
756             return;
757         }
758     } else if (sym == XK_Up) {
759         dir = OB_DIRECTION_NORTH;
760         if (key_resize_edge != OB_DIRECTION_NORTH &&
761             key_resize_edge != OB_DIRECTION_SOUTH)
762         {
763             key_resize_edge = OB_DIRECTION_NORTH;
764             return;
765         }
766     } else /* if (sym == XK_Down) */ {
767         dir = OB_DIRECTION_SOUTH;
768         if (key_resize_edge != OB_DIRECTION_NORTH &&
769             key_resize_edge != OB_DIRECTION_SOUTH)
770         {
771             key_resize_edge = OB_DIRECTION_SOUTH;
772             return;
773         }
774     }
775
776     /* shift means jump to edge */
777     if (state & obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_SHIFT))
778     {
779         gint x, y, w, h;
780
781         if (sym == XK_Right)
782             dir = OB_DIRECTION_EAST;
783         else if (sym == XK_Left)
784             dir = OB_DIRECTION_WEST;
785         else if (sym == XK_Down)
786             dir = OB_DIRECTION_SOUTH;
787         else /* if (sym == XK_Up)) */
788             dir = OB_DIRECTION_NORTH;
789
790         client_find_resize_directional(moveresize_client, key_resize_edge,
791                                        key_resize_edge == dir,
792                                        &x, &y, &w, &h);
793         dw = w - moveresize_client->area.width;
794         dh = h - moveresize_client->area.height;
795     } else {
796         gint distw, disth;
797
798         /* control means fine grained */
799         if (moveresize_client->size_inc.width > 1) {
800             distw = moveresize_client->size_inc.width;
801             resist = 1;
802         }
803         else if (state &
804                  obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CONTROL))
805         {
806             distw = 1;
807             resist = 1;
808         }
809         else {
810             distw = KEY_DIST;
811             resist = KEY_DIST;
812         }
813         if (moveresize_client->size_inc.height > 1) {
814             disth = moveresize_client->size_inc.height;
815             resist = 1;
816         }
817         else if (state &
818                  obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CONTROL))
819         {
820             disth = 1;
821             resist = 1;
822         }
823         else {
824             disth = KEY_DIST;
825             resist = KEY_DIST;
826         }
827
828         if (key_resize_edge == OB_DIRECTION_WEST) {
829             if (dir == OB_DIRECTION_WEST)
830                 dw = distw;
831             else
832                 dw = -distw;
833         }
834         else if (key_resize_edge == OB_DIRECTION_EAST) {
835             if (dir == OB_DIRECTION_EAST)
836                 dw = distw;
837             else
838                 dw = -distw;
839         }
840         else if (key_resize_edge == OB_DIRECTION_NORTH) {
841             if (dir == OB_DIRECTION_NORTH)
842                 dh = disth;
843             else
844                 dh = -disth;
845         }
846         else /*if (key_resize_edge == OB_DIRECTION_SOUTH)*/ {
847             if (dir == OB_DIRECTION_SOUTH)
848                 dh = disth;
849             else
850                 dh = -disth;
851         }
852     }
853
854     if (moveresize_client->max_horz &&
855         (key_resize_edge == OB_DIRECTION_WEST ||
856          key_resize_edge == OB_DIRECTION_EAST))
857     {
858         /* unmax horz */
859         was_max_horz = TRUE;
860         pre_max_area.x = moveresize_client->pre_max_area.x;
861         pre_max_area.width = moveresize_client->pre_max_area.width;
862
863         moveresize_client->pre_max_area.x = cur_x;
864         moveresize_client->pre_max_area.width = cur_w;
865         client_maximize(moveresize_client, FALSE, 1);
866     }
867     else if (moveresize_client->max_vert &&
868              (key_resize_edge == OB_DIRECTION_NORTH ||
869               key_resize_edge == OB_DIRECTION_SOUTH))
870     {
871         /* unmax vert */
872         was_max_vert = TRUE;
873         pre_max_area.y = moveresize_client->pre_max_area.y;
874         pre_max_area.height = moveresize_client->pre_max_area.height;
875
876         moveresize_client->pre_max_area.y = cur_y;
877         moveresize_client->pre_max_area.height = cur_h;
878         client_maximize(moveresize_client, FALSE, 2);
879     }
880
881     calc_resize(TRUE, resist, &dw, &dh, dir);
882     if (key_resize_edge == OB_DIRECTION_WEST)
883         cur_x -= dw;
884     else if (key_resize_edge == OB_DIRECTION_NORTH)
885         cur_y -= dh;
886     cur_w += dw;
887     cur_h += dh;
888
889     /* how to move the pointer to keep up with the change */
890     if (key_resize_edge == OB_DIRECTION_WEST)
891         pdx = -dw;
892     else if (key_resize_edge == OB_DIRECTION_EAST)
893         pdx = dw;
894     else if (key_resize_edge == OB_DIRECTION_NORTH)
895         pdy = -dh;
896     else if (key_resize_edge == OB_DIRECTION_SOUTH)
897         pdy = dh;
898
899     screen_pointer_pos(&opx, &opy);
900     XWarpPointer(obt_display, None, None, 0, 0, 0, 0, pdx, pdy);
901     /* steal the motion events this causes */
902     XSync(obt_display, FALSE);
903     {
904         XEvent ce;
905         while (xqueue_remove_local(&ce, xqueue_match_type,
906                                    GINT_TO_POINTER(MotionNotify)));
907     }
908     screen_pointer_pos(&px, &py);
909
910     do_resize();
911
912     /* because the cursor moves even though the window does
913        not nessesarily (resistance), this adjusts where the cursor
914        thinks it started so that it keeps up with where the window
915        actually is */
916     start_x += (px - opx) - dw;
917     start_y += (py - opy) - dh;
918
919 }
920
921 gboolean moveresize_event(XEvent *e)
922 {
923     gboolean used = FALSE;
924
925     if (!moveresize_in_progress) return FALSE;
926
927     if (e->type == ButtonPress) {
928         if (!button) {
929             start_x = e->xbutton.x_root;
930             start_y = e->xbutton.y_root;
931             button = e->xbutton.button; /* this will end it now */
932         }
933         used = e->xbutton.button == button;
934     } else if (e->type == ButtonRelease) {
935         if (!button || e->xbutton.button == button) {
936             moveresize_end(FALSE);
937             used = TRUE;
938         }
939     } else if (e->type == MotionNotify) {
940         if (moving) {
941             cur_x = start_cx + e->xmotion.x_root - start_x;
942             cur_y = start_cy + e->xmotion.y_root - start_y;
943             do_move(FALSE, 0);
944             do_edge_warp(e->xmotion.x_root, e->xmotion.y_root);
945         } else {
946             gint dw, dh;
947             ObDirection dir;
948
949             if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT)) {
950                 dw = -(e->xmotion.x_root - start_x);
951                 dh = -(e->xmotion.y_root - start_y);
952                 dir = OB_DIRECTION_NORTHWEST;
953             } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP)) {
954                 dw = 0;
955                 dh = -(e->xmotion.y_root - start_y);
956                 dir = OB_DIRECTION_NORTH;
957             } else if (corner ==
958                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT)) {
959                 dw = (e->xmotion.x_root - start_x);
960                 dh = -(e->xmotion.y_root - start_y);
961                 dir = OB_DIRECTION_NORTHEAST;
962             } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT)) {
963                 dw = (e->xmotion.x_root - start_x);
964                 dh = 0;
965                 dir = OB_DIRECTION_EAST;
966             } else if (corner ==
967                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT)) {
968                 dw = (e->xmotion.x_root - start_x);
969                 dh = (e->xmotion.y_root - start_y);
970                 dir = OB_DIRECTION_SOUTHEAST;
971             } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOM))
972             {
973                 dw = 0;
974                 dh = (e->xmotion.y_root - start_y);
975                 dir = OB_DIRECTION_SOUTH;
976             } else if (corner ==
977                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT)) {
978                 dw = -(e->xmotion.x_root - start_x);
979                 dh = (e->xmotion.y_root - start_y);
980                 dir = OB_DIRECTION_SOUTHWEST;
981             } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT)) {
982                 dw = -(e->xmotion.x_root - start_x);
983                 dh = 0;
984                 dir = OB_DIRECTION_WEST;
985             } else if (corner ==
986                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD)) {
987                 dw = (e->xmotion.x_root - start_x);
988                 dh = (e->xmotion.y_root - start_y);
989                 dir = OB_DIRECTION_SOUTHEAST;
990             } else
991                 g_assert_not_reached();
992
993             /* override the client's max state if desired */
994             if (ABS(dw) >= config_resist_edge) {
995                 if (moveresize_client->max_horz) {
996                     /* unmax horz */
997                     was_max_horz = TRUE;
998                     pre_max_area.x = moveresize_client->pre_max_area.x;
999                     pre_max_area.width = moveresize_client->pre_max_area.width;
1000
1001                     moveresize_client->pre_max_area.x = cur_x;
1002                     moveresize_client->pre_max_area.width = cur_w;
1003                     client_maximize(moveresize_client, FALSE, 1);
1004                 }
1005             }
1006             else if (was_max_horz && !moveresize_client->max_horz) {
1007                 /* remax horz and put the premax back */
1008                 client_maximize(moveresize_client, TRUE, 1);
1009                 moveresize_client->pre_max_area.x = pre_max_area.x;
1010                 moveresize_client->pre_max_area.width = pre_max_area.width;
1011             }
1012
1013             if (ABS(dh) >= config_resist_edge) {
1014                 if (moveresize_client->max_vert) {
1015                     /* unmax vert */
1016                     was_max_vert = TRUE;
1017                     pre_max_area.y = moveresize_client->pre_max_area.y;
1018                     pre_max_area.height =
1019                         moveresize_client->pre_max_area.height;
1020
1021                     moveresize_client->pre_max_area.y = cur_y;
1022                     moveresize_client->pre_max_area.height = cur_h;
1023                     client_maximize(moveresize_client, FALSE, 2);
1024                 }
1025             }
1026             else if (was_max_vert && !moveresize_client->max_vert) {
1027                 /* remax vert and put the premax back */
1028                 client_maximize(moveresize_client, TRUE, 2);
1029                 moveresize_client->pre_max_area.y = pre_max_area.y;
1030                 moveresize_client->pre_max_area.height = pre_max_area.height;
1031             }
1032
1033             dw -= cur_w - start_cw;
1034             dh -= cur_h - start_ch;
1035
1036             calc_resize(FALSE, 0, &dw, &dh, dir);
1037             cur_w += dw;
1038             cur_h += dh;
1039
1040             if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT) ||
1041                 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT) ||
1042                 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT))
1043             {
1044                 cur_x -= dw;
1045             }
1046             if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT) ||
1047                 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP) ||
1048                 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT))
1049             {
1050                 cur_y -= dh;
1051             }
1052
1053             do_resize();
1054         }
1055         used = TRUE;
1056     } else if (e->type == KeyPress) {
1057         KeySym sym = obt_keyboard_keypress_to_keysym(e);
1058
1059         if (sym == XK_Escape) {
1060             moveresize_end(TRUE);
1061             used = TRUE;
1062         } else if (sym == XK_Return || sym == XK_KP_Enter) {
1063             moveresize_end(FALSE);
1064             used = TRUE;
1065         } else if (sym == XK_Right || sym == XK_Left ||
1066                    sym == XK_Up || sym == XK_Down)
1067         {
1068             if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD)) {
1069                 resize_with_keys(sym, e->xkey.state);
1070                 used = TRUE;
1071             } else if (corner ==
1072                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD))
1073             {
1074                 move_with_keys(sym, e->xkey.state);
1075                 used = TRUE;
1076             }
1077         }
1078     }
1079 #ifdef SYNC
1080     else if (e->type == obt_display_extension_sync_basep + XSyncAlarmNotify)
1081     {
1082         waiting_for_sync = 0; /* we got our sync... */
1083         do_resize(); /* ...so try resize if there is more change pending */
1084         used = TRUE;
1085     }
1086 #endif
1087     return used;
1088 }