little bit of an actions overhaul, added action_run* so that duplicated code can...
[mikachu/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                      start_cw, start_ch, TRUE, FALSE);
175 }
176
177 static void do_resize(gboolean resist)
178 {
179     /* resist_size_* needs the frame size */
180     cur_x += moveresize_client->frame->size.left +
181         moveresize_client->frame->size.right;
182     cur_y += moveresize_client->frame->size.top +
183         moveresize_client->frame->size.bottom;
184
185     if (resist)
186         resist_size_windows(moveresize_client, &cur_x, &cur_y, lockcorner);
187     resist_size_monitors(moveresize_client, &cur_x, &cur_y, lockcorner);
188
189     cur_x -= moveresize_client->frame->size.left +
190         moveresize_client->frame->size.right;
191     cur_y -= moveresize_client->frame->size.top +
192         moveresize_client->frame->size.bottom;
193  
194     client_configure(moveresize_client, lockcorner, 
195                      moveresize_client->area.x, moveresize_client->area.y,
196                      cur_x, cur_y, TRUE, FALSE);
197
198     /* this would be better with a fixed width font ... XXX can do it better
199        if there are 2 text boxes */
200     if (moveresize_client->size_inc.width > 1 ||
201         moveresize_client->size_inc.height > 1)
202         popup_coords(moveresize_client, "%d x %d",
203                      moveresize_client->logical_size.width,
204                      moveresize_client->logical_size.height);
205 }
206
207 void moveresize_event(XEvent *e)
208 {
209     g_assert(moveresize_in_progress);
210
211     if (e->type == ButtonPress) {
212         if (!button) {
213             start_x = e->xbutton.x_root;
214             start_y = e->xbutton.y_root;
215             button = e->xbutton.button; /* this will end it now */
216         }
217     } else if (e->type == ButtonRelease) {
218         if (!button || e->xbutton.button == button) {
219             moveresize_end(FALSE);
220         }
221     } else if (e->type == MotionNotify) {
222         if (moving) {
223             cur_x = start_cx + e->xmotion.x_root - start_x;
224             cur_y = start_cy + e->xmotion.y_root - start_y;
225             do_move(TRUE);
226         } else {
227             if (corner == prop_atoms.net_wm_moveresize_size_topleft) {
228                 cur_x = start_cw - (e->xmotion.x_root - start_x);
229                 cur_y = start_ch - (e->xmotion.y_root - start_y);
230                 lockcorner = OB_CORNER_BOTTOMRIGHT;
231             } else if (corner == prop_atoms.net_wm_moveresize_size_top) {
232                 cur_x = start_cw;
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_topright) {
236                 cur_x = start_cw + (e->xmotion.x_root - start_x);
237                 cur_y = start_ch - (e->xmotion.y_root - start_y);
238                 lockcorner = OB_CORNER_BOTTOMLEFT;
239             } else if (corner == prop_atoms.net_wm_moveresize_size_right) { 
240                 cur_x = start_cw + (e->xmotion.x_root - start_x);
241                 cur_y = start_ch;
242                 lockcorner = OB_CORNER_BOTTOMLEFT;
243             } else if (corner ==
244                        prop_atoms.net_wm_moveresize_size_bottomright) {
245                 cur_x = start_cw + (e->xmotion.x_root - start_x);
246                 cur_y = start_ch + (e->xmotion.y_root - start_y);
247                 lockcorner = OB_CORNER_TOPLEFT;
248             } else if (corner == prop_atoms.net_wm_moveresize_size_bottom) {
249                 cur_x = start_cw;
250                 cur_y = start_ch + (e->xmotion.y_root - start_y);
251                 lockcorner = OB_CORNER_TOPLEFT;
252             } else if (corner ==
253                        prop_atoms.net_wm_moveresize_size_bottomleft) {
254                 cur_x = start_cw - (e->xmotion.x_root - start_x);
255                 cur_y = start_ch + (e->xmotion.y_root - start_y);
256                 lockcorner = OB_CORNER_TOPRIGHT;
257             } else if (corner == prop_atoms.net_wm_moveresize_size_left) {
258                 cur_x = start_cw - (e->xmotion.x_root - start_x);
259                 cur_y = start_ch;
260                 lockcorner = OB_CORNER_TOPRIGHT;
261             } else if (corner == prop_atoms.net_wm_moveresize_size_keyboard) {
262                 cur_x = start_cw + (e->xmotion.x_root - start_x);
263                 cur_y = start_ch + (e->xmotion.y_root - start_y);
264                 lockcorner = OB_CORNER_TOPLEFT;
265             } else
266                 g_assert_not_reached();
267
268             do_resize(TRUE);
269         }
270     } else if (e->type == KeyPress) {
271         if (e->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
272             moveresize_end(TRUE);
273         else if (e->xkey.keycode == ob_keycode(OB_KEY_RETURN))
274             moveresize_end(FALSE);
275         else {
276             if (corner == prop_atoms.net_wm_moveresize_size_keyboard) {
277                 int dx = 0, dy = 0, ox = cur_x, oy = cur_y;
278
279                 if (e->xkey.keycode == ob_keycode(OB_KEY_RIGHT))
280                     dx = MAX(4, moveresize_client->size_inc.width);
281                 else if (e->xkey.keycode == ob_keycode(OB_KEY_LEFT))
282                     dx = -MAX(4, moveresize_client->size_inc.width);
283                 else if (e->xkey.keycode == ob_keycode(OB_KEY_DOWN))
284                     dy = MAX(4, moveresize_client->size_inc.height);
285                 else if (e->xkey.keycode == ob_keycode(OB_KEY_UP))
286                     dy = -MAX(4, moveresize_client->size_inc.height);
287                 else
288                     return;
289
290                 cur_x += dx;
291                 cur_y += dy;
292                 XWarpPointer(ob_display, None, None, 0, 0, 0, 0, dx, dy);
293                 /* steal the motion events this causes */
294                 XSync(ob_display, FALSE);
295                 {
296                     XEvent ce;
297                     while (XCheckTypedEvent(ob_display, MotionNotify, &ce));
298                 }
299
300                 do_resize(FALSE);
301
302                 /* because the cursor moves even though the window does
303                    not nessesarily (resistance), this adjusts where the curor
304                    thinks it started so that it keeps up with where the window
305                    actually is */
306                 start_x += dx - (cur_x - ox);
307                 start_y += dy - (cur_y - oy);
308             } else if (corner == prop_atoms.net_wm_moveresize_move_keyboard) {
309                 int dx = 0, dy = 0, ox = cur_x, oy = cur_y;
310                 int opx, px, opy, py;
311
312                 if (e->xkey.keycode == ob_keycode(OB_KEY_RIGHT))
313                     dx = 4;
314                 else if (e->xkey.keycode == ob_keycode(OB_KEY_LEFT))
315                     dx = -4;
316                 else if (e->xkey.keycode == ob_keycode(OB_KEY_DOWN))
317                     dy = 4;
318                 else if (e->xkey.keycode == ob_keycode(OB_KEY_UP))
319                     dy = -4;
320                 else
321                     return;
322
323                 cur_x += dx;
324                 cur_y += dy;
325                 screen_pointer_pos(&opx, &opy);
326                 XWarpPointer(ob_display, None, None, 0, 0, 0, 0, dx, dy);
327                 /* steal the motion events this causes */
328                 XSync(ob_display, FALSE);
329                 {
330                     XEvent ce;
331                     while (XCheckTypedEvent(ob_display, MotionNotify, &ce));
332                 }
333                 screen_pointer_pos(&px, &py);
334
335                 do_move(FALSE);
336
337                 /* because the cursor moves even though the window does
338                    not nessesarily (resistance), this adjusts where the curor
339                    thinks it started so that it keeps up with where the window
340                    actually is */
341                 start_x += (px - opx) - (cur_x - ox);
342                 start_y += (py - opy) - (cur_y - oy);
343             }
344         }
345     }
346 }