]> Dogcows Code - chaz/openbox/blob - openbox/moveresize.c
woa.. let you do mouse actions while in an interactive keyboard action, and let you...
[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 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 gboolean moveresize_event(XEvent *e)
399 {
400 gboolean used = FALSE;
401
402 g_assert(moveresize_in_progress);
403
404 if (e->type == ButtonPress) {
405 if (!button) {
406 start_x = e->xbutton.x_root;
407 start_y = e->xbutton.y_root;
408 button = e->xbutton.button; /* this will end it now */
409 }
410 used = TRUE;
411 } else if (e->type == ButtonRelease) {
412 if (!button || e->xbutton.button == button) {
413 moveresize_end(FALSE);
414 }
415 used = TRUE;
416 } else if (e->type == MotionNotify) {
417 if (moving) {
418 cur_x = start_cx + e->xmotion.x_root - start_x;
419 cur_y = start_cy + e->xmotion.y_root - start_y;
420 do_move(TRUE);
421 } else {
422 if (corner == prop_atoms.net_wm_moveresize_size_topleft) {
423 cur_x = start_cw - (e->xmotion.x_root - start_x);
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_top) {
427 cur_x = start_cw;
428 cur_y = start_ch - (e->xmotion.y_root - start_y);
429 lockcorner = OB_CORNER_BOTTOMRIGHT;
430 } else if (corner == prop_atoms.net_wm_moveresize_size_topright) {
431 cur_x = start_cw + (e->xmotion.x_root - start_x);
432 cur_y = start_ch - (e->xmotion.y_root - start_y);
433 lockcorner = OB_CORNER_BOTTOMLEFT;
434 } else if (corner == prop_atoms.net_wm_moveresize_size_right) {
435 cur_x = start_cw + (e->xmotion.x_root - start_x);
436 cur_y = start_ch;
437 lockcorner = OB_CORNER_BOTTOMLEFT;
438 } else if (corner ==
439 prop_atoms.net_wm_moveresize_size_bottomright) {
440 cur_x = start_cw + (e->xmotion.x_root - start_x);
441 cur_y = start_ch + (e->xmotion.y_root - start_y);
442 lockcorner = OB_CORNER_TOPLEFT;
443 } else if (corner == prop_atoms.net_wm_moveresize_size_bottom) {
444 cur_x = start_cw;
445 cur_y = start_ch + (e->xmotion.y_root - start_y);
446 lockcorner = OB_CORNER_TOPLEFT;
447 } else if (corner ==
448 prop_atoms.net_wm_moveresize_size_bottomleft) {
449 cur_x = start_cw - (e->xmotion.x_root - start_x);
450 cur_y = start_ch + (e->xmotion.y_root - start_y);
451 lockcorner = OB_CORNER_TOPRIGHT;
452 } else if (corner == prop_atoms.net_wm_moveresize_size_left) {
453 cur_x = start_cw - (e->xmotion.x_root - start_x);
454 cur_y = start_ch;
455 lockcorner = OB_CORNER_TOPRIGHT;
456 } else if (corner == prop_atoms.net_wm_moveresize_size_keyboard) {
457 cur_x = start_cw + (e->xmotion.x_root - start_x);
458 cur_y = start_ch + (e->xmotion.y_root - start_y);
459 lockcorner = OB_CORNER_TOPLEFT;
460 } else
461 g_assert_not_reached();
462
463 calc_resize(TRUE);
464 do_resize();
465 }
466 used = TRUE;
467 } else if (e->type == KeyPress) {
468 if (e->xkey.keycode == ob_keycode(OB_KEY_ESCAPE)) {
469 moveresize_end(TRUE);
470 used = TRUE;
471 } else if (e->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
472 moveresize_end(FALSE);
473 used = TRUE;
474 } else if (e->xkey.keycode == ob_keycode(OB_KEY_RIGHT) ||
475 e->xkey.keycode == ob_keycode(OB_KEY_LEFT) ||
476 e->xkey.keycode == ob_keycode(OB_KEY_DOWN) ||
477 e->xkey.keycode == ob_keycode(OB_KEY_UP))
478 {
479 if (corner == prop_atoms.net_wm_moveresize_size_keyboard) {
480 gint dx = 0, dy = 0, ox = cur_x, oy = cur_y;
481
482 if (e->xkey.keycode == ob_keycode(OB_KEY_RIGHT))
483 dx = MAX(4, moveresize_client->size_inc.width);
484 else if (e->xkey.keycode == ob_keycode(OB_KEY_LEFT))
485 dx = -MAX(4, moveresize_client->size_inc.width);
486 else if (e->xkey.keycode == ob_keycode(OB_KEY_DOWN))
487 dy = MAX(4, moveresize_client->size_inc.height);
488 else /* if (e->xkey.keycode == ob_keycode(OB_KEY_UP)) */
489 dy = -MAX(4, moveresize_client->size_inc.height);
490
491 cur_x += dx;
492 cur_y += dy;
493 XWarpPointer(ob_display, None, None, 0, 0, 0, 0, dx, dy);
494 /* steal the motion events this causes */
495 XSync(ob_display, FALSE);
496 {
497 XEvent ce;
498 while (XCheckTypedEvent(ob_display, MotionNotify, &ce));
499 }
500
501 do_resize(FALSE);
502
503 /* because the cursor moves even though the window does
504 not nessesarily (resistance), this adjusts where the curor
505 thinks it started so that it keeps up with where the window
506 actually is */
507 start_x += dx - (cur_x - ox);
508 start_y += dy - (cur_y - oy);
509
510 used = TRUE;
511 } else if (corner == prop_atoms.net_wm_moveresize_move_keyboard) {
512 gint dx = 0, dy = 0, ox = cur_x, oy = cur_y;
513 gint opx, px, opy, py;
514
515 if (e->xkey.keycode == ob_keycode(OB_KEY_RIGHT))
516 dx = 4;
517 else if (e->xkey.keycode == ob_keycode(OB_KEY_LEFT))
518 dx = -4;
519 else if (e->xkey.keycode == ob_keycode(OB_KEY_DOWN))
520 dy = 4;
521 else /* if (e->xkey.keycode == ob_keycode(OB_KEY_UP)) */
522 dy = -4;
523
524 cur_x += dx;
525 cur_y += dy;
526 screen_pointer_pos(&opx, &opy);
527 XWarpPointer(ob_display, None, None, 0, 0, 0, 0, dx, dy);
528 /* steal the motion events this causes */
529 XSync(ob_display, FALSE);
530 {
531 XEvent ce;
532 while (XCheckTypedEvent(ob_display, MotionNotify, &ce));
533 }
534 screen_pointer_pos(&px, &py);
535
536 do_move(FALSE);
537
538 /* because the cursor moves even though the window does
539 not nessesarily (resistance), this adjusts where the curor
540 thinks it started so that it keeps up with where the window
541 actually is */
542 start_x += (px - opx) - (cur_x - ox);
543 start_y += (py - opy) - (cur_y - oy);
544
545 used = TRUE;
546 }
547 }
548 }
549 #ifdef SYNC
550 else if (e->type == extensions_sync_event_basep + XSyncAlarmNotify)
551 {
552 waiting_for_sync = FALSE; /* we got our sync... */
553 do_resize(); /* ...so try resize if there is more change pending */
554 used = TRUE;
555 }
556 #endif
557 return used;
558 }
This page took 0.057246 seconds and 5 git commands to generate.