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