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