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
)
155 gboolean mv
= (cnr
== prop_atoms
.net_wm_moveresize_move
||
156 cnr
== prop_atoms
.net_wm_moveresize_move_keyboard
);
158 if (moveresize_in_progress
|| !c
->frame
->visible
||
160 (c
->functions
& OB_CLIENT_FUNC_MOVE
) :
161 (c
->functions
& OB_CLIENT_FUNC_RESIZE
)))
164 if (cnr
== prop_atoms
.net_wm_moveresize_size_topleft
)
165 cur
= OB_CURSOR_NORTHWEST
;
166 else if (cnr
== prop_atoms
.net_wm_moveresize_size_top
)
167 cur
= OB_CURSOR_NORTH
;
168 else if (cnr
== prop_atoms
.net_wm_moveresize_size_topright
)
169 cur
= OB_CURSOR_NORTHEAST
;
170 else if (cnr
== prop_atoms
.net_wm_moveresize_size_right
)
171 cur
= OB_CURSOR_EAST
;
172 else if (cnr
== prop_atoms
.net_wm_moveresize_size_bottomright
)
173 cur
= OB_CURSOR_SOUTHEAST
;
174 else if (cnr
== prop_atoms
.net_wm_moveresize_size_bottom
)
175 cur
= OB_CURSOR_SOUTH
;
176 else if (cnr
== prop_atoms
.net_wm_moveresize_size_bottomleft
)
177 cur
= OB_CURSOR_SOUTHWEST
;
178 else if (cnr
== prop_atoms
.net_wm_moveresize_size_left
)
179 cur
= OB_CURSOR_WEST
;
180 else if (cnr
== prop_atoms
.net_wm_moveresize_size_keyboard
)
181 cur
= OB_CURSOR_SOUTHEAST
;
182 else if (cnr
== prop_atoms
.net_wm_moveresize_move
)
183 cur
= OB_CURSOR_MOVE
;
184 else if (cnr
== prop_atoms
.net_wm_moveresize_move_keyboard
)
185 cur
= OB_CURSOR_MOVE
;
187 g_assert_not_reached();
189 /* keep the pointer bounded to the screen for move/resize */
190 if (!grab_pointer(FALSE
, TRUE
, cur
))
192 if (!grab_keyboard()) {
197 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
),
304 moveresize_in_progress
= FALSE
;
305 moveresize_client
= NULL
;
308 static void do_move(gboolean keyboard
)
312 if (keyboard
) resist
= KEY_DIST
- 1; /* resist for one key press */
313 else resist
= config_resist_win
;
314 resist_move_windows(moveresize_client
, resist
, &cur_x
, &cur_y
);
315 if (!keyboard
) resist
= config_resist_edge
;
316 resist_move_monitors(moveresize_client
, resist
, &cur_x
, &cur_y
);
318 client_configure(moveresize_client
, cur_x
, cur_y
,
319 moveresize_client
->area
.width
,
320 moveresize_client
->area
.height
,
322 if (config_resize_popup_show
== 2) /* == "Always" */
323 popup_coords(moveresize_client
, "%d x %d",
324 moveresize_client
->frame
->area
.x
,
325 moveresize_client
->frame
->area
.y
);
328 static void do_resize()
330 gint x
, y
, w
, h
, lw
, lh
;
332 /* see if it is actually going to resize */
337 client_try_configure(moveresize_client
, &x
, &y
, &w
, &h
,
339 if (w
== moveresize_client
->area
.width
&&
340 h
== moveresize_client
->area
.height
)
346 if (config_resize_redraw
&& extensions_sync
&&
347 moveresize_client
->sync_request
&& moveresize_client
->sync_counter
)
352 /* are we already waiting for the sync counter to catch up? */
353 if (waiting_for_sync
)
356 /* increment the value we're waiting for */
357 ++moveresize_client
->sync_counter_value
;
358 XSyncIntToValue(&val
, moveresize_client
->sync_counter_value
);
360 /* tell the client what we're waiting for */
361 ce
.xclient
.type
= ClientMessage
;
362 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
363 ce
.xclient
.display
= ob_display
;
364 ce
.xclient
.window
= moveresize_client
->window
;
365 ce
.xclient
.format
= 32;
366 ce
.xclient
.data
.l
[0] = prop_atoms
.net_wm_sync_request
;
367 ce
.xclient
.data
.l
[1] = event_curtime
;
368 ce
.xclient
.data
.l
[2] = XSyncValueLow32(val
);
369 ce
.xclient
.data
.l
[3] = XSyncValueHigh32(val
);
370 ce
.xclient
.data
.l
[4] = 0l;
371 XSendEvent(ob_display
, moveresize_client
->window
, FALSE
,
374 waiting_for_sync
= TRUE
;
378 get_resize_position(&x
, &y
, FALSE
);
379 client_configure(moveresize_client
, x
, y
, cur_x
, cur_y
, TRUE
, FALSE
);
381 /* this would be better with a fixed width font ... XXX can do it better
382 if there are 2 text boxes */
383 if (config_resize_popup_show
== 2 || /* == "Always" */
384 (config_resize_popup_show
== 1 && /* == "Nonpixel" */
385 moveresize_client
->size_inc
.width
> 1 &&
386 moveresize_client
->size_inc
.height
> 1))
387 popup_coords(moveresize_client
, "%d x %d",
388 moveresize_client
->logical_size
.width
,
389 moveresize_client
->logical_size
.height
);
392 static void calc_resize(gboolean keyboard
)
396 /* resist_size_* needs the frame size */
397 cur_x
+= moveresize_client
->frame
->size
.left
+
398 moveresize_client
->frame
->size
.right
;
399 cur_y
+= moveresize_client
->frame
->size
.top
+
400 moveresize_client
->frame
->size
.bottom
;
402 if (keyboard
) resist
= KEY_DIST
- 1; /* resist for one key press */
403 else resist
= config_resist_win
;
404 resist_size_windows(moveresize_client
, resist
, &cur_x
, &cur_y
, lockcorner
);
405 if (!keyboard
) resist
= config_resist_edge
;
406 resist_size_monitors(moveresize_client
, resist
, &cur_x
, &cur_y
,lockcorner
);
408 cur_x
-= moveresize_client
->frame
->size
.left
+
409 moveresize_client
->frame
->size
.right
;
410 cur_y
-= moveresize_client
->frame
->size
.top
+
411 moveresize_client
->frame
->size
.bottom
;
414 gboolean
moveresize_event(XEvent
*e
)
416 gboolean used
= FALSE
;
418 g_assert(moveresize_in_progress
);
420 if (e
->type
== ButtonPress
) {
422 start_x
= e
->xbutton
.x_root
;
423 start_y
= e
->xbutton
.y_root
;
424 button
= e
->xbutton
.button
; /* this will end it now */
426 used
= e
->xbutton
.button
== button
;
427 } else if (e
->type
== ButtonRelease
) {
428 if (!button
|| e
->xbutton
.button
== button
) {
429 moveresize_end(FALSE
);
432 } else if (e
->type
== MotionNotify
) {
434 cur_x
= start_cx
+ e
->xmotion
.x_root
- start_x
;
435 cur_y
= start_cy
+ e
->xmotion
.y_root
- start_y
;
438 if (corner
== prop_atoms
.net_wm_moveresize_size_topleft
) {
439 cur_x
= start_cw
- (e
->xmotion
.x_root
- start_x
);
440 cur_y
= start_ch
- (e
->xmotion
.y_root
- start_y
);
441 lockcorner
= OB_CORNER_BOTTOMRIGHT
;
442 } else if (corner
== prop_atoms
.net_wm_moveresize_size_top
) {
444 cur_y
= start_ch
- (e
->xmotion
.y_root
- start_y
);
445 lockcorner
= OB_CORNER_BOTTOMRIGHT
;
446 } else if (corner
== prop_atoms
.net_wm_moveresize_size_topright
) {
447 cur_x
= start_cw
+ (e
->xmotion
.x_root
- start_x
);
448 cur_y
= start_ch
- (e
->xmotion
.y_root
- start_y
);
449 lockcorner
= OB_CORNER_BOTTOMLEFT
;
450 } else if (corner
== prop_atoms
.net_wm_moveresize_size_right
) {
451 cur_x
= start_cw
+ (e
->xmotion
.x_root
- start_x
);
453 lockcorner
= OB_CORNER_BOTTOMLEFT
;
455 prop_atoms
.net_wm_moveresize_size_bottomright
) {
456 cur_x
= start_cw
+ (e
->xmotion
.x_root
- start_x
);
457 cur_y
= start_ch
+ (e
->xmotion
.y_root
- start_y
);
458 lockcorner
= OB_CORNER_TOPLEFT
;
459 } else if (corner
== prop_atoms
.net_wm_moveresize_size_bottom
) {
461 cur_y
= start_ch
+ (e
->xmotion
.y_root
- start_y
);
462 lockcorner
= OB_CORNER_TOPLEFT
;
464 prop_atoms
.net_wm_moveresize_size_bottomleft
) {
465 cur_x
= start_cw
- (e
->xmotion
.x_root
- start_x
);
466 cur_y
= start_ch
+ (e
->xmotion
.y_root
- start_y
);
467 lockcorner
= OB_CORNER_TOPRIGHT
;
468 } else if (corner
== prop_atoms
.net_wm_moveresize_size_left
) {
469 cur_x
= start_cw
- (e
->xmotion
.x_root
- start_x
);
471 lockcorner
= OB_CORNER_TOPRIGHT
;
472 } else if (corner
== prop_atoms
.net_wm_moveresize_size_keyboard
) {
473 cur_x
= start_cw
+ (e
->xmotion
.x_root
- start_x
);
474 cur_y
= start_ch
+ (e
->xmotion
.y_root
- start_y
);
475 lockcorner
= OB_CORNER_TOPLEFT
;
477 g_assert_not_reached();
483 } else if (e
->type
== KeyPress
) {
484 if (e
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
)) {
485 moveresize_end(TRUE
);
487 } else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
488 moveresize_end(FALSE
);
490 } else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
) ||
491 e
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
) ||
492 e
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
) ||
493 e
->xkey
.keycode
== ob_keycode(OB_KEY_UP
))
495 if (corner
== prop_atoms
.net_wm_moveresize_size_keyboard
) {
496 gint dx
= 0, dy
= 0, ox
= cur_x
, oy
= cur_y
;
498 if (e
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
))
499 dx
= MAX(KEY_DIST
, moveresize_client
->size_inc
.width
);
500 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
))
501 dx
= -MAX(KEY_DIST
, moveresize_client
->size_inc
.width
);
502 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
))
503 dy
= MAX(KEY_DIST
, moveresize_client
->size_inc
.height
);
504 else /* if (e->xkey.keycode == ob_keycode(OB_KEY_UP)) */
505 dy
= -MAX(KEY_DIST
, moveresize_client
->size_inc
.height
);
509 XWarpPointer(ob_display
, None
, None
, 0, 0, 0, 0, dx
, dy
);
510 /* steal the motion events this causes */
511 XSync(ob_display
, FALSE
);
514 while (XCheckTypedEvent(ob_display
, MotionNotify
, &ce
));
520 /* because the cursor moves even though the window does
521 not nessesarily (resistance), this adjusts where the curor
522 thinks it started so that it keeps up with where the window
524 start_x
+= dx
- (cur_x
- ox
);
525 start_y
+= dy
- (cur_y
- oy
);
528 } else if (corner
== prop_atoms
.net_wm_moveresize_move_keyboard
) {
529 gint dx
= 0, dy
= 0, ox
= cur_x
, oy
= cur_y
;
530 gint opx
, px
, opy
, py
;
532 if (e
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
))
534 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
))
536 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
))
538 else /* if (e->xkey.keycode == ob_keycode(OB_KEY_UP)) */
543 screen_pointer_pos(&opx
, &opy
);
544 XWarpPointer(ob_display
, None
, None
, 0, 0, 0, 0, dx
, dy
);
545 /* steal the motion events this causes */
546 XSync(ob_display
, FALSE
);
549 while (XCheckTypedEvent(ob_display
, MotionNotify
, &ce
));
551 screen_pointer_pos(&px
, &py
);
555 /* because the cursor moves even though the window does
556 not nessesarily (resistance), this adjusts where the curor
557 thinks it started so that it keeps up with where the window
559 start_x
+= (px
- opx
) - (cur_x
- ox
);
560 start_y
+= (py
- opy
) - (cur_y
- oy
);
567 else if (e
->type
== extensions_sync_event_basep
+ XSyncAlarmNotify
)
569 waiting_for_sync
= FALSE
; /* we got our sync... */
570 do_resize(); /* ...so try resize if there is more change pending */