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