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