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