rename the Client struct to ObClient
[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 "dispatch.h"
7 #include "openbox.h"
8 #include "popup.h"
9 #include "config.h"
10 #include "render/render.h"
11 #include "render/theme.h"
12
13 #include <X11/Xlib.h>
14 #include <glib.h>
15
16 gboolean moveresize_in_progress = FALSE;
17 ObClient *moveresize_client = NULL;
18
19 static gboolean moving = FALSE; /* TRUE - moving, FALSE - resizing */
20
21 static int start_x, start_y, start_cx, start_cy, start_cw, start_ch;
22 static int cur_x, cur_y;
23 static guint button;
24 static guint32 corner;
25 static ObCorner lockcorner;
26
27 static Popup *popup = NULL;
28 static InternalWindow opaque_window = { { Window_Internal }, None };
29 static GC opaque_gc = None;
30 static gboolean first_draw = FALSE;
31
32 #define POPUP_X (10)
33 #define POPUP_Y (10)
34
35 void moveresize_startup()
36 {
37     XSetWindowAttributes attrib;
38     XGCValues gcv;
39
40     popup = popup_new(FALSE);
41     popup_size_to_string(popup, "W:  0000  W:  0000");
42
43     attrib.save_under = True;
44     opaque_window.win = XCreateWindow(ob_display, ob_root, 0, 0, 1, 1, 0,
45                                       RrDepth(ob_rr_inst), InputOutput,
46                                       RrVisual(ob_rr_inst),
47                                       CWSaveUnder, &attrib);
48     stacking_add(INTERNAL_AS_WINDOW(&opaque_window));
49     stacking_raise(INTERNAL_AS_WINDOW(&opaque_window));
50
51     /* a GC to invert stuff */
52     gcv.function = GXxor;
53     gcv.line_width = ob_rr_theme->bwidth;
54     gcv.foreground = (WhitePixel(ob_display, ob_screen) ^
55                       BlackPixel(ob_display, ob_screen));
56     opaque_gc = XCreateGC(ob_display, opaque_window.win,
57                           GCFunction | GCForeground | GCLineWidth, &gcv);
58 }
59
60 void moveresize_shutdown()
61 {
62     popup_free(popup);
63     popup = NULL;
64     stacking_remove(&opaque_window);
65     XFreeGC(ob_display, opaque_gc);
66     XDestroyWindow(ob_display, opaque_window.win);
67 }
68
69 static void popup_coords(char *format, int a, int b)
70 {
71     char *text;
72     Rect *area;
73
74     text = g_strdup_printf(format, a, b);
75     area = screen_physical_area_monitor(0);
76     popup_position(popup, NorthWestGravity,
77                    POPUP_X + area->x, POPUP_Y + area->y);
78     popup_show(popup, text, NULL);
79     g_free(text);
80 }
81
82 void moveresize_start(ObClient *c, int x, int y, guint b, guint32 cnr)
83 {
84     ObCursor cur;
85     Rect *a;
86
87     g_assert(!moveresize_in_progress);
88
89     moveresize_client = c;
90     start_cx = c->frame->area.x;
91     start_cy = c->frame->area.y;
92     start_cw = c->area.width;
93     start_ch = c->area.height;
94     start_x = x;
95     start_y = y;
96     if (corner == prop_atoms.net_wm_moveresize_move_keyboard ||
97         corner == prop_atoms.net_wm_moveresize_size_keyboard)
98         button = 0; /* mouse can't end it without being pressed first */
99     else
100         button = b;
101     corner = cnr;
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     a = screen_physical_area();
145
146     XMoveResizeWindow(ob_display, opaque_window.win,
147                       a->x, a->y, a->width, a->height);
148     stacking_raise(INTERNAL_AS_WINDOW(&opaque_window));
149     if (corner == prop_atoms.net_wm_moveresize_move ||
150         corner == prop_atoms.net_wm_moveresize_move_keyboard) {
151         if (!config_opaque_move)
152             XMapWindow(ob_display, opaque_window.win);
153     } else {
154         if (!config_opaque_resize)
155             XMapWindow(ob_display, opaque_window.win);
156     }
157     first_draw = TRUE;
158 }
159
160 void moveresize_end(gboolean cancel)
161 {
162     XUnmapWindow(ob_display, opaque_window.win);
163
164     grab_keyboard(FALSE);
165     grab_pointer(FALSE, None);
166
167     popup_hide(popup);
168
169     if (moving) {
170         client_configure(moveresize_client, OB_CORNER_TOPLEFT,
171                          (cancel ? start_cx : cur_x),
172                          (cancel ? start_cy : cur_y),
173                          start_cw, start_ch, TRUE, TRUE);
174     } else {
175         client_configure(moveresize_client, lockcorner,
176                          moveresize_client->area.x,
177                          moveresize_client->area.y,
178                          (cancel ? start_cw : cur_x),
179                          (cancel ? start_ch : cur_y), TRUE, TRUE);
180     }
181
182     moveresize_in_progress = FALSE;
183     moveresize_client = NULL;
184 }
185
186 static void do_move()
187 {
188     int oldx, oldy, oldw, oldh;
189
190     dispatch_move(moveresize_client, &cur_x, &cur_y);
191
192     oldx = moveresize_client->frame->area.x;
193     oldy = moveresize_client->frame->area.y;
194     oldw = moveresize_client->frame->area.width;
195     oldh = moveresize_client->frame->area.height;
196     /* get where the client should be */
197     frame_frame_gravity(moveresize_client->frame, &cur_x, &cur_y);
198     client_configure(moveresize_client, OB_CORNER_TOPLEFT, cur_x, cur_y,
199                      start_cw, start_ch, TRUE, FALSE);
200     /* draw the new one */
201     if (moveresize_client->frame->area.x != oldx ||
202         moveresize_client->frame->area.y != oldy ||
203         moveresize_client->frame->area.width != oldw ||
204         moveresize_client->frame->area.height != oldh) {
205         if (!config_opaque_move)
206             XDrawRectangle(ob_display, opaque_window.win, opaque_gc,
207                            moveresize_client->frame->area.x,
208                            moveresize_client->frame->area.y,
209                            moveresize_client->frame->area.width - 1,
210                            moveresize_client->frame->area.height - 1);
211         /* erase the old one */
212         if (!config_opaque_move && !first_draw)
213             XDrawRectangle(ob_display, opaque_window.win, opaque_gc,
214                            oldx, oldy, oldw - 1, oldh - 1);
215         first_draw = FALSE;
216     }
217
218     /* this would be better with a fixed width font ... XXX can do it better
219        if there are 2 text boxes */
220     popup_coords("X:  %4d  Y:  %4d", moveresize_client->frame->area.x,
221                  moveresize_client->frame->area.y);
222 }
223
224 static void do_resize()
225 {
226     int oldx, oldy, oldw, oldh;
227
228     /* dispatch_resize needs the frame size */
229     cur_x += moveresize_client->frame->size.left +
230         moveresize_client->frame->size.right;
231     cur_y += moveresize_client->frame->size.top +
232         moveresize_client->frame->size.bottom;
233
234     dispatch_resize(moveresize_client, &cur_x, &cur_y, lockcorner);
235
236     cur_x -= moveresize_client->frame->size.left +
237         moveresize_client->frame->size.right;
238     cur_y -= moveresize_client->frame->size.top +
239         moveresize_client->frame->size.bottom;
240     
241     oldx = moveresize_client->frame->area.x;
242     oldy = moveresize_client->frame->area.y;
243     oldw = moveresize_client->frame->area.width;
244     oldh = moveresize_client->frame->area.height;
245     client_configure(moveresize_client, lockcorner, 
246                      moveresize_client->area.x, moveresize_client->area.y,
247                      cur_x, cur_y, TRUE, FALSE);
248     /* draw the new one */
249     if (!config_opaque_resize)
250         XDrawRectangle(ob_display, opaque_window.win, opaque_gc,
251                        moveresize_client->frame->area.x,
252                        moveresize_client->frame->area.y,
253                        moveresize_client->frame->area.width - 1,
254                        moveresize_client->frame->area.height - 1);
255     /* erase the old one */
256     if (!config_opaque_resize && !first_draw)
257         XDrawRectangle(ob_display, opaque_window.win, opaque_gc,
258                        oldx, oldy, oldw - 1, oldh - 1);
259     first_draw = FALSE;
260
261     /* this would be better with a fixed width font ... XXX can do it better
262        if there are 2 text boxes */
263     popup_coords("W:  %4d  H:  %4d", moveresize_client->logical_size.width,
264                  moveresize_client->logical_size.height);
265 }
266
267 void moveresize_event(XEvent *e)
268 {
269     g_assert(moveresize_in_progress);
270
271     if (e->type == ButtonPress) {
272         if (!button) {
273             start_x = e->xbutton.x_root;
274             start_y = e->xbutton.y_root;
275             button = e->xbutton.button; /* this will end it now */
276         }
277     } else if (e->type == ButtonRelease) {
278         if (!button || e->xbutton.button == button) {
279             moveresize_end(FALSE);
280         }
281     } else if (e->type == MotionNotify) {
282         if (moving) {
283             cur_x = start_cx + e->xmotion.x_root - start_x;
284             cur_y = start_cy + e->xmotion.y_root - start_y;
285             do_move();
286         } else {
287             if (corner == prop_atoms.net_wm_moveresize_size_topleft) {
288                 cur_x = start_cw - (e->xmotion.x_root - start_x);
289                 cur_y = start_ch - (e->xmotion.y_root - start_y);
290                 lockcorner = OB_CORNER_BOTTOMRIGHT;
291             } else if (corner == prop_atoms.net_wm_moveresize_size_top) {
292                 cur_x = start_cw;
293                 cur_y = start_ch - (e->xmotion.y_root - start_y);
294                 lockcorner = OB_CORNER_BOTTOMRIGHT;
295             } else if (corner == prop_atoms.net_wm_moveresize_size_topright) {
296                 cur_x = start_cw + (e->xmotion.x_root - start_x);
297                 cur_y = start_ch - (e->xmotion.y_root - start_y);
298                 lockcorner = OB_CORNER_BOTTOMLEFT;
299             } else if (corner == prop_atoms.net_wm_moveresize_size_right) { 
300                 cur_x = start_cw + (e->xmotion.x_root - start_x);
301                 cur_y = start_ch;
302                 lockcorner = OB_CORNER_BOTTOMLEFT;
303             } else if (corner ==
304                        prop_atoms.net_wm_moveresize_size_bottomright) {
305                 cur_x = start_cw + (e->xmotion.x_root - start_x);
306                 cur_y = start_ch + (e->xmotion.y_root - start_y);
307                 lockcorner = OB_CORNER_TOPLEFT;
308             } else if (corner == prop_atoms.net_wm_moveresize_size_bottom) {
309                 cur_x = start_cw;
310                 cur_y = start_ch + (e->xmotion.y_root - start_y);
311                 lockcorner = OB_CORNER_TOPLEFT;
312             } else if (corner ==
313                        prop_atoms.net_wm_moveresize_size_bottomleft) {
314                 cur_x = start_cw - (e->xmotion.x_root - start_x);
315                 cur_y = start_ch + (e->xmotion.y_root - start_y);
316                 lockcorner = OB_CORNER_TOPRIGHT;
317             } else if (corner == prop_atoms.net_wm_moveresize_size_left) {
318                 cur_x = start_cw - (e->xmotion.x_root - start_x);
319                 cur_y = start_ch;
320                 lockcorner = OB_CORNER_TOPRIGHT;
321             } else if (corner == prop_atoms.net_wm_moveresize_size_keyboard) {
322                 cur_x = start_cw + (e->xmotion.x_root - start_x);
323                 cur_y = start_ch + (e->xmotion.y_root - start_y);
324                 lockcorner = OB_CORNER_TOPLEFT;
325             } else
326                 g_assert_not_reached();
327
328             do_resize();
329         }
330     } else if (e->type == KeyPress) {
331         if (e->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
332             moveresize_end(TRUE);
333         else if (e->xkey.keycode == ob_keycode(OB_KEY_RETURN))
334             moveresize_end(FALSE);
335         else {
336             if (corner == prop_atoms.net_wm_moveresize_size_keyboard) {
337                 if (e->xkey.keycode == ob_keycode(OB_KEY_RIGHT))
338                     cur_x += MAX(4, moveresize_client->size_inc.width);
339                 else if (e->xkey.keycode == ob_keycode(OB_KEY_LEFT))
340                     cur_x -= MAX(4, moveresize_client->size_inc.width);
341                 else if (e->xkey.keycode == ob_keycode(OB_KEY_DOWN))
342                     cur_y += MAX(4, moveresize_client->size_inc.height);
343                 else if (e->xkey.keycode == ob_keycode(OB_KEY_UP))
344                     cur_y -= MAX(4, moveresize_client->size_inc.height);
345                 else
346                     return;
347                 do_resize();
348             } else if (corner == prop_atoms.net_wm_moveresize_move_keyboard) {
349                 if (e->xkey.keycode == ob_keycode(OB_KEY_RIGHT))
350                     cur_x += 4;
351                 else if (e->xkey.keycode == ob_keycode(OB_KEY_LEFT))
352                     cur_x -= 4;
353                 else if (e->xkey.keycode == ob_keycode(OB_KEY_DOWN))
354                     cur_y += 4;
355                 else if (e->xkey.keycode == ob_keycode(OB_KEY_UP))
356                     cur_y -= 4;
357                 else
358                     return;
359                 do_move();
360             }
361         }
362     }
363 }