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