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
)
322 if (keyboard
) resist
= KEY_DIST
- 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 gboolean
moveresize_event(XEvent
*e
)
485 gboolean used
= FALSE
;
487 if (!moveresize_in_progress
) return FALSE
;
489 if (e
->type
== ButtonPress
) {
491 start_x
= e
->xbutton
.x_root
;
492 start_y
= e
->xbutton
.y_root
;
493 button
= e
->xbutton
.button
; /* this will end it now */
495 used
= e
->xbutton
.button
== button
;
496 } else if (e
->type
== ButtonRelease
) {
497 if (!button
|| e
->xbutton
.button
== button
) {
498 moveresize_end(FALSE
);
501 } else if (e
->type
== MotionNotify
) {
503 cur_x
= start_cx
+ e
->xmotion
.x_root
- start_x
;
504 cur_y
= start_cy
+ e
->xmotion
.y_root
- start_y
;
506 do_edge_warp(e
->xmotion
.x_root
, e
->xmotion
.y_root
);
508 if (corner
== prop_atoms
.net_wm_moveresize_size_topleft
) {
509 cur_x
= start_cw
- (e
->xmotion
.x_root
- start_x
);
510 cur_y
= start_ch
- (e
->xmotion
.y_root
- start_y
);
511 lockcorner
= OB_CORNER_BOTTOMRIGHT
;
512 } else if (corner
== prop_atoms
.net_wm_moveresize_size_top
) {
514 cur_y
= start_ch
- (e
->xmotion
.y_root
- start_y
);
515 lockcorner
= OB_CORNER_BOTTOMRIGHT
;
516 } else if (corner
== prop_atoms
.net_wm_moveresize_size_topright
) {
517 cur_x
= start_cw
+ (e
->xmotion
.x_root
- start_x
);
518 cur_y
= start_ch
- (e
->xmotion
.y_root
- start_y
);
519 lockcorner
= OB_CORNER_BOTTOMLEFT
;
520 } else if (corner
== prop_atoms
.net_wm_moveresize_size_right
) {
521 cur_x
= start_cw
+ (e
->xmotion
.x_root
- start_x
);
523 lockcorner
= OB_CORNER_BOTTOMLEFT
;
525 prop_atoms
.net_wm_moveresize_size_bottomright
) {
526 cur_x
= start_cw
+ (e
->xmotion
.x_root
- start_x
);
527 cur_y
= start_ch
+ (e
->xmotion
.y_root
- start_y
);
528 lockcorner
= OB_CORNER_TOPLEFT
;
529 } else if (corner
== prop_atoms
.net_wm_moveresize_size_bottom
) {
531 cur_y
= start_ch
+ (e
->xmotion
.y_root
- start_y
);
532 lockcorner
= OB_CORNER_TOPLEFT
;
534 prop_atoms
.net_wm_moveresize_size_bottomleft
) {
535 cur_x
= start_cw
- (e
->xmotion
.x_root
- start_x
);
536 cur_y
= start_ch
+ (e
->xmotion
.y_root
- start_y
);
537 lockcorner
= OB_CORNER_TOPRIGHT
;
538 } else if (corner
== prop_atoms
.net_wm_moveresize_size_left
) {
539 cur_x
= start_cw
- (e
->xmotion
.x_root
- start_x
);
541 lockcorner
= OB_CORNER_TOPRIGHT
;
542 } else if (corner
== prop_atoms
.net_wm_moveresize_size_keyboard
) {
543 cur_x
= start_cw
+ (e
->xmotion
.x_root
- start_x
);
544 cur_y
= start_ch
+ (e
->xmotion
.y_root
- start_y
);
545 lockcorner
= OB_CORNER_TOPLEFT
;
547 g_assert_not_reached();
553 } else if (e
->type
== KeyPress
) {
554 if (e
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
)) {
555 moveresize_end(TRUE
);
557 } else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
558 moveresize_end(FALSE
);
560 } else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
) ||
561 e
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
) ||
562 e
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
) ||
563 e
->xkey
.keycode
== ob_keycode(OB_KEY_UP
))
565 if (corner
== prop_atoms
.net_wm_moveresize_size_keyboard
) {
566 gint dx
= 0, dy
= 0, ox
= cur_x
, oy
= cur_y
;
568 if (e
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
))
569 dx
= MAX(KEY_DIST
, moveresize_client
->size_inc
.width
);
570 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
))
571 dx
= -MAX(KEY_DIST
, moveresize_client
->size_inc
.width
);
572 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
))
573 dy
= MAX(KEY_DIST
, moveresize_client
->size_inc
.height
);
574 else /* if (e->xkey.keycode == ob_keycode(OB_KEY_UP)) */
575 dy
= -MAX(KEY_DIST
, moveresize_client
->size_inc
.height
);
579 XWarpPointer(ob_display
, None
, None
, 0, 0, 0, 0, dx
, dy
);
580 /* steal the motion events this causes */
581 XSync(ob_display
, FALSE
);
584 while (XCheckTypedEvent(ob_display
, MotionNotify
, &ce
));
590 /* because the cursor moves even though the window does
591 not nessesarily (resistance), this adjusts where the curor
592 thinks it started so that it keeps up with where the window
594 start_x
+= dx
- (cur_x
- ox
);
595 start_y
+= dy
- (cur_y
- oy
);
598 } else if (corner
== prop_atoms
.net_wm_moveresize_move_keyboard
) {
599 gint dx
= 0, dy
= 0, ox
= cur_x
, oy
= cur_y
;
600 gint opx
, px
, opy
, py
;
601 gint dist
= KEY_DIST
;
603 /* shift means jump to edge */
604 if (e
->xkey
.state
& modkeys_key_to_mask(OB_MODKEY_KEY_SHIFT
)) {
608 if (e
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
))
609 dir
= OB_DIRECTION_EAST
;
610 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
))
611 dir
= OB_DIRECTION_WEST
;
612 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
))
613 dir
= OB_DIRECTION_SOUTH
;
614 else /* if (e->xkey.keycode == ob_keycode(OB_KEY_UP)) */
615 dir
= OB_DIRECTION_NORTH
;
617 client_find_move_directional(moveresize_client
, dir
,
619 dx
= x
- moveresize_client
->area
.x
;
620 dy
= y
- moveresize_client
->area
.y
;
622 /* control means fine grained */
624 modkeys_key_to_mask(OB_MODKEY_KEY_CONTROL
))
627 if (e
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
))
629 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
))
631 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
))
633 else /* if (e->xkey.keycode == ob_keycode(OB_KEY_UP)) */
639 screen_pointer_pos(&opx
, &opy
);
640 XWarpPointer(ob_display
, None
, None
, 0, 0, 0, 0, dx
, dy
);
641 /* steal the motion events this causes */
642 XSync(ob_display
, FALSE
);
645 while (XCheckTypedEvent(ob_display
, MotionNotify
, &ce
));
647 screen_pointer_pos(&px
, &py
);
651 /* because the cursor moves even though the window does
652 not nessesarily (resistance), this adjusts where the curor
653 thinks it started so that it keeps up with where the window
655 start_x
+= (px
- opx
) - (cur_x
- ox
);
656 start_y
+= (py
- opy
) - (cur_y
- oy
);
663 else if (e
->type
== extensions_sync_event_basep
+ XSyncAlarmNotify
)
665 waiting_for_sync
= FALSE
; /* we got our sync... */
666 do_resize(); /* ...so try resize if there is more change pending */