1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 moveresize.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
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.
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.
17 See the COPYING file for a copy of the GNU General Public License.
21 #include "framerender.h"
31 #include "moveresize.h"
35 #include "extensions.h"
36 #include "render/render.h"
37 #include "render/theme.h"
42 /* how far windows move and resize with the keyboard arrows */
45 gboolean moveresize_in_progress
= FALSE
;
46 ObClient
*moveresize_client
= NULL
;
48 XSyncAlarm moveresize_alarm
= None
;
51 static gboolean moving
= FALSE
; /* TRUE - moving, FALSE - resizing */
53 static gint start_x
, start_y
, start_cx
, start_cy
, start_cw
, start_ch
;
54 static gint cur_x
, cur_y
;
56 static guint32 corner
;
57 static ObCorner lockcorner
;
58 static ObDirection edge_warp_dir
= -1;
60 static gboolean waiting_for_sync
;
63 static ObPopup
*popup
= NULL
;
65 static void do_edge_warp(gint x
, gint y
);
66 static void cancel_edge_warp();
68 static void client_dest(ObClient
*client
, gpointer data
)
70 if (moveresize_client
== client
)
74 void moveresize_startup(gboolean reconfig
)
76 popup
= popup_new(FALSE
);
77 popup_set_text_align(popup
, RR_JUSTIFY_CENTER
);
80 client_add_destroy_notify(client_dest
, NULL
);
83 void moveresize_shutdown(gboolean reconfig
)
86 if (moveresize_in_progress
)
87 moveresize_end(FALSE
);
88 client_remove_destroy_notify(client_dest
);
95 static void get_resize_position(gint
*x
, gint
*y
, gboolean cancel
)
100 *x
= moveresize_client
->frame
->area
.x
;
101 *y
= moveresize_client
->frame
->area
.y
;
111 /* see how much it is actually going to resize */
113 gint cx
= *x
, cy
= *y
;
114 frame_frame_gravity(moveresize_client
->frame
, &cx
, &cy
);
115 client_try_configure(moveresize_client
, &cx
, &cy
, &w
, &h
,
118 dw
= w
- moveresize_client
->area
.width
;
119 dh
= h
- moveresize_client
->area
.height
;
121 switch (lockcorner
) {
122 case OB_CORNER_TOPLEFT
:
124 case OB_CORNER_TOPRIGHT
:
127 case OB_CORNER_BOTTOMLEFT
:
130 case OB_CORNER_BOTTOMRIGHT
:
136 frame_frame_gravity(moveresize_client
->frame
, x
, y
);
139 static void popup_coords(ObClient
*c
, const gchar
*format
, gint a
, gint b
)
143 text
= g_strdup_printf(format
, a
, b
);
144 if (config_resize_popup_pos
== 1) /* == "Top" */
145 popup_position(popup
, SouthGravity
,
147 + c
->frame
->area
.width
/2,
148 c
->frame
->area
.y
- ob_rr_theme
->fbwidth
);
149 else /* == "Center" */
150 popup_position(popup
, CenterGravity
,
151 c
->frame
->area
.x
+ c
->frame
->size
.left
+
153 c
->frame
->area
.y
+ c
->frame
->size
.top
+
155 popup_show(popup
, text
);
159 void moveresize_start(ObClient
*c
, gint x
, gint y
, guint b
, guint32 cnr
)
162 gboolean mv
= (cnr
== prop_atoms
.net_wm_moveresize_move
||
163 cnr
== prop_atoms
.net_wm_moveresize_move_keyboard
);
165 if (moveresize_in_progress
|| !c
->frame
->visible
||
167 (c
->functions
& OB_CLIENT_FUNC_MOVE
) :
168 (c
->functions
& OB_CLIENT_FUNC_RESIZE
)))
171 if (cnr
== prop_atoms
.net_wm_moveresize_size_topleft
)
172 cur
= OB_CURSOR_NORTHWEST
;
173 else if (cnr
== prop_atoms
.net_wm_moveresize_size_top
)
174 cur
= OB_CURSOR_NORTH
;
175 else if (cnr
== prop_atoms
.net_wm_moveresize_size_topright
)
176 cur
= OB_CURSOR_NORTHEAST
;
177 else if (cnr
== prop_atoms
.net_wm_moveresize_size_right
)
178 cur
= OB_CURSOR_EAST
;
179 else if (cnr
== prop_atoms
.net_wm_moveresize_size_bottomright
)
180 cur
= OB_CURSOR_SOUTHEAST
;
181 else if (cnr
== prop_atoms
.net_wm_moveresize_size_bottom
)
182 cur
= OB_CURSOR_SOUTH
;
183 else if (cnr
== prop_atoms
.net_wm_moveresize_size_bottomleft
)
184 cur
= OB_CURSOR_SOUTHWEST
;
185 else if (cnr
== prop_atoms
.net_wm_moveresize_size_left
)
186 cur
= OB_CURSOR_WEST
;
187 else if (cnr
== prop_atoms
.net_wm_moveresize_size_keyboard
)
188 cur
= OB_CURSOR_SOUTHEAST
;
189 else if (cnr
== prop_atoms
.net_wm_moveresize_move
)
190 cur
= OB_CURSOR_MOVE
;
191 else if (cnr
== prop_atoms
.net_wm_moveresize_move_keyboard
)
192 cur
= OB_CURSOR_MOVE
;
194 g_assert_not_reached();
196 /* keep the pointer bounded to the screen for move/resize */
197 if (!grab_pointer(FALSE
, TRUE
, cur
))
199 if (!grab_keyboard()) {
204 frame_end_iconify_animation(c
->frame
);
207 moveresize_client
= c
;
208 start_cx
= c
->area
.x
;
209 start_cy
= c
->area
.y
;
210 /* these adjustments for the size_inc make resizing a terminal more
211 friendly. you essentially start the resize in the middle of the
212 increment instead of at 0, so you have to move half an increment
213 either way instead of a full increment one and 1 px the other. and this
214 is one large mother fucking comment. */
215 start_cw
= c
->area
.width
+ c
->size_inc
.width
/ 2;
216 start_ch
= c
->area
.height
+ c
->size_inc
.height
/ 2;
223 have to change start_cx and start_cy if going to do this..
224 if (corner == prop_atoms.net_wm_moveresize_move_keyboard ||
225 corner == prop_atoms.net_wm_moveresize_size_keyboard)
226 XWarpPointer(ob_display, None, c->window, 0, 0, 0, 0,
227 c->area.width / 2, c->area.height / 2);
238 moveresize_in_progress
= TRUE
;
241 if (config_resize_redraw
&& !moving
&& extensions_shape
&&
242 moveresize_client
->sync_request
&& moveresize_client
->sync_counter
)
244 /* Initialize values for the resize syncing, and create an alarm for
245 the client's xsync counter */
248 XSyncAlarmAttributes aa
;
250 /* set the counter to an initial value */
251 XSyncIntToValue(&val
, 0);
252 XSyncSetCounter(ob_display
, moveresize_client
->sync_counter
, val
);
254 /* this will be incremented when we tell the client what we're
256 moveresize_client
->sync_counter_value
= 0;
258 /* the next sequence we're waiting for with the alarm */
259 XSyncIntToValue(&val
, 1);
261 /* set an alarm on the counter */
262 aa
.trigger
.counter
= moveresize_client
->sync_counter
;
263 aa
.trigger
.wait_value
= val
;
264 aa
.trigger
.value_type
= XSyncAbsolute
;
265 aa
.trigger
.test_type
= XSyncPositiveTransition
;
267 XSyncIntToValue(&aa
.delta
, 1);
268 moveresize_alarm
= XSyncCreateAlarm(ob_display
,
277 waiting_for_sync
= FALSE
;
282 void moveresize_end(gboolean cancel
)
292 client_move(moveresize_client
,
293 (cancel
? start_cx
: cur_x
),
294 (cancel
? start_cy
: cur_y
));
297 /* turn off the alarm */
298 if (moveresize_alarm
!= None
) {
299 XSyncDestroyAlarm(ob_display
, moveresize_alarm
);
300 moveresize_alarm
= None
;
304 get_resize_position(&x
, &y
, cancel
);
305 client_configure(moveresize_client
, x
, y
,
306 (cancel
? start_cw
: cur_x
),
307 (cancel
? start_ch
: cur_y
),
311 /* dont edge warp after its ended */
314 moveresize_in_progress
= FALSE
;
315 moveresize_client
= NULL
;
318 static void do_move(gboolean keyboard
, gint keydist
)
322 if (keyboard
) resist
= keydist
- 1; /* resist for one key press */
323 else resist
= config_resist_win
;
324 resist_move_windows(moveresize_client
, resist
, &cur_x
, &cur_y
);
325 if (!keyboard
) resist
= config_resist_edge
;
326 resist_move_monitors(moveresize_client
, resist
, &cur_x
, &cur_y
);
328 client_configure(moveresize_client
, cur_x
, cur_y
,
329 moveresize_client
->area
.width
,
330 moveresize_client
->area
.height
,
332 if (config_resize_popup_show
== 2) /* == "Always" */
333 popup_coords(moveresize_client
, "%d x %d",
334 moveresize_client
->frame
->area
.x
,
335 moveresize_client
->frame
->area
.y
);
338 static void do_resize()
340 gint x
, y
, w
, h
, lw
, lh
;
342 /* see if it is actually going to resize */
347 client_try_configure(moveresize_client
, &x
, &y
, &w
, &h
,
349 if (w
== moveresize_client
->area
.width
&&
350 h
== moveresize_client
->area
.height
)
356 if (config_resize_redraw
&& extensions_sync
&&
357 moveresize_client
->sync_request
&& moveresize_client
->sync_counter
)
362 /* are we already waiting for the sync counter to catch up? */
363 if (waiting_for_sync
)
366 /* increment the value we're waiting for */
367 ++moveresize_client
->sync_counter_value
;
368 XSyncIntToValue(&val
, moveresize_client
->sync_counter_value
);
370 /* tell the client what we're waiting for */
371 ce
.xclient
.type
= ClientMessage
;
372 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
373 ce
.xclient
.display
= ob_display
;
374 ce
.xclient
.window
= moveresize_client
->window
;
375 ce
.xclient
.format
= 32;
376 ce
.xclient
.data
.l
[0] = prop_atoms
.net_wm_sync_request
;
377 ce
.xclient
.data
.l
[1] = event_curtime
;
378 ce
.xclient
.data
.l
[2] = XSyncValueLow32(val
);
379 ce
.xclient
.data
.l
[3] = XSyncValueHigh32(val
);
380 ce
.xclient
.data
.l
[4] = 0l;
381 XSendEvent(ob_display
, moveresize_client
->window
, FALSE
,
384 waiting_for_sync
= TRUE
;
388 get_resize_position(&x
, &y
, FALSE
);
389 client_configure(moveresize_client
, x
, y
, cur_x
, cur_y
, TRUE
, FALSE
, FALSE
);
391 /* this would be better with a fixed width font ... XXX can do it better
392 if there are 2 text boxes */
393 if (config_resize_popup_show
== 2 || /* == "Always" */
394 (config_resize_popup_show
== 1 && /* == "Nonpixel" */
395 moveresize_client
->size_inc
.width
> 1 &&
396 moveresize_client
->size_inc
.height
> 1))
397 popup_coords(moveresize_client
, "%d x %d",
398 moveresize_client
->logical_size
.width
,
399 moveresize_client
->logical_size
.height
);
402 static void calc_resize(gboolean keyboard
)
406 /* resist_size_* needs the frame size */
407 cur_x
+= moveresize_client
->frame
->size
.left
+
408 moveresize_client
->frame
->size
.right
;
409 cur_y
+= moveresize_client
->frame
->size
.top
+
410 moveresize_client
->frame
->size
.bottom
;
412 if (keyboard
) resist
= KEY_DIST
- 1; /* resist for one key press */
413 else resist
= config_resist_win
;
414 resist_size_windows(moveresize_client
, resist
, &cur_x
, &cur_y
, lockcorner
);
415 if (!keyboard
) resist
= config_resist_edge
;
416 resist_size_monitors(moveresize_client
, resist
, &cur_x
, &cur_y
,lockcorner
);
418 cur_x
-= moveresize_client
->frame
->size
.left
+
419 moveresize_client
->frame
->size
.right
;
420 cur_y
-= moveresize_client
->frame
->size
.top
+
421 moveresize_client
->frame
->size
.bottom
;
424 static gboolean
edge_warp_delay_func(gpointer data
)
428 d
= screen_find_desktop(screen_desktop
, edge_warp_dir
, TRUE
, FALSE
);
429 if (d
!= screen_desktop
) screen_set_desktop(d
, TRUE
);
433 return FALSE
; /* don't repeat */
436 static void do_edge_warp(gint x
, gint y
)
441 if (!config_mouse_screenedgetime
) return;
445 for (i
= 0; i
< screen_num_monitors
; ++i
) {
446 Rect
*a
= screen_physical_area_monitor(i
);
447 if (x
== RECT_LEFT(*a
)) dir
= OB_DIRECTION_WEST
;
448 if (x
== RECT_RIGHT(*a
)) dir
= OB_DIRECTION_EAST
;
449 if (y
== RECT_TOP(*a
)) dir
= OB_DIRECTION_NORTH
;
450 if (y
== RECT_BOTTOM(*a
)) dir
= OB_DIRECTION_SOUTH
;
452 /* try check for xinerama boundaries */
453 if ((x
+ 1 == RECT_LEFT(*a
) || x
- 1 == RECT_RIGHT(*a
)) &&
454 (dir
== OB_DIRECTION_WEST
|| dir
== OB_DIRECTION_EAST
))
458 if ((y
+ 1 == RECT_TOP(*a
) || y
- 1 == RECT_BOTTOM(*a
)) &&
459 (dir
== OB_DIRECTION_NORTH
|| dir
== OB_DIRECTION_SOUTH
))
466 if (dir
!= edge_warp_dir
) {
467 if (dir
== (ObDirection
)-1)
470 ob_main_loop_timeout_add(ob_main_loop
,
471 config_mouse_screenedgetime
* 1000,
472 edge_warp_delay_func
,
478 static void cancel_edge_warp()
480 ob_main_loop_timeout_remove(ob_main_loop
, edge_warp_delay_func
);
483 static void move_with_keys(gint keycode
, gint state
)
485 gint dx
= 0, dy
= 0, ox
= cur_x
, oy
= cur_y
;
486 gint opx
, px
, opy
, py
;
489 /* shift means jump to edge */
490 if (state
& modkeys_key_to_mask(OB_MODKEY_KEY_SHIFT
)) {
494 if (keycode
== ob_keycode(OB_KEY_RIGHT
))
495 dir
= OB_DIRECTION_EAST
;
496 else if (keycode
== ob_keycode(OB_KEY_LEFT
))
497 dir
= OB_DIRECTION_WEST
;
498 else if (keycode
== ob_keycode(OB_KEY_DOWN
))
499 dir
= OB_DIRECTION_SOUTH
;
500 else /* if (keycode == ob_keycode(OB_KEY_UP)) */
501 dir
= OB_DIRECTION_NORTH
;
503 client_find_move_directional(moveresize_client
, dir
, &x
, &y
);
504 dx
= x
- moveresize_client
->area
.x
;
505 dy
= y
- moveresize_client
->area
.y
;
507 /* control means fine grained */
508 if (state
& modkeys_key_to_mask(OB_MODKEY_KEY_CONTROL
))
513 if (keycode
== ob_keycode(OB_KEY_RIGHT
))
515 else if (keycode
== ob_keycode(OB_KEY_LEFT
))
517 else if (keycode
== ob_keycode(OB_KEY_DOWN
))
519 else /* if (keycode == ob_keycode(OB_KEY_UP)) */
523 screen_pointer_pos(&opx
, &opy
);
524 XWarpPointer(ob_display
, None
, None
, 0, 0, 0, 0, dx
, dy
);
525 /* steal the motion events this causes */
526 XSync(ob_display
, FALSE
);
529 while (XCheckTypedEvent(ob_display
, MotionNotify
, &ce
));
531 screen_pointer_pos(&px
, &py
);
537 /* because the cursor moves even though the window does
538 not nessesarily (resistance), this adjusts where the curor
539 thinks it started so that it keeps up with where the window
541 start_x
+= (px
- opx
) - (cur_x
- ox
);
542 start_y
+= (py
- opy
) - (cur_y
- oy
);
545 gboolean
moveresize_event(XEvent
*e
)
547 gboolean used
= FALSE
;
549 if (!moveresize_in_progress
) return FALSE
;
551 if (e
->type
== ButtonPress
) {
553 start_x
= e
->xbutton
.x_root
;
554 start_y
= e
->xbutton
.y_root
;
555 button
= e
->xbutton
.button
; /* this will end it now */
557 used
= e
->xbutton
.button
== button
;
558 } else if (e
->type
== ButtonRelease
) {
559 if (!button
|| e
->xbutton
.button
== button
) {
560 moveresize_end(FALSE
);
563 } else if (e
->type
== MotionNotify
) {
565 cur_x
= start_cx
+ e
->xmotion
.x_root
- start_x
;
566 cur_y
= start_cy
+ e
->xmotion
.y_root
- start_y
;
568 do_edge_warp(e
->xmotion
.x_root
, e
->xmotion
.y_root
);
570 if (corner
== prop_atoms
.net_wm_moveresize_size_topleft
) {
571 cur_x
= start_cw
- (e
->xmotion
.x_root
- start_x
);
572 cur_y
= start_ch
- (e
->xmotion
.y_root
- start_y
);
573 lockcorner
= OB_CORNER_BOTTOMRIGHT
;
574 } else if (corner
== prop_atoms
.net_wm_moveresize_size_top
) {
576 cur_y
= start_ch
- (e
->xmotion
.y_root
- start_y
);
577 lockcorner
= OB_CORNER_BOTTOMRIGHT
;
578 } else if (corner
== prop_atoms
.net_wm_moveresize_size_topright
) {
579 cur_x
= start_cw
+ (e
->xmotion
.x_root
- start_x
);
580 cur_y
= start_ch
- (e
->xmotion
.y_root
- start_y
);
581 lockcorner
= OB_CORNER_BOTTOMLEFT
;
582 } else if (corner
== prop_atoms
.net_wm_moveresize_size_right
) {
583 cur_x
= start_cw
+ (e
->xmotion
.x_root
- start_x
);
585 lockcorner
= OB_CORNER_BOTTOMLEFT
;
587 prop_atoms
.net_wm_moveresize_size_bottomright
) {
588 cur_x
= start_cw
+ (e
->xmotion
.x_root
- start_x
);
589 cur_y
= start_ch
+ (e
->xmotion
.y_root
- start_y
);
590 lockcorner
= OB_CORNER_TOPLEFT
;
591 } else if (corner
== prop_atoms
.net_wm_moveresize_size_bottom
) {
593 cur_y
= start_ch
+ (e
->xmotion
.y_root
- start_y
);
594 lockcorner
= OB_CORNER_TOPLEFT
;
596 prop_atoms
.net_wm_moveresize_size_bottomleft
) {
597 cur_x
= start_cw
- (e
->xmotion
.x_root
- start_x
);
598 cur_y
= start_ch
+ (e
->xmotion
.y_root
- start_y
);
599 lockcorner
= OB_CORNER_TOPRIGHT
;
600 } else if (corner
== prop_atoms
.net_wm_moveresize_size_left
) {
601 cur_x
= start_cw
- (e
->xmotion
.x_root
- start_x
);
603 lockcorner
= OB_CORNER_TOPRIGHT
;
604 } else if (corner
== prop_atoms
.net_wm_moveresize_size_keyboard
) {
605 cur_x
= start_cw
+ (e
->xmotion
.x_root
- start_x
);
606 cur_y
= start_ch
+ (e
->xmotion
.y_root
- start_y
);
607 lockcorner
= OB_CORNER_TOPLEFT
;
609 g_assert_not_reached();
615 } else if (e
->type
== KeyPress
) {
616 if (e
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
)) {
617 moveresize_end(TRUE
);
619 } else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
620 moveresize_end(FALSE
);
622 } else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
) ||
623 e
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
) ||
624 e
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
) ||
625 e
->xkey
.keycode
== ob_keycode(OB_KEY_UP
))
627 if (corner
== prop_atoms
.net_wm_moveresize_size_keyboard
) {
628 gint dx
= 0, dy
= 0, ox
= cur_x
, oy
= cur_y
;
630 if (e
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
))
631 dx
= MAX(KEY_DIST
, moveresize_client
->size_inc
.width
);
632 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
))
633 dx
= -MAX(KEY_DIST
, moveresize_client
->size_inc
.width
);
634 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
))
635 dy
= MAX(KEY_DIST
, moveresize_client
->size_inc
.height
);
636 else /* if (e->xkey.keycode == ob_keycode(OB_KEY_UP)) */
637 dy
= -MAX(KEY_DIST
, moveresize_client
->size_inc
.height
);
641 XWarpPointer(ob_display
, None
, None
, 0, 0, 0, 0, dx
, dy
);
642 /* steal the motion events this causes */
643 XSync(ob_display
, FALSE
);
646 while (XCheckTypedEvent(ob_display
, MotionNotify
, &ce
));
652 /* because the cursor moves even though the window does
653 not nessesarily (resistance), this adjusts where the curor
654 thinks it started so that it keeps up with where the window
656 start_x
+= dx
- (cur_x
- ox
);
657 start_y
+= dy
- (cur_y
- oy
);
660 } else if (corner
== prop_atoms
.net_wm_moveresize_move_keyboard
) {
661 move_with_keys(e
->xkey
.keycode
, e
->xkey
.state
);
667 else if (e
->type
== extensions_sync_event_basep
+ XSyncAlarmNotify
)
669 waiting_for_sync
= FALSE
; /* we got our sync... */
670 do_resize(); /* ...so try resize if there is more change pending */