]> Dogcows Code - chaz/openbox/blob - openbox/moveresize.c
yay. way way cleaner code for iconify animations. let people show/hide the frame...
[chaz/openbox] / 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 }
This page took 0.061299 seconds and 5 git commands to generate.