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 (corner
== prop_atoms
.net_wm_moveresize_size_topleft
)
166 cur
= OB_CURSOR_NORTHWEST
;
167 else if (corner
== prop_atoms
.net_wm_moveresize_size_top
)
168 cur
= OB_CURSOR_NORTH
;
169 else if (corner
== prop_atoms
.net_wm_moveresize_size_topright
)
170 cur
= OB_CURSOR_NORTHEAST
;
171 else if (corner
== prop_atoms
.net_wm_moveresize_size_right
)
172 cur
= OB_CURSOR_EAST
;
173 else if (corner
== prop_atoms
.net_wm_moveresize_size_bottomright
)
174 cur
= OB_CURSOR_SOUTHEAST
;
175 else if (corner
== prop_atoms
.net_wm_moveresize_size_bottom
)
176 cur
= OB_CURSOR_SOUTH
;
177 else if (corner
== prop_atoms
.net_wm_moveresize_size_bottomleft
)
178 cur
= OB_CURSOR_SOUTHWEST
;
179 else if (corner
== prop_atoms
.net_wm_moveresize_size_left
)
180 cur
= OB_CURSOR_WEST
;
181 else if (corner
== prop_atoms
.net_wm_moveresize_size_keyboard
)
182 cur
= OB_CURSOR_SOUTHEAST
;
183 else if (corner
== prop_atoms
.net_wm_moveresize_move
)
184 cur
= OB_CURSOR_MOVE
;
185 else if (corner
== prop_atoms
.net_wm_moveresize_move_keyboard
)
186 cur
= OB_CURSOR_MOVE
;
188 g_assert_not_reached();
190 if (!grab_pointer(TRUE
, FALSE
, cur
))
192 if (!grab_keyboard(TRUE
)) {
193 grab_pointer(FALSE
, FALSE
, OB_CURSOR_NONE
);
197 frame_end_iconify_animation(c
->frame
);
199 moveresize_client
= c
;
200 start_cx
= c
->area
.x
;
201 start_cy
= c
->area
.y
;
202 /* these adjustments for the size_inc make resizing a terminal more
203 friendly. you essentially start the resize in the middle of the
204 increment instead of at 0, so you have to move half an increment
205 either way instead of a full increment one and 1 px the other. and this
206 is one large mother fucking comment. */
207 start_cw
= c
->area
.width
+ c
->size_inc
.width
/ 2;
208 start_ch
= c
->area
.height
+ c
->size_inc
.height
/ 2;
215 have to change start_cx and start_cy if going to do this..
216 if (corner == prop_atoms.net_wm_moveresize_move_keyboard ||
217 corner == prop_atoms.net_wm_moveresize_size_keyboard)
218 XWarpPointer(ob_display, None, c->window, 0, 0, 0, 0,
219 c->area.width / 2, c->area.height / 2);
230 moveresize_in_progress
= TRUE
;
233 if (config_resize_redraw
&& !moving
&& extensions_shape
&&
234 moveresize_client
->sync_request
&& moveresize_client
->sync_counter
)
236 /* Initialize values for the resize syncing, and create an alarm for
237 the client's xsync counter */
240 XSyncAlarmAttributes aa
;
242 /* set the counter to an initial value */
243 XSyncIntToValue(&val
, 0);
244 XSyncSetCounter(ob_display
, moveresize_client
->sync_counter
, val
);
246 /* this will be incremented when we tell the client what we're
248 moveresize_client
->sync_counter_value
= 0;
250 /* the next sequence we're waiting for with the alarm */
251 XSyncIntToValue(&val
, 1);
253 /* set an alarm on the counter */
254 aa
.trigger
.counter
= moveresize_client
->sync_counter
;
255 aa
.trigger
.wait_value
= val
;
256 aa
.trigger
.value_type
= XSyncAbsolute
;
257 aa
.trigger
.test_type
= XSyncPositiveTransition
;
259 XSyncIntToValue(&aa
.delta
, 1);
260 moveresize_alarm
= XSyncCreateAlarm(ob_display
,
269 waiting_for_sync
= FALSE
;
274 void moveresize_end(gboolean cancel
)
278 grab_keyboard(FALSE
);
279 grab_pointer(FALSE
, FALSE
, OB_CURSOR_NONE
);
284 client_move(moveresize_client
,
285 (cancel
? start_cx
: cur_x
),
286 (cancel
? start_cy
: cur_y
));
289 /* turn off the alarm */
290 if (moveresize_alarm
!= None
) {
291 XSyncDestroyAlarm(ob_display
, moveresize_alarm
);
292 moveresize_alarm
= None
;
296 get_resize_position(&x
, &y
, cancel
);
297 client_configure(moveresize_client
, x
, y
,
298 (cancel
? start_cw
: cur_x
),
299 (cancel
? start_ch
: cur_y
), TRUE
, TRUE
);
302 moveresize_in_progress
= FALSE
;
303 moveresize_client
= NULL
;
306 static void do_move(gboolean keyboard
)
310 if (keyboard
) resist
= KEY_DIST
- 1; /* resist for one key press */
311 else resist
= config_resist_win
;
312 resist_move_windows(moveresize_client
, resist
, &cur_x
, &cur_y
);
313 if (!keyboard
) resist
= config_resist_edge
;
314 resist_move_monitors(moveresize_client
, resist
, &cur_x
, &cur_y
);
316 client_configure(moveresize_client
, cur_x
, cur_y
,
317 moveresize_client
->area
.width
,
318 moveresize_client
->area
.height
, TRUE
, FALSE
);
319 if (config_resize_popup_show
== 2) /* == "Always" */
320 popup_coords(moveresize_client
, "%d x %d",
321 moveresize_client
->frame
->area
.x
,
322 moveresize_client
->frame
->area
.y
);
325 static void do_resize()
328 if (config_resize_redraw
&& extensions_sync
&&
329 moveresize_client
->sync_request
&& moveresize_client
->sync_counter
)
333 gint x
, y
, w
, h
, lw
, lh
;
335 /* are we already waiting for the sync counter to catch up? */
336 if (waiting_for_sync
)
339 /* see if it is actually going to resize */
344 client_try_configure(moveresize_client
, &x
, &y
, &w
, &h
,
346 if (w
== moveresize_client
->area
.width
&&
347 h
== moveresize_client
->area
.height
)
352 /* increment the value we're waiting for */
353 ++moveresize_client
->sync_counter_value
;
354 XSyncIntToValue(&val
, moveresize_client
->sync_counter_value
);
356 /* tell the client what we're waiting for */
357 ce
.xclient
.type
= ClientMessage
;
358 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
359 ce
.xclient
.display
= ob_display
;
360 ce
.xclient
.window
= moveresize_client
->window
;
361 ce
.xclient
.format
= 32;
362 ce
.xclient
.data
.l
[0] = prop_atoms
.net_wm_sync_request
;
363 ce
.xclient
.data
.l
[1] = event_curtime
;
364 ce
.xclient
.data
.l
[2] = XSyncValueLow32(val
);
365 ce
.xclient
.data
.l
[3] = XSyncValueHigh32(val
);
366 ce
.xclient
.data
.l
[4] = 0l;
367 XSendEvent(ob_display
, moveresize_client
->window
, FALSE
,
370 waiting_for_sync
= TRUE
;
376 get_resize_position(&x
, &y
, FALSE
);
377 client_configure(moveresize_client
, x
, y
, cur_x
, cur_y
, TRUE
, FALSE
);
380 /* this would be better with a fixed width font ... XXX can do it better
381 if there are 2 text boxes */
382 if (config_resize_popup_show
== 2 || /* == "Always" */
383 (config_resize_popup_show
== 1 && /* == "Nonpixel" */
384 moveresize_client
->size_inc
.width
> 1 &&
385 moveresize_client
->size_inc
.height
> 1))
386 popup_coords(moveresize_client
, "%d x %d",
387 moveresize_client
->logical_size
.width
,
388 moveresize_client
->logical_size
.height
);
391 static void calc_resize(gboolean keyboard
)
395 /* resist_size_* needs the frame size */
396 cur_x
+= moveresize_client
->frame
->size
.left
+
397 moveresize_client
->frame
->size
.right
;
398 cur_y
+= moveresize_client
->frame
->size
.top
+
399 moveresize_client
->frame
->size
.bottom
;
401 if (keyboard
) resist
= KEY_DIST
- 1; /* resist for one key press */
402 else resist
= config_resist_win
;
403 resist_size_windows(moveresize_client
, resist
, &cur_x
, &cur_y
, lockcorner
);
404 if (!keyboard
) resist
= config_resist_edge
;
405 resist_size_monitors(moveresize_client
, resist
, &cur_x
, &cur_y
,lockcorner
);
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
;
413 gboolean
moveresize_event(XEvent
*e
)
415 gboolean used
= FALSE
;
417 g_assert(moveresize_in_progress
);
419 if (e
->type
== ButtonPress
) {
421 start_x
= e
->xbutton
.x_root
;
422 start_y
= e
->xbutton
.y_root
;
423 button
= e
->xbutton
.button
; /* this will end it now */
425 used
= e
->xbutton
.button
== button
;
426 } else if (e
->type
== ButtonRelease
) {
427 if (!button
|| e
->xbutton
.button
== button
) {
428 moveresize_end(FALSE
);
431 } else if (e
->type
== MotionNotify
) {
433 cur_x
= start_cx
+ e
->xmotion
.x_root
- start_x
;
434 cur_y
= start_cy
+ e
->xmotion
.y_root
- start_y
;
437 if (corner
== prop_atoms
.net_wm_moveresize_size_topleft
) {
438 cur_x
= start_cw
- (e
->xmotion
.x_root
- start_x
);
439 cur_y
= start_ch
- (e
->xmotion
.y_root
- start_y
);
440 lockcorner
= OB_CORNER_BOTTOMRIGHT
;
441 } else if (corner
== prop_atoms
.net_wm_moveresize_size_top
) {
443 cur_y
= start_ch
- (e
->xmotion
.y_root
- start_y
);
444 lockcorner
= OB_CORNER_BOTTOMRIGHT
;
445 } else if (corner
== prop_atoms
.net_wm_moveresize_size_topright
) {
446 cur_x
= start_cw
+ (e
->xmotion
.x_root
- start_x
);
447 cur_y
= start_ch
- (e
->xmotion
.y_root
- start_y
);
448 lockcorner
= OB_CORNER_BOTTOMLEFT
;
449 } else if (corner
== prop_atoms
.net_wm_moveresize_size_right
) {
450 cur_x
= start_cw
+ (e
->xmotion
.x_root
- start_x
);
452 lockcorner
= OB_CORNER_BOTTOMLEFT
;
454 prop_atoms
.net_wm_moveresize_size_bottomright
) {
455 cur_x
= start_cw
+ (e
->xmotion
.x_root
- start_x
);
456 cur_y
= start_ch
+ (e
->xmotion
.y_root
- start_y
);
457 lockcorner
= OB_CORNER_TOPLEFT
;
458 } else if (corner
== prop_atoms
.net_wm_moveresize_size_bottom
) {
460 cur_y
= start_ch
+ (e
->xmotion
.y_root
- start_y
);
461 lockcorner
= OB_CORNER_TOPLEFT
;
463 prop_atoms
.net_wm_moveresize_size_bottomleft
) {
464 cur_x
= start_cw
- (e
->xmotion
.x_root
- start_x
);
465 cur_y
= start_ch
+ (e
->xmotion
.y_root
- start_y
);
466 lockcorner
= OB_CORNER_TOPRIGHT
;
467 } else if (corner
== prop_atoms
.net_wm_moveresize_size_left
) {
468 cur_x
= start_cw
- (e
->xmotion
.x_root
- start_x
);
470 lockcorner
= OB_CORNER_TOPRIGHT
;
471 } else if (corner
== prop_atoms
.net_wm_moveresize_size_keyboard
) {
472 cur_x
= start_cw
+ (e
->xmotion
.x_root
- start_x
);
473 cur_y
= start_ch
+ (e
->xmotion
.y_root
- start_y
);
474 lockcorner
= OB_CORNER_TOPLEFT
;
476 g_assert_not_reached();
482 } else if (e
->type
== KeyPress
) {
483 if (e
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
)) {
484 moveresize_end(TRUE
);
486 } else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
487 moveresize_end(FALSE
);
489 } else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
) ||
490 e
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
) ||
491 e
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
) ||
492 e
->xkey
.keycode
== ob_keycode(OB_KEY_UP
))
494 if (corner
== prop_atoms
.net_wm_moveresize_size_keyboard
) {
495 gint dx
= 0, dy
= 0, ox
= cur_x
, oy
= cur_y
;
497 if (e
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
))
498 dx
= MAX(KEY_DIST
, moveresize_client
->size_inc
.width
);
499 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
))
500 dx
= -MAX(KEY_DIST
, moveresize_client
->size_inc
.width
);
501 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
))
502 dy
= MAX(KEY_DIST
, moveresize_client
->size_inc
.height
);
503 else /* if (e->xkey.keycode == ob_keycode(OB_KEY_UP)) */
504 dy
= -MAX(KEY_DIST
, moveresize_client
->size_inc
.height
);
508 XWarpPointer(ob_display
, None
, None
, 0, 0, 0, 0, dx
, dy
);
509 /* steal the motion events this causes */
510 XSync(ob_display
, FALSE
);
513 while (XCheckTypedEvent(ob_display
, MotionNotify
, &ce
));
519 /* because the cursor moves even though the window does
520 not nessesarily (resistance), this adjusts where the curor
521 thinks it started so that it keeps up with where the window
523 start_x
+= dx
- (cur_x
- ox
);
524 start_y
+= dy
- (cur_y
- oy
);
527 } else if (corner
== prop_atoms
.net_wm_moveresize_move_keyboard
) {
528 gint dx
= 0, dy
= 0, ox
= cur_x
, oy
= cur_y
;
529 gint opx
, px
, opy
, py
;
531 if (e
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
))
533 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
))
535 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
))
537 else /* if (e->xkey.keycode == ob_keycode(OB_KEY_UP)) */
542 screen_pointer_pos(&opx
, &opy
);
543 XWarpPointer(ob_display
, None
, None
, 0, 0, 0, 0, dx
, dy
);
544 /* steal the motion events this causes */
545 XSync(ob_display
, FALSE
);
548 while (XCheckTypedEvent(ob_display
, MotionNotify
, &ce
));
550 screen_pointer_pos(&px
, &py
);
554 /* because the cursor moves even though the window does
555 not nessesarily (resistance), this adjusts where the curor
556 thinks it started so that it keeps up with where the window
558 start_x
+= (px
- opx
) - (cur_x
- ox
);
559 start_y
+= (py
- opy
) - (cur_y
- oy
);
566 else if (e
->type
== extensions_sync_event_basep
+ XSyncAlarmNotify
)
568 waiting_for_sync
= FALSE
; /* we got our sync... */
569 do_resize(); /* ...so try resize if there is more change pending */