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