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