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 gboolean moveresize_in_progress
= FALSE
;
41 ObClient
*moveresize_client
= NULL
;
43 XSyncAlarm moveresize_alarm
= None
;
46 static gboolean moving
= FALSE
; /* TRUE - moving, FALSE - resizing */
48 static gint start_x
, start_y
, start_cx
, start_cy
, start_cw
, start_ch
;
49 static gint cur_x
, cur_y
;
51 static guint32 corner
;
52 static ObCorner lockcorner
;
54 static gboolean waiting_for_sync
;
57 static ObPopup
*popup
= NULL
;
59 static void client_dest(ObClient
*client
, gpointer data
)
61 if (moveresize_client
== client
)
65 void moveresize_startup(gboolean reconfig
)
67 popup
= popup_new(FALSE
);
70 client_add_destructor(client_dest
, NULL
);
73 void moveresize_shutdown(gboolean reconfig
)
76 if (moveresize_in_progress
)
77 moveresize_end(FALSE
);
78 client_remove_destructor(client_dest
);
85 static void get_resize_position(gint
*x
, gint
*y
, gboolean cancel
)
90 *x
= moveresize_client
->frame
->area
.x
;
91 *y
= moveresize_client
->frame
->area
.y
;
101 /* see how much it is actually going to resize */
103 gint cx
= *x
, cy
= *y
;
104 frame_frame_gravity(moveresize_client
->frame
, &cx
, &cy
, w
, h
);
105 client_try_configure(moveresize_client
, &cx
, &cy
, &w
, &h
,
108 dw
= w
- moveresize_client
->area
.width
;
109 dh
= h
- moveresize_client
->area
.height
;
111 switch (lockcorner
) {
112 case OB_CORNER_TOPLEFT
:
114 case OB_CORNER_TOPRIGHT
:
117 case OB_CORNER_BOTTOMLEFT
:
120 case OB_CORNER_BOTTOMRIGHT
:
126 frame_frame_gravity(moveresize_client
->frame
, x
, y
, w
, h
);
129 static void popup_coords(ObClient
*c
, const gchar
*format
, gint a
, gint b
)
133 text
= g_strdup_printf(format
, a
, b
);
134 if (config_resize_popup_pos
== 1) /* == "Top" */
135 popup_position(popup
, SouthGravity
,
137 + c
->frame
->area
.width
/2,
138 c
->frame
->area
.y
- ob_rr_theme
->fbwidth
);
139 else /* == "Center" */
140 popup_position(popup
, CenterGravity
,
141 c
->frame
->area
.x
+ c
->frame
->size
.left
+
143 c
->frame
->area
.y
+ c
->frame
->size
.top
+
145 popup_show(popup
, text
);
149 void moveresize_start(ObClient
*c
, gint x
, gint y
, guint b
, guint32 cnr
)
153 moving
= (cnr
== prop_atoms
.net_wm_moveresize_move
||
154 cnr
== prop_atoms
.net_wm_moveresize_move_keyboard
);
156 if (moveresize_in_progress
|| !c
->frame
->visible
||
158 (c
->functions
& OB_CLIENT_FUNC_MOVE
) :
159 (c
->functions
& OB_CLIENT_FUNC_RESIZE
)))
162 moveresize_client
= c
;
163 start_cx
= c
->area
.x
;
164 start_cy
= c
->area
.y
;
165 /* these adjustments for the size_inc make resizing a terminal more
166 friendly. you essentially start the resize in the middle of the
167 increment instead of at 0, so you have to move half an increment
168 either way instead of a full increment one and 1 px the other. and this
169 is one large mother fucking comment. */
170 start_cw
= c
->area
.width
+ c
->size_inc
.width
/ 2;
171 start_ch
= c
->area
.height
+ c
->size_inc
.height
/ 2;
178 have to change start_cx and start_cy if going to do this..
179 if (corner == prop_atoms.net_wm_moveresize_move_keyboard ||
180 corner == prop_atoms.net_wm_moveresize_size_keyboard)
181 XWarpPointer(ob_display, None, c->window, 0, 0, 0, 0,
182 c->area.width / 2, c->area.height / 2);
193 moveresize_in_progress
= TRUE
;
195 if (corner
== prop_atoms
.net_wm_moveresize_size_topleft
)
196 cur
= OB_CURSOR_NORTHWEST
;
197 else if (corner
== prop_atoms
.net_wm_moveresize_size_top
)
198 cur
= OB_CURSOR_NORTH
;
199 else if (corner
== prop_atoms
.net_wm_moveresize_size_topright
)
200 cur
= OB_CURSOR_NORTHEAST
;
201 else if (corner
== prop_atoms
.net_wm_moveresize_size_right
)
202 cur
= OB_CURSOR_EAST
;
203 else if (corner
== prop_atoms
.net_wm_moveresize_size_bottomright
)
204 cur
= OB_CURSOR_SOUTHEAST
;
205 else if (corner
== prop_atoms
.net_wm_moveresize_size_bottom
)
206 cur
= OB_CURSOR_SOUTH
;
207 else if (corner
== prop_atoms
.net_wm_moveresize_size_bottomleft
)
208 cur
= OB_CURSOR_SOUTHWEST
;
209 else if (corner
== prop_atoms
.net_wm_moveresize_size_left
)
210 cur
= OB_CURSOR_WEST
;
211 else if (corner
== prop_atoms
.net_wm_moveresize_size_keyboard
)
212 cur
= OB_CURSOR_SOUTHEAST
;
213 else if (corner
== prop_atoms
.net_wm_moveresize_move
)
214 cur
= OB_CURSOR_MOVE
;
215 else if (corner
== prop_atoms
.net_wm_moveresize_move_keyboard
)
216 cur
= OB_CURSOR_MOVE
;
218 g_assert_not_reached();
221 if (config_resize_redraw
&& !moving
&& extensions_shape
&&
222 moveresize_client
->sync_request
&& moveresize_client
->sync_counter
)
224 /* Initialize values for the resize syncing, and create an alarm for
225 the client's xsync counter */
228 XSyncAlarmAttributes aa
;
230 /* set the counter to an initial value */
231 XSyncIntToValue(&val
, 0);
232 XSyncSetCounter(ob_display
, moveresize_client
->sync_counter
, val
);
234 /* this will be incremented when we tell the client what we're
236 moveresize_client
->sync_counter_value
= 0;
238 /* the next sequence we're waiting for with the alarm */
239 XSyncIntToValue(&val
, 1);
241 /* set an alarm on the counter */
242 aa
.trigger
.counter
= moveresize_client
->sync_counter
;
243 aa
.trigger
.wait_value
= val
;
244 aa
.trigger
.value_type
= XSyncAbsolute
;
245 aa
.trigger
.test_type
= XSyncPositiveTransition
;
247 XSyncIntToValue(&aa
.delta
, 1);
248 moveresize_alarm
= XSyncCreateAlarm(ob_display
,
257 waiting_for_sync
= FALSE
;
261 grab_pointer(TRUE
, FALSE
, cur
);
265 void moveresize_end(gboolean cancel
)
269 grab_keyboard(FALSE
);
270 grab_pointer(FALSE
, FALSE
, OB_CURSOR_NONE
);
275 client_move(moveresize_client
,
276 (cancel
? start_cx
: cur_x
),
277 (cancel
? start_cy
: cur_y
));
280 /* turn off the alarm */
281 if (moveresize_alarm
!= None
) {
282 XSyncDestroyAlarm(ob_display
, moveresize_alarm
);
283 moveresize_alarm
= None
;
287 get_resize_position(&x
, &y
, cancel
);
288 client_configure(moveresize_client
, x
, y
,
289 (cancel
? start_cw
: cur_x
),
290 (cancel
? start_ch
: cur_y
), TRUE
, TRUE
);
293 moveresize_in_progress
= FALSE
;
294 moveresize_client
= NULL
;
297 static void do_move(gboolean resist
)
300 resist_move_windows(moveresize_client
, &cur_x
, &cur_y
);
301 resist_move_monitors(moveresize_client
, &cur_x
, &cur_y
);
304 client_configure(moveresize_client
, cur_x
, cur_y
,
305 moveresize_client
->area
.width
,
306 moveresize_client
->area
.height
, TRUE
, FALSE
);
307 if (config_resize_popup_show
== 2) /* == "Always" */
308 popup_coords(moveresize_client
, "%d x %d",
309 moveresize_client
->frame
->area
.x
,
310 moveresize_client
->frame
->area
.y
);
313 static void do_resize()
316 if (config_resize_redraw
&& extensions_sync
&&
317 moveresize_client
->sync_request
&& moveresize_client
->sync_counter
)
321 gint x
, y
, w
, h
, lw
, lh
;
323 /* are we already waiting for the sync counter to catch up? */
324 if (waiting_for_sync
)
327 /* see if it is actually going to resize */
332 client_try_configure(moveresize_client
, &x
, &y
, &w
, &h
,
334 if (w
== moveresize_client
->area
.width
&&
335 h
== moveresize_client
->area
.height
)
340 /* increment the value we're waiting for */
341 ++moveresize_client
->sync_counter_value
;
342 XSyncIntToValue(&val
, moveresize_client
->sync_counter_value
);
344 /* tell the client what we're waiting for */
345 ce
.xclient
.type
= ClientMessage
;
346 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
347 ce
.xclient
.display
= ob_display
;
348 ce
.xclient
.window
= moveresize_client
->window
;
349 ce
.xclient
.format
= 32;
350 ce
.xclient
.data
.l
[0] = prop_atoms
.net_wm_sync_request
;
351 ce
.xclient
.data
.l
[1] = event_curtime
;
352 ce
.xclient
.data
.l
[2] = XSyncValueLow32(val
);
353 ce
.xclient
.data
.l
[3] = XSyncValueHigh32(val
);
354 ce
.xclient
.data
.l
[4] = 0l;
355 XSendEvent(ob_display
, moveresize_client
->window
, FALSE
,
358 waiting_for_sync
= TRUE
;
364 get_resize_position(&x
, &y
, FALSE
);
365 client_configure(moveresize_client
, x
, y
, cur_x
, cur_y
, TRUE
, FALSE
);
368 /* this would be better with a fixed width font ... XXX can do it better
369 if there are 2 text boxes */
370 if (config_resize_popup_show
== 2 || /* == "Always" */
371 (config_resize_popup_show
== 1 && /* == "Nonpixel" */
372 (moveresize_client
->size_inc
.width
> 1 ||
373 moveresize_client
->size_inc
.height
> 1))
375 popup_coords(moveresize_client
, "%d x %d",
376 moveresize_client
->logical_size
.width
,
377 moveresize_client
->logical_size
.height
);
380 static void calc_resize(gboolean resist
)
382 /* resist_size_* needs the frame size */
383 cur_x
+= moveresize_client
->frame
->size
.left
+
384 moveresize_client
->frame
->size
.right
;
385 cur_y
+= moveresize_client
->frame
->size
.top
+
386 moveresize_client
->frame
->size
.bottom
;
389 resist_size_windows(moveresize_client
, &cur_x
, &cur_y
, lockcorner
);
390 resist_size_monitors(moveresize_client
, &cur_x
, &cur_y
, lockcorner
);
393 cur_x
-= moveresize_client
->frame
->size
.left
+
394 moveresize_client
->frame
->size
.right
;
395 cur_y
-= moveresize_client
->frame
->size
.top
+
396 moveresize_client
->frame
->size
.bottom
;
399 void moveresize_event(XEvent
*e
)
401 g_assert(moveresize_in_progress
);
403 if (e
->type
== ButtonPress
) {
405 start_x
= e
->xbutton
.x_root
;
406 start_y
= e
->xbutton
.y_root
;
407 button
= e
->xbutton
.button
; /* this will end it now */
409 } else if (e
->type
== ButtonRelease
) {
410 if (!button
|| e
->xbutton
.button
== button
) {
411 moveresize_end(FALSE
);
413 } else if (e
->type
== MotionNotify
) {
415 cur_x
= start_cx
+ e
->xmotion
.x_root
- start_x
;
416 cur_y
= start_cy
+ e
->xmotion
.y_root
- start_y
;
419 if (corner
== prop_atoms
.net_wm_moveresize_size_topleft
) {
420 cur_x
= start_cw
- (e
->xmotion
.x_root
- start_x
);
421 cur_y
= start_ch
- (e
->xmotion
.y_root
- start_y
);
422 lockcorner
= OB_CORNER_BOTTOMRIGHT
;
423 } else if (corner
== prop_atoms
.net_wm_moveresize_size_top
) {
425 cur_y
= start_ch
- (e
->xmotion
.y_root
- start_y
);
426 lockcorner
= OB_CORNER_BOTTOMRIGHT
;
427 } else if (corner
== prop_atoms
.net_wm_moveresize_size_topright
) {
428 cur_x
= start_cw
+ (e
->xmotion
.x_root
- start_x
);
429 cur_y
= start_ch
- (e
->xmotion
.y_root
- start_y
);
430 lockcorner
= OB_CORNER_BOTTOMLEFT
;
431 } else if (corner
== prop_atoms
.net_wm_moveresize_size_right
) {
432 cur_x
= start_cw
+ (e
->xmotion
.x_root
- start_x
);
434 lockcorner
= OB_CORNER_BOTTOMLEFT
;
436 prop_atoms
.net_wm_moveresize_size_bottomright
) {
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_TOPLEFT
;
440 } else if (corner
== prop_atoms
.net_wm_moveresize_size_bottom
) {
442 cur_y
= start_ch
+ (e
->xmotion
.y_root
- start_y
);
443 lockcorner
= OB_CORNER_TOPLEFT
;
445 prop_atoms
.net_wm_moveresize_size_bottomleft
) {
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_TOPRIGHT
;
449 } else if (corner
== prop_atoms
.net_wm_moveresize_size_left
) {
450 cur_x
= start_cw
- (e
->xmotion
.x_root
- start_x
);
452 lockcorner
= OB_CORNER_TOPRIGHT
;
453 } else if (corner
== prop_atoms
.net_wm_moveresize_size_keyboard
) {
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
;
458 g_assert_not_reached();
463 } else if (e
->type
== KeyPress
) {
464 if (e
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
))
465 moveresize_end(TRUE
);
466 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
))
467 moveresize_end(FALSE
);
469 if (corner
== prop_atoms
.net_wm_moveresize_size_keyboard
) {
470 gint dx
= 0, dy
= 0, ox
= cur_x
, oy
= cur_y
;
472 if (e
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
))
473 dx
= MAX(4, moveresize_client
->size_inc
.width
);
474 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
))
475 dx
= -MAX(4, moveresize_client
->size_inc
.width
);
476 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
))
477 dy
= MAX(4, moveresize_client
->size_inc
.height
);
478 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_UP
))
479 dy
= -MAX(4, moveresize_client
->size_inc
.height
);
485 XWarpPointer(ob_display
, None
, None
, 0, 0, 0, 0, dx
, dy
);
486 /* steal the motion events this causes */
487 XSync(ob_display
, FALSE
);
490 while (XCheckTypedEvent(ob_display
, MotionNotify
, &ce
));
495 /* because the cursor moves even though the window does
496 not nessesarily (resistance), this adjusts where the curor
497 thinks it started so that it keeps up with where the window
499 start_x
+= dx
- (cur_x
- ox
);
500 start_y
+= dy
- (cur_y
- oy
);
501 } else if (corner
== prop_atoms
.net_wm_moveresize_move_keyboard
) {
502 gint dx
= 0, dy
= 0, ox
= cur_x
, oy
= cur_y
;
503 gint opx
, px
, opy
, py
;
505 if (e
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
))
507 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
))
509 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
))
511 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_UP
))
518 screen_pointer_pos(&opx
, &opy
);
519 XWarpPointer(ob_display
, None
, None
, 0, 0, 0, 0, dx
, dy
);
520 /* steal the motion events this causes */
521 XSync(ob_display
, FALSE
);
524 while (XCheckTypedEvent(ob_display
, MotionNotify
, &ce
));
526 screen_pointer_pos(&px
, &py
);
530 /* because the cursor moves even though the window does
531 not nessesarily (resistance), this adjusts where the curor
532 thinks it started so that it keeps up with where the window
534 start_x
+= (px
- opx
) - (cur_x
- ox
);
535 start_y
+= (py
- opy
) - (cur_y
- oy
);
540 else if (e
->type
== extensions_sync_event_basep
+ XSyncAlarmNotify
)
542 waiting_for_sync
= FALSE
; /* we got our sync... */
543 do_resize(); /* ...so try resize if there is more change pending */