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"
30 #include "moveresize.h"
34 #include "extensions.h"
35 #include "render/render.h"
36 #include "render/theme.h"
41 /* how far windows move and resize with the keyboard arrows */
44 gboolean moveresize_in_progress
= FALSE
;
45 ObClient
*moveresize_client
= NULL
;
47 XSyncAlarm moveresize_alarm
= None
;
50 static gboolean moving
= FALSE
; /* TRUE - moving, FALSE - resizing */
52 static gint start_x
, start_y
, start_cx
, start_cy
, start_cw
, start_ch
;
53 static gint cur_x
, cur_y
;
55 static guint32 corner
;
56 static ObCorner lockcorner
;
57 static ObDirection edge_warp_dir
= -1;
59 static gboolean waiting_for_sync
;
62 static ObPopup
*popup
= NULL
;
64 static void do_edge_warp(gint x
, gint y
);
65 static void cancel_edge_warp();
67 static void client_dest(ObClient
*client
, gpointer data
)
69 if (moveresize_client
== client
)
73 void moveresize_startup(gboolean reconfig
)
75 popup
= popup_new(FALSE
);
76 popup_set_text_align(popup
, RR_JUSTIFY_CENTER
);
79 client_add_destroy_notify(client_dest
, NULL
);
82 void moveresize_shutdown(gboolean reconfig
)
85 if (moveresize_in_progress
)
86 moveresize_end(FALSE
);
87 client_remove_destroy_notify(client_dest
);
94 static void get_resize_position(gint
*x
, gint
*y
, gboolean cancel
)
99 *x
= moveresize_client
->frame
->area
.x
;
100 *y
= moveresize_client
->frame
->area
.y
;
110 /* see how much it is actually going to resize */
112 gint cx
= *x
, cy
= *y
;
113 frame_frame_gravity(moveresize_client
->frame
, &cx
, &cy
);
114 client_try_configure(moveresize_client
, &cx
, &cy
, &w
, &h
,
117 dw
= w
- moveresize_client
->area
.width
;
118 dh
= h
- moveresize_client
->area
.height
;
120 switch (lockcorner
) {
121 case OB_CORNER_TOPLEFT
:
123 case OB_CORNER_TOPRIGHT
:
126 case OB_CORNER_BOTTOMLEFT
:
129 case OB_CORNER_BOTTOMRIGHT
:
135 frame_frame_gravity(moveresize_client
->frame
, x
, y
);
138 static void popup_coords(ObClient
*c
, const gchar
*format
, gint a
, gint b
)
142 text
= g_strdup_printf(format
, a
, b
);
143 if (config_resize_popup_pos
== 1) /* == "Top" */
144 popup_position(popup
, SouthGravity
,
146 + c
->frame
->area
.width
/2,
147 c
->frame
->area
.y
- ob_rr_theme
->fbwidth
);
148 else /* == "Center" */
149 popup_position(popup
, CenterGravity
,
150 c
->frame
->area
.x
+ c
->frame
->size
.left
+
152 c
->frame
->area
.y
+ c
->frame
->size
.top
+
154 popup_show(popup
, text
);
158 void moveresize_start(ObClient
*c
, gint x
, gint y
, guint b
, guint32 cnr
)
161 gboolean mv
= (cnr
== prop_atoms
.net_wm_moveresize_move
||
162 cnr
== prop_atoms
.net_wm_moveresize_move_keyboard
);
164 if (moveresize_in_progress
|| !c
->frame
->visible
||
166 (c
->functions
& OB_CLIENT_FUNC_MOVE
) :
167 (c
->functions
& OB_CLIENT_FUNC_RESIZE
)))
170 if (cnr
== prop_atoms
.net_wm_moveresize_size_topleft
)
171 cur
= OB_CURSOR_NORTHWEST
;
172 else if (cnr
== prop_atoms
.net_wm_moveresize_size_top
)
173 cur
= OB_CURSOR_NORTH
;
174 else if (cnr
== prop_atoms
.net_wm_moveresize_size_topright
)
175 cur
= OB_CURSOR_NORTHEAST
;
176 else if (cnr
== prop_atoms
.net_wm_moveresize_size_right
)
177 cur
= OB_CURSOR_EAST
;
178 else if (cnr
== prop_atoms
.net_wm_moveresize_size_bottomright
)
179 cur
= OB_CURSOR_SOUTHEAST
;
180 else if (cnr
== prop_atoms
.net_wm_moveresize_size_bottom
)
181 cur
= OB_CURSOR_SOUTH
;
182 else if (cnr
== prop_atoms
.net_wm_moveresize_size_bottomleft
)
183 cur
= OB_CURSOR_SOUTHWEST
;
184 else if (cnr
== prop_atoms
.net_wm_moveresize_size_left
)
185 cur
= OB_CURSOR_WEST
;
186 else if (cnr
== prop_atoms
.net_wm_moveresize_size_keyboard
)
187 cur
= OB_CURSOR_SOUTHEAST
;
188 else if (cnr
== prop_atoms
.net_wm_moveresize_move
)
189 cur
= OB_CURSOR_MOVE
;
190 else if (cnr
== prop_atoms
.net_wm_moveresize_move_keyboard
)
191 cur
= OB_CURSOR_MOVE
;
193 g_assert_not_reached();
195 /* keep the pointer bounded to the screen for move/resize */
196 if (!grab_pointer(FALSE
, TRUE
, cur
))
198 if (!grab_keyboard()) {
203 frame_end_iconify_animation(c
->frame
);
206 moveresize_client
= c
;
207 start_cx
= c
->area
.x
;
208 start_cy
= c
->area
.y
;
209 /* these adjustments for the size_inc make resizing a terminal more
210 friendly. you essentially start the resize in the middle of the
211 increment instead of at 0, so you have to move half an increment
212 either way instead of a full increment one and 1 px the other. and this
213 is one large mother fucking comment. */
214 start_cw
= c
->area
.width
+ c
->size_inc
.width
/ 2;
215 start_ch
= c
->area
.height
+ c
->size_inc
.height
/ 2;
222 have to change start_cx and start_cy if going to do this..
223 if (corner == prop_atoms.net_wm_moveresize_move_keyboard ||
224 corner == prop_atoms.net_wm_moveresize_size_keyboard)
225 XWarpPointer(ob_display, None, c->window, 0, 0, 0, 0,
226 c->area.width / 2, c->area.height / 2);
237 moveresize_in_progress
= TRUE
;
240 if (config_resize_redraw
&& !moving
&& extensions_shape
&&
241 moveresize_client
->sync_request
&& moveresize_client
->sync_counter
)
243 /* Initialize values for the resize syncing, and create an alarm for
244 the client's xsync counter */
247 XSyncAlarmAttributes aa
;
249 /* set the counter to an initial value */
250 XSyncIntToValue(&val
, 0);
251 XSyncSetCounter(ob_display
, moveresize_client
->sync_counter
, val
);
253 /* this will be incremented when we tell the client what we're
255 moveresize_client
->sync_counter_value
= 0;
257 /* the next sequence we're waiting for with the alarm */
258 XSyncIntToValue(&val
, 1);
260 /* set an alarm on the counter */
261 aa
.trigger
.counter
= moveresize_client
->sync_counter
;
262 aa
.trigger
.wait_value
= val
;
263 aa
.trigger
.value_type
= XSyncAbsolute
;
264 aa
.trigger
.test_type
= XSyncPositiveTransition
;
266 XSyncIntToValue(&aa
.delta
, 1);
267 moveresize_alarm
= XSyncCreateAlarm(ob_display
,
276 waiting_for_sync
= FALSE
;
281 void moveresize_end(gboolean cancel
)
291 client_move(moveresize_client
,
292 (cancel
? start_cx
: cur_x
),
293 (cancel
? start_cy
: cur_y
));
296 /* turn off the alarm */
297 if (moveresize_alarm
!= None
) {
298 XSyncDestroyAlarm(ob_display
, moveresize_alarm
);
299 moveresize_alarm
= None
;
303 get_resize_position(&x
, &y
, cancel
);
304 client_configure(moveresize_client
, x
, y
,
305 (cancel
? start_cw
: cur_x
),
306 (cancel
? start_ch
: cur_y
),
310 /* dont edge warp after its ended */
313 moveresize_in_progress
= FALSE
;
314 moveresize_client
= NULL
;
317 static void do_move(gboolean keyboard
)
321 if (keyboard
) resist
= KEY_DIST
- 1; /* resist for one key press */
322 else resist
= config_resist_win
;
323 resist_move_windows(moveresize_client
, resist
, &cur_x
, &cur_y
);
324 if (!keyboard
) resist
= config_resist_edge
;
325 resist_move_monitors(moveresize_client
, resist
, &cur_x
, &cur_y
);
327 client_configure(moveresize_client
, cur_x
, cur_y
,
328 moveresize_client
->area
.width
,
329 moveresize_client
->area
.height
,
331 if (config_resize_popup_show
== 2) /* == "Always" */
332 popup_coords(moveresize_client
, "%d x %d",
333 moveresize_client
->frame
->area
.x
,
334 moveresize_client
->frame
->area
.y
);
337 static void do_resize()
339 gint x
, y
, w
, h
, lw
, lh
;
341 /* see if it is actually going to resize */
346 client_try_configure(moveresize_client
, &x
, &y
, &w
, &h
,
348 if (w
== moveresize_client
->area
.width
&&
349 h
== moveresize_client
->area
.height
)
355 if (config_resize_redraw
&& extensions_sync
&&
356 moveresize_client
->sync_request
&& moveresize_client
->sync_counter
)
361 /* are we already waiting for the sync counter to catch up? */
362 if (waiting_for_sync
)
365 /* increment the value we're waiting for */
366 ++moveresize_client
->sync_counter_value
;
367 XSyncIntToValue(&val
, moveresize_client
->sync_counter_value
);
369 /* tell the client what we're waiting for */
370 ce
.xclient
.type
= ClientMessage
;
371 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
372 ce
.xclient
.display
= ob_display
;
373 ce
.xclient
.window
= moveresize_client
->window
;
374 ce
.xclient
.format
= 32;
375 ce
.xclient
.data
.l
[0] = prop_atoms
.net_wm_sync_request
;
376 ce
.xclient
.data
.l
[1] = event_curtime
;
377 ce
.xclient
.data
.l
[2] = XSyncValueLow32(val
);
378 ce
.xclient
.data
.l
[3] = XSyncValueHigh32(val
);
379 ce
.xclient
.data
.l
[4] = 0l;
380 XSendEvent(ob_display
, moveresize_client
->window
, FALSE
,
383 waiting_for_sync
= TRUE
;
387 get_resize_position(&x
, &y
, FALSE
);
388 client_configure(moveresize_client
, x
, y
, cur_x
, cur_y
, TRUE
, FALSE
, FALSE
);
390 /* this would be better with a fixed width font ... XXX can do it better
391 if there are 2 text boxes */
392 if (config_resize_popup_show
== 2 || /* == "Always" */
393 (config_resize_popup_show
== 1 && /* == "Nonpixel" */
394 moveresize_client
->size_inc
.width
> 1 &&
395 moveresize_client
->size_inc
.height
> 1))
396 popup_coords(moveresize_client
, "%d x %d",
397 moveresize_client
->logical_size
.width
,
398 moveresize_client
->logical_size
.height
);
401 static void calc_resize(gboolean keyboard
)
405 /* resist_size_* needs the frame size */
406 cur_x
+= moveresize_client
->frame
->size
.left
+
407 moveresize_client
->frame
->size
.right
;
408 cur_y
+= moveresize_client
->frame
->size
.top
+
409 moveresize_client
->frame
->size
.bottom
;
411 if (keyboard
) resist
= KEY_DIST
- 1; /* resist for one key press */
412 else resist
= config_resist_win
;
413 resist_size_windows(moveresize_client
, resist
, &cur_x
, &cur_y
, lockcorner
);
414 if (!keyboard
) resist
= config_resist_edge
;
415 resist_size_monitors(moveresize_client
, resist
, &cur_x
, &cur_y
,lockcorner
);
417 cur_x
-= moveresize_client
->frame
->size
.left
+
418 moveresize_client
->frame
->size
.right
;
419 cur_y
-= moveresize_client
->frame
->size
.top
+
420 moveresize_client
->frame
->size
.bottom
;
423 static gboolean
edge_warp_delay_func(gpointer data
)
427 d
= screen_find_desktop(screen_desktop
, edge_warp_dir
, TRUE
, FALSE
);
428 if (d
!= screen_desktop
) screen_set_desktop(d
, TRUE
);
432 return FALSE
; /* don't repeat */
435 static void do_edge_warp(gint x
, gint y
)
440 if (!config_mouse_screenedgetime
) return;
444 for (i
= 0; i
< screen_num_monitors
; ++i
) {
445 Rect
*a
= screen_physical_area_monitor(i
);
446 if (x
== RECT_LEFT(*a
)) dir
= OB_DIRECTION_WEST
;
447 if (x
== RECT_RIGHT(*a
)) dir
= OB_DIRECTION_EAST
;
448 if (y
== RECT_TOP(*a
)) dir
= OB_DIRECTION_NORTH
;
449 if (y
== RECT_BOTTOM(*a
)) dir
= OB_DIRECTION_SOUTH
;
451 /* try check for xinerama boundaries */
452 if ((x
+ 1 == RECT_LEFT(*a
) || x
- 1 == RECT_RIGHT(*a
)) &&
453 (dir
== OB_DIRECTION_WEST
|| dir
== OB_DIRECTION_EAST
))
457 if ((y
+ 1 == RECT_TOP(*a
) || y
- 1 == RECT_BOTTOM(*a
)) &&
458 (dir
== OB_DIRECTION_NORTH
|| dir
== OB_DIRECTION_SOUTH
))
465 if (dir
!= edge_warp_dir
) {
466 if (dir
== (ObDirection
)-1)
469 ob_main_loop_timeout_add(ob_main_loop
,
470 config_mouse_screenedgetime
* 1000,
471 edge_warp_delay_func
,
477 static void cancel_edge_warp()
479 ob_main_loop_timeout_remove(ob_main_loop
, edge_warp_delay_func
);
482 gboolean
moveresize_event(XEvent
*e
)
484 gboolean used
= FALSE
;
486 if (!moveresize_in_progress
) return FALSE
;
488 if (e
->type
== ButtonPress
) {
490 start_x
= e
->xbutton
.x_root
;
491 start_y
= e
->xbutton
.y_root
;
492 button
= e
->xbutton
.button
; /* this will end it now */
494 used
= e
->xbutton
.button
== button
;
495 } else if (e
->type
== ButtonRelease
) {
496 if (!button
|| e
->xbutton
.button
== button
) {
497 moveresize_end(FALSE
);
500 } else if (e
->type
== MotionNotify
) {
502 cur_x
= start_cx
+ e
->xmotion
.x_root
- start_x
;
503 cur_y
= start_cy
+ e
->xmotion
.y_root
- start_y
;
505 do_edge_warp(e
->xmotion
.x_root
, e
->xmotion
.y_root
);
507 if (corner
== prop_atoms
.net_wm_moveresize_size_topleft
) {
508 cur_x
= start_cw
- (e
->xmotion
.x_root
- start_x
);
509 cur_y
= start_ch
- (e
->xmotion
.y_root
- start_y
);
510 lockcorner
= OB_CORNER_BOTTOMRIGHT
;
511 } else if (corner
== prop_atoms
.net_wm_moveresize_size_top
) {
513 cur_y
= start_ch
- (e
->xmotion
.y_root
- start_y
);
514 lockcorner
= OB_CORNER_BOTTOMRIGHT
;
515 } else if (corner
== prop_atoms
.net_wm_moveresize_size_topright
) {
516 cur_x
= start_cw
+ (e
->xmotion
.x_root
- start_x
);
517 cur_y
= start_ch
- (e
->xmotion
.y_root
- start_y
);
518 lockcorner
= OB_CORNER_BOTTOMLEFT
;
519 } else if (corner
== prop_atoms
.net_wm_moveresize_size_right
) {
520 cur_x
= start_cw
+ (e
->xmotion
.x_root
- start_x
);
522 lockcorner
= OB_CORNER_BOTTOMLEFT
;
524 prop_atoms
.net_wm_moveresize_size_bottomright
) {
525 cur_x
= start_cw
+ (e
->xmotion
.x_root
- start_x
);
526 cur_y
= start_ch
+ (e
->xmotion
.y_root
- start_y
);
527 lockcorner
= OB_CORNER_TOPLEFT
;
528 } else if (corner
== prop_atoms
.net_wm_moveresize_size_bottom
) {
530 cur_y
= start_ch
+ (e
->xmotion
.y_root
- start_y
);
531 lockcorner
= OB_CORNER_TOPLEFT
;
533 prop_atoms
.net_wm_moveresize_size_bottomleft
) {
534 cur_x
= start_cw
- (e
->xmotion
.x_root
- start_x
);
535 cur_y
= start_ch
+ (e
->xmotion
.y_root
- start_y
);
536 lockcorner
= OB_CORNER_TOPRIGHT
;
537 } else if (corner
== prop_atoms
.net_wm_moveresize_size_left
) {
538 cur_x
= start_cw
- (e
->xmotion
.x_root
- start_x
);
540 lockcorner
= OB_CORNER_TOPRIGHT
;
541 } else if (corner
== prop_atoms
.net_wm_moveresize_size_keyboard
) {
542 cur_x
= start_cw
+ (e
->xmotion
.x_root
- start_x
);
543 cur_y
= start_ch
+ (e
->xmotion
.y_root
- start_y
);
544 lockcorner
= OB_CORNER_TOPLEFT
;
546 g_assert_not_reached();
552 } else if (e
->type
== KeyPress
) {
553 if (e
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
)) {
554 moveresize_end(TRUE
);
556 } else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
557 moveresize_end(FALSE
);
559 } else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
) ||
560 e
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
) ||
561 e
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
) ||
562 e
->xkey
.keycode
== ob_keycode(OB_KEY_UP
))
564 if (corner
== prop_atoms
.net_wm_moveresize_size_keyboard
) {
565 gint dx
= 0, dy
= 0, ox
= cur_x
, oy
= cur_y
;
567 if (e
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
))
568 dx
= MAX(KEY_DIST
, moveresize_client
->size_inc
.width
);
569 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
))
570 dx
= -MAX(KEY_DIST
, moveresize_client
->size_inc
.width
);
571 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
))
572 dy
= MAX(KEY_DIST
, moveresize_client
->size_inc
.height
);
573 else /* if (e->xkey.keycode == ob_keycode(OB_KEY_UP)) */
574 dy
= -MAX(KEY_DIST
, moveresize_client
->size_inc
.height
);
578 XWarpPointer(ob_display
, None
, None
, 0, 0, 0, 0, dx
, dy
);
579 /* steal the motion events this causes */
580 XSync(ob_display
, FALSE
);
583 while (XCheckTypedEvent(ob_display
, MotionNotify
, &ce
));
589 /* because the cursor moves even though the window does
590 not nessesarily (resistance), this adjusts where the curor
591 thinks it started so that it keeps up with where the window
593 start_x
+= dx
- (cur_x
- ox
);
594 start_y
+= dy
- (cur_y
- oy
);
597 } else if (corner
== prop_atoms
.net_wm_moveresize_move_keyboard
) {
598 gint dx
= 0, dy
= 0, ox
= cur_x
, oy
= cur_y
;
599 gint opx
, px
, opy
, py
;
601 if (e
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
))
603 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
))
605 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
))
607 else /* if (e->xkey.keycode == ob_keycode(OB_KEY_UP)) */
612 screen_pointer_pos(&opx
, &opy
);
613 XWarpPointer(ob_display
, None
, None
, 0, 0, 0, 0, dx
, dy
);
614 /* steal the motion events this causes */
615 XSync(ob_display
, FALSE
);
618 while (XCheckTypedEvent(ob_display
, MotionNotify
, &ce
));
620 screen_pointer_pos(&px
, &py
);
624 /* because the cursor moves even though the window does
625 not nessesarily (resistance), this adjusts where the curor
626 thinks it started so that it keeps up with where the window
628 start_x
+= (px
- opx
) - (cur_x
- ox
);
629 start_y
+= (py
- opy
) - (cur_y
- oy
);
636 else if (e
->type
== extensions_sync_event_basep
+ XSyncAlarmNotify
)
638 waiting_for_sync
= FALSE
; /* we got our sync... */
639 do_resize(); /* ...so try resize if there is more change pending */