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