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