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 (!grab_pointer(TRUE
, FALSE
, cur
))
167 if (!grab_keyboard(TRUE
)) {
168 grab_pointer(FALSE
, FALSE
, OB_CURSOR_NONE
);
172 frame_end_iconify_animation(c
->frame
);
174 moveresize_client
= c
;
175 start_cx
= c
->area
.x
;
176 start_cy
= c
->area
.y
;
177 /* these adjustments for the size_inc make resizing a terminal more
178 friendly. you essentially start the resize in the middle of the
179 increment instead of at 0, so you have to move half an increment
180 either way instead of a full increment one and 1 px the other. and this
181 is one large mother fucking comment. */
182 start_cw
= c
->area
.width
+ c
->size_inc
.width
/ 2;
183 start_ch
= c
->area
.height
+ c
->size_inc
.height
/ 2;
190 have to change start_cx and start_cy if going to do this..
191 if (corner == prop_atoms.net_wm_moveresize_move_keyboard ||
192 corner == prop_atoms.net_wm_moveresize_size_keyboard)
193 XWarpPointer(ob_display, None, c->window, 0, 0, 0, 0,
194 c->area.width / 2, c->area.height / 2);
205 moveresize_in_progress
= TRUE
;
207 if (corner
== prop_atoms
.net_wm_moveresize_size_topleft
)
208 cur
= OB_CURSOR_NORTHWEST
;
209 else if (corner
== prop_atoms
.net_wm_moveresize_size_top
)
210 cur
= OB_CURSOR_NORTH
;
211 else if (corner
== prop_atoms
.net_wm_moveresize_size_topright
)
212 cur
= OB_CURSOR_NORTHEAST
;
213 else if (corner
== prop_atoms
.net_wm_moveresize_size_right
)
214 cur
= OB_CURSOR_EAST
;
215 else if (corner
== prop_atoms
.net_wm_moveresize_size_bottomright
)
216 cur
= OB_CURSOR_SOUTHEAST
;
217 else if (corner
== prop_atoms
.net_wm_moveresize_size_bottom
)
218 cur
= OB_CURSOR_SOUTH
;
219 else if (corner
== prop_atoms
.net_wm_moveresize_size_bottomleft
)
220 cur
= OB_CURSOR_SOUTHWEST
;
221 else if (corner
== prop_atoms
.net_wm_moveresize_size_left
)
222 cur
= OB_CURSOR_WEST
;
223 else if (corner
== prop_atoms
.net_wm_moveresize_size_keyboard
)
224 cur
= OB_CURSOR_SOUTHEAST
;
225 else if (corner
== prop_atoms
.net_wm_moveresize_move
)
226 cur
= OB_CURSOR_MOVE
;
227 else if (corner
== prop_atoms
.net_wm_moveresize_move_keyboard
)
228 cur
= OB_CURSOR_MOVE
;
230 g_assert_not_reached();
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 */