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