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