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 popup_coords(ObClient
*c
, const gchar
*format
, gint a
, gint b
)
89 text
= g_strdup_printf(format
, a
, b
);
90 if (config_resize_popup_pos
== 1) /* == "Top" */
91 popup_position(popup
, SouthGravity
,
93 + c
->frame
->area
.width
/2,
94 c
->frame
->area
.y
- ob_rr_theme
->fbwidth
);
95 else /* == "Center" */
96 popup_position(popup
, CenterGravity
,
97 c
->frame
->area
.x
+ c
->frame
->size
.left
+
99 c
->frame
->area
.y
+ c
->frame
->size
.top
+
101 popup_show(popup
, text
);
105 void moveresize_start(ObClient
*c
, gint x
, gint y
, guint b
, guint32 cnr
)
109 moving
= (cnr
== prop_atoms
.net_wm_moveresize_move
||
110 cnr
== prop_atoms
.net_wm_moveresize_move_keyboard
);
112 if (moveresize_in_progress
|| !c
->frame
->visible
||
114 (c
->functions
& OB_CLIENT_FUNC_MOVE
) :
115 (c
->functions
& OB_CLIENT_FUNC_RESIZE
)))
118 moveresize_client
= c
;
119 start_cx
= c
->frame
->area
.x
;
120 start_cy
= c
->frame
->area
.y
;
121 /* these adjustments for the size_inc make resizing a terminal more
122 friendly. you essentially start the resize in the middle of the
123 increment instead of at 0, so you have to move half an increment
124 either way instead of a full increment one and 1 px the other. and this
125 is one large mother fucking comment. */
126 start_cw
= c
->area
.width
+ c
->size_inc
.width
/ 2;
127 start_ch
= c
->area
.height
+ c
->size_inc
.height
/ 2;
134 have to change start_cx and start_cy if going to do this..
135 if (corner == prop_atoms.net_wm_moveresize_move_keyboard ||
136 corner == prop_atoms.net_wm_moveresize_size_keyboard)
137 XWarpPointer(ob_display, None, c->window, 0, 0, 0, 0,
138 c->area.width / 2, c->area.height / 2);
149 moveresize_in_progress
= TRUE
;
151 if (corner
== prop_atoms
.net_wm_moveresize_size_topleft
)
152 cur
= OB_CURSOR_NORTHWEST
;
153 else if (corner
== prop_atoms
.net_wm_moveresize_size_top
)
154 cur
= OB_CURSOR_NORTH
;
155 else if (corner
== prop_atoms
.net_wm_moveresize_size_topright
)
156 cur
= OB_CURSOR_NORTHEAST
;
157 else if (corner
== prop_atoms
.net_wm_moveresize_size_right
)
158 cur
= OB_CURSOR_EAST
;
159 else if (corner
== prop_atoms
.net_wm_moveresize_size_bottomright
)
160 cur
= OB_CURSOR_SOUTHEAST
;
161 else if (corner
== prop_atoms
.net_wm_moveresize_size_bottom
)
162 cur
= OB_CURSOR_SOUTH
;
163 else if (corner
== prop_atoms
.net_wm_moveresize_size_bottomleft
)
164 cur
= OB_CURSOR_SOUTHWEST
;
165 else if (corner
== prop_atoms
.net_wm_moveresize_size_left
)
166 cur
= OB_CURSOR_WEST
;
167 else if (corner
== prop_atoms
.net_wm_moveresize_size_keyboard
)
168 cur
= OB_CURSOR_SOUTHEAST
;
169 else if (corner
== prop_atoms
.net_wm_moveresize_move
)
170 cur
= OB_CURSOR_MOVE
;
171 else if (corner
== prop_atoms
.net_wm_moveresize_move_keyboard
)
172 cur
= OB_CURSOR_MOVE
;
174 g_assert_not_reached();
177 if (config_resize_redraw
&& !moving
&& extensions_shape
&&
178 moveresize_client
->sync_request
&& moveresize_client
->sync_counter
)
180 /* Initialize values for the resize syncing, and create an alarm for
181 the client's xsync counter */
184 XSyncAlarmAttributes aa
;
186 /* set the counter to an initial value */
187 XSyncIntToValue(&val
, 0);
188 XSyncSetCounter(ob_display
, moveresize_client
->sync_counter
, val
);
190 /* this will be incremented when we tell the client what we're
192 moveresize_client
->sync_counter_value
= 0;
194 /* the next sequence we're waiting for with the alarm */
195 XSyncIntToValue(&val
, 1);
197 /* set an alarm on the counter */
198 aa
.trigger
.counter
= moveresize_client
->sync_counter
;
199 aa
.trigger
.wait_value
= val
;
200 aa
.trigger
.value_type
= XSyncAbsolute
;
201 aa
.trigger
.test_type
= XSyncPositiveTransition
;
203 XSyncIntToValue(&aa
.delta
, 1);
204 moveresize_alarm
= XSyncCreateAlarm(ob_display
,
213 waiting_for_sync
= FALSE
;
217 grab_pointer(TRUE
, FALSE
, cur
);
221 void moveresize_end(gboolean cancel
)
223 grab_keyboard(FALSE
);
224 grab_pointer(FALSE
, FALSE
, OB_CURSOR_NONE
);
229 client_move(moveresize_client
,
230 (cancel
? start_cx
: cur_x
),
231 (cancel
? start_cy
: cur_y
));
234 /* turn off the alarm */
235 if (moveresize_alarm
!= None
) {
236 XSyncDestroyAlarm(ob_display
, moveresize_alarm
);
237 moveresize_alarm
= None
;
241 client_configure(moveresize_client
, lockcorner
,
242 moveresize_client
->area
.x
,
243 moveresize_client
->area
.y
,
244 (cancel
? start_cw
: cur_x
),
245 (cancel
? start_ch
: cur_y
), TRUE
, TRUE
);
248 moveresize_in_progress
= FALSE
;
249 moveresize_client
= NULL
;
252 static void do_move(gboolean resist
)
255 resist_move_windows(moveresize_client
, &cur_x
, &cur_y
);
256 resist_move_monitors(moveresize_client
, &cur_x
, &cur_y
);
259 /* get where the client should be */
260 frame_frame_gravity(moveresize_client
->frame
, &cur_x
, &cur_y
);
261 client_configure(moveresize_client
, OB_CORNER_TOPLEFT
, cur_x
, cur_y
,
262 moveresize_client
->area
.width
,
263 moveresize_client
->area
.height
, TRUE
, FALSE
);
264 if (config_resize_popup_show
== 2) /* == "Always" */
265 popup_coords(moveresize_client
, "%d x %d",
266 moveresize_client
->frame
->area
.x
,
267 moveresize_client
->frame
->area
.y
);
270 static void do_resize()
273 if (config_resize_redraw
&& extensions_sync
&&
274 moveresize_client
->sync_request
&& moveresize_client
->sync_counter
)
278 gint x
, y
, w
, h
, lw
, lh
;
280 /* are we already waiting for the sync counter to catch up? */
281 if (waiting_for_sync
)
284 /* see if it is actually going to resize */
285 x
= moveresize_client
->area
.x
;
286 y
= moveresize_client
->area
.y
;
289 client_try_configure(moveresize_client
, lockcorner
, &x
, &y
, &w
, &h
,
291 if (w
== moveresize_client
->area
.width
&&
292 h
== moveresize_client
->area
.height
)
297 /* increment the value we're waiting for */
298 ++moveresize_client
->sync_counter_value
;
299 XSyncIntToValue(&val
, moveresize_client
->sync_counter_value
);
301 /* tell the client what we're waiting for */
302 ce
.xclient
.type
= ClientMessage
;
303 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
304 ce
.xclient
.display
= ob_display
;
305 ce
.xclient
.window
= moveresize_client
->window
;
306 ce
.xclient
.format
= 32;
307 ce
.xclient
.data
.l
[0] = prop_atoms
.net_wm_sync_request
;
308 ce
.xclient
.data
.l
[1] = event_curtime
;
309 ce
.xclient
.data
.l
[2] = XSyncValueLow32(val
);
310 ce
.xclient
.data
.l
[3] = XSyncValueHigh32(val
);
311 ce
.xclient
.data
.l
[4] = 0l;
312 XSendEvent(ob_display
, moveresize_client
->window
, FALSE
,
315 waiting_for_sync
= TRUE
;
319 client_configure(moveresize_client
, lockcorner
,
320 moveresize_client
->area
.x
, moveresize_client
->area
.y
,
321 cur_x
, cur_y
, TRUE
, FALSE
);
323 /* this would be better with a fixed width font ... XXX can do it better
324 if there are 2 text boxes */
325 if (config_resize_popup_show
== 2 || /* == "Always" */
326 (config_resize_popup_show
== 1 && /* == "Nonpixel" */
327 (moveresize_client
->size_inc
.width
> 1 ||
328 moveresize_client
->size_inc
.height
> 1))
330 popup_coords(moveresize_client
, "%d x %d",
331 moveresize_client
->logical_size
.width
,
332 moveresize_client
->logical_size
.height
);
335 static void calc_resize(gboolean resist
)
337 /* resist_size_* needs the frame size */
338 cur_x
+= moveresize_client
->frame
->size
.left
+
339 moveresize_client
->frame
->size
.right
;
340 cur_y
+= moveresize_client
->frame
->size
.top
+
341 moveresize_client
->frame
->size
.bottom
;
344 resist_size_windows(moveresize_client
, &cur_x
, &cur_y
, lockcorner
);
345 resist_size_monitors(moveresize_client
, &cur_x
, &cur_y
, lockcorner
);
348 cur_x
-= moveresize_client
->frame
->size
.left
+
349 moveresize_client
->frame
->size
.right
;
350 cur_y
-= moveresize_client
->frame
->size
.top
+
351 moveresize_client
->frame
->size
.bottom
;
354 void moveresize_event(XEvent
*e
)
356 g_assert(moveresize_in_progress
);
358 if (e
->type
== ButtonPress
) {
360 start_x
= e
->xbutton
.x_root
;
361 start_y
= e
->xbutton
.y_root
;
362 button
= e
->xbutton
.button
; /* this will end it now */
364 } else if (e
->type
== ButtonRelease
) {
365 if (!button
|| e
->xbutton
.button
== button
) {
366 moveresize_end(FALSE
);
368 } else if (e
->type
== MotionNotify
) {
370 cur_x
= start_cx
+ e
->xmotion
.x_root
- start_x
;
371 cur_y
= start_cy
+ e
->xmotion
.y_root
- start_y
;
374 if (corner
== prop_atoms
.net_wm_moveresize_size_topleft
) {
375 cur_x
= start_cw
- (e
->xmotion
.x_root
- start_x
);
376 cur_y
= start_ch
- (e
->xmotion
.y_root
- start_y
);
377 lockcorner
= OB_CORNER_BOTTOMRIGHT
;
378 } else if (corner
== prop_atoms
.net_wm_moveresize_size_top
) {
380 cur_y
= start_ch
- (e
->xmotion
.y_root
- start_y
);
381 lockcorner
= OB_CORNER_BOTTOMRIGHT
;
382 } else if (corner
== prop_atoms
.net_wm_moveresize_size_topright
) {
383 cur_x
= start_cw
+ (e
->xmotion
.x_root
- start_x
);
384 cur_y
= start_ch
- (e
->xmotion
.y_root
- start_y
);
385 lockcorner
= OB_CORNER_BOTTOMLEFT
;
386 } else if (corner
== prop_atoms
.net_wm_moveresize_size_right
) {
387 cur_x
= start_cw
+ (e
->xmotion
.x_root
- start_x
);
389 lockcorner
= OB_CORNER_BOTTOMLEFT
;
391 prop_atoms
.net_wm_moveresize_size_bottomright
) {
392 cur_x
= start_cw
+ (e
->xmotion
.x_root
- start_x
);
393 cur_y
= start_ch
+ (e
->xmotion
.y_root
- start_y
);
394 lockcorner
= OB_CORNER_TOPLEFT
;
395 } else if (corner
== prop_atoms
.net_wm_moveresize_size_bottom
) {
397 cur_y
= start_ch
+ (e
->xmotion
.y_root
- start_y
);
398 lockcorner
= OB_CORNER_TOPLEFT
;
400 prop_atoms
.net_wm_moveresize_size_bottomleft
) {
401 cur_x
= start_cw
- (e
->xmotion
.x_root
- start_x
);
402 cur_y
= start_ch
+ (e
->xmotion
.y_root
- start_y
);
403 lockcorner
= OB_CORNER_TOPRIGHT
;
404 } else if (corner
== prop_atoms
.net_wm_moveresize_size_left
) {
405 cur_x
= start_cw
- (e
->xmotion
.x_root
- start_x
);
407 lockcorner
= OB_CORNER_TOPRIGHT
;
408 } else if (corner
== prop_atoms
.net_wm_moveresize_size_keyboard
) {
409 cur_x
= start_cw
+ (e
->xmotion
.x_root
- start_x
);
410 cur_y
= start_ch
+ (e
->xmotion
.y_root
- start_y
);
411 lockcorner
= OB_CORNER_TOPLEFT
;
413 g_assert_not_reached();
418 } else if (e
->type
== KeyPress
) {
419 if (e
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
))
420 moveresize_end(TRUE
);
421 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
))
422 moveresize_end(FALSE
);
424 if (corner
== prop_atoms
.net_wm_moveresize_size_keyboard
) {
425 gint dx
= 0, dy
= 0, ox
= cur_x
, oy
= cur_y
;
427 if (e
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
))
428 dx
= MAX(4, moveresize_client
->size_inc
.width
);
429 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
))
430 dx
= -MAX(4, moveresize_client
->size_inc
.width
);
431 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
))
432 dy
= MAX(4, moveresize_client
->size_inc
.height
);
433 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_UP
))
434 dy
= -MAX(4, moveresize_client
->size_inc
.height
);
440 XWarpPointer(ob_display
, None
, None
, 0, 0, 0, 0, dx
, dy
);
441 /* steal the motion events this causes */
442 XSync(ob_display
, FALSE
);
445 while (XCheckTypedEvent(ob_display
, MotionNotify
, &ce
));
450 /* because the cursor moves even though the window does
451 not nessesarily (resistance), this adjusts where the curor
452 thinks it started so that it keeps up with where the window
454 start_x
+= dx
- (cur_x
- ox
);
455 start_y
+= dy
- (cur_y
- oy
);
456 } else if (corner
== prop_atoms
.net_wm_moveresize_move_keyboard
) {
457 gint dx
= 0, dy
= 0, ox
= cur_x
, oy
= cur_y
;
458 gint opx
, px
, opy
, py
;
460 if (e
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
))
462 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
))
464 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
))
466 else if (e
->xkey
.keycode
== ob_keycode(OB_KEY_UP
))
473 screen_pointer_pos(&opx
, &opy
);
474 XWarpPointer(ob_display
, None
, None
, 0, 0, 0, 0, dx
, dy
);
475 /* steal the motion events this causes */
476 XSync(ob_display
, FALSE
);
479 while (XCheckTypedEvent(ob_display
, MotionNotify
, &ce
));
481 screen_pointer_pos(&px
, &py
);
485 /* because the cursor moves even though the window does
486 not nessesarily (resistance), this adjusts where the curor
487 thinks it started so that it keeps up with where the window
489 start_x
+= (px
- opx
) - (cur_x
- ox
);
490 start_y
+= (py
- opy
) - (cur_y
- oy
);
495 else if (e
->type
== extensions_sync_event_basep
+ XSyncAlarmNotify
)
497 waiting_for_sync
= FALSE
; /* we got our sync... */
498 do_resize(); /* ...so try resize if there is more change pending */