move the code for tracking what extensions are available on the display into the...
[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 "modkeys.h"
29 #include "popup.h"
30 #include "moveresize.h"
31 #include "config.h"
32 #include "event.h"
33 #include "debug.h"
34 #include "render/render.h"
35 #include "render/theme.h"
36 #include "obt/display.h"
37
38 #include <X11/Xlib.h>
39 #include <glib.h>
40
41 /* how far windows move and resize with the keyboard arrows */
42 #define KEY_DIST 8
43
44 gboolean moveresize_in_progress = FALSE;
45 ObClient *moveresize_client = NULL;
46 #ifdef SYNC
47 XSyncAlarm moveresize_alarm = None;
48 #endif
49
50 static gboolean moving = FALSE; /* TRUE - moving, FALSE - resizing */
51
52 static gint start_x, start_y, start_cx, start_cy, start_cw, start_ch;
53 static gint cur_x, cur_y, cur_w, cur_h;
54 static guint button;
55 static guint32 corner;
56 static ObDirection edge_warp_dir = -1;
57 static gboolean edge_warp_odd = FALSE;
58 static ObDirection key_resize_edge = -1;
59 #ifdef SYNC
60 static gboolean waiting_for_sync;
61 #endif
62
63 static ObPopup *popup = NULL;
64
65 static void do_edge_warp(gint x, gint y);
66 static void cancel_edge_warp();
67 #ifdef SYNC
68 static gboolean sync_timeout_func(gpointer data);
69 #endif
70
71 static void client_dest(ObClient *client, gpointer data)
72 {
73     if (moveresize_client == client)
74         moveresize_end(TRUE);
75 }
76
77 void moveresize_startup(gboolean reconfig)
78 {
79     popup = popup_new();
80     popup_set_text_align(popup, RR_JUSTIFY_CENTER);
81
82     if (!reconfig)
83         client_add_destroy_notify(client_dest, NULL);
84 }
85
86 void moveresize_shutdown(gboolean reconfig)
87 {
88     if (!reconfig) {
89         if (moveresize_in_progress)
90             moveresize_end(FALSE);
91         client_remove_destroy_notify(client_dest);
92     }
93
94     popup_free(popup);
95     popup = NULL;
96 }
97
98 static void popup_coords(ObClient *c, const gchar *format, gint a, gint b)
99 {
100     gchar *text;
101
102     text = g_strdup_printf(format, a, b);
103     if (config_resize_popup_pos == OB_RESIZE_POS_TOP)
104         popup_position(popup, SouthGravity,
105                        c->frame->area.x
106                      + c->frame->area.width/2,
107                        c->frame->area.y - ob_rr_theme->fbwidth);
108     else if (config_resize_popup_pos == OB_RESIZE_POS_CENTER)
109         popup_position(popup, CenterGravity,
110                        c->frame->area.x + c->frame->size.left +
111                        c->area.width / 2,
112                        c->frame->area.y + c->frame->size.top +
113                        c->area.height / 2);
114     else /* Fixed */ {
115         Rect *area = screen_physical_area_active();
116         gint gravity, x, y;
117
118         x = config_resize_popup_fixed.x.pos;
119         if (config_resize_popup_fixed.x.center)
120             x = area->x + area->width/2;
121         else if (config_resize_popup_fixed.x.opposite)
122             x = RECT_RIGHT(*area) - x;
123         else
124             x = area->x + x;
125
126         y = config_resize_popup_fixed.y.pos;
127         if (config_resize_popup_fixed.y.center)
128             y = area->y + area->height/2;
129         else if (config_resize_popup_fixed.y.opposite)
130             y = RECT_RIGHT(*area) - y;
131         else
132             y = area->y + y;
133
134         if (config_resize_popup_fixed.x.center) {
135             if (config_resize_popup_fixed.y.center)
136                 gravity = CenterGravity;
137             else if (config_resize_popup_fixed.y.opposite)
138                 gravity = SouthGravity;
139             else
140                 gravity = NorthGravity;
141         }
142         else if (config_resize_popup_fixed.x.opposite) {
143             if (config_resize_popup_fixed.y.center)
144                 gravity = EastGravity;
145             else if (config_resize_popup_fixed.y.opposite)
146                 gravity = SouthEastGravity;
147             else
148                 gravity = NorthEastGravity;
149         }
150         else {
151             if (config_resize_popup_fixed.y.center)
152                 gravity = WestGravity;
153             else if (config_resize_popup_fixed.y.opposite)
154                 gravity = SouthWestGravity;
155             else
156                 gravity = NorthWestGravity;
157         }
158
159         popup_position(popup, gravity, x, y);
160
161         g_free(area);
162     }
163     popup_show(popup, text);
164     g_free(text);
165 }
166
167 void moveresize_start(ObClient *c, gint x, gint y, guint b, guint32 cnr)
168 {
169     ObCursor cur;
170     gboolean mv = (cnr == prop_atoms.net_wm_moveresize_move ||
171                    cnr == prop_atoms.net_wm_moveresize_move_keyboard);
172     gint up = 1;
173     gint left = 1;
174
175     if (moveresize_in_progress || !c->frame->visible ||
176         !(mv ?
177           (c->functions & OB_CLIENT_FUNC_MOVE) :
178           (c->functions & OB_CLIENT_FUNC_RESIZE)))
179         return;
180
181     if (cnr == prop_atoms.net_wm_moveresize_size_topleft) {
182         cur = OB_CURSOR_NORTHWEST;
183         up = left = -1;
184     } else if (cnr == prop_atoms.net_wm_moveresize_size_top) {
185         cur = OB_CURSOR_NORTH;
186         up = -1;
187     } else if (cnr == prop_atoms.net_wm_moveresize_size_topright) {
188         cur = OB_CURSOR_NORTHEAST;
189         up = -1;
190     } else if (cnr == prop_atoms.net_wm_moveresize_size_right)
191         cur = OB_CURSOR_EAST;
192     else if (cnr == prop_atoms.net_wm_moveresize_size_bottomright)
193         cur = OB_CURSOR_SOUTHEAST;
194     else if (cnr == prop_atoms.net_wm_moveresize_size_bottom)
195         cur = OB_CURSOR_SOUTH;
196     else if (cnr == prop_atoms.net_wm_moveresize_size_bottomleft) {
197         cur = OB_CURSOR_SOUTHWEST;
198         left = -1;
199     } else if (cnr == prop_atoms.net_wm_moveresize_size_left) {
200         cur = OB_CURSOR_WEST;
201         left = -1;
202     } else if (cnr == prop_atoms.net_wm_moveresize_size_keyboard)
203         cur = OB_CURSOR_SOUTHEAST;
204     else if (cnr == prop_atoms.net_wm_moveresize_move)
205         cur = OB_CURSOR_MOVE;
206     else if (cnr == prop_atoms.net_wm_moveresize_move_keyboard)
207         cur = OB_CURSOR_MOVE;
208     else
209         g_assert_not_reached();
210
211     /* keep the pointer bounded to the screen for move/resize */
212     if (!grab_pointer(FALSE, TRUE, cur))
213         return;
214     if (!grab_keyboard()) {
215         ungrab_pointer();
216         return;
217     }
218
219     frame_end_iconify_animation(c->frame);
220
221     moving = mv;
222     moveresize_client = c;
223     start_cx = c->area.x;
224     start_cy = c->area.y;
225     start_cw = c->area.width;
226     start_ch = c->area.height;
227     /* these adjustments for the size_inc make resizing a terminal more
228        friendly. you essentially start the resize in the middle of the
229        increment instead of at 0, so you have to move half an increment
230        either way instead of a full increment one and 1 px the other. */
231     start_x = x - (mv ? 0 : left * c->size_inc.width / 2);
232     start_y = y - (mv ? 0 : up * c->size_inc.height / 2);
233     corner = cnr;
234     button = b;
235     key_resize_edge = -1;
236
237     /*
238       have to change start_cx and start_cy if going to do this..
239     if (corner == prop_atoms.net_wm_moveresize_move_keyboard ||
240         corner == prop_atoms.net_wm_moveresize_size_keyboard)
241         XWarpPointer(ob_display, None, c->window, 0, 0, 0, 0,
242                      c->area.width / 2, c->area.height / 2);
243     */
244
245     cur_x = start_cx;
246     cur_y = start_cy;
247     cur_w = start_cw;
248     cur_h = start_ch;
249
250     moveresize_in_progress = TRUE;
251
252 #ifdef SYNC
253     if (config_resize_redraw && !moving && obt_display_extension_sync &&
254         moveresize_client->sync_request && moveresize_client->sync_counter)
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         obt_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
350 static void do_resize(void)
351 {
352     gint x, y, w, h, lw, lh;
353
354     /* see if it is actually going to resize */
355     x = 0;
356     y = 0;
357     w = cur_w;
358     h = cur_h;
359     client_try_configure(moveresize_client, &x, &y, &w, &h,
360                          &lw, &lh, TRUE);
361     if (w == moveresize_client->area.width &&
362         h == moveresize_client->area.height)
363     {
364         return;
365     }
366
367 #ifdef SYNC
368     if (config_resize_redraw && obt_display_extension_sync &&
369         moveresize_client->sync_request && moveresize_client->sync_counter)
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         obt_main_loop_timeout_remove(ob_main_loop, sync_timeout_func);
399         obt_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             obt_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     obt_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 == obt_display_extension_sync_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 }