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