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_destructor(client_dest
, NULL
);
76 void moveresize_shutdown(gboolean reconfig
)
79 if (moveresize_in_progress
)
80 moveresize_end(FALSE
);
81 client_remove_destructor(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 frame_end_iconify_animation(c
->frame
);
167 moveresize_client
= c
;
168 start_cx
= c
->area
.x
;
169 start_cy
= c
->area
.y
;
170 /* these adjustments for the size_inc make resizing a terminal more
171 friendly. you essentially start the resize in the middle of the
172 increment instead of at 0, so you have to move half an increment
173 either way instead of a full increment one and 1 px the other. and this
174 is one large mother fucking comment. */
175 start_cw
= c
->area
.width
+ c
->size_inc
.width
/ 2;
176 start_ch
= c
->area
.height
+ c
->size_inc
.height
/ 2;
183 have to change start_cx and start_cy if going to do this..
184 if (corner == prop_atoms.net_wm_moveresize_move_keyboard ||
185 corner == prop_atoms.net_wm_moveresize_size_keyboard)
186 XWarpPointer(ob_display, None, c->window, 0, 0, 0, 0,
187 c->area.width / 2, c->area.height / 2);
198 moveresize_in_progress
= TRUE
;
200 if (corner
== prop_atoms
.net_wm_moveresize_size_topleft
)
201 cur
= OB_CURSOR_NORTHWEST
;
202 else if (corner
== prop_atoms
.net_wm_moveresize_size_top
)
203 cur
= OB_CURSOR_NORTH
;
204 else if (corner
== prop_atoms
.net_wm_moveresize_size_topright
)
205 cur
= OB_CURSOR_NORTHEAST
;
206 else if (corner
== prop_atoms
.net_wm_moveresize_size_right
)
207 cur
= OB_CURSOR_EAST
;
208 else if (corner
== prop_atoms
.net_wm_moveresize_size_bottomright
)
209 cur
= OB_CURSOR_SOUTHEAST
;
210 else if (corner
== prop_atoms
.net_wm_moveresize_size_bottom
)
211 cur
= OB_CURSOR_SOUTH
;
212 else if (corner
== prop_atoms
.net_wm_moveresize_size_bottomleft
)
213 cur
= OB_CURSOR_SOUTHWEST
;
214 else if (corner
== prop_atoms
.net_wm_moveresize_size_left
)
215 cur
= OB_CURSOR_WEST
;
216 else if (corner
== prop_atoms
.net_wm_moveresize_size_keyboard
)
217 cur
= OB_CURSOR_SOUTHEAST
;
218 else if (corner
== prop_atoms
.net_wm_moveresize_move
)
219 cur
= OB_CURSOR_MOVE
;
220 else if (corner
== prop_atoms
.net_wm_moveresize_move_keyboard
)
221 cur
= OB_CURSOR_MOVE
;
223 g_assert_not_reached();
226 if (config_resize_redraw
&& !moving
&& extensions_shape
&&
227 moveresize_client
->sync_request
&& moveresize_client
->sync_counter
)
229 /* Initialize values for the resize syncing, and create an alarm for
230 the client's xsync counter */
233 XSyncAlarmAttributes aa
;
235 /* set the counter to an initial value */
236 XSyncIntToValue(&val
, 0);
237 XSyncSetCounter(ob_display
, moveresize_client
->sync_counter
, val
);
239 /* this will be incremented when we tell the client what we're
241 moveresize_client
->sync_counter_value
= 0;
243 /* the next sequence we're waiting for with the alarm */
244 XSyncIntToValue(&val
, 1);
246 /* set an alarm on the counter */
247 aa
.trigger
.counter
= moveresize_client
->sync_counter
;
248 aa
.trigger
.wait_value
= val
;
249 aa
.trigger
.value_type
= XSyncAbsolute
;
250 aa
.trigger
.test_type
= XSyncPositiveTransition
;
252 XSyncIntToValue(&aa
.delta
, 1);
253 moveresize_alarm
= XSyncCreateAlarm(ob_display
,
262 waiting_for_sync
= FALSE
;
266 grab_pointer(TRUE
, FALSE
, cur
);
270 void moveresize_end(gboolean cancel
)
274 grab_keyboard(FALSE
);
275 grab_pointer(FALSE
, FALSE
, OB_CURSOR_NONE
);
280 client_move(moveresize_client
,
281 (cancel
? start_cx
: cur_x
),
282 (cancel
? start_cy
: cur_y
));
285 /* turn off the alarm */
286 if (moveresize_alarm
!= None
) {
287 XSyncDestroyAlarm(ob_display
, moveresize_alarm
);
288 moveresize_alarm
= None
;
292 get_resize_position(&x
, &y
, cancel
);
293 client_configure(moveresize_client
, x
, y
,
294 (cancel
? start_cw
: cur_x
),
295 (cancel
? start_ch
: cur_y
), TRUE
, TRUE
);
298 moveresize_in_progress
= FALSE
;
299 moveresize_client
= NULL
;
302 static void do_move(gboolean keyboard
)
306 if (keyboard
) resist
= KEY_DIST
- 1; /* resist for one key press */
307 else resist
= config_resist_win
;
308 resist_move_windows(moveresize_client
, resist
, &cur_x
, &cur_y
);
309 if (!keyboard
) resist
= config_resist_edge
;
310 resist_move_monitors(moveresize_client
, resist
, &cur_x
, &cur_y
);
312 client_configure(moveresize_client
, cur_x
, cur_y
,
313 moveresize_client
->area
.width
,
314 moveresize_client
->area
.height
, TRUE
, FALSE
);
315 if (config_resize_popup_show
== 2) /* == "Always" */
316 popup_coords(moveresize_client
, "%d x %d",
317 moveresize_client
->frame
->area
.x
,
318 moveresize_client
->frame
->area
.y
);
321 static void do_resize()
324 if (config_resize_redraw
&& extensions_sync
&&
325 moveresize_client
->sync_request
&& moveresize_client
->sync_counter
)
329 gint x
, y
, w
, h
, lw
, lh
;
331 /* are we already waiting for the sync counter to catch up? */
332 if (waiting_for_sync
)
335 /* see if it is actually going to resize */
340 client_try_configure(moveresize_client
, &x
, &y
, &w
, &h
,
342 if (w
== moveresize_client
->area
.width
&&
343 h
== moveresize_client
->area
.height
)
348 /* increment the value we're waiting for */
349 ++moveresize_client
->sync_counter_value
;
350 XSyncIntToValue(&val
, moveresize_client
->sync_counter_value
);
352 /* tell the client what we're waiting for */
353 ce
.xclient
.type
= ClientMessage
;
354 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
355 ce
.xclient
.display
= ob_display
;
356 ce
.xclient
.window
= moveresize_client
->window
;
357 ce
.xclient
.format
= 32;
358 ce
.xclient
.data
.l
[0] = prop_atoms
.net_wm_sync_request
;
359 ce
.xclient
.data
.l
[1] = event_curtime
;
360 ce
.xclient
.data
.l
[2] = XSyncValueLow32(val
);
361 ce
.xclient
.data
.l
[3] = XSyncValueHigh32(val
);
362 ce
.xclient
.data
.l
[4] = 0l;
363 XSendEvent(ob_display
, moveresize_client
->window
, FALSE
,
366 waiting_for_sync
= TRUE
;
372 get_resize_position(&x
, &y
, FALSE
);
373 client_configure(moveresize_client
, x
, y
, cur_x
, cur_y
, TRUE
, FALSE
);
376 /* this would be better with a fixed width font ... XXX can do it better
377 if there are 2 text boxes */
378 if (config_resize_popup_show
== 2 || /* == "Always" */
379 (config_resize_popup_show
== 1 && /* == "Nonpixel" */
380 moveresize_client
->size_inc
.width
> 1 &&
381 moveresize_client
->size_inc
.height
> 1))
382 popup_coords(moveresize_client
, "%d x %d",
383 moveresize_client
->logical_size
.width
,
384 moveresize_client
->logical_size
.height
);
387 static void calc_resize(gboolean keyboard
)
391 /* resist_size_* needs the frame size */
392 cur_x
+= moveresize_client
->frame
->size
.left
+
393 moveresize_client
->frame
->size
.right
;
394 cur_y
+= moveresize_client
->frame
->size
.top
+
395 moveresize_client
->frame
->size
.bottom
;
397 if (keyboard
) resist
= KEY_DIST
- 1; /* resist for one key press */
398 else resist
= config_resist_win
;
399 resist_size_windows(moveresize_client
, resist
, &cur_x
, &cur_y
, lockcorner
);
400 if (!keyboard
) resist
= config_resist_edge
;
401 resist_size_monitors(moveresize_client
, resist
, &cur_x
, &cur_y
,lockcorner
);
403 cur_x
-= moveresize_client
->frame
->size
.left
+
404 moveresize_client
->frame
->size
.right
;
405 cur_y
-= moveresize_client
->frame
->size
.top
+
406 moveresize_client
->frame
->size
.bottom
;
409 gboolean
moveresize_event(XEvent
*e
)
411 gboolean used
= FALSE
;
413 g_assert(moveresize_in_progress
);
415 if (e
->type
== ButtonPress
) {
417 start_x
= e
->xbutton
.x_root
;
418 start_y
= e
->xbutton
.y_root
;
419 button
= e
->xbutton
.button
; /* this will end it now */
421 used
= e
->xbutton
.button
== button
;
422 } else if (e
->type
== ButtonRelease
) {
423 if (!button
|| e
->xbutton
.button
== button
) {
424 moveresize_end(FALSE
);
427 } else if (e
->type
== MotionNotify
) {
429 cur_x
= start_cx
+ e
->xmotion
.x_root
- start_x
;
430 cur_y
= start_cy
+ e
->xmotion
.y_root
- start_y
;
433 if (corner
== prop_atoms
.net_wm_moveresize_size_topleft
) {
434 cur_x
= start_cw
- (e
->xmotion
.x_root
- start_x
);
435 cur_y
= start_ch
- (e
->xmotion
.y_root
- start_y
);
436 lockcorner
= OB_CORNER_BOTTOMRIGHT
;
437 } else if (corner
== prop_atoms
.net_wm_moveresize_size_top
) {
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_topright
) {
442 cur_x
= start_cw
+ (e
->xmotion
.x_root
- start_x
);
443 cur_y
= start_ch
- (e
->xmotion
.y_root
- start_y
);
444 lockcorner
= OB_CORNER_BOTTOMLEFT
;
445 } else if (corner
== prop_atoms
.net_wm_moveresize_size_right
) {
446 cur_x
= start_cw
+ (e
->xmotion
.x_root
- start_x
);
448 lockcorner
= OB_CORNER_BOTTOMLEFT
;
450 prop_atoms
.net_wm_moveresize_size_bottomright
) {
451 cur_x
= start_cw
+ (e
->xmotion
.x_root
- start_x
);
452 cur_y
= start_ch
+ (e
->xmotion
.y_root
- start_y
);
453 lockcorner
= OB_CORNER_TOPLEFT
;
454 } else if (corner
== prop_atoms
.net_wm_moveresize_size_bottom
) {
456 cur_y
= start_ch
+ (e
->xmotion
.y_root
- start_y
);
457 lockcorner
= OB_CORNER_TOPLEFT
;
459 prop_atoms
.net_wm_moveresize_size_bottomleft
) {
460 cur_x
= start_cw
- (e
->xmotion
.x_root
- start_x
);
461 cur_y
= start_ch
+ (e
->xmotion
.y_root
- start_y
);
462 lockcorner
= OB_CORNER_TOPRIGHT
;
463 } else if (corner
== prop_atoms
.net_wm_moveresize_size_left
) {
464 cur_x
= start_cw
- (e
->xmotion
.x_root
- start_x
);
466 lockcorner
= OB_CORNER_TOPRIGHT
;
467 } else if (corner
== prop_atoms
.net_wm_moveresize_size_keyboard
) {
468 cur_x
= start_cw
+ (e
->xmotion
.x_root
- start_x
);
469 cur_y
= start_ch
+ (e
->xmotion
.y_root
- start_y
);
470 lockcorner
= OB_CORNER_TOPLEFT
;
472 g_assert_not_reached();
478 } else if (e
->type
== KeyPress
) {
479 if (e
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
)) {
480 moveresize_end(TRUE
);
482 } else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
483 moveresize_end(FALSE
);
485 } else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
) ||
486 e
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
) ||
487 e
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
) ||
488 e
->xkey
.keycode
== ob_keycode(OB_KEY_UP
))
490 if (corner
== prop_atoms
.net_wm_moveresize_size_keyboard
) {
491 gint dx
= 0, dy
= 0, ox
= cur_x
, oy
= cur_y
;
493 if (e
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
))
494 dx
= MAX(KEY_DIST
, moveresize_client
->size_inc
.width
);
495 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
))
496 dx
= -MAX(KEY_DIST
, moveresize_client
->size_inc
.width
);
497 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
))
498 dy
= MAX(KEY_DIST
, moveresize_client
->size_inc
.height
);
499 else /* if (e->xkey.keycode == ob_keycode(OB_KEY_UP)) */
500 dy
= -MAX(KEY_DIST
, moveresize_client
->size_inc
.height
);
504 XWarpPointer(ob_display
, None
, None
, 0, 0, 0, 0, dx
, dy
);
505 /* steal the motion events this causes */
506 XSync(ob_display
, FALSE
);
509 while (XCheckTypedEvent(ob_display
, MotionNotify
, &ce
));
515 /* because the cursor moves even though the window does
516 not nessesarily (resistance), this adjusts where the curor
517 thinks it started so that it keeps up with where the window
519 start_x
+= dx
- (cur_x
- ox
);
520 start_y
+= dy
- (cur_y
- oy
);
523 } else if (corner
== prop_atoms
.net_wm_moveresize_move_keyboard
) {
524 gint dx
= 0, dy
= 0, ox
= cur_x
, oy
= cur_y
;
525 gint opx
, px
, opy
, py
;
527 if (e
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
))
529 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
))
531 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
))
533 else /* if (e->xkey.keycode == ob_keycode(OB_KEY_UP)) */
538 screen_pointer_pos(&opx
, &opy
);
539 XWarpPointer(ob_display
, None
, None
, 0, 0, 0, 0, dx
, dy
);
540 /* steal the motion events this causes */
541 XSync(ob_display
, FALSE
);
544 while (XCheckTypedEvent(ob_display
, MotionNotify
, &ce
));
546 screen_pointer_pos(&px
, &py
);
550 /* because the cursor moves even though the window does
551 not nessesarily (resistance), this adjusts where the curor
552 thinks it started so that it keeps up with where the window
554 start_x
+= (px
- opx
) - (cur_x
- ox
);
555 start_y
+= (py
- opy
) - (cur_y
- oy
);
562 else if (e
->type
== extensions_sync_event_basep
+ XSyncAlarmNotify
)
564 waiting_for_sync
= FALSE
; /* we got our sync... */
565 do_resize(); /* ...so try resize if there is more change pending */