use the current size not the start size for moving
[dana/openbox.git] / openbox / moveresize.c
1 #include "grab.h"
2 #include "framerender.h"
3 #include "screen.h"
4 #include "prop.h"
5 #include "client.h"
6 #include "frame.h"
7 #include "openbox.h"
8 #include "resist.h"
9 #include "popup.h"
10 #include "moveresize.h"
11 #include "config.h"
12 #include "render/render.h"
13 #include "render/theme.h"
14
15 #include <X11/Xlib.h>
16 #include <glib.h>
17
18 gboolean moveresize_in_progress = FALSE;
19 ObClient *moveresize_client = NULL;
20
21 static gboolean moving = FALSE; /* TRUE - moving, FALSE - resizing */
22
23 static int start_x, start_y, start_cx, start_cy, start_cw, start_ch;
24 static int cur_x, cur_y;
25 static guint button;
26 static guint32 corner;
27 static ObCorner lockcorner;
28
29 static Popup *popup = NULL;
30
31 static void client_dest(gpointer client)
32 {
33     if (moveresize_client == client)
34         moveresize_end(TRUE);    
35 }
36
37 void moveresize_startup(gboolean reconfig)
38 {
39     popup = popup_new(FALSE);
40
41     if (!reconfig)
42         client_add_destructor(client_dest);
43 }
44
45 void moveresize_shutdown(gboolean reconfig)
46 {
47     if (!reconfig)
48         client_remove_destructor(client_dest);
49
50     popup_free(popup);
51     popup = NULL;
52 }
53
54 static void popup_coords(ObClient *c, char *format, int a, int b)
55 {
56     char *text;
57
58     text = g_strdup_printf(format, a, b);
59     popup_position(popup, CenterGravity,
60                    c->frame->area.x + c->frame->size.left +
61                    c->area.width / 2,
62                    c->frame->area.y + c->frame->size.top +
63                    c->area.height / 2);
64     popup_show(popup, text, NULL);
65     g_free(text);
66 }
67
68 void moveresize_start(ObClient *c, int x, int y, guint b, guint32 cnr)
69 {
70     ObCursor cur;
71
72     g_assert(!moveresize_in_progress);
73
74     if (!c->frame->visible)
75         return;
76
77     moveresize_client = c;
78     start_cx = c->frame->area.x;
79     start_cy = c->frame->area.y;
80     /* these adjustments for the size_inc make resizing a terminal more
81        friendly. you essentially start the resize in the middle of the
82        increment instead of at 0, so you have to move half an increment
83        either way instead of a full increment one and 1 px the other. and this
84        is one large mother fucking comment. */
85     start_cw = c->area.width + c->size_inc.width / 2;
86     start_ch = c->area.height + c->size_inc.height / 2;
87     start_x = x;
88     start_y = y;
89     corner = cnr;
90     button = b;
91
92     /*
93       have to change start_cx and start_cy if going to do this..
94     if (corner == prop_atoms.net_wm_moveresize_move_keyboard ||
95         corner == prop_atoms.net_wm_moveresize_size_keyboard)
96         XWarpPointer(ob_display, None, c->window, 0, 0, 0, 0,
97                      c->area.width / 2, c->area.height / 2);
98     */
99
100     if (corner == prop_atoms.net_wm_moveresize_move ||
101         corner == prop_atoms.net_wm_moveresize_move_keyboard) {
102         cur_x = start_cx;
103         cur_y = start_cy;
104         moving = TRUE;
105     } else {
106         cur_x = start_cw;
107         cur_y = start_ch;
108         moving = FALSE;
109     }
110
111     moveresize_in_progress = TRUE;
112
113     if (corner == prop_atoms.net_wm_moveresize_size_topleft)
114         cur = OB_CURSOR_NORTHWEST;
115     else if (corner == prop_atoms.net_wm_moveresize_size_top)
116         cur = OB_CURSOR_NORTH;
117     else if (corner == prop_atoms.net_wm_moveresize_size_topright)
118         cur = OB_CURSOR_NORTHEAST;
119     else if (corner == prop_atoms.net_wm_moveresize_size_right)
120         cur = OB_CURSOR_EAST;
121     else if (corner == prop_atoms.net_wm_moveresize_size_bottomright)
122         cur = OB_CURSOR_SOUTHEAST;
123     else if (corner == prop_atoms.net_wm_moveresize_size_bottom)
124         cur = OB_CURSOR_SOUTH;
125     else if (corner == prop_atoms.net_wm_moveresize_size_bottomleft)
126         cur = OB_CURSOR_SOUTHWEST;
127     else if (corner == prop_atoms.net_wm_moveresize_size_left)
128         cur = OB_CURSOR_WEST;
129     else if (corner == prop_atoms.net_wm_moveresize_size_keyboard)
130         cur = OB_CURSOR_SOUTHEAST;
131     else if (corner == prop_atoms.net_wm_moveresize_move)
132         cur = OB_CURSOR_MOVE;
133     else if (corner == prop_atoms.net_wm_moveresize_move_keyboard)
134         cur = OB_CURSOR_MOVE;
135     else
136         g_assert_not_reached();
137
138     grab_pointer(TRUE, cur);
139     grab_keyboard(TRUE);
140 }
141
142 void moveresize_end(gboolean cancel)
143 {
144     grab_keyboard(FALSE);
145     grab_pointer(FALSE, OB_CURSOR_NONE);
146
147     popup_hide(popup);
148
149     if (moving) {
150         client_move(moveresize_client,
151                     (cancel ? start_cx : cur_x),
152                     (cancel ? start_cy : cur_y));
153     } else {
154         client_configure(moveresize_client, lockcorner,
155                          moveresize_client->area.x,
156                          moveresize_client->area.y,
157                          (cancel ? start_cw : cur_x),
158                          (cancel ? start_ch : cur_y), TRUE, TRUE);
159     }
160
161     moveresize_in_progress = FALSE;
162     moveresize_client = NULL;
163 }
164
165 static void do_move(gboolean resist)
166 {
167     if (resist)
168         resist_move_windows(moveresize_client, &cur_x, &cur_y);
169     resist_move_monitors(moveresize_client, &cur_x, &cur_y);
170
171     /* get where the client should be */
172     frame_frame_gravity(moveresize_client->frame, &cur_x, &cur_y);
173     client_configure(moveresize_client, OB_CORNER_TOPLEFT, cur_x, cur_y,
174                      moveresize_client->area.width,
175                      moveresize_client->area.height, TRUE, FALSE);
176 }
177
178 static void do_resize(gboolean resist)
179 {
180     /* resist_size_* needs the frame size */
181     cur_x += moveresize_client->frame->size.left +
182         moveresize_client->frame->size.right;
183     cur_y += moveresize_client->frame->size.top +
184         moveresize_client->frame->size.bottom;
185
186     if (resist)
187         resist_size_windows(moveresize_client, &cur_x, &cur_y, lockcorner);
188     resist_size_monitors(moveresize_client, &cur_x, &cur_y, lockcorner);
189
190     cur_x -= moveresize_client->frame->size.left +
191         moveresize_client->frame->size.right;
192     cur_y -= moveresize_client->frame->size.top +
193         moveresize_client->frame->size.bottom;
194  
195     client_configure(moveresize_client, lockcorner, 
196                      moveresize_client->area.x, moveresize_client->area.y,
197                      cur_x, cur_y, TRUE, FALSE);
198
199     /* this would be better with a fixed width font ... XXX can do it better
200        if there are 2 text boxes */
201     if (moveresize_client->size_inc.width > 1 ||
202         moveresize_client->size_inc.height > 1)
203         popup_coords(moveresize_client, "%d x %d",
204                      moveresize_client->logical_size.width,
205                      moveresize_client->logical_size.height);
206 }
207
208 void moveresize_event(XEvent *e)
209 {
210     g_assert(moveresize_in_progress);
211
212     if (e->type == ButtonPress) {
213         if (!button) {
214             start_x = e->xbutton.x_root;
215             start_y = e->xbutton.y_root;
216             button = e->xbutton.button; /* this will end it now */
217         }
218     } else if (e->type == ButtonRelease) {
219         if (!button || e->xbutton.button == button) {
220             moveresize_end(FALSE);
221         }
222     } else if (e->type == MotionNotify) {
223         if (moving) {
224             cur_x = start_cx + e->xmotion.x_root - start_x;
225             cur_y = start_cy + e->xmotion.y_root - start_y;
226             do_move(TRUE);
227         } else {
228             if (corner == prop_atoms.net_wm_moveresize_size_topleft) {
229                 cur_x = start_cw - (e->xmotion.x_root - start_x);
230                 cur_y = start_ch - (e->xmotion.y_root - start_y);
231                 lockcorner = OB_CORNER_BOTTOMRIGHT;
232             } else if (corner == prop_atoms.net_wm_moveresize_size_top) {
233                 cur_x = start_cw;
234                 cur_y = start_ch - (e->xmotion.y_root - start_y);
235                 lockcorner = OB_CORNER_BOTTOMRIGHT;
236             } else if (corner == prop_atoms.net_wm_moveresize_size_topright) {
237                 cur_x = start_cw + (e->xmotion.x_root - start_x);
238                 cur_y = start_ch - (e->xmotion.y_root - start_y);
239                 lockcorner = OB_CORNER_BOTTOMLEFT;
240             } else if (corner == prop_atoms.net_wm_moveresize_size_right) { 
241                 cur_x = start_cw + (e->xmotion.x_root - start_x);
242                 cur_y = start_ch;
243                 lockcorner = OB_CORNER_BOTTOMLEFT;
244             } else if (corner ==
245                        prop_atoms.net_wm_moveresize_size_bottomright) {
246                 cur_x = start_cw + (e->xmotion.x_root - start_x);
247                 cur_y = start_ch + (e->xmotion.y_root - start_y);
248                 lockcorner = OB_CORNER_TOPLEFT;
249             } else if (corner == prop_atoms.net_wm_moveresize_size_bottom) {
250                 cur_x = start_cw;
251                 cur_y = start_ch + (e->xmotion.y_root - start_y);
252                 lockcorner = OB_CORNER_TOPLEFT;
253             } else if (corner ==
254                        prop_atoms.net_wm_moveresize_size_bottomleft) {
255                 cur_x = start_cw - (e->xmotion.x_root - start_x);
256                 cur_y = start_ch + (e->xmotion.y_root - start_y);
257                 lockcorner = OB_CORNER_TOPRIGHT;
258             } else if (corner == prop_atoms.net_wm_moveresize_size_left) {
259                 cur_x = start_cw - (e->xmotion.x_root - start_x);
260                 cur_y = start_ch;
261                 lockcorner = OB_CORNER_TOPRIGHT;
262             } else if (corner == prop_atoms.net_wm_moveresize_size_keyboard) {
263                 cur_x = start_cw + (e->xmotion.x_root - start_x);
264                 cur_y = start_ch + (e->xmotion.y_root - start_y);
265                 lockcorner = OB_CORNER_TOPLEFT;
266             } else
267                 g_assert_not_reached();
268
269             do_resize(TRUE);
270         }
271     } else if (e->type == KeyPress) {
272         if (e->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
273             moveresize_end(TRUE);
274         else if (e->xkey.keycode == ob_keycode(OB_KEY_RETURN))
275             moveresize_end(FALSE);
276         else {
277             if (corner == prop_atoms.net_wm_moveresize_size_keyboard) {
278                 int dx = 0, dy = 0, ox = cur_x, oy = cur_y;
279
280                 if (e->xkey.keycode == ob_keycode(OB_KEY_RIGHT))
281                     dx = MAX(4, moveresize_client->size_inc.width);
282                 else if (e->xkey.keycode == ob_keycode(OB_KEY_LEFT))
283                     dx = -MAX(4, moveresize_client->size_inc.width);
284                 else if (e->xkey.keycode == ob_keycode(OB_KEY_DOWN))
285                     dy = MAX(4, moveresize_client->size_inc.height);
286                 else if (e->xkey.keycode == ob_keycode(OB_KEY_UP))
287                     dy = -MAX(4, moveresize_client->size_inc.height);
288                 else
289                     return;
290
291                 cur_x += dx;
292                 cur_y += dy;
293                 XWarpPointer(ob_display, None, None, 0, 0, 0, 0, dx, dy);
294                 /* steal the motion events this causes */
295                 XSync(ob_display, FALSE);
296                 {
297                     XEvent ce;
298                     while (XCheckTypedEvent(ob_display, MotionNotify, &ce));
299                 }
300
301                 do_resize(FALSE);
302
303                 /* because the cursor moves even though the window does
304                    not nessesarily (resistance), this adjusts where the curor
305                    thinks it started so that it keeps up with where the window
306                    actually is */
307                 start_x += dx - (cur_x - ox);
308                 start_y += dy - (cur_y - oy);
309             } else if (corner == prop_atoms.net_wm_moveresize_move_keyboard) {
310                 int dx = 0, dy = 0, ox = cur_x, oy = cur_y;
311                 int opx, px, opy, py;
312
313                 if (e->xkey.keycode == ob_keycode(OB_KEY_RIGHT))
314                     dx = 4;
315                 else if (e->xkey.keycode == ob_keycode(OB_KEY_LEFT))
316                     dx = -4;
317                 else if (e->xkey.keycode == ob_keycode(OB_KEY_DOWN))
318                     dy = 4;
319                 else if (e->xkey.keycode == ob_keycode(OB_KEY_UP))
320                     dy = -4;
321                 else
322                     return;
323
324                 cur_x += dx;
325                 cur_y += dy;
326                 screen_pointer_pos(&opx, &opy);
327                 XWarpPointer(ob_display, None, None, 0, 0, 0, 0, dx, dy);
328                 /* steal the motion events this causes */
329                 XSync(ob_display, FALSE);
330                 {
331                     XEvent ce;
332                     while (XCheckTypedEvent(ob_display, MotionNotify, &ce));
333                 }
334                 screen_pointer_pos(&px, &py);
335
336                 do_move(FALSE);
337
338                 /* because the cursor moves even though the window does
339                    not nessesarily (resistance), this adjusts where the curor
340                    thinks it started so that it keeps up with where the window
341                    actually is */
342                 start_x += (px - opx) - (cur_x - ox);
343                 start_y += (py - opy) - (cur_y - oy);
344             }
345         }
346     }
347 }