]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/moveresize.c
try to fix focus switching with mouse actions up a bit
[mikachu/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) 2004        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, 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     /* get where the client should be */
201     frame_frame_gravity(moveresize_client->frame, &cur_x, &cur_y);
202     client_configure(moveresize_client, OB_CORNER_TOPLEFT, cur_x, cur_y,
203                      moveresize_client->area.width,
204                      moveresize_client->area.height, TRUE, FALSE);
205     if (config_resize_popup_show == 2) /* == "Always" */
206         popup_coords(moveresize_client, "%d x %d",
207                 moveresize_client->frame->area.x,
208                 moveresize_client->frame->area.y);
209 }
210
211 static void do_resize(gboolean resist)
212 {
213     /* resist_size_* needs the frame size */
214     cur_x += moveresize_client->frame->size.left +
215         moveresize_client->frame->size.right;
216     cur_y += moveresize_client->frame->size.top +
217         moveresize_client->frame->size.bottom;
218
219     if (resist)
220         resist_size_windows(moveresize_client, &cur_x, &cur_y, lockcorner);
221     resist_size_monitors(moveresize_client, &cur_x, &cur_y, lockcorner);
222
223     cur_x -= moveresize_client->frame->size.left +
224         moveresize_client->frame->size.right;
225     cur_y -= moveresize_client->frame->size.top +
226         moveresize_client->frame->size.bottom;
227  
228     client_configure(moveresize_client, lockcorner, 
229                      moveresize_client->area.x, moveresize_client->area.y,
230                      cur_x, cur_y, TRUE, FALSE);
231
232     /* this would be better with a fixed width font ... XXX can do it better
233        if there are 2 text boxes */
234     if (config_resize_popup_show == 2 || /* == "Always" */
235             (config_resize_popup_show == 1 && /* == "Nonpixel" */
236                 (moveresize_client->size_inc.width > 1 ||
237                  moveresize_client->size_inc.height > 1))
238         )
239         popup_coords(moveresize_client, "%d x %d",
240                      moveresize_client->logical_size.width,
241                      moveresize_client->logical_size.height);
242 }
243
244 void moveresize_event(XEvent *e)
245 {
246     g_assert(moveresize_in_progress);
247
248     if (e->type == ButtonPress) {
249         if (!button) {
250             start_x = e->xbutton.x_root;
251             start_y = e->xbutton.y_root;
252             button = e->xbutton.button; /* this will end it now */
253         }
254     } else if (e->type == ButtonRelease) {
255         if (!button || e->xbutton.button == button) {
256             moveresize_end(FALSE);
257         }
258     } else if (e->type == MotionNotify) {
259         if (moving) {
260             cur_x = start_cx + e->xmotion.x_root - start_x;
261             cur_y = start_cy + e->xmotion.y_root - start_y;
262             do_move(TRUE);
263         } else {
264             if (corner == prop_atoms.net_wm_moveresize_size_topleft) {
265                 cur_x = start_cw - (e->xmotion.x_root - start_x);
266                 cur_y = start_ch - (e->xmotion.y_root - start_y);
267                 lockcorner = OB_CORNER_BOTTOMRIGHT;
268             } else if (corner == prop_atoms.net_wm_moveresize_size_top) {
269                 cur_x = start_cw;
270                 cur_y = start_ch - (e->xmotion.y_root - start_y);
271                 lockcorner = OB_CORNER_BOTTOMRIGHT;
272             } else if (corner == prop_atoms.net_wm_moveresize_size_topright) {
273                 cur_x = start_cw + (e->xmotion.x_root - start_x);
274                 cur_y = start_ch - (e->xmotion.y_root - start_y);
275                 lockcorner = OB_CORNER_BOTTOMLEFT;
276             } else if (corner == prop_atoms.net_wm_moveresize_size_right) { 
277                 cur_x = start_cw + (e->xmotion.x_root - start_x);
278                 cur_y = start_ch;
279                 lockcorner = OB_CORNER_BOTTOMLEFT;
280             } else if (corner ==
281                        prop_atoms.net_wm_moveresize_size_bottomright) {
282                 cur_x = start_cw + (e->xmotion.x_root - start_x);
283                 cur_y = start_ch + (e->xmotion.y_root - start_y);
284                 lockcorner = OB_CORNER_TOPLEFT;
285             } else if (corner == prop_atoms.net_wm_moveresize_size_bottom) {
286                 cur_x = start_cw;
287                 cur_y = start_ch + (e->xmotion.y_root - start_y);
288                 lockcorner = OB_CORNER_TOPLEFT;
289             } else if (corner ==
290                        prop_atoms.net_wm_moveresize_size_bottomleft) {
291                 cur_x = start_cw - (e->xmotion.x_root - start_x);
292                 cur_y = start_ch + (e->xmotion.y_root - start_y);
293                 lockcorner = OB_CORNER_TOPRIGHT;
294             } else if (corner == prop_atoms.net_wm_moveresize_size_left) {
295                 cur_x = start_cw - (e->xmotion.x_root - start_x);
296                 cur_y = start_ch;
297                 lockcorner = OB_CORNER_TOPRIGHT;
298             } else if (corner == prop_atoms.net_wm_moveresize_size_keyboard) {
299                 cur_x = start_cw + (e->xmotion.x_root - start_x);
300                 cur_y = start_ch + (e->xmotion.y_root - start_y);
301                 lockcorner = OB_CORNER_TOPLEFT;
302             } else
303                 g_assert_not_reached();
304
305             do_resize(TRUE);
306         }
307     } else if (e->type == KeyPress) {
308         if (e->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
309             moveresize_end(TRUE);
310         else if (e->xkey.keycode == ob_keycode(OB_KEY_RETURN))
311             moveresize_end(FALSE);
312         else {
313             if (corner == prop_atoms.net_wm_moveresize_size_keyboard) {
314                 gint dx = 0, dy = 0, ox = cur_x, oy = cur_y;
315
316                 if (e->xkey.keycode == ob_keycode(OB_KEY_RIGHT))
317                     dx = MAX(4, moveresize_client->size_inc.width);
318                 else if (e->xkey.keycode == ob_keycode(OB_KEY_LEFT))
319                     dx = -MAX(4, moveresize_client->size_inc.width);
320                 else if (e->xkey.keycode == ob_keycode(OB_KEY_DOWN))
321                     dy = MAX(4, moveresize_client->size_inc.height);
322                 else if (e->xkey.keycode == ob_keycode(OB_KEY_UP))
323                     dy = -MAX(4, moveresize_client->size_inc.height);
324                 else
325                     return;
326
327                 cur_x += dx;
328                 cur_y += dy;
329                 XWarpPointer(ob_display, None, None, 0, 0, 0, 0, dx, dy);
330                 /* steal the motion events this causes */
331                 XSync(ob_display, FALSE);
332                 {
333                     XEvent ce;
334                     while (XCheckTypedEvent(ob_display, MotionNotify, &ce));
335                 }
336
337                 do_resize(FALSE);
338
339                 /* because the cursor moves even though the window does
340                    not nessesarily (resistance), this adjusts where the curor
341                    thinks it started so that it keeps up with where the window
342                    actually is */
343                 start_x += dx - (cur_x - ox);
344                 start_y += dy - (cur_y - oy);
345             } else if (corner == prop_atoms.net_wm_moveresize_move_keyboard) {
346                 gint dx = 0, dy = 0, ox = cur_x, oy = cur_y;
347                 gint opx, px, opy, py;
348
349                 if (e->xkey.keycode == ob_keycode(OB_KEY_RIGHT))
350                     dx = 4;
351                 else if (e->xkey.keycode == ob_keycode(OB_KEY_LEFT))
352                     dx = -4;
353                 else if (e->xkey.keycode == ob_keycode(OB_KEY_DOWN))
354                     dy = 4;
355                 else if (e->xkey.keycode == ob_keycode(OB_KEY_UP))
356                     dy = -4;
357                 else
358                     return;
359
360                 cur_x += dx;
361                 cur_y += dy;
362                 screen_pointer_pos(&opx, &opy);
363                 XWarpPointer(ob_display, None, None, 0, 0, 0, 0, dx, dy);
364                 /* steal the motion events this causes */
365                 XSync(ob_display, FALSE);
366                 {
367                     XEvent ce;
368                     while (XCheckTypedEvent(ob_display, MotionNotify, &ce));
369                 }
370                 screen_pointer_pos(&px, &py);
371
372                 do_move(FALSE);
373
374                 /* because the cursor moves even though the window does
375                    not nessesarily (resistance), this adjusts where the curor
376                    thinks it started so that it keeps up with where the window
377                    actually is */
378                 start_x += (px - opx) - (cur_x - ox);
379                 start_y += (py - opy) - (cur_y - oy);
380             }
381         }
382     }
383 }