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