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"
29 #include "moveresize.h"
33 #include "extensions.h"
34 #include "render/render.h"
35 #include "render/theme.h"
40 /* how far windows move and resize with the keyboard arrows */
43 gboolean moveresize_in_progress
= FALSE
;
44 ObClient
*moveresize_client
= NULL
;
46 XSyncAlarm moveresize_alarm
= None
;
49 static gboolean moving
= FALSE
; /* TRUE - moving, FALSE - resizing */
51 static gint start_x
, start_y
, start_cx
, start_cy
, start_cw
, start_ch
;
52 static gint cur_x
, cur_y
;
54 static guint32 corner
;
55 static ObCorner lockcorner
;
57 static gboolean waiting_for_sync
;
60 static ObPopup
*popup
= NULL
;
62 static void client_dest(ObClient
*client
, gpointer data
)
64 if (moveresize_client
== client
)
68 void moveresize_startup(gboolean reconfig
)
70 popup
= popup_new(FALSE
);
73 client_add_destroy_notify(client_dest
, NULL
);
76 void moveresize_shutdown(gboolean reconfig
)
79 if (moveresize_in_progress
)
80 moveresize_end(FALSE
);
81 client_remove_destroy_notify(client_dest
);
88 static void get_resize_position(gint
*x
, gint
*y
, gboolean cancel
)
93 *x
= moveresize_client
->frame
->area
.x
;
94 *y
= moveresize_client
->frame
->area
.y
;
104 /* see how much it is actually going to resize */
106 gint cx
= *x
, cy
= *y
;
107 frame_frame_gravity(moveresize_client
->frame
, &cx
, &cy
, w
, h
);
108 client_try_configure(moveresize_client
, &cx
, &cy
, &w
, &h
,
111 dw
= w
- moveresize_client
->area
.width
;
112 dh
= h
- moveresize_client
->area
.height
;
114 switch (lockcorner
) {
115 case OB_CORNER_TOPLEFT
:
117 case OB_CORNER_TOPRIGHT
:
120 case OB_CORNER_BOTTOMLEFT
:
123 case OB_CORNER_BOTTOMRIGHT
:
129 frame_frame_gravity(moveresize_client
->frame
, x
, y
, w
, h
);
132 static void popup_coords(ObClient
*c
, const gchar
*format
, gint a
, gint b
)
136 text
= g_strdup_printf(format
, a
, b
);
137 if (config_resize_popup_pos
== 1) /* == "Top" */
138 popup_position(popup
, SouthGravity
,
140 + c
->frame
->area
.width
/2,
141 c
->frame
->area
.y
- ob_rr_theme
->fbwidth
);
142 else /* == "Center" */
143 popup_position(popup
, CenterGravity
,
144 c
->frame
->area
.x
+ c
->frame
->size
.left
+
146 c
->frame
->area
.y
+ c
->frame
->size
.top
+
148 popup_show(popup
, text
);
152 void moveresize_start(ObClient
*c
, gint x
, gint y
, guint b
, guint32 cnr
)
156 moving
= (cnr
== prop_atoms
.net_wm_moveresize_move
||
157 cnr
== prop_atoms
.net_wm_moveresize_move_keyboard
);
159 if (moveresize_in_progress
|| !c
->frame
->visible
||
161 (c
->functions
& OB_CLIENT_FUNC_MOVE
) :
162 (c
->functions
& OB_CLIENT_FUNC_RESIZE
)))
165 if (cnr
== prop_atoms
.net_wm_moveresize_size_topleft
)
166 cur
= OB_CURSOR_NORTHWEST
;
167 else if (cnr
== prop_atoms
.net_wm_moveresize_size_top
)
168 cur
= OB_CURSOR_NORTH
;
169 else if (cnr
== prop_atoms
.net_wm_moveresize_size_topright
)
170 cur
= OB_CURSOR_NORTHEAST
;
171 else if (cnr
== prop_atoms
.net_wm_moveresize_size_right
)
172 cur
= OB_CURSOR_EAST
;
173 else if (cnr
== prop_atoms
.net_wm_moveresize_size_bottomright
)
174 cur
= OB_CURSOR_SOUTHEAST
;
175 else if (cnr
== prop_atoms
.net_wm_moveresize_size_bottom
)
176 cur
= OB_CURSOR_SOUTH
;
177 else if (cnr
== prop_atoms
.net_wm_moveresize_size_bottomleft
)
178 cur
= OB_CURSOR_SOUTHWEST
;
179 else if (cnr
== prop_atoms
.net_wm_moveresize_size_left
)
180 cur
= OB_CURSOR_WEST
;
181 else if (cnr
== prop_atoms
.net_wm_moveresize_size_keyboard
)
182 cur
= OB_CURSOR_SOUTHEAST
;
183 else if (cnr
== prop_atoms
.net_wm_moveresize_move
)
184 cur
= OB_CURSOR_MOVE
;
185 else if (cnr
== prop_atoms
.net_wm_moveresize_move_keyboard
)
186 cur
= OB_CURSOR_MOVE
;
188 g_assert_not_reached();
190 /* keep the pointer bounded to the screen for move/resize */
191 if (!grab_pointer(FALSE
, TRUE
, cur
))
193 if (!grab_keyboard()) {
198 frame_end_iconify_animation(c
->frame
);
200 moveresize_client
= c
;
201 start_cx
= c
->area
.x
;
202 start_cy
= c
->area
.y
;
203 /* these adjustments for the size_inc make resizing a terminal more
204 friendly. you essentially start the resize in the middle of the
205 increment instead of at 0, so you have to move half an increment
206 either way instead of a full increment one and 1 px the other. and this
207 is one large mother fucking comment. */
208 start_cw
= c
->area
.width
+ c
->size_inc
.width
/ 2;
209 start_ch
= c
->area
.height
+ c
->size_inc
.height
/ 2;
216 have to change start_cx and start_cy if going to do this..
217 if (corner == prop_atoms.net_wm_moveresize_move_keyboard ||
218 corner == prop_atoms.net_wm_moveresize_size_keyboard)
219 XWarpPointer(ob_display, None, c->window, 0, 0, 0, 0,
220 c->area.width / 2, c->area.height / 2);
231 moveresize_in_progress
= TRUE
;
234 if (config_resize_redraw
&& !moving
&& extensions_shape
&&
235 moveresize_client
->sync_request
&& moveresize_client
->sync_counter
)
237 /* Initialize values for the resize syncing, and create an alarm for
238 the client's xsync counter */
241 XSyncAlarmAttributes aa
;
243 /* set the counter to an initial value */
244 XSyncIntToValue(&val
, 0);
245 XSyncSetCounter(ob_display
, moveresize_client
->sync_counter
, val
);
247 /* this will be incremented when we tell the client what we're
249 moveresize_client
->sync_counter_value
= 0;
251 /* the next sequence we're waiting for with the alarm */
252 XSyncIntToValue(&val
, 1);
254 /* set an alarm on the counter */
255 aa
.trigger
.counter
= moveresize_client
->sync_counter
;
256 aa
.trigger
.wait_value
= val
;
257 aa
.trigger
.value_type
= XSyncAbsolute
;
258 aa
.trigger
.test_type
= XSyncPositiveTransition
;
260 XSyncIntToValue(&aa
.delta
, 1);
261 moveresize_alarm
= XSyncCreateAlarm(ob_display
,
270 waiting_for_sync
= FALSE
;
275 void moveresize_end(gboolean cancel
)
285 client_move(moveresize_client
,
286 (cancel
? start_cx
: cur_x
),
287 (cancel
? start_cy
: cur_y
));
290 /* turn off the alarm */
291 if (moveresize_alarm
!= None
) {
292 XSyncDestroyAlarm(ob_display
, moveresize_alarm
);
293 moveresize_alarm
= None
;
297 get_resize_position(&x
, &y
, cancel
);
298 client_configure(moveresize_client
, x
, y
,
299 (cancel
? start_cw
: cur_x
),
300 (cancel
? start_ch
: cur_y
), TRUE
, TRUE
);
303 moveresize_in_progress
= FALSE
;
304 moveresize_client
= NULL
;
307 static void do_move(gboolean keyboard
)
311 if (keyboard
) resist
= KEY_DIST
- 1; /* resist for one key press */
312 else resist
= config_resist_win
;
313 resist_move_windows(moveresize_client
, resist
, &cur_x
, &cur_y
);
314 if (!keyboard
) resist
= config_resist_edge
;
315 resist_move_monitors(moveresize_client
, resist
, &cur_x
, &cur_y
);
317 client_configure(moveresize_client
, cur_x
, cur_y
,
318 moveresize_client
->area
.width
,
319 moveresize_client
->area
.height
, TRUE
, FALSE
);
320 if (config_resize_popup_show
== 2) /* == "Always" */
321 popup_coords(moveresize_client
, "%d x %d",
322 moveresize_client
->frame
->area
.x
,
323 moveresize_client
->frame
->area
.y
);
326 static void do_resize()
328 gint x
, y
, w
, h
, lw
, lh
;
330 /* see if it is actually going to resize */
335 client_try_configure(moveresize_client
, &x
, &y
, &w
, &h
,
337 if (w
== moveresize_client
->area
.width
&&
338 h
== moveresize_client
->area
.height
)
344 if (config_resize_redraw
&& extensions_sync
&&
345 moveresize_client
->sync_request
&& moveresize_client
->sync_counter
)
350 /* are we already waiting for the sync counter to catch up? */
351 if (waiting_for_sync
)
354 /* increment the value we're waiting for */
355 ++moveresize_client
->sync_counter_value
;
356 XSyncIntToValue(&val
, moveresize_client
->sync_counter_value
);
358 /* tell the client what we're waiting for */
359 ce
.xclient
.type
= ClientMessage
;
360 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
361 ce
.xclient
.display
= ob_display
;
362 ce
.xclient
.window
= moveresize_client
->window
;
363 ce
.xclient
.format
= 32;
364 ce
.xclient
.data
.l
[0] = prop_atoms
.net_wm_sync_request
;
365 ce
.xclient
.data
.l
[1] = event_curtime
;
366 ce
.xclient
.data
.l
[2] = XSyncValueLow32(val
);
367 ce
.xclient
.data
.l
[3] = XSyncValueHigh32(val
);
368 ce
.xclient
.data
.l
[4] = 0l;
369 XSendEvent(ob_display
, moveresize_client
->window
, FALSE
,
372 waiting_for_sync
= TRUE
;
376 get_resize_position(&x
, &y
, FALSE
);
377 client_configure(moveresize_client
, x
, y
, cur_x
, cur_y
, TRUE
, FALSE
);
379 /* this would be better with a fixed width font ... XXX can do it better
380 if there are 2 text boxes */
381 if (config_resize_popup_show
== 2 || /* == "Always" */
382 (config_resize_popup_show
== 1 && /* == "Nonpixel" */
383 moveresize_client
->size_inc
.width
> 1 &&
384 moveresize_client
->size_inc
.height
> 1))
385 popup_coords(moveresize_client
, "%d x %d",
386 moveresize_client
->logical_size
.width
,
387 moveresize_client
->logical_size
.height
);
390 static void calc_resize(gboolean keyboard
)
394 /* resist_size_* needs the frame size */
395 cur_x
+= moveresize_client
->frame
->size
.left
+
396 moveresize_client
->frame
->size
.right
;
397 cur_y
+= moveresize_client
->frame
->size
.top
+
398 moveresize_client
->frame
->size
.bottom
;
400 if (keyboard
) resist
= KEY_DIST
- 1; /* resist for one key press */
401 else resist
= config_resist_win
;
402 resist_size_windows(moveresize_client
, resist
, &cur_x
, &cur_y
, lockcorner
);
403 if (!keyboard
) resist
= config_resist_edge
;
404 resist_size_monitors(moveresize_client
, resist
, &cur_x
, &cur_y
,lockcorner
);
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
;
412 gboolean
moveresize_event(XEvent
*e
)
414 gboolean used
= FALSE
;
416 g_assert(moveresize_in_progress
);
418 if (e
->type
== ButtonPress
) {
420 start_x
= e
->xbutton
.x_root
;
421 start_y
= e
->xbutton
.y_root
;
422 button
= e
->xbutton
.button
; /* this will end it now */
424 used
= e
->xbutton
.button
== button
;
425 } else if (e
->type
== ButtonRelease
) {
426 if (!button
|| e
->xbutton
.button
== button
) {
427 moveresize_end(FALSE
);
430 } else if (e
->type
== MotionNotify
) {
432 cur_x
= start_cx
+ e
->xmotion
.x_root
- start_x
;
433 cur_y
= start_cy
+ e
->xmotion
.y_root
- start_y
;
436 if (corner
== prop_atoms
.net_wm_moveresize_size_topleft
) {
437 cur_x
= start_cw
- (e
->xmotion
.x_root
- start_x
);
438 cur_y
= start_ch
- (e
->xmotion
.y_root
- start_y
);
439 lockcorner
= OB_CORNER_BOTTOMRIGHT
;
440 } else if (corner
== prop_atoms
.net_wm_moveresize_size_top
) {
442 cur_y
= start_ch
- (e
->xmotion
.y_root
- start_y
);
443 lockcorner
= OB_CORNER_BOTTOMRIGHT
;
444 } else if (corner
== prop_atoms
.net_wm_moveresize_size_topright
) {
445 cur_x
= start_cw
+ (e
->xmotion
.x_root
- start_x
);
446 cur_y
= start_ch
- (e
->xmotion
.y_root
- start_y
);
447 lockcorner
= OB_CORNER_BOTTOMLEFT
;
448 } else if (corner
== prop_atoms
.net_wm_moveresize_size_right
) {
449 cur_x
= start_cw
+ (e
->xmotion
.x_root
- start_x
);
451 lockcorner
= OB_CORNER_BOTTOMLEFT
;
453 prop_atoms
.net_wm_moveresize_size_bottomright
) {
454 cur_x
= start_cw
+ (e
->xmotion
.x_root
- start_x
);
455 cur_y
= start_ch
+ (e
->xmotion
.y_root
- start_y
);
456 lockcorner
= OB_CORNER_TOPLEFT
;
457 } else if (corner
== prop_atoms
.net_wm_moveresize_size_bottom
) {
459 cur_y
= start_ch
+ (e
->xmotion
.y_root
- start_y
);
460 lockcorner
= OB_CORNER_TOPLEFT
;
462 prop_atoms
.net_wm_moveresize_size_bottomleft
) {
463 cur_x
= start_cw
- (e
->xmotion
.x_root
- start_x
);
464 cur_y
= start_ch
+ (e
->xmotion
.y_root
- start_y
);
465 lockcorner
= OB_CORNER_TOPRIGHT
;
466 } else if (corner
== prop_atoms
.net_wm_moveresize_size_left
) {
467 cur_x
= start_cw
- (e
->xmotion
.x_root
- start_x
);
469 lockcorner
= OB_CORNER_TOPRIGHT
;
470 } else if (corner
== prop_atoms
.net_wm_moveresize_size_keyboard
) {
471 cur_x
= start_cw
+ (e
->xmotion
.x_root
- start_x
);
472 cur_y
= start_ch
+ (e
->xmotion
.y_root
- start_y
);
473 lockcorner
= OB_CORNER_TOPLEFT
;
475 g_assert_not_reached();
481 } else if (e
->type
== KeyPress
) {
482 if (e
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
)) {
483 moveresize_end(TRUE
);
485 } else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
486 moveresize_end(FALSE
);
488 } else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
) ||
489 e
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
) ||
490 e
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
) ||
491 e
->xkey
.keycode
== ob_keycode(OB_KEY_UP
))
493 if (corner
== prop_atoms
.net_wm_moveresize_size_keyboard
) {
494 gint dx
= 0, dy
= 0, ox
= cur_x
, oy
= cur_y
;
496 if (e
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
))
497 dx
= MAX(KEY_DIST
, moveresize_client
->size_inc
.width
);
498 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
))
499 dx
= -MAX(KEY_DIST
, moveresize_client
->size_inc
.width
);
500 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
))
501 dy
= MAX(KEY_DIST
, moveresize_client
->size_inc
.height
);
502 else /* if (e->xkey.keycode == ob_keycode(OB_KEY_UP)) */
503 dy
= -MAX(KEY_DIST
, moveresize_client
->size_inc
.height
);
507 XWarpPointer(ob_display
, None
, None
, 0, 0, 0, 0, dx
, dy
);
508 /* steal the motion events this causes */
509 XSync(ob_display
, FALSE
);
512 while (XCheckTypedEvent(ob_display
, MotionNotify
, &ce
));
518 /* because the cursor moves even though the window does
519 not nessesarily (resistance), this adjusts where the curor
520 thinks it started so that it keeps up with where the window
522 start_x
+= dx
- (cur_x
- ox
);
523 start_y
+= dy
- (cur_y
- oy
);
526 } else if (corner
== prop_atoms
.net_wm_moveresize_move_keyboard
) {
527 gint dx
= 0, dy
= 0, ox
= cur_x
, oy
= cur_y
;
528 gint opx
, px
, opy
, py
;
530 if (e
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
))
532 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
))
534 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
))
536 else /* if (e->xkey.keycode == ob_keycode(OB_KEY_UP)) */
541 screen_pointer_pos(&opx
, &opy
);
542 XWarpPointer(ob_display
, None
, None
, 0, 0, 0, 0, dx
, dy
);
543 /* steal the motion events this causes */
544 XSync(ob_display
, FALSE
);
547 while (XCheckTypedEvent(ob_display
, MotionNotify
, &ce
));
549 screen_pointer_pos(&px
, &py
);
553 /* because the cursor moves even though the window does
554 not nessesarily (resistance), this adjusts where the curor
555 thinks it started so that it keeps up with where the window
557 start_x
+= (px
- opx
) - (cur_x
- ox
);
558 start_y
+= (py
- opy
) - (cur_y
- oy
);
565 else if (e
->type
== extensions_sync_event_basep
+ XSyncAlarmNotify
)
567 waiting_for_sync
= FALSE
; /* we got our sync... */
568 do_resize(); /* ...so try resize if there is more change pending */