]> Dogcows Code - chaz/openbox/blob - openbox/moveresize.c
update the last user interaction timestamp during a move/resize of the focused window
[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 "client.h"
24 #include "focus.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 "obrender/render.h"
34 #include "obrender/theme.h"
35 #include "obt/display.h"
36 #include "obt/xqueue.h"
37 #include "obt/prop.h"
38 #include "obt/keyboard.h"
39
40 #include <X11/Xlib.h>
41 #include <glib.h>
42
43 /* how far windows move and resize with the keyboard arrows */
44 #define KEY_DIST 8
45 #define SYNC_TIMEOUTS 4
46
47 gboolean moveresize_in_progress = FALSE;
48 ObClient *moveresize_client = NULL;
49 #ifdef SYNC
50 XSyncAlarm moveresize_alarm = None;
51 #endif
52
53 static gboolean moving = FALSE; /* TRUE - moving, FALSE - resizing */
54
55 /* starting geometry for the window being moved/resized, so it can be
56 restored */
57 static gint start_x, start_y, start_cx, start_cy, start_cw, start_ch;
58 static gboolean was_max_horz, was_max_vert;
59 static Rect pre_max_area;
60 static gint cur_x, cur_y, cur_w, cur_h;
61 static guint button;
62 static guint32 corner;
63 static ObDirection edge_warp_dir = -1;
64 static gboolean edge_warp_odd = FALSE;
65 static guint edge_warp_timer = 0;
66 static ObDirection key_resize_edge = -1;
67 #ifdef SYNC
68 static guint waiting_for_sync;
69 static guint sync_timer = 0;
70 #endif
71
72 static ObPopup *popup = NULL;
73
74 static void do_move(gboolean keyboard, gint keydist);
75 static void do_resize(void);
76 static void do_edge_warp(gint x, gint y);
77 static void cancel_edge_warp();
78 #ifdef SYNC
79 static gboolean sync_timeout_func(gpointer data);
80 #endif
81
82 static void client_dest(ObClient *client, gpointer data)
83 {
84 if (moveresize_client == client)
85 moveresize_end(TRUE);
86 }
87
88 void moveresize_startup(gboolean reconfig)
89 {
90 popup = popup_new();
91 popup_set_text_align(popup, RR_JUSTIFY_CENTER);
92
93 if (!reconfig)
94 client_add_destroy_notify(client_dest, NULL);
95 }
96
97 void moveresize_shutdown(gboolean reconfig)
98 {
99 if (!reconfig) {
100 if (moveresize_in_progress)
101 moveresize_end(FALSE);
102 client_remove_destroy_notify(client_dest);
103 }
104
105 popup_free(popup);
106 popup = NULL;
107 }
108
109 static void popup_coords(ObClient *c, const gchar *format, gint a, gint b)
110 {
111 gchar *text;
112
113 text = g_strdup_printf(format, a, b);
114 if (config_resize_popup_pos == OB_RESIZE_POS_TOP)
115 popup_position(popup, SouthGravity,
116 c->frame->area.x
117 + c->frame->area.width/2,
118 c->frame->area.y - ob_rr_theme->fbwidth);
119 else if (config_resize_popup_pos == OB_RESIZE_POS_CENTER)
120 popup_position(popup, CenterGravity,
121 c->frame->area.x + c->frame->area.width / 2,
122 c->frame->area.y + c->frame->area.height / 2);
123 else /* Fixed */ {
124 const Rect *area = screen_physical_area_active();
125 gint gravity, x, y;
126
127 x = config_resize_popup_fixed.x.pos;
128 if (config_resize_popup_fixed.x.center)
129 x = area->x + area->width/2;
130 else if (config_resize_popup_fixed.x.opposite)
131 x = RECT_RIGHT(*area) - x;
132 else
133 x = area->x + x;
134
135 y = config_resize_popup_fixed.y.pos;
136 if (config_resize_popup_fixed.y.center)
137 y = area->y + area->height/2;
138 else if (config_resize_popup_fixed.y.opposite)
139 y = RECT_RIGHT(*area) - y;
140 else
141 y = area->y + y;
142
143 if (config_resize_popup_fixed.x.center) {
144 if (config_resize_popup_fixed.y.center)
145 gravity = CenterGravity;
146 else if (config_resize_popup_fixed.y.opposite)
147 gravity = SouthGravity;
148 else
149 gravity = NorthGravity;
150 }
151 else if (config_resize_popup_fixed.x.opposite) {
152 if (config_resize_popup_fixed.y.center)
153 gravity = EastGravity;
154 else if (config_resize_popup_fixed.y.opposite)
155 gravity = SouthEastGravity;
156 else
157 gravity = NorthEastGravity;
158 }
159 else {
160 if (config_resize_popup_fixed.y.center)
161 gravity = WestGravity;
162 else if (config_resize_popup_fixed.y.opposite)
163 gravity = SouthWestGravity;
164 else
165 gravity = NorthWestGravity;
166 }
167
168 popup_position(popup, gravity, x, y);
169 }
170 popup_show(popup, text);
171 g_free(text);
172 }
173
174 void moveresize_start(ObClient *c, gint x, gint y, guint b, guint32 cnr)
175 {
176 ObCursor cur;
177 gboolean mv = (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE) ||
178 cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD));
179 gint up = 1;
180 gint left = 1;
181
182 if (moveresize_in_progress || !c->frame->visible ||
183 !(mv ?
184 (c->functions & OB_CLIENT_FUNC_MOVE) :
185 (c->functions & OB_CLIENT_FUNC_RESIZE)))
186 return;
187
188 if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT)) {
189 cur = OB_CURSOR_NORTHWEST;
190 up = left = -1;
191 }
192 else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP)) {
193 cur = OB_CURSOR_NORTH;
194 up = -1;
195 }
196 else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT)) {
197 cur = OB_CURSOR_NORTHEAST;
198 up = -1;
199 }
200 else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT))
201 cur = OB_CURSOR_EAST;
202 else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT))
203 cur = OB_CURSOR_SOUTHEAST;
204 else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOM))
205 cur = OB_CURSOR_SOUTH;
206 else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT)) {
207 cur = OB_CURSOR_SOUTHWEST;
208 left = -1;
209 }
210 else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT)) {
211 cur = OB_CURSOR_WEST;
212 left = -1;
213 }
214 else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD))
215 cur = OB_CURSOR_SOUTHEAST;
216 else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE))
217 cur = OB_CURSOR_MOVE;
218 else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD))
219 cur = OB_CURSOR_MOVE;
220 else
221 g_assert_not_reached();
222
223 /* keep the pointer bounded to the screen for move/resize */
224 if (!grab_pointer(FALSE, TRUE, cur))
225 return;
226 if (!grab_keyboard()) {
227 ungrab_pointer();
228 return;
229 }
230
231 frame_end_iconify_animation(c->frame);
232
233 moving = mv;
234 moveresize_client = c;
235 start_cx = c->area.x;
236 start_cy = c->area.y;
237 start_cw = c->area.width;
238 start_ch = c->area.height;
239 /* these adjustments for the size_inc make resizing a terminal more
240 friendly. you essentially start the resize in the middle of the
241 increment instead of at 0, so you have to move half an increment
242 either way instead of a full increment one and 1 px the other. */
243 start_x = x - (mv ? 0 : left * c->size_inc.width / 2);
244 start_y = y - (mv ? 0 : up * c->size_inc.height / 2);
245 corner = cnr;
246 button = b;
247 key_resize_edge = -1;
248
249 /* default to not putting max back on cancel */
250 was_max_horz = was_max_vert = FALSE;
251
252 /*
253 have to change start_cx and start_cy if going to do this..
254 if (corner == prop_atoms.net_wm_moveresize_move_keyboard ||
255 corner == prop_atoms.net_wm_moveresize_size_keyboard)
256 XWarpPointer(ob_display, None, c->window, 0, 0, 0, 0,
257 c->area.width / 2, c->area.height / 2);
258 */
259
260 cur_x = start_cx;
261 cur_y = start_cy;
262 cur_w = start_cw;
263 cur_h = start_ch;
264
265 moveresize_in_progress = TRUE;
266
267 #ifdef SYNC
268 if (config_resize_redraw && !moving && obt_display_extension_sync &&
269 moveresize_client->sync_request && moveresize_client->sync_counter &&
270 !moveresize_client->not_responding)
271 {
272 /* Initialize values for the resize syncing, and create an alarm for
273 the client's xsync counter */
274
275 XSyncValue val;
276 XSyncAlarmAttributes aa;
277
278 /* set the counter to an initial value */
279 XSyncIntToValue(&val, 0);
280 XSyncSetCounter(obt_display, moveresize_client->sync_counter, val);
281
282 /* this will be incremented when we tell the client what we're
283 looking for */
284 moveresize_client->sync_counter_value = 0;
285
286 /* the next sequence we're waiting for with the alarm */
287 XSyncIntToValue(&val, 1);
288
289 /* set an alarm on the counter */
290 aa.trigger.counter = moveresize_client->sync_counter;
291 aa.trigger.wait_value = val;
292 aa.trigger.value_type = XSyncAbsolute;
293 aa.trigger.test_type = XSyncPositiveTransition;
294 aa.events = True;
295 XSyncIntToValue(&aa.delta, 1);
296 moveresize_alarm = XSyncCreateAlarm(obt_display,
297 XSyncCACounter |
298 XSyncCAValue |
299 XSyncCAValueType |
300 XSyncCATestType |
301 XSyncCADelta |
302 XSyncCAEvents,
303 &aa);
304
305 waiting_for_sync = 0;
306 }
307 #endif
308 }
309
310 void moveresize_end(gboolean cancel)
311 {
312 ungrab_keyboard();
313 ungrab_pointer();
314
315 popup_hide(popup);
316
317 if (!moving) {
318 #ifdef SYNC
319 /* turn off the alarm */
320 if (moveresize_alarm != None) {
321 XSyncDestroyAlarm(obt_display, moveresize_alarm);
322 moveresize_alarm = None;
323 }
324
325 if (sync_timer) g_source_remove(sync_timer);
326 sync_timer = 0;
327 #endif
328 }
329
330 /* don't use client_move() here, use the same width/height as
331 we've been using during the move, otherwise we get different results
332 when moving maximized windows between monitors of different sizes !
333 */
334 client_configure(moveresize_client,
335 (cancel ? start_cx : cur_x),
336 (cancel ? start_cy : cur_y),
337 (cancel ? start_cw : cur_w),
338 (cancel ? start_ch : cur_h),
339 TRUE, TRUE, FALSE);
340
341 /* restore the client's maximized state. do this after putting the window
342 back in its original spot to minimize visible flicker */
343 if (cancel && (was_max_horz || was_max_vert)) {
344 const gboolean h = moveresize_client->max_horz;
345 const gboolean v = moveresize_client->max_vert;
346
347 client_maximize(moveresize_client, TRUE,
348 was_max_horz && was_max_vert ? 0 :
349 (was_max_horz ? 1 : 2));
350
351 /* replace the premax values with the ones we had saved if
352 the client doesn't have any already set */
353 if (was_max_horz && !h) {
354 moveresize_client->pre_max_area.x = pre_max_area.x;
355 moveresize_client->pre_max_area.width = pre_max_area.width;
356 }
357 if (was_max_vert && !v) {
358 moveresize_client->pre_max_area.y = pre_max_area.y;
359 moveresize_client->pre_max_area.height = pre_max_area.height;
360 }
361 }
362
363 /* dont edge warp after its ended */
364 cancel_edge_warp();
365
366 moveresize_in_progress = FALSE;
367 moveresize_client = NULL;
368 }
369
370 static void do_move(gboolean keyboard, gint keydist)
371 {
372 gint resist;
373
374 if (keyboard) resist = keydist - 1; /* resist for one key press */
375 else resist = config_resist_win;
376 resist_move_windows(moveresize_client, resist, &cur_x, &cur_y);
377 if (!keyboard) resist = config_resist_edge;
378 resist_move_monitors(moveresize_client, resist, &cur_x, &cur_y);
379
380 client_configure(moveresize_client, cur_x, cur_y, cur_w, cur_h,
381 TRUE, FALSE, FALSE);
382 if (config_resize_popup_show == 2) /* == "Always" */
383 popup_coords(moveresize_client, "%d x %d",
384 moveresize_client->frame->area.x,
385 moveresize_client->frame->area.y);
386 }
387
388 static void do_resize(void)
389 {
390 gint x, y, w, h, lw, lh;
391
392 /* see if it is actually going to resize
393 USE cur_x AND cur_y HERE ! Otherwise the try_configure won't know
394 what struts to use !!
395 */
396 x = cur_x;
397 y = cur_y;
398 w = cur_w;
399 h = cur_h;
400 client_try_configure(moveresize_client, &x, &y, &w, &h,
401 &lw, &lh, TRUE);
402 if (!(w == moveresize_client->area.width &&
403 h == moveresize_client->area.height) &&
404 /* if waiting_for_sync == 0, then we aren't waiting.
405 if it is > SYNC_TIMEOUTS, then we have timed out
406 that many times already, so forget about waiting more */
407 (waiting_for_sync == 0 || waiting_for_sync > SYNC_TIMEOUTS))
408 {
409 #ifdef SYNC
410 if (config_resize_redraw && obt_display_extension_sync &&
411 /* don't send another sync when one is pending */
412 waiting_for_sync == 0 &&
413 moveresize_client->sync_request &&
414 moveresize_client->sync_counter &&
415 !moveresize_client->not_responding)
416 {
417 XEvent ce;
418 XSyncValue val;
419
420 /* increment the value we're waiting for */
421 ++moveresize_client->sync_counter_value;
422 XSyncIntToValue(&val, moveresize_client->sync_counter_value);
423
424 /* tell the client what we're waiting for */
425 ce.xclient.type = ClientMessage;
426 ce.xclient.message_type = OBT_PROP_ATOM(WM_PROTOCOLS);
427 ce.xclient.display = obt_display;
428 ce.xclient.window = moveresize_client->window;
429 ce.xclient.format = 32;
430 ce.xclient.data.l[0] = OBT_PROP_ATOM(NET_WM_SYNC_REQUEST);
431 ce.xclient.data.l[1] = event_time();
432 ce.xclient.data.l[2] = XSyncValueLow32(val);
433 ce.xclient.data.l[3] = XSyncValueHigh32(val);
434 ce.xclient.data.l[4] = 0l;
435 XSendEvent(obt_display, moveresize_client->window, FALSE,
436 NoEventMask, &ce);
437
438 waiting_for_sync = 1;
439
440 if (sync_timer) g_source_remove(sync_timer);
441 sync_timer = g_timeout_add(2000, sync_timeout_func, NULL);
442 }
443 #endif
444
445 /* force a ConfigureNotify, it is part of the spec for SYNC resizing
446 and MUST follow the sync counter notification */
447 client_configure(moveresize_client, cur_x, cur_y, cur_w, cur_h,
448 TRUE, FALSE, TRUE);
449 }
450
451 /* this would be better with a fixed width font ... XXX can do it better
452 if there are 2 text boxes */
453 if (config_resize_popup_show == 2 || /* == "Always" */
454 (config_resize_popup_show == 1 && /* == "Nonpixel" */
455 moveresize_client->size_inc.width > 1 &&
456 moveresize_client->size_inc.height > 1))
457 popup_coords(moveresize_client, "%d x %d", lw, lh);
458 }
459
460 #ifdef SYNC
461 static gboolean sync_timeout_func(gpointer data)
462 {
463 ++waiting_for_sync; /* we timed out waiting for our sync... */
464 do_resize(); /* ...so let any pending resizes through */
465
466 if (waiting_for_sync > SYNC_TIMEOUTS) {
467 sync_timer = 0;
468 return FALSE; /* don't repeat */
469 }
470 else
471 return TRUE; /* keep waiting */
472 }
473 #endif
474
475 static void calc_resize(gboolean keyboard, gint keydist, gint *dw, gint *dh,
476 ObDirection dir)
477 {
478 gint resist, x = 0, y = 0, lw, lh, ow, oh, nw, nh;
479 gint trydw, trydh;
480
481 ow = cur_w;
482 oh = cur_h;
483 nw = ow + *dw;
484 nh = oh + *dh;
485
486 if (!keyboard &&
487 (moveresize_client->max_ratio || moveresize_client->min_ratio))
488 {
489 switch (dir) {
490 case OB_DIRECTION_NORTH:
491 case OB_DIRECTION_SOUTH:
492 /* resize the width based on the height */
493 if (moveresize_client->min_ratio) {
494 if (nh * moveresize_client->min_ratio > nw)
495 nw = (gint)(nh * moveresize_client->min_ratio);
496 }
497 if (moveresize_client->max_ratio) {
498 if (nh * moveresize_client->max_ratio < nw)
499 nw = (gint)(nh * moveresize_client->max_ratio);
500 }
501 break;
502 default:
503 /* resize the height based on the width */
504 if (moveresize_client->min_ratio) {
505 if (nh * moveresize_client->min_ratio > nw)
506 nh = (gint)(nw / moveresize_client->min_ratio);
507 }
508 if (moveresize_client->max_ratio) {
509 if (nh * moveresize_client->max_ratio < nw)
510 nh = (gint)(nw / moveresize_client->max_ratio);
511 }
512 break;
513 }
514
515 /* see its actual size (apply aspect ratios) */
516 client_try_configure(moveresize_client, &x, &y, &nw, &nh, &lw, &lh,
517 TRUE);
518 trydw = nw - ow;
519 trydh = nh - oh;
520 }
521
522 /* resist_size_* needs the frame size */
523 nw += moveresize_client->frame->size.left +
524 moveresize_client->frame->size.right;
525 nh += moveresize_client->frame->size.top +
526 moveresize_client->frame->size.bottom;
527
528 if (keyboard) resist = keydist - 1; /* resist for one key press */
529 else resist = config_resist_win;
530 resist_size_windows(moveresize_client, resist, &nw, &nh, dir);
531 if (!keyboard) resist = config_resist_edge;
532 resist_size_monitors(moveresize_client, resist, &nw, &nh, dir);
533
534 nw -= moveresize_client->frame->size.left +
535 moveresize_client->frame->size.right;
536 nh -= moveresize_client->frame->size.top +
537 moveresize_client->frame->size.bottom;
538
539 *dw = nw - ow;
540 *dh = nh - oh;
541
542 /* take aspect ratios into account for resistance */
543 if (!keyboard &&
544 (moveresize_client->max_ratio || moveresize_client->min_ratio))
545 {
546 if (*dh != trydh) { /* got resisted */
547 /* resize the width based on the height */
548 if (moveresize_client->min_ratio) {
549 if (nh * moveresize_client->min_ratio > nw)
550 nw = (gint)(nh * moveresize_client->min_ratio);
551 }
552 if (moveresize_client->max_ratio) {
553 if (nh * moveresize_client->max_ratio < nw)
554 nw = (gint)(nh * moveresize_client->max_ratio);
555 }
556 }
557 if (*dw != trydw) { /* got resisted */
558 /* resize the height based on the width */
559 if (moveresize_client->min_ratio) {
560 if (nh * moveresize_client->min_ratio > nw)
561 nh = (gint)(nw / moveresize_client->min_ratio);
562 }
563 if (moveresize_client->max_ratio) {
564 if (nh * moveresize_client->max_ratio < nw)
565 nh = (gint)(nw / moveresize_client->max_ratio);
566 }
567 }
568 }
569
570 /* make sure it's all valid */
571 client_try_configure(moveresize_client, &x, &y, &nw, &nh, &lw, &lh, TRUE);
572
573 *dw = nw - ow;
574 *dh = nh - oh;
575 }
576
577 static void edge_warp_move_ptr(void)
578 {
579 gint x, y;
580 const Rect* a;
581
582 screen_pointer_pos(&x, &y);
583 a = screen_physical_area_all_monitors();
584
585 switch (edge_warp_dir) {
586 case OB_DIRECTION_NORTH:
587 y = a->height - 1;
588 break;
589 case OB_DIRECTION_EAST:
590 x = a->x;
591 break;
592 case OB_DIRECTION_SOUTH:
593 y = a->y;
594 break;
595 case OB_DIRECTION_WEST:
596 x = a->width - 1;
597 break;
598 default:
599 g_assert_not_reached();
600 }
601
602 XWarpPointer(obt_display, 0, obt_root(ob_screen), 0, 0, 0, 0, x, y);
603 }
604
605 static gboolean edge_warp_delay_func(gpointer data)
606 {
607 guint d;
608
609 /* only fire every second time. so it's fast the first time, but slower
610 after that */
611 if (edge_warp_odd) {
612 d = screen_find_desktop(screen_desktop, edge_warp_dir, TRUE, FALSE);
613 if (d != screen_desktop) {
614 if (config_mouse_screenedgewarp) edge_warp_move_ptr();
615 screen_set_desktop(d, TRUE);
616 }
617 }
618 edge_warp_odd = !edge_warp_odd;
619
620 return TRUE; /* do repeat ! */
621 }
622
623 static void do_edge_warp(gint x, gint y)
624 {
625 guint i;
626 ObDirection dir;
627
628 if (!config_mouse_screenedgetime) return;
629
630 dir = -1;
631
632 for (i = 0; i < screen_num_monitors; ++i) {
633 const Rect *a = screen_physical_area_monitor(i);
634 if (x == RECT_LEFT(*a)) dir = OB_DIRECTION_WEST;
635 if (x == RECT_RIGHT(*a)) dir = OB_DIRECTION_EAST;
636 if (y == RECT_TOP(*a)) dir = OB_DIRECTION_NORTH;
637 if (y == RECT_BOTTOM(*a)) dir = OB_DIRECTION_SOUTH;
638
639 /* try check for xinerama boundaries */
640 if ((x + 1 == RECT_LEFT(*a) || x - 1 == RECT_RIGHT(*a)) &&
641 (dir == OB_DIRECTION_WEST || dir == OB_DIRECTION_EAST))
642 {
643 dir = -1;
644 }
645 if ((y + 1 == RECT_TOP(*a) || y - 1 == RECT_BOTTOM(*a)) &&
646 (dir == OB_DIRECTION_NORTH || dir == OB_DIRECTION_SOUTH))
647 {
648 dir = -1;
649 }
650 }
651
652 if (dir != edge_warp_dir) {
653 cancel_edge_warp();
654 if (dir != (ObDirection)-1) {
655 edge_warp_odd = TRUE; /* switch on the first timeout */
656 edge_warp_timer = g_timeout_add(config_mouse_screenedgetime,
657 edge_warp_delay_func, NULL);
658 }
659 edge_warp_dir = dir;
660 }
661 }
662
663 static void cancel_edge_warp(void)
664 {
665 if (edge_warp_timer) g_source_remove(edge_warp_timer);
666 edge_warp_timer = 0;
667 }
668
669 static void move_with_keys(KeySym sym, guint state)
670 {
671 gint dx = 0, dy = 0, ox = cur_x, oy = cur_y;
672 gint opx, px, opy, py;
673 gint dist = 0;
674
675 /* shift means jump to edge */
676 if (state & obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_SHIFT))
677 {
678 gint x, y;
679 ObDirection dir;
680
681 if (sym == XK_Right)
682 dir = OB_DIRECTION_EAST;
683 else if (sym == XK_Left)
684 dir = OB_DIRECTION_WEST;
685 else if (sym == XK_Down)
686 dir = OB_DIRECTION_SOUTH;
687 else /* sym == XK_Up */
688 dir = OB_DIRECTION_NORTH;
689
690 client_find_move_directional(moveresize_client, dir, &x, &y);
691 dx = x - moveresize_client->area.x;
692 dy = y - moveresize_client->area.y;
693 } else {
694 /* control means fine grained */
695 if (state &
696 obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CONTROL))
697 {
698 dist = 1;
699 }
700 else
701 dist = KEY_DIST;
702
703 if (sym == XK_Right)
704 dx = dist;
705 else if (sym == XK_Left)
706 dx = -dist;
707 else if (sym == XK_Down)
708 dy = dist;
709 else /* if (sym == XK_Up) */
710 dy = -dist;
711 }
712
713 screen_pointer_pos(&opx, &opy);
714 XWarpPointer(obt_display, None, None, 0, 0, 0, 0, dx, dy);
715 /* steal the motion events this causes */
716 XSync(obt_display, FALSE);
717 {
718 XEvent ce;
719 while (xqueue_remove_local(&ce, xqueue_match_type,
720 GINT_TO_POINTER(MotionNotify)));
721 }
722 screen_pointer_pos(&px, &py);
723
724 cur_x += dx;
725 cur_y += dy;
726 do_move(TRUE, dist);
727
728 /* because the cursor moves even though the window does
729 not nessesarily (resistance), this adjusts where the curor
730 thinks it started so that it keeps up with where the window
731 actually is */
732 start_x += (px - opx) - (cur_x - ox);
733 start_y += (py - opy) - (cur_y - oy);
734 }
735
736 static void resize_with_keys(KeySym sym, guint state)
737 {
738 gint dw = 0, dh = 0, pdx = 0, pdy = 0, opx, opy, px, py;
739 gint resist = 0;
740 ObDirection dir;
741
742 /* pick the edge if it needs to move */
743 if (sym == XK_Right) {
744 dir = OB_DIRECTION_EAST;
745 if (key_resize_edge != OB_DIRECTION_WEST &&
746 key_resize_edge != OB_DIRECTION_EAST)
747 {
748 key_resize_edge = OB_DIRECTION_EAST;
749 return;
750 }
751 } else if (sym == XK_Left) {
752 dir = OB_DIRECTION_WEST;
753 if (key_resize_edge != OB_DIRECTION_WEST &&
754 key_resize_edge != OB_DIRECTION_EAST)
755 {
756 key_resize_edge = OB_DIRECTION_WEST;
757 return;
758 }
759 } else if (sym == XK_Up) {
760 dir = OB_DIRECTION_NORTH;
761 if (key_resize_edge != OB_DIRECTION_NORTH &&
762 key_resize_edge != OB_DIRECTION_SOUTH)
763 {
764 key_resize_edge = OB_DIRECTION_NORTH;
765 return;
766 }
767 } else /* if (sym == XK_Down) */ {
768 dir = OB_DIRECTION_SOUTH;
769 if (key_resize_edge != OB_DIRECTION_NORTH &&
770 key_resize_edge != OB_DIRECTION_SOUTH)
771 {
772 key_resize_edge = OB_DIRECTION_SOUTH;
773 return;
774 }
775 }
776
777 /* shift means jump to edge */
778 if (state & obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_SHIFT))
779 {
780 gint x, y, w, h;
781
782 if (sym == XK_Right)
783 dir = OB_DIRECTION_EAST;
784 else if (sym == XK_Left)
785 dir = OB_DIRECTION_WEST;
786 else if (sym == XK_Down)
787 dir = OB_DIRECTION_SOUTH;
788 else /* if (sym == XK_Up)) */
789 dir = OB_DIRECTION_NORTH;
790
791 client_find_resize_directional(moveresize_client, key_resize_edge,
792 key_resize_edge == dir,
793 &x, &y, &w, &h);
794 dw = w - moveresize_client->area.width;
795 dh = h - moveresize_client->area.height;
796 } else {
797 gint distw, disth;
798
799 /* control means fine grained */
800 if (moveresize_client->size_inc.width > 1) {
801 distw = moveresize_client->size_inc.width;
802 resist = 1;
803 }
804 else if (state &
805 obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CONTROL))
806 {
807 distw = 1;
808 resist = 1;
809 }
810 else {
811 distw = KEY_DIST;
812 resist = KEY_DIST;
813 }
814 if (moveresize_client->size_inc.height > 1) {
815 disth = moveresize_client->size_inc.height;
816 resist = 1;
817 }
818 else if (state &
819 obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CONTROL))
820 {
821 disth = 1;
822 resist = 1;
823 }
824 else {
825 disth = KEY_DIST;
826 resist = KEY_DIST;
827 }
828
829 if (key_resize_edge == OB_DIRECTION_WEST) {
830 if (dir == OB_DIRECTION_WEST)
831 dw = distw;
832 else
833 dw = -distw;
834 }
835 else if (key_resize_edge == OB_DIRECTION_EAST) {
836 if (dir == OB_DIRECTION_EAST)
837 dw = distw;
838 else
839 dw = -distw;
840 }
841 else if (key_resize_edge == OB_DIRECTION_NORTH) {
842 if (dir == OB_DIRECTION_NORTH)
843 dh = disth;
844 else
845 dh = -disth;
846 }
847 else /*if (key_resize_edge == OB_DIRECTION_SOUTH)*/ {
848 if (dir == OB_DIRECTION_SOUTH)
849 dh = disth;
850 else
851 dh = -disth;
852 }
853 }
854
855 if (moveresize_client->max_horz &&
856 (key_resize_edge == OB_DIRECTION_WEST ||
857 key_resize_edge == OB_DIRECTION_EAST))
858 {
859 /* unmax horz */
860 was_max_horz = TRUE;
861 pre_max_area.x = moveresize_client->pre_max_area.x;
862 pre_max_area.width = moveresize_client->pre_max_area.width;
863
864 moveresize_client->pre_max_area.x = cur_x;
865 moveresize_client->pre_max_area.width = cur_w;
866 client_maximize(moveresize_client, FALSE, 1);
867 }
868 else if (moveresize_client->max_vert &&
869 (key_resize_edge == OB_DIRECTION_NORTH ||
870 key_resize_edge == OB_DIRECTION_SOUTH))
871 {
872 /* unmax vert */
873 was_max_vert = TRUE;
874 pre_max_area.y = moveresize_client->pre_max_area.y;
875 pre_max_area.height = moveresize_client->pre_max_area.height;
876
877 moveresize_client->pre_max_area.y = cur_y;
878 moveresize_client->pre_max_area.height = cur_h;
879 client_maximize(moveresize_client, FALSE, 2);
880 }
881
882 calc_resize(TRUE, resist, &dw, &dh, dir);
883 if (key_resize_edge == OB_DIRECTION_WEST)
884 cur_x -= dw;
885 else if (key_resize_edge == OB_DIRECTION_NORTH)
886 cur_y -= dh;
887 cur_w += dw;
888 cur_h += dh;
889
890 /* how to move the pointer to keep up with the change */
891 if (key_resize_edge == OB_DIRECTION_WEST)
892 pdx = -dw;
893 else if (key_resize_edge == OB_DIRECTION_EAST)
894 pdx = dw;
895 else if (key_resize_edge == OB_DIRECTION_NORTH)
896 pdy = -dh;
897 else if (key_resize_edge == OB_DIRECTION_SOUTH)
898 pdy = dh;
899
900 screen_pointer_pos(&opx, &opy);
901 XWarpPointer(obt_display, None, None, 0, 0, 0, 0, pdx, pdy);
902 /* steal the motion events this causes */
903 XSync(obt_display, FALSE);
904 {
905 XEvent ce;
906 while (xqueue_remove_local(&ce, xqueue_match_type,
907 GINT_TO_POINTER(MotionNotify)));
908 }
909 screen_pointer_pos(&px, &py);
910
911 do_resize();
912
913 /* because the cursor moves even though the window does
914 not nessesarily (resistance), this adjusts where the cursor
915 thinks it started so that it keeps up with where the window
916 actually is */
917 start_x += (px - opx) - dw;
918 start_y += (py - opy) - dh;
919
920 }
921
922 gboolean moveresize_event(XEvent *e)
923 {
924 gboolean used = FALSE;
925
926 if (!moveresize_in_progress) return FALSE;
927
928 if (e->type == ButtonPress) {
929 if (!button) {
930 start_x = e->xbutton.x_root;
931 start_y = e->xbutton.y_root;
932 button = e->xbutton.button; /* this will end it now */
933 }
934 used = e->xbutton.button == button;
935 } else if (e->type == ButtonRelease) {
936 if (!button || e->xbutton.button == button) {
937 moveresize_end(FALSE);
938 used = TRUE;
939 }
940 } else if (e->type == MotionNotify) {
941 if (moving) {
942 cur_x = start_cx + e->xmotion.x_root - start_x;
943 cur_y = start_cy + e->xmotion.y_root - start_y;
944 do_move(FALSE, 0);
945 do_edge_warp(e->xmotion.x_root, e->xmotion.y_root);
946 } else {
947 gint dw, dh;
948 ObDirection dir;
949
950 if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT)) {
951 dw = -(e->xmotion.x_root - start_x);
952 dh = -(e->xmotion.y_root - start_y);
953 dir = OB_DIRECTION_NORTHWEST;
954 } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP)) {
955 dw = 0;
956 dh = -(e->xmotion.y_root - start_y);
957 dir = OB_DIRECTION_NORTH;
958 } else if (corner ==
959 OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT)) {
960 dw = (e->xmotion.x_root - start_x);
961 dh = -(e->xmotion.y_root - start_y);
962 dir = OB_DIRECTION_NORTHEAST;
963 } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT)) {
964 dw = (e->xmotion.x_root - start_x);
965 dh = 0;
966 dir = OB_DIRECTION_EAST;
967 } else if (corner ==
968 OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT)) {
969 dw = (e->xmotion.x_root - start_x);
970 dh = (e->xmotion.y_root - start_y);
971 dir = OB_DIRECTION_SOUTHEAST;
972 } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOM))
973 {
974 dw = 0;
975 dh = (e->xmotion.y_root - start_y);
976 dir = OB_DIRECTION_SOUTH;
977 } else if (corner ==
978 OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT)) {
979 dw = -(e->xmotion.x_root - start_x);
980 dh = (e->xmotion.y_root - start_y);
981 dir = OB_DIRECTION_SOUTHWEST;
982 } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT)) {
983 dw = -(e->xmotion.x_root - start_x);
984 dh = 0;
985 dir = OB_DIRECTION_WEST;
986 } else if (corner ==
987 OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD)) {
988 dw = (e->xmotion.x_root - start_x);
989 dh = (e->xmotion.y_root - start_y);
990 dir = OB_DIRECTION_SOUTHEAST;
991 } else
992 g_assert_not_reached();
993
994 /* override the client's max state if desired */
995 if (ABS(dw) >= config_resist_edge) {
996 if (moveresize_client->max_horz) {
997 /* unmax horz */
998 was_max_horz = TRUE;
999 pre_max_area.x = moveresize_client->pre_max_area.x;
1000 pre_max_area.width = moveresize_client->pre_max_area.width;
1001
1002 moveresize_client->pre_max_area.x = cur_x;
1003 moveresize_client->pre_max_area.width = cur_w;
1004 client_maximize(moveresize_client, FALSE, 1);
1005 }
1006 }
1007 else if (was_max_horz && !moveresize_client->max_horz) {
1008 /* remax horz and put the premax back */
1009 client_maximize(moveresize_client, TRUE, 1);
1010 moveresize_client->pre_max_area.x = pre_max_area.x;
1011 moveresize_client->pre_max_area.width = pre_max_area.width;
1012 }
1013
1014 if (ABS(dh) >= config_resist_edge) {
1015 if (moveresize_client->max_vert) {
1016 /* unmax vert */
1017 was_max_vert = TRUE;
1018 pre_max_area.y = moveresize_client->pre_max_area.y;
1019 pre_max_area.height =
1020 moveresize_client->pre_max_area.height;
1021
1022 moveresize_client->pre_max_area.y = cur_y;
1023 moveresize_client->pre_max_area.height = cur_h;
1024 client_maximize(moveresize_client, FALSE, 2);
1025 }
1026 }
1027 else if (was_max_vert && !moveresize_client->max_vert) {
1028 /* remax vert and put the premax back */
1029 client_maximize(moveresize_client, TRUE, 2);
1030 moveresize_client->pre_max_area.y = pre_max_area.y;
1031 moveresize_client->pre_max_area.height = pre_max_area.height;
1032 }
1033
1034 dw -= cur_w - start_cw;
1035 dh -= cur_h - start_ch;
1036
1037 calc_resize(FALSE, 0, &dw, &dh, dir);
1038 cur_w += dw;
1039 cur_h += dh;
1040
1041 if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT) ||
1042 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT) ||
1043 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT))
1044 {
1045 cur_x -= dw;
1046 }
1047 if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT) ||
1048 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP) ||
1049 corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT))
1050 {
1051 cur_y -= dh;
1052 }
1053
1054 do_resize();
1055 }
1056 used = TRUE;
1057 } else if (e->type == KeyPress) {
1058 KeySym sym = obt_keyboard_keypress_to_keysym(e);
1059
1060 if (sym == XK_Escape) {
1061 moveresize_end(TRUE);
1062 used = TRUE;
1063 } else if (sym == XK_Return || sym == XK_KP_Enter) {
1064 moveresize_end(FALSE);
1065 used = TRUE;
1066 } else if (sym == XK_Right || sym == XK_Left ||
1067 sym == XK_Up || sym == XK_Down)
1068 {
1069 if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD)) {
1070 resize_with_keys(sym, e->xkey.state);
1071 used = TRUE;
1072 } else if (corner ==
1073 OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD))
1074 {
1075 move_with_keys(sym, e->xkey.state);
1076 used = TRUE;
1077 }
1078 }
1079 }
1080 #ifdef SYNC
1081 else if (e->type == obt_display_extension_sync_basep + XSyncAlarmNotify)
1082 {
1083 waiting_for_sync = 0; /* we got our sync... */
1084 do_resize(); /* ...so try resize if there is more change pending */
1085 used = TRUE;
1086 }
1087 #endif
1088
1089 if (used && moveresize_client == focus_client)
1090 event_update_user_time();
1091
1092 return used;
1093 }
This page took 0.084681 seconds and 5 git commands to generate.