]> Dogcows Code - chaz/openbox/blob - openbox/moveresize.c
use the right cursor for moveresize
[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
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 }
This page took 0.057302 seconds and 5 git commands to generate.