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