add support for interactive/keyboard move/resize
[dana/openbox.git] / openbox / moveresize.c
1 #include "grab.h"
2 #include "framerender.h"
3 #include "prop.h"
4 #include "client.h"
5 #include "dispatch.h"
6 #include "openbox.h"
7
8 #include <X11/Xlib.h>
9 #include <glib.h>
10
11 gboolean moveresize_in_progress = FALSE;
12 static gboolean moving = FALSE; /* TRUE - moving, FALSE - resizing */
13
14 static Window coords = None;
15 static int start_x, start_y, start_cx, start_cy, start_cw, start_ch;
16 static int cur_x, cur_y;
17 static Client *client;
18 static guint button;
19 static guint32 corner;
20 static Corner lockcorner;
21
22 static guint button_return, button_escape, button_left, button_right,
23     button_up, button_down;
24
25 #define POPUP_X (10)
26 #define POPUP_Y (10)
27
28 void moveresize_startup()
29 {
30     button_return = XKeysymToKeycode(ob_display, XStringToKeysym("Return"));
31     button_escape = XKeysymToKeycode(ob_display, XStringToKeysym("Escape"));
32     button_left = XKeysymToKeycode(ob_display, XStringToKeysym("Left"));
33     button_right = XKeysymToKeycode(ob_display, XStringToKeysym("Right"));
34     button_up = XKeysymToKeycode(ob_display, XStringToKeysym("Up"));
35     button_down = XKeysymToKeycode(ob_display, XStringToKeysym("Down"));
36 }
37
38 static void popup_coords(char *format, int a, int b)
39 {
40     XSetWindowAttributes attrib;
41     Size s;
42     char *text;
43
44     if (coords == None) {
45         attrib.override_redirect = TRUE;
46         coords = XCreateWindow(ob_display, ob_root,
47                                0, 0, 1, 1, 0, render_depth, InputOutput,
48                                render_visual, CWOverrideRedirect, &attrib);
49         g_assert(coords != None);
50
51         XMapWindow(ob_display, coords);
52     }
53
54     text = g_strdup_printf(format, a, b);
55     framerender_size_popup_label(text, &s);
56     XMoveResizeWindow(ob_display, coords,
57                       POPUP_X, POPUP_Y, s.width, s.height);
58     framerender_popup_label(coords, &s, text);
59     g_free(text);
60 }
61
62 void moveresize_start(Client *c, int x, int y, guint b, guint32 cnr)
63 {
64     Cursor cur;
65
66     g_assert(!moveresize_in_progress);
67
68     client = c;
69     start_cx = c->frame->area.x;
70     start_cy = c->frame->area.y;
71     start_cw = c->area.width;
72     start_ch = c->area.height;
73     if (corner == prop_atoms.net_wm_moveresize_move_keyboard ||
74         corner == prop_atoms.net_wm_moveresize_size_keyboard)
75         button = 0; /* mouse can't end it without being pressed first */
76     else
77         button = b;
78     corner = cnr;
79
80     if (corner == prop_atoms.net_wm_moveresize_move ||
81         corner == prop_atoms.net_wm_moveresize_move_keyboard) {
82         cur_x = start_cx;
83         cur_y = start_cy;
84         moving = TRUE;
85     } else {
86         cur_x = start_cw;
87         cur_y = start_ch;
88         moving = FALSE;
89     }
90
91     moveresize_in_progress = TRUE;
92
93     if (corner == prop_atoms.net_wm_moveresize_size_topleft)
94         cur = ob_cursors.tl;
95     else if (corner == prop_atoms.net_wm_moveresize_size_top)
96         cur = ob_cursors.tl;
97     else if (corner == prop_atoms.net_wm_moveresize_size_topright)
98         cur = ob_cursors.tr;
99     else if (corner == prop_atoms.net_wm_moveresize_size_right)
100         cur = ob_cursors.tr;
101     else if (corner == prop_atoms.net_wm_moveresize_size_bottomright)
102         cur = ob_cursors.br;
103     else if (corner == prop_atoms.net_wm_moveresize_size_bottom)
104         cur = ob_cursors.br;
105     else if (corner == prop_atoms.net_wm_moveresize_size_bottomleft)
106         cur = ob_cursors.bl;
107     else if (corner == prop_atoms.net_wm_moveresize_size_left)
108         cur = ob_cursors.bl;
109     else if (corner == prop_atoms.net_wm_moveresize_size_keyboard)
110         cur = ob_cursors.br;
111     else if (corner == prop_atoms.net_wm_moveresize_move)
112         cur = ob_cursors.move;
113     else if (corner == prop_atoms.net_wm_moveresize_move_keyboard)
114         cur = ob_cursors.move;
115     else
116         g_assert_not_reached();
117
118     grab_keyboard(TRUE);
119     grab_pointer(TRUE, cur);
120 }
121
122 static void end_moveresize(gboolean cancel)
123 {
124     grab_keyboard(FALSE);
125     grab_pointer(FALSE, None);
126
127     XDestroyWindow(ob_display, coords);
128     coords = None;
129
130     moveresize_in_progress = FALSE;
131
132     if (moving) {
133         client_configure(client, Corner_TopLeft, (cancel ? start_cx : cur_x),
134                          (cancel ? start_cy : cur_y),
135                          start_cw, start_ch, TRUE, TRUE);
136     } else {
137         client_configure(client, lockcorner, client->area.x,
138                          client->area.y, (cancel ? start_cw : cur_x),
139                          (cancel ? start_ch : cur_y), TRUE, TRUE);
140     }
141 }
142
143 static void do_move()
144 {
145     dispatch_move(client, &cur_x, &cur_y);
146
147     popup_coords("X:  %d  Y:  %d", cur_x, cur_y);
148
149     /* get where the client should be */
150     frame_frame_gravity(client->frame, &cur_x, &cur_y);
151     client_configure(client, Corner_TopLeft, cur_x, cur_y,
152                      start_cw, start_ch, TRUE, FALSE);
153 }
154
155 static void do_resize()
156 {
157     /* dispatch_resize needs the frame size */
158     cur_x += client->frame->size.left + client->frame->size.right;
159     cur_y += client->frame->size.top + client->frame->size.bottom;
160
161     dispatch_resize(client, &cur_x, &cur_y, lockcorner);
162
163     cur_x -= client->frame->size.left + client->frame->size.right;
164     cur_y -= client->frame->size.top + client->frame->size.bottom;
165     
166     client_configure(client, lockcorner, client->area.x,
167                      client->area.y, cur_x, cur_y, TRUE, FALSE);
168
169     popup_coords("W:  %d  H:  %d", client->logical_size.width,
170                  client->logical_size.height);
171 }
172
173 void moveresize_event(XEvent *e)
174 {
175     g_assert(moveresize_in_progress);
176
177     if (e->type == ButtonPress) {
178         if (!button) {
179             start_x = e->xbutton.x_root;
180             start_y = e->xbutton.y_root;
181             button = e->xbutton.button; /* this will end it now */
182         }
183     } else if (e->type == ButtonRelease) {
184         if (e->xbutton.button == button) {
185             end_moveresize(FALSE);
186         }
187     } else if (e->type == MotionNotify) {
188         if (moving) {
189             cur_x = start_cx + e->xmotion.x_root - start_x;
190             cur_y = start_cy + e->xmotion.y_root - start_y;
191
192             do_move();
193         } else {
194             if (corner == prop_atoms.net_wm_moveresize_size_topleft) {
195                 cur_x = start_cw - (e->xmotion.x_root - start_x);
196                 cur_y = start_ch - (e->xmotion.y_root - start_y);
197                 lockcorner = Corner_BottomRight;
198             } else if (corner == prop_atoms.net_wm_moveresize_size_top) {
199                 cur_x = start_cw;
200                 cur_y = start_ch - (e->xmotion.y_root - start_y);
201                 lockcorner = Corner_BottomRight;
202             } else if (corner == prop_atoms.net_wm_moveresize_size_topright) {
203                 cur_x = start_cw + (e->xmotion.x_root - start_x);
204                 cur_y = start_ch - (e->xmotion.y_root - start_y);
205                 lockcorner = Corner_BottomLeft;
206             } else if (corner == prop_atoms.net_wm_moveresize_size_right) { 
207                 cur_x = start_cw + (e->xmotion.x_root - start_x);
208                 cur_y = start_ch;
209                 lockcorner = Corner_BottomLeft;
210             } else if (corner ==
211                        prop_atoms.net_wm_moveresize_size_bottomright) {
212                 cur_x = start_cw + (e->xmotion.x_root - start_x);
213                 cur_y = start_ch + (e->xmotion.y_root - start_y);
214                 lockcorner = Corner_TopLeft;
215             } else if (corner == prop_atoms.net_wm_moveresize_size_bottom) {
216                 cur_x = start_cw;
217                 cur_y = start_ch + (e->xmotion.y_root - start_y);
218                 lockcorner = Corner_TopLeft;
219             } else if (corner ==
220                        prop_atoms.net_wm_moveresize_size_bottomleft) {
221                 cur_x = start_cw - (e->xmotion.x_root - start_x);
222                 cur_y = start_ch + (e->xmotion.y_root - start_y);
223                 lockcorner = Corner_TopRight;
224             } else if (corner == prop_atoms.net_wm_moveresize_size_left) {
225                 cur_x = start_cw - (e->xmotion.x_root - start_x);
226                 cur_y = start_ch;
227                 lockcorner = Corner_TopRight;
228             } else if (corner == prop_atoms.net_wm_moveresize_size_keyboard) {
229                 cur_x = start_cw + (e->xmotion.x_root - start_x);
230                 cur_y = start_ch + (e->xmotion.y_root - start_y);
231                 lockcorner = Corner_TopLeft;
232             } else
233                 g_assert_not_reached();
234
235             do_resize();
236         }
237     } else if (e->type == KeyPress) {
238         if (e->xkey.keycode == button_escape)
239             end_moveresize(TRUE);
240         else if (e->xkey.keycode == button_return)
241             end_moveresize(FALSE);
242         else {
243             if (corner == prop_atoms.net_wm_moveresize_size_keyboard) {
244                 if (e->xkey.keycode == button_right)
245                     cur_x += MAX(4, client->size_inc.width);
246                 else if (e->xkey.keycode == button_left)
247                     cur_x -= MAX(4, client->size_inc.width);
248                 else if (e->xkey.keycode == button_down)
249                     cur_y += MAX(4, client->size_inc.height);
250                 else if (e->xkey.keycode == button_up)
251                     cur_y -= MAX(4, client->size_inc.height);
252                 else
253                     return;
254                 do_resize();
255             } else if (corner == prop_atoms.net_wm_moveresize_move_keyboard) {
256                 if (e->xkey.keycode == button_right)
257                     cur_x += 4;
258                 else if (e->xkey.keycode == button_left)
259                     cur_x -= 4;
260                 else if (e->xkey.keycode == button_down)
261                     cur_y += 4;
262                 else if (e->xkey.keycode == button_up)
263                     cur_y -= 4;
264                 else
265                     return;
266                 do_move();
267             }
268         }
269     }
270 }