]> icculus.org git repositories - dana/openbox.git/blob - openbox/moveresize.c
popups fixes. if the text for the popup is empty now, there wont be extra padding...
[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 "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     frame_end_iconify_animation(c->frame);
163
164     moveresize_client = c;
165     start_cx = c->area.x;
166     start_cy = c->area.y;
167     /* these adjustments for the size_inc make resizing a terminal more
168        friendly. you essentially start the resize in the middle of the
169        increment instead of at 0, so you have to move half an increment
170        either way instead of a full increment one and 1 px the other. and this
171        is one large mother fucking comment. */
172     start_cw = c->area.width + c->size_inc.width / 2;
173     start_ch = c->area.height + c->size_inc.height / 2;
174     start_x = x;
175     start_y = y;
176     corner = cnr;
177     button = b;
178
179     /*
180       have to change start_cx and start_cy if going to do this..
181     if (corner == prop_atoms.net_wm_moveresize_move_keyboard ||
182         corner == prop_atoms.net_wm_moveresize_size_keyboard)
183         XWarpPointer(ob_display, None, c->window, 0, 0, 0, 0,
184                      c->area.width / 2, c->area.height / 2);
185     */
186
187     if (moving) {
188         cur_x = start_cx;
189         cur_y = start_cy;
190     } else {
191         cur_x = start_cw;
192         cur_y = start_ch;
193     }
194
195     moveresize_in_progress = TRUE;
196
197     if (corner == prop_atoms.net_wm_moveresize_size_topleft)
198         cur = OB_CURSOR_NORTHWEST;
199     else if (corner == prop_atoms.net_wm_moveresize_size_top)
200         cur = OB_CURSOR_NORTH;
201     else if (corner == prop_atoms.net_wm_moveresize_size_topright)
202         cur = OB_CURSOR_NORTHEAST;
203     else if (corner == prop_atoms.net_wm_moveresize_size_right)
204         cur = OB_CURSOR_EAST;
205     else if (corner == prop_atoms.net_wm_moveresize_size_bottomright)
206         cur = OB_CURSOR_SOUTHEAST;
207     else if (corner == prop_atoms.net_wm_moveresize_size_bottom)
208         cur = OB_CURSOR_SOUTH;
209     else if (corner == prop_atoms.net_wm_moveresize_size_bottomleft)
210         cur = OB_CURSOR_SOUTHWEST;
211     else if (corner == prop_atoms.net_wm_moveresize_size_left)
212         cur = OB_CURSOR_WEST;
213     else if (corner == prop_atoms.net_wm_moveresize_size_keyboard)
214         cur = OB_CURSOR_SOUTHEAST;
215     else if (corner == prop_atoms.net_wm_moveresize_move)
216         cur = OB_CURSOR_MOVE;
217     else if (corner == prop_atoms.net_wm_moveresize_move_keyboard)
218         cur = OB_CURSOR_MOVE;
219     else
220         g_assert_not_reached();
221
222 #ifdef SYNC
223     if (config_resize_redraw && !moving && extensions_shape &&
224         moveresize_client->sync_request && moveresize_client->sync_counter)
225     {
226         /* Initialize values for the resize syncing, and create an alarm for
227            the client's xsync counter */
228
229         XSyncValue val;
230         XSyncAlarmAttributes aa;
231
232         /* set the counter to an initial value */
233         XSyncIntToValue(&val, 0);
234         XSyncSetCounter(ob_display, moveresize_client->sync_counter, val);
235
236         /* this will be incremented when we tell the client what we're
237            looking for */
238         moveresize_client->sync_counter_value = 0;
239
240         /* the next sequence we're waiting for with the alarm */
241         XSyncIntToValue(&val, 1);
242
243         /* set an alarm on the counter */
244         aa.trigger.counter = moveresize_client->sync_counter;
245         aa.trigger.wait_value = val;
246         aa.trigger.value_type = XSyncAbsolute;
247         aa.trigger.test_type = XSyncPositiveTransition;
248         aa.events = True;
249         XSyncIntToValue(&aa.delta, 1);
250         moveresize_alarm = XSyncCreateAlarm(ob_display,
251                                             XSyncCACounter |
252                                             XSyncCAValue |
253                                             XSyncCAValueType |
254                                             XSyncCATestType |
255                                             XSyncCADelta |
256                                             XSyncCAEvents,
257                                             &aa);
258
259         waiting_for_sync = FALSE;
260     }
261 #endif
262
263     grab_pointer(TRUE, FALSE, cur);
264     grab_keyboard(TRUE);
265 }
266
267 void moveresize_end(gboolean cancel)
268 {
269     gint x, y;
270
271     grab_keyboard(FALSE);
272     grab_pointer(FALSE, FALSE, OB_CURSOR_NONE);
273
274     popup_hide(popup);
275
276     if (moving) {
277         client_move(moveresize_client,
278                     (cancel ? start_cx : cur_x),
279                     (cancel ? start_cy : cur_y));
280     } else {
281 #ifdef SYNC
282         /* turn off the alarm */
283         if (moveresize_alarm != None) {
284             XSyncDestroyAlarm(ob_display, moveresize_alarm);
285             moveresize_alarm = None;
286         }
287 #endif
288
289         get_resize_position(&x, &y, cancel);
290         client_configure(moveresize_client, x, y,
291                          (cancel ? start_cw : cur_x),
292                          (cancel ? start_ch : cur_y), TRUE, TRUE);
293     }
294
295     moveresize_in_progress = FALSE;
296     moveresize_client = NULL;
297 }
298
299 static void do_move(gboolean resist)
300 {
301     if (resist) {
302         resist_move_windows(moveresize_client, &cur_x, &cur_y);
303         resist_move_monitors(moveresize_client, &cur_x, &cur_y);
304     }
305
306     client_configure(moveresize_client, cur_x, cur_y,
307                      moveresize_client->area.width,
308                      moveresize_client->area.height, TRUE, FALSE);
309     if (config_resize_popup_show == 2) /* == "Always" */
310         popup_coords(moveresize_client, "%d x %d",
311                 moveresize_client->frame->area.x,
312                 moveresize_client->frame->area.y);
313 }
314
315 static void do_resize()
316 {
317 #ifdef SYNC
318     if (config_resize_redraw && extensions_sync &&
319         moveresize_client->sync_request && moveresize_client->sync_counter)
320     {
321         XEvent ce;
322         XSyncValue val;
323         gint x, y, w, h, lw, lh;
324
325         /* are we already waiting for the sync counter to catch up? */
326         if (waiting_for_sync)
327             return;
328
329         /* see if it is actually going to resize */
330         x = 0;
331         y = 0;
332         w = cur_x;
333         h = cur_y;
334         client_try_configure(moveresize_client, &x, &y, &w, &h,
335                              &lw, &lh, TRUE);
336         if (w == moveresize_client->area.width &&
337             h == moveresize_client->area.height)
338         {
339             return;
340         }
341
342         /* increment the value we're waiting for */
343         ++moveresize_client->sync_counter_value;
344         XSyncIntToValue(&val, moveresize_client->sync_counter_value);
345
346         /* tell the client what we're waiting for */
347         ce.xclient.type = ClientMessage;
348         ce.xclient.message_type = prop_atoms.wm_protocols;
349         ce.xclient.display = ob_display;
350         ce.xclient.window = moveresize_client->window;
351         ce.xclient.format = 32;
352         ce.xclient.data.l[0] = prop_atoms.net_wm_sync_request;
353         ce.xclient.data.l[1] = event_curtime;
354         ce.xclient.data.l[2] = XSyncValueLow32(val);
355         ce.xclient.data.l[3] = XSyncValueHigh32(val);
356         ce.xclient.data.l[4] = 0l;
357         XSendEvent(ob_display, moveresize_client->window, FALSE,
358                    NoEventMask, &ce);
359
360         waiting_for_sync = TRUE;
361     }
362 #endif
363
364     {
365         gint x, y;
366         get_resize_position(&x, &y, FALSE);
367         client_configure(moveresize_client, x, y, cur_x, cur_y, TRUE, FALSE);
368     }
369
370     /* this would be better with a fixed width font ... XXX can do it better
371        if there are 2 text boxes */
372     if (config_resize_popup_show == 2 || /* == "Always" */
373             (config_resize_popup_show == 1 && /* == "Nonpixel" */
374              moveresize_client->size_inc.width > 1 &&
375              moveresize_client->size_inc.height > 1))
376         popup_coords(moveresize_client, "%d x %d",
377                      moveresize_client->logical_size.width,
378                      moveresize_client->logical_size.height);
379 }
380
381 static void calc_resize(gboolean resist)
382 {
383     /* resist_size_* needs the frame size */
384     cur_x += moveresize_client->frame->size.left +
385         moveresize_client->frame->size.right;
386     cur_y += moveresize_client->frame->size.top +
387         moveresize_client->frame->size.bottom;
388
389     if (resist) {
390         resist_size_windows(moveresize_client, &cur_x, &cur_y, lockcorner);
391         resist_size_monitors(moveresize_client, &cur_x, &cur_y, lockcorner);
392     }
393
394     cur_x -= moveresize_client->frame->size.left +
395         moveresize_client->frame->size.right;
396     cur_y -= moveresize_client->frame->size.top +
397         moveresize_client->frame->size.bottom;
398 }
399
400 gboolean moveresize_event(XEvent *e)
401 {
402     gboolean used = FALSE;
403
404     g_assert(moveresize_in_progress);
405
406     if (e->type == ButtonPress) {
407         if (!button) {
408             start_x = e->xbutton.x_root;
409             start_y = e->xbutton.y_root;
410             button = e->xbutton.button; /* this will end it now */
411         }
412         used = e->xbutton.button == button;
413     } else if (e->type == ButtonRelease) {
414         if (!button || e->xbutton.button == button) {
415             moveresize_end(FALSE);
416             used = TRUE;
417         }
418     } else if (e->type == MotionNotify) {
419         if (moving) {
420             cur_x = start_cx + e->xmotion.x_root - start_x;
421             cur_y = start_cy + e->xmotion.y_root - start_y;
422             do_move(TRUE);
423         } else {
424             if (corner == prop_atoms.net_wm_moveresize_size_topleft) {
425                 cur_x = start_cw - (e->xmotion.x_root - start_x);
426                 cur_y = start_ch - (e->xmotion.y_root - start_y);
427                 lockcorner = OB_CORNER_BOTTOMRIGHT;
428             } else if (corner == prop_atoms.net_wm_moveresize_size_top) {
429                 cur_x = start_cw;
430                 cur_y = start_ch - (e->xmotion.y_root - start_y);
431                 lockcorner = OB_CORNER_BOTTOMRIGHT;
432             } else if (corner == prop_atoms.net_wm_moveresize_size_topright) {
433                 cur_x = start_cw + (e->xmotion.x_root - start_x);
434                 cur_y = start_ch - (e->xmotion.y_root - start_y);
435                 lockcorner = OB_CORNER_BOTTOMLEFT;
436             } else if (corner == prop_atoms.net_wm_moveresize_size_right) { 
437                 cur_x = start_cw + (e->xmotion.x_root - start_x);
438                 cur_y = start_ch;
439                 lockcorner = OB_CORNER_BOTTOMLEFT;
440             } else if (corner ==
441                        prop_atoms.net_wm_moveresize_size_bottomright) {
442                 cur_x = start_cw + (e->xmotion.x_root - start_x);
443                 cur_y = start_ch + (e->xmotion.y_root - start_y);
444                 lockcorner = OB_CORNER_TOPLEFT;
445             } else if (corner == prop_atoms.net_wm_moveresize_size_bottom) {
446                 cur_x = start_cw;
447                 cur_y = start_ch + (e->xmotion.y_root - start_y);
448                 lockcorner = OB_CORNER_TOPLEFT;
449             } else if (corner ==
450                        prop_atoms.net_wm_moveresize_size_bottomleft) {
451                 cur_x = start_cw - (e->xmotion.x_root - start_x);
452                 cur_y = start_ch + (e->xmotion.y_root - start_y);
453                 lockcorner = OB_CORNER_TOPRIGHT;
454             } else if (corner == prop_atoms.net_wm_moveresize_size_left) {
455                 cur_x = start_cw - (e->xmotion.x_root - start_x);
456                 cur_y = start_ch;
457                 lockcorner = OB_CORNER_TOPRIGHT;
458             } else if (corner == prop_atoms.net_wm_moveresize_size_keyboard) {
459                 cur_x = start_cw + (e->xmotion.x_root - start_x);
460                 cur_y = start_ch + (e->xmotion.y_root - start_y);
461                 lockcorner = OB_CORNER_TOPLEFT;
462             } else
463                 g_assert_not_reached();
464
465             calc_resize(TRUE);
466             do_resize();
467         }
468         used = TRUE;
469     } else if (e->type == KeyPress) {
470         if (e->xkey.keycode == ob_keycode(OB_KEY_ESCAPE)) {
471             moveresize_end(TRUE);
472             used = TRUE;
473         } else if (e->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
474             moveresize_end(FALSE);
475             used = TRUE;
476         } else if (e->xkey.keycode == ob_keycode(OB_KEY_RIGHT) ||
477                    e->xkey.keycode == ob_keycode(OB_KEY_LEFT) ||
478                    e->xkey.keycode == ob_keycode(OB_KEY_DOWN) ||
479                    e->xkey.keycode == ob_keycode(OB_KEY_UP))
480         {
481             if (corner == prop_atoms.net_wm_moveresize_size_keyboard) {
482                 gint dx = 0, dy = 0, ox = cur_x, oy = cur_y;
483
484                 if (e->xkey.keycode == ob_keycode(OB_KEY_RIGHT))
485                     dx = MAX(4, moveresize_client->size_inc.width);
486                 else if (e->xkey.keycode == ob_keycode(OB_KEY_LEFT))
487                     dx = -MAX(4, moveresize_client->size_inc.width);
488                 else if (e->xkey.keycode == ob_keycode(OB_KEY_DOWN))
489                     dy = MAX(4, moveresize_client->size_inc.height);
490                 else /* if (e->xkey.keycode == ob_keycode(OB_KEY_UP)) */
491                     dy = -MAX(4, moveresize_client->size_inc.height);
492
493                 cur_x += dx;
494                 cur_y += dy;
495                 XWarpPointer(ob_display, None, None, 0, 0, 0, 0, dx, dy);
496                 /* steal the motion events this causes */
497                 XSync(ob_display, FALSE);
498                 {
499                     XEvent ce;
500                     while (XCheckTypedEvent(ob_display, MotionNotify, &ce));
501                 }
502
503                 do_resize(FALSE);
504
505                 /* because the cursor moves even though the window does
506                    not nessesarily (resistance), this adjusts where the curor
507                    thinks it started so that it keeps up with where the window
508                    actually is */
509                 start_x += dx - (cur_x - ox);
510                 start_y += dy - (cur_y - oy);
511
512                 used = TRUE;
513             } else if (corner == prop_atoms.net_wm_moveresize_move_keyboard) {
514                 gint dx = 0, dy = 0, ox = cur_x, oy = cur_y;
515                 gint opx, px, opy, py;
516
517                 if (e->xkey.keycode == ob_keycode(OB_KEY_RIGHT))
518                     dx = 4;
519                 else if (e->xkey.keycode == ob_keycode(OB_KEY_LEFT))
520                     dx = -4;
521                 else if (e->xkey.keycode == ob_keycode(OB_KEY_DOWN))
522                     dy = 4;
523                 else /* if (e->xkey.keycode == ob_keycode(OB_KEY_UP)) */
524                     dy = -4;
525
526                 cur_x += dx;
527                 cur_y += dy;
528                 screen_pointer_pos(&opx, &opy);
529                 XWarpPointer(ob_display, None, None, 0, 0, 0, 0, dx, dy);
530                 /* steal the motion events this causes */
531                 XSync(ob_display, FALSE);
532                 {
533                     XEvent ce;
534                     while (XCheckTypedEvent(ob_display, MotionNotify, &ce));
535                 }
536                 screen_pointer_pos(&px, &py);
537
538                 do_move(FALSE);
539
540                 /* because the cursor moves even though the window does
541                    not nessesarily (resistance), this adjusts where the curor
542                    thinks it started so that it keeps up with where the window
543                    actually is */
544                 start_x += (px - opx) - (cur_x - ox);
545                 start_y += (py - opy) - (cur_y - oy);
546
547                 used = TRUE;
548             }
549         }
550     }
551 #ifdef SYNC
552     else if (e->type == extensions_sync_event_basep + XSyncAlarmNotify)
553     {
554         waiting_for_sync = FALSE; /* we got our sync... */
555         do_resize(); /* ...so try resize if there is more change pending */
556         used = TRUE;
557     }
558 #endif
559     return used;
560 }