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