]> icculus.org git repositories - dana/openbox.git/blob - openbox/moveresize.c
oops, bad idea
[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 popup_coords(ObClient *c, const gchar *format, gint a, gint b)
86 {
87     gchar *text;
88
89     text = g_strdup_printf(format, a, b);
90     if (config_resize_popup_pos == 1) /* == "Top" */
91         popup_position(popup, SouthGravity,
92                        c->frame->area.x
93                      + c->frame->area.width/2,
94                        c->frame->area.y - ob_rr_theme->fbwidth);
95     else /* == "Center" */
96         popup_position(popup, CenterGravity,
97                        c->frame->area.x + c->frame->size.left +
98                        c->area.width / 2,
99                        c->frame->area.y + c->frame->size.top +
100                        c->area.height / 2);
101     popup_show(popup, text);
102     g_free(text);
103 }
104
105 void moveresize_start(ObClient *c, gint x, gint y, guint b, guint32 cnr)
106 {
107     ObCursor cur;
108
109     moving = (cnr == prop_atoms.net_wm_moveresize_move ||
110               cnr == prop_atoms.net_wm_moveresize_move_keyboard);
111
112     if (moveresize_in_progress || !c->frame->visible ||
113         !(moving ?
114           (c->functions & OB_CLIENT_FUNC_MOVE) :
115           (c->functions & OB_CLIENT_FUNC_RESIZE)))
116         return;
117
118     moveresize_client = c;
119     start_cx = c->frame->area.x;
120     start_cy = c->frame->area.y;
121     /* these adjustments for the size_inc make resizing a terminal more
122        friendly. you essentially start the resize in the middle of the
123        increment instead of at 0, so you have to move half an increment
124        either way instead of a full increment one and 1 px the other. and this
125        is one large mother fucking comment. */
126     start_cw = c->area.width + c->size_inc.width / 2;
127     start_ch = c->area.height + c->size_inc.height / 2;
128     start_x = x;
129     start_y = y;
130     corner = cnr;
131     button = b;
132
133     /*
134       have to change start_cx and start_cy if going to do this..
135     if (corner == prop_atoms.net_wm_moveresize_move_keyboard ||
136         corner == prop_atoms.net_wm_moveresize_size_keyboard)
137         XWarpPointer(ob_display, None, c->window, 0, 0, 0, 0,
138                      c->area.width / 2, c->area.height / 2);
139     */
140
141     if (moving) {
142         cur_x = start_cx;
143         cur_y = start_cy;
144     } else {
145         cur_x = start_cw;
146         cur_y = start_ch;
147     }
148
149     moveresize_in_progress = TRUE;
150
151     if (corner == prop_atoms.net_wm_moveresize_size_topleft)
152         cur = OB_CURSOR_NORTHWEST;
153     else if (corner == prop_atoms.net_wm_moveresize_size_top)
154         cur = OB_CURSOR_NORTH;
155     else if (corner == prop_atoms.net_wm_moveresize_size_topright)
156         cur = OB_CURSOR_NORTHEAST;
157     else if (corner == prop_atoms.net_wm_moveresize_size_right)
158         cur = OB_CURSOR_EAST;
159     else if (corner == prop_atoms.net_wm_moveresize_size_bottomright)
160         cur = OB_CURSOR_SOUTHEAST;
161     else if (corner == prop_atoms.net_wm_moveresize_size_bottom)
162         cur = OB_CURSOR_SOUTH;
163     else if (corner == prop_atoms.net_wm_moveresize_size_bottomleft)
164         cur = OB_CURSOR_SOUTHWEST;
165     else if (corner == prop_atoms.net_wm_moveresize_size_left)
166         cur = OB_CURSOR_WEST;
167     else if (corner == prop_atoms.net_wm_moveresize_size_keyboard)
168         cur = OB_CURSOR_SOUTHEAST;
169     else if (corner == prop_atoms.net_wm_moveresize_move)
170         cur = OB_CURSOR_MOVE;
171     else if (corner == prop_atoms.net_wm_moveresize_move_keyboard)
172         cur = OB_CURSOR_MOVE;
173     else
174         g_assert_not_reached();
175
176 #ifdef SYNC
177     if (config_resize_redraw && !moving && extensions_shape &&
178         moveresize_client->sync_request && moveresize_client->sync_counter)
179     {
180         /* Initialize values for the resize syncing, and create an alarm for
181            the client's xsync counter */
182
183         XSyncValue val;
184         XSyncAlarmAttributes aa;
185
186         /* set the counter to an initial value */
187         XSyncIntToValue(&val, 0);
188         XSyncSetCounter(ob_display, moveresize_client->sync_counter, val);
189
190         /* this will be incremented when we tell the client what we're
191            looking for */
192         moveresize_client->sync_counter_value = 0;
193
194         /* the next sequence we're waiting for with the alarm */
195         XSyncIntToValue(&val, 1);
196
197         /* set an alarm on the counter */
198         aa.trigger.counter = moveresize_client->sync_counter;
199         aa.trigger.wait_value = val;
200         aa.trigger.value_type = XSyncAbsolute;
201         aa.trigger.test_type = XSyncPositiveTransition;
202         aa.events = True;
203         XSyncIntToValue(&aa.delta, 1);
204         moveresize_alarm = XSyncCreateAlarm(ob_display,
205                                             XSyncCACounter |
206                                             XSyncCAValue |
207                                             XSyncCAValueType |
208                                             XSyncCATestType |
209                                             XSyncCADelta |
210                                             XSyncCAEvents,
211                                             &aa);
212
213         waiting_for_sync = FALSE;
214     }
215 #endif
216
217     grab_pointer(TRUE, FALSE, cur);
218     grab_keyboard(TRUE);
219 }
220
221 void moveresize_end(gboolean cancel)
222 {
223     grab_keyboard(FALSE);
224     grab_pointer(FALSE, FALSE, OB_CURSOR_NONE);
225
226     popup_hide(popup);
227
228     if (moving) {
229         client_move(moveresize_client,
230                     (cancel ? start_cx : cur_x),
231                     (cancel ? start_cy : cur_y));
232     } else {
233 #ifdef SYNC
234         /* turn off the alarm */
235         if (moveresize_alarm != None) {
236             XSyncDestroyAlarm(ob_display, moveresize_alarm);
237             moveresize_alarm = None;
238         }
239 #endif
240
241         client_configure(moveresize_client, lockcorner,
242                          moveresize_client->area.x,
243                          moveresize_client->area.y,
244                          (cancel ? start_cw : cur_x),
245                          (cancel ? start_ch : cur_y), TRUE, TRUE);
246     }
247
248     moveresize_in_progress = FALSE;
249     moveresize_client = NULL;
250 }
251
252 static void do_move(gboolean resist)
253 {
254     if (resist) {
255         resist_move_windows(moveresize_client, &cur_x, &cur_y);
256         resist_move_monitors(moveresize_client, &cur_x, &cur_y);
257     }
258
259     /* get where the client should be */
260     frame_frame_gravity(moveresize_client->frame, &cur_x, &cur_y);
261     client_configure(moveresize_client, OB_CORNER_TOPLEFT, cur_x, cur_y,
262                      moveresize_client->area.width,
263                      moveresize_client->area.height, TRUE, FALSE);
264     if (config_resize_popup_show == 2) /* == "Always" */
265         popup_coords(moveresize_client, "%d x %d",
266                 moveresize_client->frame->area.x,
267                 moveresize_client->frame->area.y);
268 }
269
270 static void do_resize()
271 {
272 #ifdef SYNC
273     if (config_resize_redraw && extensions_sync &&
274         moveresize_client->sync_request && moveresize_client->sync_counter)
275     {
276         XEvent ce;
277         XSyncValue val;
278         gint x, y, w, h, lw, lh;
279
280         /* are we already waiting for the sync counter to catch up? */
281         if (waiting_for_sync)
282             return;
283
284         /* see if it is actually going to resize */
285         x = moveresize_client->area.x;
286         y = moveresize_client->area.y;
287         w = cur_x;
288         h = cur_y;
289         client_try_configure(moveresize_client, lockcorner, &x, &y, &w, &h,
290                              &lw, &lh, TRUE);
291         if (w == moveresize_client->area.width &&
292             h == moveresize_client->area.height)
293         {
294             return;
295         }
296
297         /* increment the value we're waiting for */
298         ++moveresize_client->sync_counter_value;
299         XSyncIntToValue(&val, moveresize_client->sync_counter_value);
300
301         /* tell the client what we're waiting for */
302         ce.xclient.type = ClientMessage;
303         ce.xclient.message_type = prop_atoms.wm_protocols;
304         ce.xclient.display = ob_display;
305         ce.xclient.window = moveresize_client->window;
306         ce.xclient.format = 32;
307         ce.xclient.data.l[0] = prop_atoms.net_wm_sync_request;
308         ce.xclient.data.l[1] = event_curtime;
309         ce.xclient.data.l[2] = XSyncValueLow32(val);
310         ce.xclient.data.l[3] = XSyncValueHigh32(val);
311         ce.xclient.data.l[4] = 0l;
312         XSendEvent(ob_display, moveresize_client->window, FALSE,
313                    NoEventMask, &ce);
314
315         waiting_for_sync = TRUE;
316     }
317 #endif
318
319     client_configure(moveresize_client, lockcorner, 
320                      moveresize_client->area.x, moveresize_client->area.y,
321                      cur_x, cur_y, TRUE, FALSE);
322
323     /* this would be better with a fixed width font ... XXX can do it better
324        if there are 2 text boxes */
325     if (config_resize_popup_show == 2 || /* == "Always" */
326             (config_resize_popup_show == 1 && /* == "Nonpixel" */
327                 (moveresize_client->size_inc.width > 1 ||
328                  moveresize_client->size_inc.height > 1))
329         )
330         popup_coords(moveresize_client, "%d x %d",
331                      moveresize_client->logical_size.width,
332                      moveresize_client->logical_size.height);
333 }
334
335 static void calc_resize(gboolean resist)
336 {
337     /* resist_size_* needs the frame size */
338     cur_x += moveresize_client->frame->size.left +
339         moveresize_client->frame->size.right;
340     cur_y += moveresize_client->frame->size.top +
341         moveresize_client->frame->size.bottom;
342
343     if (resist) {
344         resist_size_windows(moveresize_client, &cur_x, &cur_y, lockcorner);
345         resist_size_monitors(moveresize_client, &cur_x, &cur_y, lockcorner);
346     }
347
348     cur_x -= moveresize_client->frame->size.left +
349         moveresize_client->frame->size.right;
350     cur_y -= moveresize_client->frame->size.top +
351         moveresize_client->frame->size.bottom;
352 }
353
354 void moveresize_event(XEvent *e)
355 {
356     g_assert(moveresize_in_progress);
357
358     if (e->type == ButtonPress) {
359         if (!button) {
360             start_x = e->xbutton.x_root;
361             start_y = e->xbutton.y_root;
362             button = e->xbutton.button; /* this will end it now */
363         }
364     } else if (e->type == ButtonRelease) {
365         if (!button || e->xbutton.button == button) {
366             moveresize_end(FALSE);
367         }
368     } else if (e->type == MotionNotify) {
369         if (moving) {
370             cur_x = start_cx + e->xmotion.x_root - start_x;
371             cur_y = start_cy + e->xmotion.y_root - start_y;
372             do_move(TRUE);
373         } else {
374             if (corner == prop_atoms.net_wm_moveresize_size_topleft) {
375                 cur_x = start_cw - (e->xmotion.x_root - start_x);
376                 cur_y = start_ch - (e->xmotion.y_root - start_y);
377                 lockcorner = OB_CORNER_BOTTOMRIGHT;
378             } else if (corner == prop_atoms.net_wm_moveresize_size_top) {
379                 cur_x = start_cw;
380                 cur_y = start_ch - (e->xmotion.y_root - start_y);
381                 lockcorner = OB_CORNER_BOTTOMRIGHT;
382             } else if (corner == prop_atoms.net_wm_moveresize_size_topright) {
383                 cur_x = start_cw + (e->xmotion.x_root - start_x);
384                 cur_y = start_ch - (e->xmotion.y_root - start_y);
385                 lockcorner = OB_CORNER_BOTTOMLEFT;
386             } else if (corner == prop_atoms.net_wm_moveresize_size_right) { 
387                 cur_x = start_cw + (e->xmotion.x_root - start_x);
388                 cur_y = start_ch;
389                 lockcorner = OB_CORNER_BOTTOMLEFT;
390             } else if (corner ==
391                        prop_atoms.net_wm_moveresize_size_bottomright) {
392                 cur_x = start_cw + (e->xmotion.x_root - start_x);
393                 cur_y = start_ch + (e->xmotion.y_root - start_y);
394                 lockcorner = OB_CORNER_TOPLEFT;
395             } else if (corner == prop_atoms.net_wm_moveresize_size_bottom) {
396                 cur_x = start_cw;
397                 cur_y = start_ch + (e->xmotion.y_root - start_y);
398                 lockcorner = OB_CORNER_TOPLEFT;
399             } else if (corner ==
400                        prop_atoms.net_wm_moveresize_size_bottomleft) {
401                 cur_x = start_cw - (e->xmotion.x_root - start_x);
402                 cur_y = start_ch + (e->xmotion.y_root - start_y);
403                 lockcorner = OB_CORNER_TOPRIGHT;
404             } else if (corner == prop_atoms.net_wm_moveresize_size_left) {
405                 cur_x = start_cw - (e->xmotion.x_root - start_x);
406                 cur_y = start_ch;
407                 lockcorner = OB_CORNER_TOPRIGHT;
408             } else if (corner == prop_atoms.net_wm_moveresize_size_keyboard) {
409                 cur_x = start_cw + (e->xmotion.x_root - start_x);
410                 cur_y = start_ch + (e->xmotion.y_root - start_y);
411                 lockcorner = OB_CORNER_TOPLEFT;
412             } else
413                 g_assert_not_reached();
414
415             calc_resize(TRUE);
416             do_resize();
417         }
418     } else if (e->type == KeyPress) {
419         if (e->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
420             moveresize_end(TRUE);
421         else if (e->xkey.keycode == ob_keycode(OB_KEY_RETURN))
422             moveresize_end(FALSE);
423         else {
424             if (corner == prop_atoms.net_wm_moveresize_size_keyboard) {
425                 gint dx = 0, dy = 0, ox = cur_x, oy = cur_y;
426
427                 if (e->xkey.keycode == ob_keycode(OB_KEY_RIGHT))
428                     dx = MAX(4, moveresize_client->size_inc.width);
429                 else if (e->xkey.keycode == ob_keycode(OB_KEY_LEFT))
430                     dx = -MAX(4, moveresize_client->size_inc.width);
431                 else if (e->xkey.keycode == ob_keycode(OB_KEY_DOWN))
432                     dy = MAX(4, moveresize_client->size_inc.height);
433                 else if (e->xkey.keycode == ob_keycode(OB_KEY_UP))
434                     dy = -MAX(4, moveresize_client->size_inc.height);
435                 else
436                     return;
437
438                 cur_x += dx;
439                 cur_y += dy;
440                 XWarpPointer(ob_display, None, None, 0, 0, 0, 0, dx, dy);
441                 /* steal the motion events this causes */
442                 XSync(ob_display, FALSE);
443                 {
444                     XEvent ce;
445                     while (XCheckTypedEvent(ob_display, MotionNotify, &ce));
446                 }
447
448                 do_resize(FALSE);
449
450                 /* because the cursor moves even though the window does
451                    not nessesarily (resistance), this adjusts where the curor
452                    thinks it started so that it keeps up with where the window
453                    actually is */
454                 start_x += dx - (cur_x - ox);
455                 start_y += dy - (cur_y - oy);
456             } else if (corner == prop_atoms.net_wm_moveresize_move_keyboard) {
457                 gint dx = 0, dy = 0, ox = cur_x, oy = cur_y;
458                 gint opx, px, opy, py;
459
460                 if (e->xkey.keycode == ob_keycode(OB_KEY_RIGHT))
461                     dx = 4;
462                 else if (e->xkey.keycode == ob_keycode(OB_KEY_LEFT))
463                     dx = -4;
464                 else if (e->xkey.keycode == ob_keycode(OB_KEY_DOWN))
465                     dy = 4;
466                 else if (e->xkey.keycode == ob_keycode(OB_KEY_UP))
467                     dy = -4;
468                 else
469                     return;
470
471                 cur_x += dx;
472                 cur_y += dy;
473                 screen_pointer_pos(&opx, &opy);
474                 XWarpPointer(ob_display, None, None, 0, 0, 0, 0, dx, dy);
475                 /* steal the motion events this causes */
476                 XSync(ob_display, FALSE);
477                 {
478                     XEvent ce;
479                     while (XCheckTypedEvent(ob_display, MotionNotify, &ce));
480                 }
481                 screen_pointer_pos(&px, &py);
482
483                 do_move(FALSE);
484
485                 /* because the cursor moves even though the window does
486                    not nessesarily (resistance), this adjusts where the curor
487                    thinks it started so that it keeps up with where the window
488                    actually is */
489                 start_x += (px - opx) - (cur_x - ox);
490                 start_y += (py - opy) - (cur_y - oy);
491             }
492         }
493     }
494 #ifdef SYNC
495     else if (e->type == extensions_sync_event_basep + XSyncAlarmNotify)
496     {
497         waiting_for_sync = FALSE; /* we got our sync... */
498         do_resize(); /* ...so try resize if there is more change pending */
499     }
500 #endif
501 }