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