make openbox use the obt prop.c/h stuff
[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 "modkeys.h"
28 #include "popup.h"
29 #include "moveresize.h"
30 #include "config.h"
31 #include "event.h"
32 #include "debug.h"
33 #include "render/render.h"
34 #include "render/theme.h"
35 #include "obt/display.h"
36 #include "obt/prop.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 & modkeys_key_to_mask(OB_MODKEY_KEY_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 & modkeys_key_to_mask(OB_MODKEY_KEY_CONTROL))
625             dist = 1;
626         else
627             dist = KEY_DIST;
628
629         if (keycode == ob_keycode(OB_KEY_RIGHT))
630             dx = dist;
631         else if (keycode == ob_keycode(OB_KEY_LEFT))
632             dx = -dist;
633         else if (keycode == ob_keycode(OB_KEY_DOWN))
634             dy = dist;
635         else /* if (keycode == ob_keycode(OB_KEY_UP)) */
636             dy = -dist;
637     }
638
639     screen_pointer_pos(&opx, &opy);
640     XWarpPointer(obt_display, None, None, 0, 0, 0, 0, dx, dy);
641     /* steal the motion events this causes */
642     XSync(obt_display, FALSE);
643     {
644         XEvent ce;
645         while (XCheckTypedEvent(obt_display, MotionNotify, &ce));
646     }
647     screen_pointer_pos(&px, &py);
648
649     cur_x += dx;
650     cur_y += dy;
651     do_move(TRUE, dist);
652
653     /* because the cursor moves even though the window does
654        not nessesarily (resistance), this adjusts where the curor
655        thinks it started so that it keeps up with where the window
656        actually is */
657     start_x += (px - opx) - (cur_x - ox);
658     start_y += (py - opy) - (cur_y - oy);
659 }
660
661 static void resize_with_keys(gint keycode, gint state)
662 {
663     gint dw = 0, dh = 0, pdx = 0, pdy = 0, opx, opy, px, py;
664     gint dist = 0, resist = 0;
665     ObDirection dir;
666
667     /* pick the edge if it needs to move */
668     if (keycode == ob_keycode(OB_KEY_RIGHT)) {
669         dir = OB_DIRECTION_EAST;
670         if (key_resize_edge != OB_DIRECTION_WEST &&
671             key_resize_edge != OB_DIRECTION_EAST)
672         {
673             key_resize_edge = OB_DIRECTION_EAST;
674             return;
675         }
676     }
677     if (keycode == ob_keycode(OB_KEY_LEFT)) {
678         dir = OB_DIRECTION_WEST;
679         if (key_resize_edge != OB_DIRECTION_WEST &&
680             key_resize_edge != OB_DIRECTION_EAST)
681         {
682             key_resize_edge = OB_DIRECTION_WEST;
683             return;
684         }
685     }
686     if (keycode == ob_keycode(OB_KEY_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     }
695     if (keycode == ob_keycode(OB_KEY_DOWN)) {
696         dir = OB_DIRECTION_SOUTH;
697         if (key_resize_edge != OB_DIRECTION_NORTH &&
698             key_resize_edge != OB_DIRECTION_SOUTH)
699         {
700             key_resize_edge = OB_DIRECTION_SOUTH;
701             return;
702         }
703     }
704
705     /* shift means jump to edge */
706     if (state & modkeys_key_to_mask(OB_MODKEY_KEY_SHIFT)) {
707         gint x, y, w, h;
708
709         if (keycode == ob_keycode(OB_KEY_RIGHT))
710             dir = OB_DIRECTION_EAST;
711         else if (keycode == ob_keycode(OB_KEY_LEFT))
712             dir = OB_DIRECTION_WEST;
713         else if (keycode == ob_keycode(OB_KEY_DOWN))
714             dir = OB_DIRECTION_SOUTH;
715         else /* if (keycode == ob_keycode(OB_KEY_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 & modkeys_key_to_mask(OB_MODKEY_KEY_CONTROL)) {
732             distw = 1;
733             resist = 1;
734         }
735         else {
736             distw = KEY_DIST;
737             resist = KEY_DIST;
738         }
739         if (moveresize_client->size_inc.height > 1) {
740             disth = moveresize_client->size_inc.height;
741             resist = 1;
742         }
743         else if (state & modkeys_key_to_mask(OB_MODKEY_KEY_CONTROL)) {
744             disth = 1;
745             resist = 1;
746         }
747         else {
748             disth = KEY_DIST;
749             resist = KEY_DIST;
750         }
751
752         if (key_resize_edge == OB_DIRECTION_WEST) {
753             if (dir == OB_DIRECTION_WEST)
754                 dw = (dist = distw);
755             else
756                 dw = -(dist = distw);
757         }
758         else if (key_resize_edge == OB_DIRECTION_EAST) {
759             if (dir == OB_DIRECTION_EAST)
760                 dw = (dist = distw);
761             else
762                 dw = -(dist = distw);
763         }
764         else if (key_resize_edge == OB_DIRECTION_NORTH) {
765             if (dir == OB_DIRECTION_NORTH)
766                 dh = (dist = disth);
767             else
768                 dh = -(dist = disth);
769         }
770         else /*if (key_resize_edge == OB_DIRECTION_SOUTH)*/ {
771             if (dir == OB_DIRECTION_SOUTH)
772                 dh = (dist = disth);
773             else
774                 dh = -(dist = disth);
775         }
776     }
777
778     calc_resize(TRUE, resist, &dw, &dh, dir);
779     if (key_resize_edge == OB_DIRECTION_WEST)
780         cur_x -= dw;
781     else if (key_resize_edge == OB_DIRECTION_NORTH)
782         cur_y -= dh;
783     cur_w += dw;
784     cur_h += dh;
785
786     /* how to move the pointer to keep up with the change */
787     if (key_resize_edge == OB_DIRECTION_WEST)
788         pdx = -dw;
789     else if (key_resize_edge == OB_DIRECTION_EAST)
790         pdx = dw;
791     else if (key_resize_edge == OB_DIRECTION_NORTH)
792         pdy = -dh;
793     else if (key_resize_edge == OB_DIRECTION_SOUTH)
794         pdy = dh;
795
796     screen_pointer_pos(&opx, &opy);
797     XWarpPointer(obt_display, None, None, 0, 0, 0, 0, pdx, pdy);
798     /* steal the motion events this causes */
799     XSync(obt_display, FALSE);
800     {
801         XEvent ce;
802         while (XCheckTypedEvent(obt_display, MotionNotify, &ce));
803     }
804     screen_pointer_pos(&px, &py);
805
806     do_resize();
807
808     /* because the cursor moves even though the window does
809        not nessesarily (resistance), this adjusts where the cursor
810        thinks it started so that it keeps up with where the window
811        actually is */
812     start_x += (px - opx) - dw;
813     start_y += (py - opy) - dh;
814
815 }
816
817 gboolean moveresize_event(XEvent *e)
818 {
819     gboolean used = FALSE;
820
821     if (!moveresize_in_progress) return FALSE;
822
823     if (e->type == ButtonPress) {
824         if (!button) {
825             start_x = e->xbutton.x_root;
826             start_y = e->xbutton.y_root;
827             button = e->xbutton.button; /* this will end it now */
828         }
829         used = e->xbutton.button == button;
830     } else if (e->type == ButtonRelease) {
831         if (!button || e->xbutton.button == button) {
832             moveresize_end(FALSE);
833             used = TRUE;
834         }
835     } else if (e->type == MotionNotify) {
836         if (moving) {
837             cur_x = start_cx + e->xmotion.x_root - start_x;
838             cur_y = start_cy + e->xmotion.y_root - start_y;
839             do_move(FALSE, 0);
840             do_edge_warp(e->xmotion.x_root, e->xmotion.y_root);
841         } else {
842             gint dw, dh;
843             ObDirection dir;
844
845             if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT)) {
846                 dw = -(e->xmotion.x_root - start_x);
847                 dh = -(e->xmotion.y_root - start_y);
848                 dir = OB_DIRECTION_NORTHWEST;
849             } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP)) {
850                 dw = 0;
851                 dh = -(e->xmotion.y_root - start_y);
852                 dir = OB_DIRECTION_NORTH;
853             } else if (corner ==
854                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT)) {
855                 dw = (e->xmotion.x_root - start_x);
856                 dh = -(e->xmotion.y_root - start_y);
857                 dir = OB_DIRECTION_NORTHEAST;
858             } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT)) {
859                 dw = (e->xmotion.x_root - start_x);
860                 dh = 0;
861                 dir = OB_DIRECTION_EAST;
862             } else if (corner ==
863                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT)) {
864                 dw = (e->xmotion.x_root - start_x);
865                 dh = (e->xmotion.y_root - start_y);
866                 dir = OB_DIRECTION_SOUTHEAST;
867             } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOM))
868             {
869                 dw = 0;
870                 dh = (e->xmotion.y_root - start_y);
871                 dir = OB_DIRECTION_SOUTH;
872             } else if (corner ==
873                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT)) {
874                 dw = -(e->xmotion.x_root - start_x);
875                 dh = (e->xmotion.y_root - start_y);
876                 dir = OB_DIRECTION_SOUTHWEST;
877             } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT)) {
878                 dw = -(e->xmotion.x_root - start_x);
879                 dh = 0;
880                 dir = OB_DIRECTION_WEST;
881             } else if (corner ==
882                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD)) {
883                 dw = (e->xmotion.x_root - start_x);
884                 dh = (e->xmotion.y_root - start_y);
885                 dir = OB_DIRECTION_SOUTHEAST;
886             } else
887                 g_assert_not_reached();
888
889             dw -= cur_w - start_cw;
890             dh -= cur_h - start_ch;
891
892             calc_resize(FALSE, 0, &dw, &dh, dir);
893             cur_w += dw;
894             cur_h += dh;
895
896             if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT) ||
897                 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT) ||
898                 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT))
899             {
900                 cur_x -= dw;
901             }
902             if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT) ||
903                 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP) ||
904                 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT))
905             {
906                 cur_y -= dh;
907             }
908
909             do_resize();
910         }
911         used = TRUE;
912     } else if (e->type == KeyPress) {
913         if (e->xkey.keycode == ob_keycode(OB_KEY_ESCAPE)) {
914             moveresize_end(TRUE);
915             used = TRUE;
916         } else if (e->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
917             moveresize_end(FALSE);
918             used = TRUE;
919         } else if (e->xkey.keycode == ob_keycode(OB_KEY_RIGHT) ||
920                    e->xkey.keycode == ob_keycode(OB_KEY_LEFT) ||
921                    e->xkey.keycode == ob_keycode(OB_KEY_DOWN) ||
922                    e->xkey.keycode == ob_keycode(OB_KEY_UP))
923         {
924             if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD)) {
925                 resize_with_keys(e->xkey.keycode, e->xkey.state);
926                 used = TRUE;
927             } else if (corner ==
928                        OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD))
929             {
930                 move_with_keys(e->xkey.keycode, e->xkey.state);
931                 used = TRUE;
932             }
933         }
934     }
935 #ifdef SYNC
936     else if (e->type == obt_display_extension_sync_basep + XSyncAlarmNotify)
937     {
938         waiting_for_sync = FALSE; /* we got our sync... */
939         do_resize(); /* ...so try resize if there is more change pending */
940         used = TRUE;
941     }
942 #endif
943     return used;
944 }