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