]> icculus.org git repositories - manmower/openbox.git/blob - openbox/moveresize.c
change openbox to use the libotk's keyboard.[ch] stuff instead of the old modkeys...
[manmower/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 "render/render.h"
33 #include "render/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_edge_warp(gint x, gint y);
66 static void cancel_edge_warp();
67 #ifdef SYNC
68 static gboolean sync_timeout_func(gpointer data);
69 #endif
70
71 static void client_dest(ObClient *client, gpointer data)
72 {
73     if (moveresize_client == client)
74         moveresize_end(TRUE);
75 }
76
77 void moveresize_startup(gboolean reconfig)
78 {
79     popup = popup_new();
80     popup_set_text_align(popup, RR_JUSTIFY_CENTER);
81
82     if (!reconfig)
83         client_add_destroy_notify(client_dest, NULL);
84 }
85
86 void moveresize_shutdown(gboolean reconfig)
87 {
88     if (!reconfig) {
89         if (moveresize_in_progress)
90             moveresize_end(FALSE);
91         client_remove_destroy_notify(client_dest);
92     }
93
94     popup_free(popup);
95     popup = NULL;
96 }
97
98 static void popup_coords(ObClient *c, const gchar *format, gint a, gint b)
99 {
100     gchar *text;
101
102     text = g_strdup_printf(format, a, b);
103     if (config_resize_popup_pos == OB_RESIZE_POS_TOP)
104         popup_position(popup, SouthGravity,
105                        c->frame->area.x
106                      + c->frame->area.width/2,
107                        c->frame->area.y - ob_rr_theme->fbwidth);
108     else if (config_resize_popup_pos == OB_RESIZE_POS_CENTER)
109         popup_position(popup, CenterGravity,
110                        c->frame->area.x + c->frame->size.left +
111                        c->area.width / 2,
112                        c->frame->area.y + c->frame->size.top +
113                        c->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     {
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         client_move(moveresize_client,
308                     (cancel ? start_cx : cur_x),
309                     (cancel ? start_cy : cur_y));
310     } else {
311 #ifdef SYNC
312         /* turn off the alarm */
313         if (moveresize_alarm != None) {
314             XSyncDestroyAlarm(obt_display, moveresize_alarm);
315             moveresize_alarm = None;
316         }
317
318         obt_main_loop_timeout_remove(ob_main_loop, sync_timeout_func);
319 #endif
320
321         client_configure(moveresize_client,
322                          (cancel ? start_cx : cur_x),
323                          (cancel ? start_cy : cur_y),
324                          (cancel ? start_cw : cur_w),
325                          (cancel ? start_ch : cur_h),
326                          TRUE, TRUE, FALSE);
327     }
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
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         return;
370     }
371
372 #ifdef SYNC
373     if (config_resize_redraw && obt_display_extension_sync &&
374         moveresize_client->sync_request && moveresize_client->sync_counter)
375     {
376         XEvent ce;
377         XSyncValue val;
378
379         /* are we already waiting for the sync counter to catch up? */
380         if (waiting_for_sync)
381             return;
382
383         /* increment the value we're waiting for */
384         ++moveresize_client->sync_counter_value;
385         XSyncIntToValue(&val, moveresize_client->sync_counter_value);
386
387         /* tell the client what we're waiting for */
388         ce.xclient.type = ClientMessage;
389         ce.xclient.message_type = OBT_PROP_ATOM(WM_PROTOCOLS);
390         ce.xclient.display = obt_display;
391         ce.xclient.window = moveresize_client->window;
392         ce.xclient.format = 32;
393         ce.xclient.data.l[0] = OBT_PROP_ATOM(NET_WM_SYNC_REQUEST);
394         ce.xclient.data.l[1] = event_curtime;
395         ce.xclient.data.l[2] = XSyncValueLow32(val);
396         ce.xclient.data.l[3] = XSyncValueHigh32(val);
397         ce.xclient.data.l[4] = 0l;
398         XSendEvent(obt_display, moveresize_client->window, FALSE,
399                    NoEventMask, &ce);
400
401         waiting_for_sync = TRUE;
402
403         obt_main_loop_timeout_remove(ob_main_loop, sync_timeout_func);
404         obt_main_loop_timeout_add(ob_main_loop, G_USEC_PER_SEC * 2,
405                                   sync_timeout_func,
406                                   NULL, NULL, NULL);
407     }
408 #endif
409
410     client_configure(moveresize_client, cur_x, cur_y, cur_w, cur_h,
411                      TRUE, FALSE, FALSE);
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",
420                      moveresize_client->logical_size.width,
421                      moveresize_client->logical_size.height);
422 }
423
424 #ifdef SYNC
425 static gboolean sync_timeout_func(gpointer data)
426 {
427     waiting_for_sync = FALSE; /* we timed out waiting for our sync... */
428     do_resize(); /* ...so let any pending resizes through */
429
430     return FALSE; /* don't repeat */
431 }
432 #endif
433
434 static void calc_resize(gboolean keyboard, gint keydist, gint *dw, gint *dh,
435                         ObDirection dir)
436 {
437     gint resist, x = 0, y = 0, lw, lh, ow, oh, nw, nh;
438     gint trydw, trydh;
439
440     ow = cur_w;
441     oh = cur_h;
442     nw = ow + *dw;
443     nh = oh + *dh;
444
445     if (!keyboard &&
446         (moveresize_client->max_ratio || moveresize_client->min_ratio))
447     {
448         switch (dir) {
449         case OB_DIRECTION_NORTH:
450         case OB_DIRECTION_SOUTH:
451             /* resize the width based on the height */
452             if (moveresize_client->min_ratio) {
453                 if (nh * moveresize_client->min_ratio > nw)
454                     nw = (gint)(nh * moveresize_client->min_ratio);
455             }
456             if (moveresize_client->max_ratio) {
457                 if (nh * moveresize_client->max_ratio < nw)
458                     nw = (gint)(nh * moveresize_client->max_ratio);
459             }
460             break;
461         default:
462             /* resize the height based on the width */
463             if (moveresize_client->min_ratio) {
464                 if (nh * moveresize_client->min_ratio > nw)
465                     nh = (gint)(nw / moveresize_client->min_ratio);
466             }
467             if (moveresize_client->max_ratio) {
468                 if (nh * moveresize_client->max_ratio < nw)
469                     nh = (gint)(nw / moveresize_client->max_ratio);
470             }
471             break;
472         }
473
474         /* see its actual size (apply aspect ratios) */
475         client_try_configure(moveresize_client, &x, &y, &nw, &nh, &lw, &lh,
476                              TRUE);
477         trydw = nw - ow;
478         trydh = nh - oh;
479     }
480
481     /* resist_size_* needs the frame size */
482     nw += moveresize_client->frame->size.left +
483         moveresize_client->frame->size.right;
484     nh += moveresize_client->frame->size.top +
485         moveresize_client->frame->size.bottom;
486
487     if (keyboard) resist = keydist - 1; /* resist for one key press */
488     else resist = config_resist_win;
489     resist_size_windows(moveresize_client, resist, &nw, &nh, dir);
490     if (!keyboard) resist = config_resist_edge;
491     resist_size_monitors(moveresize_client, resist, &nw, &nh, dir);
492
493     nw -= moveresize_client->frame->size.left +
494         moveresize_client->frame->size.right;
495     nh -= moveresize_client->frame->size.top +
496         moveresize_client->frame->size.bottom;
497
498     *dw = nw - ow;
499     *dh = nh - oh;
500
501     /* take aspect ratios into account for resistance */
502     if (!keyboard &&
503         (moveresize_client->max_ratio || moveresize_client->min_ratio))
504     {
505         if (*dh != trydh) { /* got resisted */
506             /* resize the width based on the height */
507             if (moveresize_client->min_ratio) {
508                 if (nh * moveresize_client->min_ratio > nw)
509                     nw = (gint)(nh * moveresize_client->min_ratio);
510             }
511             if (moveresize_client->max_ratio) {
512                 if (nh * moveresize_client->max_ratio < nw)
513                     nw = (gint)(nh * moveresize_client->max_ratio);
514             }
515         }
516         if (*dw != trydw) { /* got resisted */
517             /* resize the height based on the width */
518             if (moveresize_client->min_ratio) {
519                 if (nh * moveresize_client->min_ratio > nw)
520                     nh = (gint)(nw / moveresize_client->min_ratio);
521             }
522             if (moveresize_client->max_ratio) {
523                 if (nh * moveresize_client->max_ratio < nw)
524                     nh = (gint)(nw / moveresize_client->max_ratio);
525             }
526         }
527     }
528
529     /* make sure it's all valid */
530     client_try_configure(moveresize_client, &x, &y, &nw, &nh, &lw, &lh, TRUE);
531
532     *dw = nw - ow;
533     *dh = nh - oh;
534 }
535
536 static gboolean edge_warp_delay_func(gpointer data)
537 {
538     guint d;
539
540     /* only fire every second time. so it's fast the first time, but slower
541        after that */
542     if (edge_warp_odd) {
543         d = screen_find_desktop(screen_desktop, edge_warp_dir, TRUE, FALSE);
544         if (d != screen_desktop) screen_set_desktop(d, TRUE);
545     }
546     edge_warp_odd = !edge_warp_odd;
547
548     return TRUE; /* do repeat ! */
549 }
550
551 static void do_edge_warp(gint x, gint y)
552 {
553     guint i;
554     ObDirection dir;
555
556     if (!config_mouse_screenedgetime) return;
557
558     dir = -1;
559
560     for (i = 0; i < screen_num_monitors; ++i) {
561         Rect *a = screen_physical_area_monitor(i);
562         if (x == RECT_LEFT(*a)) dir = OB_DIRECTION_WEST;
563         if (x == RECT_RIGHT(*a)) dir = OB_DIRECTION_EAST;
564         if (y == RECT_TOP(*a)) dir = OB_DIRECTION_NORTH;
565         if (y == RECT_BOTTOM(*a)) dir = OB_DIRECTION_SOUTH;
566
567         /* try check for xinerama boundaries */
568         if ((x + 1 == RECT_LEFT(*a) || x - 1 == RECT_RIGHT(*a)) &&
569             (dir == OB_DIRECTION_WEST || dir == OB_DIRECTION_EAST))
570         {
571             dir = -1;
572         }
573         if ((y + 1 == RECT_TOP(*a) || y - 1 == RECT_BOTTOM(*a)) &&
574             (dir == OB_DIRECTION_NORTH || dir == OB_DIRECTION_SOUTH))
575         {
576             dir = -1;
577         }
578         g_free(a);
579     }
580
581     if (dir != edge_warp_dir) {
582         cancel_edge_warp();
583         if (dir != (ObDirection)-1) {
584             edge_warp_odd = TRUE; /* switch on the first timeout */
585             obt_main_loop_timeout_add(ob_main_loop,
586                                       config_mouse_screenedgetime * 1000,
587                                       edge_warp_delay_func,
588                                       NULL, NULL, NULL);
589         }
590         edge_warp_dir = dir;
591     }
592 }
593
594 static void cancel_edge_warp(void)
595 {
596     obt_main_loop_timeout_remove(ob_main_loop, edge_warp_delay_func);
597 }
598
599 static void move_with_keys(gint keycode, gint state)
600 {
601     gint dx = 0, dy = 0, ox = cur_x, oy = cur_y;
602     gint opx, px, opy, py;
603     gint dist = 0;
604
605     /* shift means jump to edge */
606     if (state & obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_SHIFT)) {
607         gint x, y;
608         ObDirection dir;
609
610         if (keycode == ob_keycode(OB_KEY_RIGHT))
611             dir = OB_DIRECTION_EAST;
612         else if (keycode == ob_keycode(OB_KEY_LEFT))
613             dir = OB_DIRECTION_WEST;
614         else if (keycode == ob_keycode(OB_KEY_DOWN))
615             dir = OB_DIRECTION_SOUTH;
616         else /* if (keycode == ob_keycode(OB_KEY_UP)) */
617             dir = OB_DIRECTION_NORTH;
618
619         client_find_move_directional(moveresize_client, dir, &x, &y);
620         dx = x - moveresize_client->area.x;
621         dy = y - moveresize_client->area.y;
622     } else {
623         /* control means fine grained */
624         if (state &
625             obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CONTROL))
626         {
627             dist = 1;
628         }
629         else
630             dist = KEY_DIST;
631
632         if (keycode == ob_keycode(OB_KEY_RIGHT))
633             dx = dist;
634         else if (keycode == ob_keycode(OB_KEY_LEFT))
635             dx = -dist;
636         else if (keycode == ob_keycode(OB_KEY_DOWN))
637             dy = dist;
638         else /* if (keycode == ob_keycode(OB_KEY_UP)) */
639             dy = -dist;
640     }
641
642     screen_pointer_pos(&opx, &opy);
643     XWarpPointer(obt_display, None, None, 0, 0, 0, 0, dx, dy);
644     /* steal the motion events this causes */
645     XSync(obt_display, FALSE);
646     {
647         XEvent ce;
648         while (XCheckTypedEvent(obt_display, MotionNotify, &ce));
649     }
650     screen_pointer_pos(&px, &py);
651
652     cur_x += dx;
653     cur_y += dy;
654     do_move(TRUE, dist);
655
656     /* because the cursor moves even though the window does
657        not nessesarily (resistance), this adjusts where the curor
658        thinks it started so that it keeps up with where the window
659        actually is */
660     start_x += (px - opx) - (cur_x - ox);
661     start_y += (py - opy) - (cur_y - oy);
662 }
663
664 static void resize_with_keys(gint keycode, gint state)
665 {
666     gint dw = 0, dh = 0, pdx = 0, pdy = 0, opx, opy, px, py;
667     gint dist = 0, resist = 0;
668     ObDirection dir;
669
670     /* pick the edge if it needs to move */
671     if (keycode == ob_keycode(OB_KEY_RIGHT)) {
672         dir = OB_DIRECTION_EAST;
673         if (key_resize_edge != OB_DIRECTION_WEST &&
674             key_resize_edge != OB_DIRECTION_EAST)
675         {
676             key_resize_edge = OB_DIRECTION_EAST;
677             return;
678         }
679     }
680     if (keycode == ob_keycode(OB_KEY_LEFT)) {
681         dir = OB_DIRECTION_WEST;
682         if (key_resize_edge != OB_DIRECTION_WEST &&
683             key_resize_edge != OB_DIRECTION_EAST)
684         {
685             key_resize_edge = OB_DIRECTION_WEST;
686             return;
687         }
688     }
689     if (keycode == ob_keycode(OB_KEY_UP)) {
690         dir = OB_DIRECTION_NORTH;
691         if (key_resize_edge != OB_DIRECTION_NORTH &&
692             key_resize_edge != OB_DIRECTION_SOUTH)
693         {
694             key_resize_edge = OB_DIRECTION_NORTH;
695             return;
696         }
697     }
698     if (keycode == ob_keycode(OB_KEY_DOWN)) {
699         dir = OB_DIRECTION_SOUTH;
700         if (key_resize_edge != OB_DIRECTION_NORTH &&
701             key_resize_edge != OB_DIRECTION_SOUTH)
702         {
703             key_resize_edge = OB_DIRECTION_SOUTH;
704             return;
705         }
706     }
707
708     /* shift means jump to edge */
709     if (state & obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_SHIFT)) {
710         gint x, y, w, h;
711
712         if (keycode == ob_keycode(OB_KEY_RIGHT))
713             dir = OB_DIRECTION_EAST;
714         else if (keycode == ob_keycode(OB_KEY_LEFT))
715             dir = OB_DIRECTION_WEST;
716         else if (keycode == ob_keycode(OB_KEY_DOWN))
717             dir = OB_DIRECTION_SOUTH;
718         else /* if (keycode == ob_keycode(OB_KEY_UP)) */
719             dir = OB_DIRECTION_NORTH;
720
721         client_find_resize_directional(moveresize_client, key_resize_edge,
722                                        key_resize_edge == dir,
723                                        &x, &y, &w, &h);
724         dw = w - moveresize_client->area.width;
725         dh = h - moveresize_client->area.height;
726     } else {
727         gint distw, disth;
728
729         /* control means fine grained */
730         if (moveresize_client->size_inc.width > 1) {
731             distw = moveresize_client->size_inc.width;
732             resist = 1;
733         }
734         else if (state &
735                  obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CONTROL))
736         {
737             distw = 1;
738             resist = 1;
739         }
740         else {
741             distw = KEY_DIST;
742             resist = KEY_DIST;
743         }
744         if (moveresize_client->size_inc.height > 1) {
745             disth = moveresize_client->size_inc.height;
746             resist = 1;
747         }
748         else if (state &
749                  obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CONTROL))
750         {
751             disth = 1;
752             resist = 1;
753         }
754         else {
755             disth = KEY_DIST;
756             resist = KEY_DIST;
757         }
758
759         if (key_resize_edge == OB_DIRECTION_WEST) {
760             if (dir == OB_DIRECTION_WEST)
761                 dw = (dist = distw);
762             else
763                 dw = -(dist = distw);
764         }
765         else if (key_resize_edge == OB_DIRECTION_EAST) {
766             if (dir == OB_DIRECTION_EAST)
767                 dw = (dist = distw);
768             else
769                 dw = -(dist = distw);
770         }
771         else if (key_resize_edge == OB_DIRECTION_NORTH) {
772             if (dir == OB_DIRECTION_NORTH)
773                 dh = (dist = disth);
774             else
775                 dh = -(dist = disth);
776         }
777         else /*if (key_resize_edge == OB_DIRECTION_SOUTH)*/ {
778             if (dir == OB_DIRECTION_SOUTH)
779                 dh = (dist = disth);
780             else
781                 dh = -(dist = disth);
782         }
783     }
784
785     calc_resize(TRUE, resist, &dw, &dh, dir);
786     if (key_resize_edge == OB_DIRECTION_WEST)
787         cur_x -= dw;
788     else if (key_resize_edge == OB_DIRECTION_NORTH)
789         cur_y -= dh;
790     cur_w += dw;
791     cur_h += dh;
792
793     /* how to move the pointer to keep up with the change */
794     if (key_resize_edge == OB_DIRECTION_WEST)
795         pdx = -dw;
796     else if (key_resize_edge == OB_DIRECTION_EAST)
797         pdx = dw;
798     else if (key_resize_edge == OB_DIRECTION_NORTH)
799         pdy = -dh;
800     else if (key_resize_edge == OB_DIRECTION_SOUTH)
801         pdy = dh;
802
803     screen_pointer_pos(&opx, &opy);
804     XWarpPointer(obt_display, None, None, 0, 0, 0, 0, pdx, pdy);
805     /* steal the motion events this causes */
806     XSync(obt_display, FALSE);
807     {
808         XEvent ce;
809         while (XCheckTypedEvent(obt_display, MotionNotify, &ce));
810     }
811     screen_pointer_pos(&px, &py);
812
813     do_resize();
814
815     /* because the cursor moves even though the window does
816        not nessesarily (resistance), this adjusts where the cursor
817        thinks it started so that it keeps up with where the window
818        actually is */
819     start_x += (px - opx) - dw;
820     start_y += (py - opy) - dh;
821
822 }
823
824 gboolean moveresize_event(XEvent *e)
825 {
826     gboolean used = FALSE;
827
828     if (!moveresize_in_progress) return FALSE;
829
830     if (e->type == ButtonPress) {
831         if (!button) {
832             start_x = e->xbutton.x_root;
833             start_y = e->xbutton.y_root;
834             button = e->xbutton.button; /* this will end it now */
835         }
836         used = e->xbutton.button == button;
837     } else if (e->type == ButtonRelease) {
838         if (!button || e->xbutton.button == button) {
839             moveresize_end(FALSE);
840             used = TRUE;
841         }
842     } else if (e->type == MotionNotify) {
843         if (moving) {
844             cur_x = start_cx + e->xmotion.x_root - start_x;
845             cur_y = start_cy + e->xmotion.y_root - start_y;
846             do_move(FALSE, 0);
847             do_edge_warp(e->xmotion.x_root, e->xmotion.y_root);
848         } else {
849             gint dw, dh;
850             ObDirection dir;
851
852             if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT)) {
853                 dw = -(e->xmotion.x_root - start_x);
854                 dh = -(e->xmotion.y_root - start_y);
855                 dir = OB_DIRECTION_NORTHWEST;
856             } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP)) {
857                 dw = 0;
858                 dh = -(e->xmotion.y_root - start_y);
859                 dir = OB_DIRECTION_NORTH;
860             } else if (corner ==
861                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT)) {
862                 dw = (e->xmotion.x_root - start_x);
863                 dh = -(e->xmotion.y_root - start_y);
864                 dir = OB_DIRECTION_NORTHEAST;
865             } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT)) {
866                 dw = (e->xmotion.x_root - start_x);
867                 dh = 0;
868                 dir = OB_DIRECTION_EAST;
869             } else if (corner ==
870                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT)) {
871                 dw = (e->xmotion.x_root - start_x);
872                 dh = (e->xmotion.y_root - start_y);
873                 dir = OB_DIRECTION_SOUTHEAST;
874             } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOM))
875             {
876                 dw = 0;
877                 dh = (e->xmotion.y_root - start_y);
878                 dir = OB_DIRECTION_SOUTH;
879             } else if (corner ==
880                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT)) {
881                 dw = -(e->xmotion.x_root - start_x);
882                 dh = (e->xmotion.y_root - start_y);
883                 dir = OB_DIRECTION_SOUTHWEST;
884             } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT)) {
885                 dw = -(e->xmotion.x_root - start_x);
886                 dh = 0;
887                 dir = OB_DIRECTION_WEST;
888             } else if (corner ==
889                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD)) {
890                 dw = (e->xmotion.x_root - start_x);
891                 dh = (e->xmotion.y_root - start_y);
892                 dir = OB_DIRECTION_SOUTHEAST;
893             } else
894                 g_assert_not_reached();
895
896             dw -= cur_w - start_cw;
897             dh -= cur_h - start_ch;
898
899             calc_resize(FALSE, 0, &dw, &dh, dir);
900             cur_w += dw;
901             cur_h += dh;
902
903             if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT) ||
904                 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT) ||
905                 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT))
906             {
907                 cur_x -= dw;
908             }
909             if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT) ||
910                 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP) ||
911                 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT))
912             {
913                 cur_y -= dh;
914             }
915
916             do_resize();
917         }
918         used = TRUE;
919     } else if (e->type == KeyPress) {
920         if (e->xkey.keycode == ob_keycode(OB_KEY_ESCAPE)) {
921             moveresize_end(TRUE);
922             used = TRUE;
923         } else if (e->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
924             moveresize_end(FALSE);
925             used = TRUE;
926         } else if (e->xkey.keycode == ob_keycode(OB_KEY_RIGHT) ||
927                    e->xkey.keycode == ob_keycode(OB_KEY_LEFT) ||
928                    e->xkey.keycode == ob_keycode(OB_KEY_DOWN) ||
929                    e->xkey.keycode == ob_keycode(OB_KEY_UP))
930         {
931             if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD)) {
932                 resize_with_keys(e->xkey.keycode, e->xkey.state);
933                 used = TRUE;
934             } else if (corner ==
935                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD))
936             {
937                 move_with_keys(e->xkey.keycode, e->xkey.state);
938                 used = TRUE;
939             }
940         }
941     }
942 #ifdef SYNC
943     else if (e->type == obt_display_extension_sync_basep + XSyncAlarmNotify)
944     {
945         waiting_for_sync = FALSE; /* we got our sync... */
946         do_resize(); /* ...so try resize if there is more change pending */
947         used = TRUE;
948     }
949 #endif
950     return used;
951 }