]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/moveresize.c
only show the coords popup when both directions have a size increment
[mikachu/openbox.git] / openbox / moveresize.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    moveresize.c for the Openbox window manager
4    Copyright (c) 2006        Mikael Magnusson
5    Copyright (c) 2003-2007   Dana Jansens
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "grab.h"
21 #include "framerender.h"
22 #include "screen.h"
23 #include "prop.h"
24 #include "client.h"
25 #include "frame.h"
26 #include "openbox.h"
27 #include "resist.h"
28 #include "popup.h"
29 #include "moveresize.h"
30 #include "config.h"
31 #include "event.h"
32 #include "debug.h"
33 #include "extensions.h"
34 #include "render/render.h"
35 #include "render/theme.h"
36
37 #include <X11/Xlib.h>
38 #include <glib.h>
39
40 gboolean moveresize_in_progress = FALSE;
41 ObClient *moveresize_client = NULL;
42 #ifdef SYNC
43 XSyncAlarm moveresize_alarm = None;
44 #endif
45
46 static gboolean moving = FALSE; /* TRUE - moving, FALSE - resizing */
47
48 static gint start_x, start_y, start_cx, start_cy, start_cw, start_ch;
49 static gint cur_x, cur_y;
50 static guint button;
51 static guint32 corner;
52 static ObCorner lockcorner;
53 #ifdef SYNC
54 static gboolean waiting_for_sync;
55 #endif
56
57 static ObPopup *popup = NULL;
58
59 static void client_dest(ObClient *client, gpointer data)
60 {
61     if (moveresize_client == client)
62         moveresize_end(TRUE);    
63 }
64
65 void moveresize_startup(gboolean reconfig)
66 {
67     popup = popup_new(FALSE);
68
69     if (!reconfig)
70         client_add_destructor(client_dest, NULL);
71 }
72
73 void moveresize_shutdown(gboolean reconfig)
74 {
75     if (!reconfig) {
76         if (moveresize_in_progress)
77             moveresize_end(FALSE);
78         client_remove_destructor(client_dest);
79     }
80
81     popup_free(popup);
82     popup = NULL;
83 }
84
85 static void get_resize_position(gint *x, gint *y, gboolean cancel)
86 {
87     gint dw, dh;
88     gint w, h, lw, lh;
89
90     *x = moveresize_client->frame->area.x;
91     *y = moveresize_client->frame->area.y;
92
93     if (cancel) {
94         w = start_cw;
95         h = start_ch;
96     } else {
97         w = cur_x;
98         h = cur_y;
99     }
100
101     /* see how much it is actually going to resize */
102     {
103         gint cx = *x, cy = *y;
104         frame_frame_gravity(moveresize_client->frame, &cx, &cy, w, h);
105         client_try_configure(moveresize_client, &cx, &cy, &w, &h,
106                              &lw, &lh, TRUE);
107     }
108     dw = w - moveresize_client->area.width;
109     dh = h - moveresize_client->area.height;
110
111     switch (lockcorner) {
112     case OB_CORNER_TOPLEFT:
113         break;
114     case OB_CORNER_TOPRIGHT:
115         *x -= dw;
116         break;
117     case OB_CORNER_BOTTOMLEFT:
118         *y -= dh;
119         break;
120     case OB_CORNER_BOTTOMRIGHT:
121         *x -= dw;
122         *y -= dh;
123         break;
124     }
125
126     frame_frame_gravity(moveresize_client->frame, x, y, w, h);
127 }
128
129 static void popup_coords(ObClient *c, const gchar *format, gint a, gint b)
130 {
131     gchar *text;
132
133     text = g_strdup_printf(format, a, b);
134     if (config_resize_popup_pos == 1) /* == "Top" */
135         popup_position(popup, SouthGravity,
136                        c->frame->area.x
137                      + c->frame->area.width/2,
138                        c->frame->area.y - ob_rr_theme->fbwidth);
139     else /* == "Center" */
140         popup_position(popup, CenterGravity,
141                        c->frame->area.x + c->frame->size.left +
142                        c->area.width / 2,
143                        c->frame->area.y + c->frame->size.top +
144                        c->area.height / 2);
145     popup_show(popup, text);
146     g_free(text);
147 }
148
149 void moveresize_start(ObClient *c, gint x, gint y, guint b, guint32 cnr)
150 {
151     ObCursor cur;
152
153     moving = (cnr == prop_atoms.net_wm_moveresize_move ||
154               cnr == prop_atoms.net_wm_moveresize_move_keyboard);
155
156     if (moveresize_in_progress || !c->frame->visible ||
157         !(moving ?
158           (c->functions & OB_CLIENT_FUNC_MOVE) :
159           (c->functions & OB_CLIENT_FUNC_RESIZE)))
160         return;
161
162     moveresize_client = c;
163     start_cx = c->area.x;
164     start_cy = c->area.y;
165     /* these adjustments for the size_inc make resizing a terminal more
166        friendly. you essentially start the resize in the middle of the
167        increment instead of at 0, so you have to move half an increment
168        either way instead of a full increment one and 1 px the other. and this
169        is one large mother fucking comment. */
170     start_cw = c->area.width + c->size_inc.width / 2;
171     start_ch = c->area.height + c->size_inc.height / 2;
172     start_x = x;
173     start_y = y;
174     corner = cnr;
175     button = b;
176
177     /*
178       have to change start_cx and start_cy if going to do this..
179     if (corner == prop_atoms.net_wm_moveresize_move_keyboard ||
180         corner == prop_atoms.net_wm_moveresize_size_keyboard)
181         XWarpPointer(ob_display, None, c->window, 0, 0, 0, 0,
182                      c->area.width / 2, c->area.height / 2);
183     */
184
185     if (moving) {
186         cur_x = start_cx;
187         cur_y = start_cy;
188     } else {
189         cur_x = start_cw;
190         cur_y = start_ch;
191     }
192
193     moveresize_in_progress = TRUE;
194
195     if (corner == prop_atoms.net_wm_moveresize_size_topleft)
196         cur = OB_CURSOR_NORTHWEST;
197     else if (corner == prop_atoms.net_wm_moveresize_size_top)
198         cur = OB_CURSOR_NORTH;
199     else if (corner == prop_atoms.net_wm_moveresize_size_topright)
200         cur = OB_CURSOR_NORTHEAST;
201     else if (corner == prop_atoms.net_wm_moveresize_size_right)
202         cur = OB_CURSOR_EAST;
203     else if (corner == prop_atoms.net_wm_moveresize_size_bottomright)
204         cur = OB_CURSOR_SOUTHEAST;
205     else if (corner == prop_atoms.net_wm_moveresize_size_bottom)
206         cur = OB_CURSOR_SOUTH;
207     else if (corner == prop_atoms.net_wm_moveresize_size_bottomleft)
208         cur = OB_CURSOR_SOUTHWEST;
209     else if (corner == prop_atoms.net_wm_moveresize_size_left)
210         cur = OB_CURSOR_WEST;
211     else if (corner == prop_atoms.net_wm_moveresize_size_keyboard)
212         cur = OB_CURSOR_SOUTHEAST;
213     else if (corner == prop_atoms.net_wm_moveresize_move)
214         cur = OB_CURSOR_MOVE;
215     else if (corner == prop_atoms.net_wm_moveresize_move_keyboard)
216         cur = OB_CURSOR_MOVE;
217     else
218         g_assert_not_reached();
219
220 #ifdef SYNC
221     if (config_resize_redraw && !moving && extensions_shape &&
222         moveresize_client->sync_request && moveresize_client->sync_counter)
223     {
224         /* Initialize values for the resize syncing, and create an alarm for
225            the client's xsync counter */
226
227         XSyncValue val;
228         XSyncAlarmAttributes aa;
229
230         /* set the counter to an initial value */
231         XSyncIntToValue(&val, 0);
232         XSyncSetCounter(ob_display, moveresize_client->sync_counter, val);
233
234         /* this will be incremented when we tell the client what we're
235            looking for */
236         moveresize_client->sync_counter_value = 0;
237
238         /* the next sequence we're waiting for with the alarm */
239         XSyncIntToValue(&val, 1);
240
241         /* set an alarm on the counter */
242         aa.trigger.counter = moveresize_client->sync_counter;
243         aa.trigger.wait_value = val;
244         aa.trigger.value_type = XSyncAbsolute;
245         aa.trigger.test_type = XSyncPositiveTransition;
246         aa.events = True;
247         XSyncIntToValue(&aa.delta, 1);
248         moveresize_alarm = XSyncCreateAlarm(ob_display,
249                                             XSyncCACounter |
250                                             XSyncCAValue |
251                                             XSyncCAValueType |
252                                             XSyncCATestType |
253                                             XSyncCADelta |
254                                             XSyncCAEvents,
255                                             &aa);
256
257         waiting_for_sync = FALSE;
258     }
259 #endif
260
261     grab_pointer(TRUE, FALSE, cur);
262     grab_keyboard(TRUE);
263 }
264
265 void moveresize_end(gboolean cancel)
266 {
267     gint x, y;
268
269     grab_keyboard(FALSE);
270     grab_pointer(FALSE, FALSE, OB_CURSOR_NONE);
271
272     popup_hide(popup);
273
274     if (moving) {
275         client_move(moveresize_client,
276                     (cancel ? start_cx : cur_x),
277                     (cancel ? start_cy : cur_y));
278     } else {
279 #ifdef SYNC
280         /* turn off the alarm */
281         if (moveresize_alarm != None) {
282             XSyncDestroyAlarm(ob_display, moveresize_alarm);
283             moveresize_alarm = None;
284         }
285 #endif
286
287         get_resize_position(&x, &y, cancel);
288         client_configure(moveresize_client, x, y,
289                          (cancel ? start_cw : cur_x),
290                          (cancel ? start_ch : cur_y), TRUE, TRUE);
291     }
292
293     moveresize_in_progress = FALSE;
294     moveresize_client = NULL;
295 }
296
297 static void do_move(gboolean resist)
298 {
299     if (resist) {
300         resist_move_windows(moveresize_client, &cur_x, &cur_y);
301         resist_move_monitors(moveresize_client, &cur_x, &cur_y);
302     }
303
304     client_configure(moveresize_client, cur_x, cur_y,
305                      moveresize_client->area.width,
306                      moveresize_client->area.height, TRUE, FALSE);
307     if (config_resize_popup_show == 2) /* == "Always" */
308         popup_coords(moveresize_client, "%d x %d",
309                 moveresize_client->frame->area.x,
310                 moveresize_client->frame->area.y);
311 }
312
313 static void do_resize()
314 {
315 #ifdef SYNC
316     if (config_resize_redraw && extensions_sync &&
317         moveresize_client->sync_request && moveresize_client->sync_counter)
318     {
319         XEvent ce;
320         XSyncValue val;
321         gint x, y, w, h, lw, lh;
322
323         /* are we already waiting for the sync counter to catch up? */
324         if (waiting_for_sync)
325             return;
326
327         /* see if it is actually going to resize */
328         x = 0;
329         y = 0;
330         w = cur_x;
331         h = cur_y;
332         client_try_configure(moveresize_client, &x, &y, &w, &h,
333                              &lw, &lh, TRUE);
334         if (w == moveresize_client->area.width &&
335             h == moveresize_client->area.height)
336         {
337             return;
338         }
339
340         /* increment the value we're waiting for */
341         ++moveresize_client->sync_counter_value;
342         XSyncIntToValue(&val, moveresize_client->sync_counter_value);
343
344         /* tell the client what we're waiting for */
345         ce.xclient.type = ClientMessage;
346         ce.xclient.message_type = prop_atoms.wm_protocols;
347         ce.xclient.display = ob_display;
348         ce.xclient.window = moveresize_client->window;
349         ce.xclient.format = 32;
350         ce.xclient.data.l[0] = prop_atoms.net_wm_sync_request;
351         ce.xclient.data.l[1] = event_curtime;
352         ce.xclient.data.l[2] = XSyncValueLow32(val);
353         ce.xclient.data.l[3] = XSyncValueHigh32(val);
354         ce.xclient.data.l[4] = 0l;
355         XSendEvent(ob_display, moveresize_client->window, FALSE,
356                    NoEventMask, &ce);
357
358         waiting_for_sync = TRUE;
359     }
360 #endif
361
362     {
363         gint x, y;
364         get_resize_position(&x, &y, FALSE);
365         client_configure(moveresize_client, x, y, cur_x, cur_y, TRUE, FALSE);
366     }
367
368     /* this would be better with a fixed width font ... XXX can do it better
369        if there are 2 text boxes */
370     if (config_resize_popup_show == 2 || /* == "Always" */
371             (config_resize_popup_show == 1 && /* == "Nonpixel" */
372              moveresize_client->size_inc.width > 1 &&
373              moveresize_client->size_inc.height > 1))
374         popup_coords(moveresize_client, "%d x %d",
375                      moveresize_client->logical_size.width,
376                      moveresize_client->logical_size.height);
377 }
378
379 static void calc_resize(gboolean resist)
380 {
381     /* resist_size_* needs the frame size */
382     cur_x += moveresize_client->frame->size.left +
383         moveresize_client->frame->size.right;
384     cur_y += moveresize_client->frame->size.top +
385         moveresize_client->frame->size.bottom;
386
387     if (resist) {
388         resist_size_windows(moveresize_client, &cur_x, &cur_y, lockcorner);
389         resist_size_monitors(moveresize_client, &cur_x, &cur_y, lockcorner);
390     }
391
392     cur_x -= moveresize_client->frame->size.left +
393         moveresize_client->frame->size.right;
394     cur_y -= moveresize_client->frame->size.top +
395         moveresize_client->frame->size.bottom;
396 }
397
398 void moveresize_event(XEvent *e)
399 {
400     g_assert(moveresize_in_progress);
401
402     if (e->type == ButtonPress) {
403         if (!button) {
404             start_x = e->xbutton.x_root;
405             start_y = e->xbutton.y_root;
406             button = e->xbutton.button; /* this will end it now */
407         }
408     } else if (e->type == ButtonRelease) {
409         if (!button || e->xbutton.button == button) {
410             moveresize_end(FALSE);
411         }
412     } else if (e->type == MotionNotify) {
413         if (moving) {
414             cur_x = start_cx + e->xmotion.x_root - start_x;
415             cur_y = start_cy + e->xmotion.y_root - start_y;
416             do_move(TRUE);
417         } else {
418             if (corner == prop_atoms.net_wm_moveresize_size_topleft) {
419                 cur_x = start_cw - (e->xmotion.x_root - start_x);
420                 cur_y = start_ch - (e->xmotion.y_root - start_y);
421                 lockcorner = OB_CORNER_BOTTOMRIGHT;
422             } else if (corner == prop_atoms.net_wm_moveresize_size_top) {
423                 cur_x = start_cw;
424                 cur_y = start_ch - (e->xmotion.y_root - start_y);
425                 lockcorner = OB_CORNER_BOTTOMRIGHT;
426             } else if (corner == prop_atoms.net_wm_moveresize_size_topright) {
427                 cur_x = start_cw + (e->xmotion.x_root - start_x);
428                 cur_y = start_ch - (e->xmotion.y_root - start_y);
429                 lockcorner = OB_CORNER_BOTTOMLEFT;
430             } else if (corner == prop_atoms.net_wm_moveresize_size_right) { 
431                 cur_x = start_cw + (e->xmotion.x_root - start_x);
432                 cur_y = start_ch;
433                 lockcorner = OB_CORNER_BOTTOMLEFT;
434             } else if (corner ==
435                        prop_atoms.net_wm_moveresize_size_bottomright) {
436                 cur_x = start_cw + (e->xmotion.x_root - start_x);
437                 cur_y = start_ch + (e->xmotion.y_root - start_y);
438                 lockcorner = OB_CORNER_TOPLEFT;
439             } else if (corner == prop_atoms.net_wm_moveresize_size_bottom) {
440                 cur_x = start_cw;
441                 cur_y = start_ch + (e->xmotion.y_root - start_y);
442                 lockcorner = OB_CORNER_TOPLEFT;
443             } else if (corner ==
444                        prop_atoms.net_wm_moveresize_size_bottomleft) {
445                 cur_x = start_cw - (e->xmotion.x_root - start_x);
446                 cur_y = start_ch + (e->xmotion.y_root - start_y);
447                 lockcorner = OB_CORNER_TOPRIGHT;
448             } else if (corner == prop_atoms.net_wm_moveresize_size_left) {
449                 cur_x = start_cw - (e->xmotion.x_root - start_x);
450                 cur_y = start_ch;
451                 lockcorner = OB_CORNER_TOPRIGHT;
452             } else if (corner == prop_atoms.net_wm_moveresize_size_keyboard) {
453                 cur_x = start_cw + (e->xmotion.x_root - start_x);
454                 cur_y = start_ch + (e->xmotion.y_root - start_y);
455                 lockcorner = OB_CORNER_TOPLEFT;
456             } else
457                 g_assert_not_reached();
458
459             calc_resize(TRUE);
460             do_resize();
461         }
462     } else if (e->type == KeyPress) {
463         if (e->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
464             moveresize_end(TRUE);
465         else if (e->xkey.keycode == ob_keycode(OB_KEY_RETURN))
466             moveresize_end(FALSE);
467         else {
468             if (corner == prop_atoms.net_wm_moveresize_size_keyboard) {
469                 gint dx = 0, dy = 0, ox = cur_x, oy = cur_y;
470
471                 if (e->xkey.keycode == ob_keycode(OB_KEY_RIGHT))
472                     dx = MAX(4, moveresize_client->size_inc.width);
473                 else if (e->xkey.keycode == ob_keycode(OB_KEY_LEFT))
474                     dx = -MAX(4, moveresize_client->size_inc.width);
475                 else if (e->xkey.keycode == ob_keycode(OB_KEY_DOWN))
476                     dy = MAX(4, moveresize_client->size_inc.height);
477                 else if (e->xkey.keycode == ob_keycode(OB_KEY_UP))
478                     dy = -MAX(4, moveresize_client->size_inc.height);
479                 else
480                     return;
481
482                 cur_x += dx;
483                 cur_y += dy;
484                 XWarpPointer(ob_display, None, None, 0, 0, 0, 0, dx, dy);
485                 /* steal the motion events this causes */
486                 XSync(ob_display, FALSE);
487                 {
488                     XEvent ce;
489                     while (XCheckTypedEvent(ob_display, MotionNotify, &ce));
490                 }
491
492                 do_resize(FALSE);
493
494                 /* because the cursor moves even though the window does
495                    not nessesarily (resistance), this adjusts where the curor
496                    thinks it started so that it keeps up with where the window
497                    actually is */
498                 start_x += dx - (cur_x - ox);
499                 start_y += dy - (cur_y - oy);
500             } else if (corner == prop_atoms.net_wm_moveresize_move_keyboard) {
501                 gint dx = 0, dy = 0, ox = cur_x, oy = cur_y;
502                 gint opx, px, opy, py;
503
504                 if (e->xkey.keycode == ob_keycode(OB_KEY_RIGHT))
505                     dx = 4;
506                 else if (e->xkey.keycode == ob_keycode(OB_KEY_LEFT))
507                     dx = -4;
508                 else if (e->xkey.keycode == ob_keycode(OB_KEY_DOWN))
509                     dy = 4;
510                 else if (e->xkey.keycode == ob_keycode(OB_KEY_UP))
511                     dy = -4;
512                 else
513                     return;
514
515                 cur_x += dx;
516                 cur_y += dy;
517                 screen_pointer_pos(&opx, &opy);
518                 XWarpPointer(ob_display, None, None, 0, 0, 0, 0, dx, dy);
519                 /* steal the motion events this causes */
520                 XSync(ob_display, FALSE);
521                 {
522                     XEvent ce;
523                     while (XCheckTypedEvent(ob_display, MotionNotify, &ce));
524                 }
525                 screen_pointer_pos(&px, &py);
526
527                 do_move(FALSE);
528
529                 /* because the cursor moves even though the window does
530                    not nessesarily (resistance), this adjusts where the curor
531                    thinks it started so that it keeps up with where the window
532                    actually is */
533                 start_x += (px - opx) - (cur_x - ox);
534                 start_y += (py - opy) - (cur_y - oy);
535             }
536         }
537     }
538 #ifdef SYNC
539     else if (e->type == extensions_sync_event_basep + XSyncAlarmNotify)
540     {
541         waiting_for_sync = FALSE; /* we got our sync... */
542         do_resize(); /* ...so try resize if there is more change pending */
543     }
544 #endif
545 }