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