]> icculus.org git repositories - manmower/openbox.git/blob - openbox/moveresize.c
set event_curtime if we're going to use it only
[manmower/openbox.git] / openbox / moveresize.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    moveresize.c for the Openbox window manager
4    Copyright (c) 2006        Mikael Magnusson
5    Copyright (c) 2003        Ben Jansens
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "grab.h"
21 #include "framerender.h"
22 #include "screen.h"
23 #include "prop.h"
24 #include "client.h"
25 #include "frame.h"
26 #include "openbox.h"
27 #include "resist.h"
28 #include "popup.h"
29 #include "moveresize.h"
30 #include "config.h"
31 #include "render/render.h"
32 #include "render/theme.h"
33
34 #include <X11/Xlib.h>
35 #include <glib.h>
36
37 gboolean moveresize_in_progress = FALSE;
38 ObClient *moveresize_client = NULL;
39
40 static gboolean moving = FALSE; /* TRUE - moving, FALSE - resizing */
41
42 static gint start_x, start_y, start_cx, start_cy, start_cw, start_ch;
43 static gint cur_x, cur_y;
44 static guint button;
45 static guint32 corner;
46 static ObCorner lockcorner;
47
48 static ObPopup *popup = NULL;
49
50 static void client_dest(ObClient *client, gpointer data)
51 {
52     if (moveresize_client == client)
53         moveresize_end(TRUE);    
54 }
55
56 void moveresize_startup(gboolean reconfig)
57 {
58     popup = popup_new(FALSE);
59
60     if (!reconfig)
61         client_add_destructor(client_dest, NULL);
62 }
63
64 void moveresize_shutdown(gboolean reconfig)
65 {
66     if (!reconfig) {
67         if (moveresize_in_progress)
68             moveresize_end(FALSE);
69         client_remove_destructor(client_dest);
70     }
71
72     popup_free(popup);
73     popup = NULL;
74 }
75
76 static void popup_coords(ObClient *c, const gchar *format, gint a, gint b)
77 {
78     gchar *text;
79
80     text = g_strdup_printf(format, a, b);
81     if (config_resize_popup_pos == 1) /* == "Top" */
82         popup_position(popup, SouthGravity,
83                        c->frame->area.x
84                      + c->frame->area.width/2,
85                        c->frame->area.y);
86     else /* == "Center" */
87         popup_position(popup, CenterGravity,
88                        c->frame->area.x + c->frame->size.left +
89                        c->area.width / 2,
90                        c->frame->area.y + c->frame->size.top +
91                        c->area.height / 2);
92     popup_show(popup, text);
93     g_free(text);
94 }
95
96 void moveresize_start(ObClient *c, gint x, gint y, guint b, guint32 cnr)
97 {
98     ObCursor cur;
99
100     moving = (cnr == prop_atoms.net_wm_moveresize_move ||
101               cnr == prop_atoms.net_wm_moveresize_move_keyboard);
102
103     if (moveresize_in_progress || !c->frame->visible ||
104         !(moving ?
105           (c->functions & OB_CLIENT_FUNC_MOVE) :
106           (c->functions & OB_CLIENT_FUNC_RESIZE)))
107         return;
108
109     moveresize_client = c;
110     start_cx = c->frame->area.x;
111     start_cy = c->frame->area.y;
112     /* these adjustments for the size_inc make resizing a terminal more
113        friendly. you essentially start the resize in the middle of the
114        increment instead of at 0, so you have to move half an increment
115        either way instead of a full increment one and 1 px the other. and this
116        is one large mother fucking comment. */
117     start_cw = c->area.width + c->size_inc.width / 2;
118     start_ch = c->area.height + c->size_inc.height / 2;
119     start_x = x;
120     start_y = y;
121     corner = cnr;
122     button = b;
123
124     /*
125       have to change start_cx and start_cy if going to do this..
126     if (corner == prop_atoms.net_wm_moveresize_move_keyboard ||
127         corner == prop_atoms.net_wm_moveresize_size_keyboard)
128         XWarpPointer(ob_display, None, c->window, 0, 0, 0, 0,
129                      c->area.width / 2, c->area.height / 2);
130     */
131
132     if (moving) {
133         cur_x = start_cx;
134         cur_y = start_cy;
135     } else {
136         cur_x = start_cw;
137         cur_y = start_ch;
138     }
139
140     moveresize_in_progress = TRUE;
141
142     if (corner == prop_atoms.net_wm_moveresize_size_topleft)
143         cur = OB_CURSOR_NORTHWEST;
144     else if (corner == prop_atoms.net_wm_moveresize_size_top)
145         cur = OB_CURSOR_NORTH;
146     else if (corner == prop_atoms.net_wm_moveresize_size_topright)
147         cur = OB_CURSOR_NORTHEAST;
148     else if (corner == prop_atoms.net_wm_moveresize_size_right)
149         cur = OB_CURSOR_EAST;
150     else if (corner == prop_atoms.net_wm_moveresize_size_bottomright)
151         cur = OB_CURSOR_SOUTHEAST;
152     else if (corner == prop_atoms.net_wm_moveresize_size_bottom)
153         cur = OB_CURSOR_SOUTH;
154     else if (corner == prop_atoms.net_wm_moveresize_size_bottomleft)
155         cur = OB_CURSOR_SOUTHWEST;
156     else if (corner == prop_atoms.net_wm_moveresize_size_left)
157         cur = OB_CURSOR_WEST;
158     else if (corner == prop_atoms.net_wm_moveresize_size_keyboard)
159         cur = OB_CURSOR_SOUTHEAST;
160     else if (corner == prop_atoms.net_wm_moveresize_move)
161         cur = OB_CURSOR_MOVE;
162     else if (corner == prop_atoms.net_wm_moveresize_move_keyboard)
163         cur = OB_CURSOR_MOVE;
164     else
165         g_assert_not_reached();
166
167     grab_pointer(TRUE, cur);
168     grab_keyboard(TRUE);
169 }
170
171 void moveresize_end(gboolean cancel)
172 {
173     grab_keyboard(FALSE);
174     grab_pointer(FALSE, OB_CURSOR_NONE);
175
176     popup_hide(popup);
177
178     if (moving) {
179         client_move(moveresize_client,
180                     (cancel ? start_cx : cur_x),
181                     (cancel ? start_cy : cur_y));
182     } else {
183         client_configure(moveresize_client, lockcorner,
184                          moveresize_client->area.x,
185                          moveresize_client->area.y,
186                          (cancel ? start_cw : cur_x),
187                          (cancel ? start_ch : cur_y), TRUE, TRUE);
188     }
189
190     moveresize_in_progress = FALSE;
191     moveresize_client = NULL;
192 }
193
194 static void do_move(gboolean resist)
195 {
196     if (resist) {
197         resist_move_windows(moveresize_client, &cur_x, &cur_y);
198         resist_move_monitors(moveresize_client, &cur_x, &cur_y);
199     }
200
201     /* get where the client should be */
202     frame_frame_gravity(moveresize_client->frame, &cur_x, &cur_y);
203     client_configure(moveresize_client, OB_CORNER_TOPLEFT, cur_x, cur_y,
204                      moveresize_client->area.width,
205                      moveresize_client->area.height, TRUE, FALSE);
206     if (config_resize_popup_show == 2) /* == "Always" */
207         popup_coords(moveresize_client, "%d x %d",
208                 moveresize_client->frame->area.x,
209                 moveresize_client->frame->area.y);
210 }
211
212 static void do_resize(gboolean resist)
213 {
214     /* resist_size_* needs the frame size */
215     cur_x += moveresize_client->frame->size.left +
216         moveresize_client->frame->size.right;
217     cur_y += moveresize_client->frame->size.top +
218         moveresize_client->frame->size.bottom;
219
220     if (resist) {
221         resist_size_windows(moveresize_client, &cur_x, &cur_y, lockcorner);
222         resist_size_monitors(moveresize_client, &cur_x, &cur_y, lockcorner);
223     }
224
225     cur_x -= moveresize_client->frame->size.left +
226         moveresize_client->frame->size.right;
227     cur_y -= moveresize_client->frame->size.top +
228         moveresize_client->frame->size.bottom;
229  
230     client_configure(moveresize_client, lockcorner, 
231                      moveresize_client->area.x, moveresize_client->area.y,
232                      cur_x, cur_y, TRUE, FALSE);
233
234     /* this would be better with a fixed width font ... XXX can do it better
235        if there are 2 text boxes */
236     if (config_resize_popup_show == 2 || /* == "Always" */
237             (config_resize_popup_show == 1 && /* == "Nonpixel" */
238                 (moveresize_client->size_inc.width > 1 ||
239                  moveresize_client->size_inc.height > 1))
240         )
241         popup_coords(moveresize_client, "%d x %d",
242                      moveresize_client->logical_size.width,
243                      moveresize_client->logical_size.height);
244 }
245
246 void moveresize_event(XEvent *e)
247 {
248     g_assert(moveresize_in_progress);
249
250     if (e->type == ButtonPress) {
251         if (!button) {
252             start_x = e->xbutton.x_root;
253             start_y = e->xbutton.y_root;
254             button = e->xbutton.button; /* this will end it now */
255         }
256     } else if (e->type == ButtonRelease) {
257         if (!button || e->xbutton.button == button) {
258             moveresize_end(FALSE);
259         }
260     } else if (e->type == MotionNotify) {
261         if (moving) {
262             cur_x = start_cx + e->xmotion.x_root - start_x;
263             cur_y = start_cy + e->xmotion.y_root - start_y;
264             do_move(TRUE);
265         } else {
266             if (corner == prop_atoms.net_wm_moveresize_size_topleft) {
267                 cur_x = start_cw - (e->xmotion.x_root - start_x);
268                 cur_y = start_ch - (e->xmotion.y_root - start_y);
269                 lockcorner = OB_CORNER_BOTTOMRIGHT;
270             } else if (corner == prop_atoms.net_wm_moveresize_size_top) {
271                 cur_x = start_cw;
272                 cur_y = start_ch - (e->xmotion.y_root - start_y);
273                 lockcorner = OB_CORNER_BOTTOMRIGHT;
274             } else if (corner == prop_atoms.net_wm_moveresize_size_topright) {
275                 cur_x = start_cw + (e->xmotion.x_root - start_x);
276                 cur_y = start_ch - (e->xmotion.y_root - start_y);
277                 lockcorner = OB_CORNER_BOTTOMLEFT;
278             } else if (corner == prop_atoms.net_wm_moveresize_size_right) { 
279                 cur_x = start_cw + (e->xmotion.x_root - start_x);
280                 cur_y = start_ch;
281                 lockcorner = OB_CORNER_BOTTOMLEFT;
282             } else if (corner ==
283                        prop_atoms.net_wm_moveresize_size_bottomright) {
284                 cur_x = start_cw + (e->xmotion.x_root - start_x);
285                 cur_y = start_ch + (e->xmotion.y_root - start_y);
286                 lockcorner = OB_CORNER_TOPLEFT;
287             } else if (corner == prop_atoms.net_wm_moveresize_size_bottom) {
288                 cur_x = start_cw;
289                 cur_y = start_ch + (e->xmotion.y_root - start_y);
290                 lockcorner = OB_CORNER_TOPLEFT;
291             } else if (corner ==
292                        prop_atoms.net_wm_moveresize_size_bottomleft) {
293                 cur_x = start_cw - (e->xmotion.x_root - start_x);
294                 cur_y = start_ch + (e->xmotion.y_root - start_y);
295                 lockcorner = OB_CORNER_TOPRIGHT;
296             } else if (corner == prop_atoms.net_wm_moveresize_size_left) {
297                 cur_x = start_cw - (e->xmotion.x_root - start_x);
298                 cur_y = start_ch;
299                 lockcorner = OB_CORNER_TOPRIGHT;
300             } else if (corner == prop_atoms.net_wm_moveresize_size_keyboard) {
301                 cur_x = start_cw + (e->xmotion.x_root - start_x);
302                 cur_y = start_ch + (e->xmotion.y_root - start_y);
303                 lockcorner = OB_CORNER_TOPLEFT;
304             } else
305                 g_assert_not_reached();
306
307             do_resize(TRUE);
308         }
309     } else if (e->type == KeyPress) {
310         if (e->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
311             moveresize_end(TRUE);
312         else if (e->xkey.keycode == ob_keycode(OB_KEY_RETURN))
313             moveresize_end(FALSE);
314         else {
315             if (corner == prop_atoms.net_wm_moveresize_size_keyboard) {
316                 gint dx = 0, dy = 0, ox = cur_x, oy = cur_y;
317
318                 if (e->xkey.keycode == ob_keycode(OB_KEY_RIGHT))
319                     dx = MAX(4, moveresize_client->size_inc.width);
320                 else if (e->xkey.keycode == ob_keycode(OB_KEY_LEFT))
321                     dx = -MAX(4, moveresize_client->size_inc.width);
322                 else if (e->xkey.keycode == ob_keycode(OB_KEY_DOWN))
323                     dy = MAX(4, moveresize_client->size_inc.height);
324                 else if (e->xkey.keycode == ob_keycode(OB_KEY_UP))
325                     dy = -MAX(4, moveresize_client->size_inc.height);
326                 else
327                     return;
328
329                 cur_x += dx;
330                 cur_y += dy;
331                 XWarpPointer(ob_display, None, None, 0, 0, 0, 0, dx, dy);
332                 /* steal the motion events this causes */
333                 XSync(ob_display, FALSE);
334                 {
335                     XEvent ce;
336                     while (XCheckTypedEvent(ob_display, MotionNotify, &ce));
337                 }
338
339                 do_resize(FALSE);
340
341                 /* because the cursor moves even though the window does
342                    not nessesarily (resistance), this adjusts where the curor
343                    thinks it started so that it keeps up with where the window
344                    actually is */
345                 start_x += dx - (cur_x - ox);
346                 start_y += dy - (cur_y - oy);
347             } else if (corner == prop_atoms.net_wm_moveresize_move_keyboard) {
348                 gint dx = 0, dy = 0, ox = cur_x, oy = cur_y;
349                 gint opx, px, opy, py;
350
351                 if (e->xkey.keycode == ob_keycode(OB_KEY_RIGHT))
352                     dx = 4;
353                 else if (e->xkey.keycode == ob_keycode(OB_KEY_LEFT))
354                     dx = -4;
355                 else if (e->xkey.keycode == ob_keycode(OB_KEY_DOWN))
356                     dy = 4;
357                 else if (e->xkey.keycode == ob_keycode(OB_KEY_UP))
358                     dy = -4;
359                 else
360                     return;
361
362                 cur_x += dx;
363                 cur_y += dy;
364                 screen_pointer_pos(&opx, &opy);
365                 XWarpPointer(ob_display, None, None, 0, 0, 0, 0, dx, dy);
366                 /* steal the motion events this causes */
367                 XSync(ob_display, FALSE);
368                 {
369                     XEvent ce;
370                     while (XCheckTypedEvent(ob_display, MotionNotify, &ce));
371                 }
372                 screen_pointer_pos(&px, &py);
373
374                 do_move(FALSE);
375
376                 /* because the cursor moves even though the window does
377                    not nessesarily (resistance), this adjusts where the curor
378                    thinks it started so that it keeps up with where the window
379                    actually is */
380                 start_x += (px - opx) - (cur_x - ox);
381                 start_y += (py - opy) - (cur_y - oy);
382             }
383         }
384     }
385 }