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 frame_end_iconify_animation(c
->frame
);
164 moveresize_client
= c
;
165 start_cx
= c
->area
.x
;
166 start_cy
= c
->area
.y
;
167 /* these adjustments for the size_inc make resizing a terminal more
168 friendly. you essentially start the resize in the middle of the
169 increment instead of at 0, so you have to move half an increment
170 either way instead of a full increment one and 1 px the other. and this
171 is one large mother fucking comment. */
172 start_cw
= c
->area
.width
+ c
->size_inc
.width
/ 2;
173 start_ch
= c
->area
.height
+ c
->size_inc
.height
/ 2;
180 have to change start_cx and start_cy if going to do this..
181 if (corner == prop_atoms.net_wm_moveresize_move_keyboard ||
182 corner == prop_atoms.net_wm_moveresize_size_keyboard)
183 XWarpPointer(ob_display, None, c->window, 0, 0, 0, 0,
184 c->area.width / 2, c->area.height / 2);
195 moveresize_in_progress
= TRUE
;
197 if (corner
== prop_atoms
.net_wm_moveresize_size_topleft
)
198 cur
= OB_CURSOR_NORTHWEST
;
199 else if (corner
== prop_atoms
.net_wm_moveresize_size_top
)
200 cur
= OB_CURSOR_NORTH
;
201 else if (corner
== prop_atoms
.net_wm_moveresize_size_topright
)
202 cur
= OB_CURSOR_NORTHEAST
;
203 else if (corner
== prop_atoms
.net_wm_moveresize_size_right
)
204 cur
= OB_CURSOR_EAST
;
205 else if (corner
== prop_atoms
.net_wm_moveresize_size_bottomright
)
206 cur
= OB_CURSOR_SOUTHEAST
;
207 else if (corner
== prop_atoms
.net_wm_moveresize_size_bottom
)
208 cur
= OB_CURSOR_SOUTH
;
209 else if (corner
== prop_atoms
.net_wm_moveresize_size_bottomleft
)
210 cur
= OB_CURSOR_SOUTHWEST
;
211 else if (corner
== prop_atoms
.net_wm_moveresize_size_left
)
212 cur
= OB_CURSOR_WEST
;
213 else if (corner
== prop_atoms
.net_wm_moveresize_size_keyboard
)
214 cur
= OB_CURSOR_SOUTHEAST
;
215 else if (corner
== prop_atoms
.net_wm_moveresize_move
)
216 cur
= OB_CURSOR_MOVE
;
217 else if (corner
== prop_atoms
.net_wm_moveresize_move_keyboard
)
218 cur
= OB_CURSOR_MOVE
;
220 g_assert_not_reached();
223 if (config_resize_redraw
&& !moving
&& extensions_shape
&&
224 moveresize_client
->sync_request
&& moveresize_client
->sync_counter
)
226 /* Initialize values for the resize syncing, and create an alarm for
227 the client's xsync counter */
230 XSyncAlarmAttributes aa
;
232 /* set the counter to an initial value */
233 XSyncIntToValue(&val
, 0);
234 XSyncSetCounter(ob_display
, moveresize_client
->sync_counter
, val
);
236 /* this will be incremented when we tell the client what we're
238 moveresize_client
->sync_counter_value
= 0;
240 /* the next sequence we're waiting for with the alarm */
241 XSyncIntToValue(&val
, 1);
243 /* set an alarm on the counter */
244 aa
.trigger
.counter
= moveresize_client
->sync_counter
;
245 aa
.trigger
.wait_value
= val
;
246 aa
.trigger
.value_type
= XSyncAbsolute
;
247 aa
.trigger
.test_type
= XSyncPositiveTransition
;
249 XSyncIntToValue(&aa
.delta
, 1);
250 moveresize_alarm
= XSyncCreateAlarm(ob_display
,
259 waiting_for_sync
= FALSE
;
263 grab_pointer(TRUE
, FALSE
, cur
);
267 void moveresize_end(gboolean cancel
)
271 grab_keyboard(FALSE
);
272 grab_pointer(FALSE
, FALSE
, OB_CURSOR_NONE
);
277 client_move(moveresize_client
,
278 (cancel
? start_cx
: cur_x
),
279 (cancel
? start_cy
: cur_y
));
282 /* turn off the alarm */
283 if (moveresize_alarm
!= None
) {
284 XSyncDestroyAlarm(ob_display
, moveresize_alarm
);
285 moveresize_alarm
= None
;
289 get_resize_position(&x
, &y
, cancel
);
290 client_configure(moveresize_client
, x
, y
,
291 (cancel
? start_cw
: cur_x
),
292 (cancel
? start_ch
: cur_y
), TRUE
, TRUE
);
295 moveresize_in_progress
= FALSE
;
296 moveresize_client
= NULL
;
299 static void do_move(gboolean resist
)
302 resist_move_windows(moveresize_client
, &cur_x
, &cur_y
);
303 resist_move_monitors(moveresize_client
, &cur_x
, &cur_y
);
306 client_configure(moveresize_client
, cur_x
, cur_y
,
307 moveresize_client
->area
.width
,
308 moveresize_client
->area
.height
, TRUE
, FALSE
);
309 if (config_resize_popup_show
== 2) /* == "Always" */
310 popup_coords(moveresize_client
, "%d x %d",
311 moveresize_client
->frame
->area
.x
,
312 moveresize_client
->frame
->area
.y
);
315 static void do_resize()
318 if (config_resize_redraw
&& extensions_sync
&&
319 moveresize_client
->sync_request
&& moveresize_client
->sync_counter
)
323 gint x
, y
, w
, h
, lw
, lh
;
325 /* are we already waiting for the sync counter to catch up? */
326 if (waiting_for_sync
)
329 /* see if it is actually going to resize */
334 client_try_configure(moveresize_client
, &x
, &y
, &w
, &h
,
336 if (w
== moveresize_client
->area
.width
&&
337 h
== moveresize_client
->area
.height
)
342 /* increment the value we're waiting for */
343 ++moveresize_client
->sync_counter_value
;
344 XSyncIntToValue(&val
, moveresize_client
->sync_counter_value
);
346 /* tell the client what we're waiting for */
347 ce
.xclient
.type
= ClientMessage
;
348 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
349 ce
.xclient
.display
= ob_display
;
350 ce
.xclient
.window
= moveresize_client
->window
;
351 ce
.xclient
.format
= 32;
352 ce
.xclient
.data
.l
[0] = prop_atoms
.net_wm_sync_request
;
353 ce
.xclient
.data
.l
[1] = event_curtime
;
354 ce
.xclient
.data
.l
[2] = XSyncValueLow32(val
);
355 ce
.xclient
.data
.l
[3] = XSyncValueHigh32(val
);
356 ce
.xclient
.data
.l
[4] = 0l;
357 XSendEvent(ob_display
, moveresize_client
->window
, FALSE
,
360 waiting_for_sync
= TRUE
;
366 get_resize_position(&x
, &y
, FALSE
);
367 client_configure(moveresize_client
, x
, y
, cur_x
, cur_y
, TRUE
, FALSE
);
370 /* this would be better with a fixed width font ... XXX can do it better
371 if there are 2 text boxes */
372 if (config_resize_popup_show
== 2 || /* == "Always" */
373 (config_resize_popup_show
== 1 && /* == "Nonpixel" */
374 moveresize_client
->size_inc
.width
> 1 &&
375 moveresize_client
->size_inc
.height
> 1))
376 popup_coords(moveresize_client
, "%d x %d",
377 moveresize_client
->logical_size
.width
,
378 moveresize_client
->logical_size
.height
);
381 static void calc_resize(gboolean resist
)
383 /* resist_size_* needs the frame size */
384 cur_x
+= moveresize_client
->frame
->size
.left
+
385 moveresize_client
->frame
->size
.right
;
386 cur_y
+= moveresize_client
->frame
->size
.top
+
387 moveresize_client
->frame
->size
.bottom
;
390 resist_size_windows(moveresize_client
, &cur_x
, &cur_y
, lockcorner
);
391 resist_size_monitors(moveresize_client
, &cur_x
, &cur_y
, lockcorner
);
394 cur_x
-= moveresize_client
->frame
->size
.left
+
395 moveresize_client
->frame
->size
.right
;
396 cur_y
-= moveresize_client
->frame
->size
.top
+
397 moveresize_client
->frame
->size
.bottom
;
400 gboolean
moveresize_event(XEvent
*e
)
402 gboolean used
= FALSE
;
404 g_assert(moveresize_in_progress
);
406 if (e
->type
== ButtonPress
) {
408 start_x
= e
->xbutton
.x_root
;
409 start_y
= e
->xbutton
.y_root
;
410 button
= e
->xbutton
.button
; /* this will end it now */
412 used
= e
->xbutton
.button
== button
;
413 } else if (e
->type
== ButtonRelease
) {
414 if (!button
|| e
->xbutton
.button
== button
) {
415 moveresize_end(FALSE
);
418 } else if (e
->type
== MotionNotify
) {
420 cur_x
= start_cx
+ e
->xmotion
.x_root
- start_x
;
421 cur_y
= start_cy
+ e
->xmotion
.y_root
- start_y
;
424 if (corner
== prop_atoms
.net_wm_moveresize_size_topleft
) {
425 cur_x
= start_cw
- (e
->xmotion
.x_root
- start_x
);
426 cur_y
= start_ch
- (e
->xmotion
.y_root
- start_y
);
427 lockcorner
= OB_CORNER_BOTTOMRIGHT
;
428 } else if (corner
== prop_atoms
.net_wm_moveresize_size_top
) {
430 cur_y
= start_ch
- (e
->xmotion
.y_root
- start_y
);
431 lockcorner
= OB_CORNER_BOTTOMRIGHT
;
432 } else if (corner
== prop_atoms
.net_wm_moveresize_size_topright
) {
433 cur_x
= start_cw
+ (e
->xmotion
.x_root
- start_x
);
434 cur_y
= start_ch
- (e
->xmotion
.y_root
- start_y
);
435 lockcorner
= OB_CORNER_BOTTOMLEFT
;
436 } else if (corner
== prop_atoms
.net_wm_moveresize_size_right
) {
437 cur_x
= start_cw
+ (e
->xmotion
.x_root
- start_x
);
439 lockcorner
= OB_CORNER_BOTTOMLEFT
;
441 prop_atoms
.net_wm_moveresize_size_bottomright
) {
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_TOPLEFT
;
445 } else if (corner
== prop_atoms
.net_wm_moveresize_size_bottom
) {
447 cur_y
= start_ch
+ (e
->xmotion
.y_root
- start_y
);
448 lockcorner
= OB_CORNER_TOPLEFT
;
450 prop_atoms
.net_wm_moveresize_size_bottomleft
) {
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_TOPRIGHT
;
454 } else if (corner
== prop_atoms
.net_wm_moveresize_size_left
) {
455 cur_x
= start_cw
- (e
->xmotion
.x_root
- start_x
);
457 lockcorner
= OB_CORNER_TOPRIGHT
;
458 } else if (corner
== prop_atoms
.net_wm_moveresize_size_keyboard
) {
459 cur_x
= start_cw
+ (e
->xmotion
.x_root
- start_x
);
460 cur_y
= start_ch
+ (e
->xmotion
.y_root
- start_y
);
461 lockcorner
= OB_CORNER_TOPLEFT
;
463 g_assert_not_reached();
469 } else if (e
->type
== KeyPress
) {
470 if (e
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
)) {
471 moveresize_end(TRUE
);
473 } else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
474 moveresize_end(FALSE
);
476 } else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
) ||
477 e
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
) ||
478 e
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
) ||
479 e
->xkey
.keycode
== ob_keycode(OB_KEY_UP
))
481 if (corner
== prop_atoms
.net_wm_moveresize_size_keyboard
) {
482 gint dx
= 0, dy
= 0, ox
= cur_x
, oy
= cur_y
;
484 if (e
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
))
485 dx
= MAX(4, moveresize_client
->size_inc
.width
);
486 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
))
487 dx
= -MAX(4, moveresize_client
->size_inc
.width
);
488 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
))
489 dy
= MAX(4, moveresize_client
->size_inc
.height
);
490 else /* if (e->xkey.keycode == ob_keycode(OB_KEY_UP)) */
491 dy
= -MAX(4, moveresize_client
->size_inc
.height
);
495 XWarpPointer(ob_display
, None
, None
, 0, 0, 0, 0, dx
, dy
);
496 /* steal the motion events this causes */
497 XSync(ob_display
, FALSE
);
500 while (XCheckTypedEvent(ob_display
, MotionNotify
, &ce
));
505 /* because the cursor moves even though the window does
506 not nessesarily (resistance), this adjusts where the curor
507 thinks it started so that it keeps up with where the window
509 start_x
+= dx
- (cur_x
- ox
);
510 start_y
+= dy
- (cur_y
- oy
);
513 } else if (corner
== prop_atoms
.net_wm_moveresize_move_keyboard
) {
514 gint dx
= 0, dy
= 0, ox
= cur_x
, oy
= cur_y
;
515 gint opx
, px
, opy
, py
;
517 if (e
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
))
519 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
))
521 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
))
523 else /* if (e->xkey.keycode == ob_keycode(OB_KEY_UP)) */
528 screen_pointer_pos(&opx
, &opy
);
529 XWarpPointer(ob_display
, None
, None
, 0, 0, 0, 0, dx
, dy
);
530 /* steal the motion events this causes */
531 XSync(ob_display
, FALSE
);
534 while (XCheckTypedEvent(ob_display
, MotionNotify
, &ce
));
536 screen_pointer_pos(&px
, &py
);
540 /* because the cursor moves even though the window does
541 not nessesarily (resistance), this adjusts where the curor
542 thinks it started so that it keeps up with where the window
544 start_x
+= (px
- opx
) - (cur_x
- ox
);
545 start_y
+= (py
- opy
) - (cur_y
- oy
);
552 else if (e
->type
== extensions_sync_event_basep
+ XSyncAlarmNotify
)
554 waiting_for_sync
= FALSE
; /* we got our sync... */
555 do_resize(); /* ...so try resize if there is more change pending */