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