warp desktops when you hit the edge of the screen while moving a window
[dana/openbox-history.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-2007   Dana 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 "mainloop.h"
29 #include "popup.h"
30 #include "moveresize.h"
31 #include "config.h"
32 #include "event.h"
33 #include "debug.h"
34 #include "extensions.h"
35 #include "render/render.h"
36 #include "render/theme.h"
37
38 #include <X11/Xlib.h>
39 #include <glib.h>
40
41 /* how far windows move and resize with the keyboard arrows */
42 #define KEY_DIST 4
43
44 gboolean moveresize_in_progress = FALSE;
45 ObClient *moveresize_client = NULL;
46 #ifdef SYNC
47 XSyncAlarm moveresize_alarm = None;
48 #endif
49
50 static gboolean moving = FALSE; /* TRUE - moving, FALSE - resizing */
51
52 static gint start_x, start_y, start_cx, start_cy, start_cw, start_ch;
53 static gint cur_x, cur_y;
54 static guint button;
55 static guint32 corner;
56 static ObCorner lockcorner;
57 static ObDirection edge_warp_dir = -1;
58 #ifdef SYNC
59 static gboolean waiting_for_sync;
60 #endif
61
62 static ObPopup *popup = NULL;
63
64 static void do_edge_warp(gint x, gint y);
65 static void cancel_edge_warp();
66
67 static void client_dest(ObClient *client, gpointer data)
68 {
69     if (moveresize_client == client)
70         moveresize_end(TRUE);    
71 }
72
73 void moveresize_startup(gboolean reconfig)
74 {
75     popup = popup_new(FALSE);
76     popup_set_text_align(popup, RR_JUSTIFY_CENTER);
77
78     if (!reconfig)
79         client_add_destroy_notify(client_dest, NULL);
80 }
81
82 void moveresize_shutdown(gboolean reconfig)
83 {
84     if (!reconfig) {
85         if (moveresize_in_progress)
86             moveresize_end(FALSE);
87         client_remove_destroy_notify(client_dest);
88     }
89
90     popup_free(popup);
91     popup = NULL;
92 }
93
94 static void get_resize_position(gint *x, gint *y, gboolean cancel)
95 {
96     gint dw, dh;
97     gint w, h, lw, lh;
98
99     *x = moveresize_client->frame->area.x;
100     *y = moveresize_client->frame->area.y;
101
102     if (cancel) {
103         w = start_cw;
104         h = start_ch;
105     } else {
106         w = cur_x;
107         h = cur_y;
108     }
109
110     /* see how much it is actually going to resize */
111     {
112         gint cx = *x, cy = *y;
113         frame_frame_gravity(moveresize_client->frame, &cx, &cy);
114         client_try_configure(moveresize_client, &cx, &cy, &w, &h,
115                              &lw, &lh, TRUE);
116     }
117     dw = w - moveresize_client->area.width;
118     dh = h - moveresize_client->area.height;
119
120     switch (lockcorner) {
121     case OB_CORNER_TOPLEFT:
122         break;
123     case OB_CORNER_TOPRIGHT:
124         *x -= dw;
125         break;
126     case OB_CORNER_BOTTOMLEFT:
127         *y -= dh;
128         break;
129     case OB_CORNER_BOTTOMRIGHT:
130         *x -= dw;
131         *y -= dh;
132         break;
133     }
134
135     frame_frame_gravity(moveresize_client->frame, x, y);
136 }
137
138 static void popup_coords(ObClient *c, const gchar *format, gint a, gint b)
139 {
140     gchar *text;
141
142     text = g_strdup_printf(format, a, b);
143     if (config_resize_popup_pos == 1) /* == "Top" */
144         popup_position(popup, SouthGravity,
145                        c->frame->area.x
146                      + c->frame->area.width/2,
147                        c->frame->area.y - ob_rr_theme->fbwidth);
148     else /* == "Center" */
149         popup_position(popup, CenterGravity,
150                        c->frame->area.x + c->frame->size.left +
151                        c->area.width / 2,
152                        c->frame->area.y + c->frame->size.top +
153                        c->area.height / 2);
154     popup_show(popup, text);
155     g_free(text);
156 }
157
158 void moveresize_start(ObClient *c, gint x, gint y, guint b, guint32 cnr)
159 {
160     ObCursor cur;
161     gboolean mv = (cnr == prop_atoms.net_wm_moveresize_move ||
162                    cnr == prop_atoms.net_wm_moveresize_move_keyboard);
163
164     if (moveresize_in_progress || !c->frame->visible ||
165         !(mv ?
166           (c->functions & OB_CLIENT_FUNC_MOVE) :
167           (c->functions & OB_CLIENT_FUNC_RESIZE)))
168         return;
169
170     if (cnr == prop_atoms.net_wm_moveresize_size_topleft)
171         cur = OB_CURSOR_NORTHWEST;
172     else if (cnr == prop_atoms.net_wm_moveresize_size_top)
173         cur = OB_CURSOR_NORTH;
174     else if (cnr == prop_atoms.net_wm_moveresize_size_topright)
175         cur = OB_CURSOR_NORTHEAST;
176     else if (cnr == prop_atoms.net_wm_moveresize_size_right)
177         cur = OB_CURSOR_EAST;
178     else if (cnr == prop_atoms.net_wm_moveresize_size_bottomright)
179         cur = OB_CURSOR_SOUTHEAST;
180     else if (cnr == prop_atoms.net_wm_moveresize_size_bottom)
181         cur = OB_CURSOR_SOUTH;
182     else if (cnr == prop_atoms.net_wm_moveresize_size_bottomleft)
183         cur = OB_CURSOR_SOUTHWEST;
184     else if (cnr == prop_atoms.net_wm_moveresize_size_left)
185         cur = OB_CURSOR_WEST;
186     else if (cnr == prop_atoms.net_wm_moveresize_size_keyboard)
187         cur = OB_CURSOR_SOUTHEAST;
188     else if (cnr == prop_atoms.net_wm_moveresize_move)
189         cur = OB_CURSOR_MOVE;
190     else if (cnr == prop_atoms.net_wm_moveresize_move_keyboard)
191         cur = OB_CURSOR_MOVE;
192     else
193         g_assert_not_reached();
194
195     /* keep the pointer bounded to the screen for move/resize */
196     if (!grab_pointer(FALSE, TRUE, cur))
197         return;
198     if (!grab_keyboard()) {
199         ungrab_pointer();
200         return;
201     }
202
203     frame_end_iconify_animation(c->frame);
204
205     moving = mv;
206     moveresize_client = c;
207     start_cx = c->area.x;
208     start_cy = c->area.y;
209     /* these adjustments for the size_inc make resizing a terminal more
210        friendly. you essentially start the resize in the middle of the
211        increment instead of at 0, so you have to move half an increment
212        either way instead of a full increment one and 1 px the other. and this
213        is one large mother fucking comment. */
214     start_cw = c->area.width + c->size_inc.width / 2;
215     start_ch = c->area.height + c->size_inc.height / 2;
216     start_x = x;
217     start_y = y;
218     corner = cnr;
219     button = b;
220
221     /*
222       have to change start_cx and start_cy if going to do this..
223     if (corner == prop_atoms.net_wm_moveresize_move_keyboard ||
224         corner == prop_atoms.net_wm_moveresize_size_keyboard)
225         XWarpPointer(ob_display, None, c->window, 0, 0, 0, 0,
226                      c->area.width / 2, c->area.height / 2);
227     */
228
229     if (moving) {
230         cur_x = start_cx;
231         cur_y = start_cy;
232     } else {
233         cur_x = start_cw;
234         cur_y = start_ch;
235     }
236
237     moveresize_in_progress = TRUE;
238
239 #ifdef SYNC
240     if (config_resize_redraw && !moving && extensions_shape &&
241         moveresize_client->sync_request && moveresize_client->sync_counter)
242     {
243         /* Initialize values for the resize syncing, and create an alarm for
244            the client's xsync counter */
245
246         XSyncValue val;
247         XSyncAlarmAttributes aa;
248
249         /* set the counter to an initial value */
250         XSyncIntToValue(&val, 0);
251         XSyncSetCounter(ob_display, moveresize_client->sync_counter, val);
252
253         /* this will be incremented when we tell the client what we're
254            looking for */
255         moveresize_client->sync_counter_value = 0;
256
257         /* the next sequence we're waiting for with the alarm */
258         XSyncIntToValue(&val, 1);
259
260         /* set an alarm on the counter */
261         aa.trigger.counter = moveresize_client->sync_counter;
262         aa.trigger.wait_value = val;
263         aa.trigger.value_type = XSyncAbsolute;
264         aa.trigger.test_type = XSyncPositiveTransition;
265         aa.events = True;
266         XSyncIntToValue(&aa.delta, 1);
267         moveresize_alarm = XSyncCreateAlarm(ob_display,
268                                             XSyncCACounter |
269                                             XSyncCAValue |
270                                             XSyncCAValueType |
271                                             XSyncCATestType |
272                                             XSyncCADelta |
273                                             XSyncCAEvents,
274                                             &aa);
275
276         waiting_for_sync = FALSE;
277     }
278 #endif
279 }
280
281 void moveresize_end(gboolean cancel)
282 {
283     gint x, y;
284
285     ungrab_keyboard();
286     ungrab_pointer();
287
288     popup_hide(popup);
289
290     if (moving) {
291         client_move(moveresize_client,
292                     (cancel ? start_cx : cur_x),
293                     (cancel ? start_cy : cur_y));
294     } else {
295 #ifdef SYNC
296         /* turn off the alarm */
297         if (moveresize_alarm != None) {
298             XSyncDestroyAlarm(ob_display, moveresize_alarm);
299             moveresize_alarm = None;
300         }
301 #endif
302
303         get_resize_position(&x, &y, cancel);
304         client_configure(moveresize_client, x, y,
305                          (cancel ? start_cw : cur_x),
306                          (cancel ? start_ch : cur_y),
307                          TRUE, TRUE, FALSE);
308     }
309
310     /* dont edge warp after its ended */
311     cancel_edge_warp();
312
313     moveresize_in_progress = FALSE;
314     moveresize_client = NULL;
315 }
316
317 static void do_move(gboolean keyboard)
318 {
319     gint resist;
320
321     if (keyboard) resist = KEY_DIST - 1; /* resist for one key press */
322     else resist = config_resist_win;
323     resist_move_windows(moveresize_client, resist, &cur_x, &cur_y);
324     if (!keyboard) resist = config_resist_edge;
325     resist_move_monitors(moveresize_client, resist, &cur_x, &cur_y);
326
327     client_configure(moveresize_client, cur_x, cur_y,
328                      moveresize_client->area.width,
329                      moveresize_client->area.height,
330                      TRUE, FALSE, FALSE);
331     if (config_resize_popup_show == 2) /* == "Always" */
332         popup_coords(moveresize_client, "%d x %d",
333                      moveresize_client->frame->area.x,
334                      moveresize_client->frame->area.y);
335 }
336
337 static void do_resize()
338 {
339     gint x, y, w, h, lw, lh;
340
341     /* see if it is actually going to resize */
342     x = 0;
343     y = 0;
344     w = cur_x;
345     h = cur_y;
346     client_try_configure(moveresize_client, &x, &y, &w, &h,
347                          &lw, &lh, TRUE);
348     if (w == moveresize_client->area.width &&
349         h == moveresize_client->area.height)
350     {
351         return;
352     }
353
354 #ifdef SYNC
355     if (config_resize_redraw && extensions_sync &&
356         moveresize_client->sync_request && moveresize_client->sync_counter)
357     {
358         XEvent ce;
359         XSyncValue val;
360
361         /* are we already waiting for the sync counter to catch up? */
362         if (waiting_for_sync)
363             return;
364
365         /* increment the value we're waiting for */
366         ++moveresize_client->sync_counter_value;
367         XSyncIntToValue(&val, moveresize_client->sync_counter_value);
368
369         /* tell the client what we're waiting for */
370         ce.xclient.type = ClientMessage;
371         ce.xclient.message_type = prop_atoms.wm_protocols;
372         ce.xclient.display = ob_display;
373         ce.xclient.window = moveresize_client->window;
374         ce.xclient.format = 32;
375         ce.xclient.data.l[0] = prop_atoms.net_wm_sync_request;
376         ce.xclient.data.l[1] = event_curtime;
377         ce.xclient.data.l[2] = XSyncValueLow32(val);
378         ce.xclient.data.l[3] = XSyncValueHigh32(val);
379         ce.xclient.data.l[4] = 0l;
380         XSendEvent(ob_display, moveresize_client->window, FALSE,
381                    NoEventMask, &ce);
382
383         waiting_for_sync = TRUE;
384     }
385 #endif
386
387     get_resize_position(&x, &y, FALSE);
388     client_configure(moveresize_client, x, y, cur_x, cur_y, TRUE, FALSE, FALSE);
389
390     /* this would be better with a fixed width font ... XXX can do it better
391        if there are 2 text boxes */
392     if (config_resize_popup_show == 2 || /* == "Always" */
393             (config_resize_popup_show == 1 && /* == "Nonpixel" */
394              moveresize_client->size_inc.width > 1 &&
395              moveresize_client->size_inc.height > 1))
396         popup_coords(moveresize_client, "%d x %d",
397                      moveresize_client->logical_size.width,
398                      moveresize_client->logical_size.height);
399 }
400
401 static void calc_resize(gboolean keyboard)
402 {
403     gint resist;
404
405     /* resist_size_* needs the frame size */
406     cur_x += moveresize_client->frame->size.left +
407         moveresize_client->frame->size.right;
408     cur_y += moveresize_client->frame->size.top +
409         moveresize_client->frame->size.bottom;
410
411     if (keyboard) resist = KEY_DIST - 1; /* resist for one key press */
412     else resist = config_resist_win;
413     resist_size_windows(moveresize_client, resist, &cur_x, &cur_y, lockcorner);
414     if (!keyboard) resist = config_resist_edge;
415     resist_size_monitors(moveresize_client, resist, &cur_x, &cur_y,lockcorner);
416
417     cur_x -= moveresize_client->frame->size.left +
418         moveresize_client->frame->size.right;
419     cur_y -= moveresize_client->frame->size.top +
420         moveresize_client->frame->size.bottom;
421 }
422
423 static gboolean edge_warp_delay_func(gpointer data)
424 {
425     guint d;
426
427     d = screen_find_desktop(screen_desktop, edge_warp_dir, TRUE, FALSE);
428     if (d != screen_desktop) screen_set_desktop(d, TRUE);
429
430     edge_warp_dir = -1;
431
432     return FALSE; /* don't repeat */
433 }
434
435 static void do_edge_warp(gint x, gint y)
436 {
437     guint i, d;
438     ObDirection dir;
439
440     if (!config_mouse_screenedgetime) return;
441
442     dir = -1;
443
444     for (i = 0; i < screen_num_monitors; ++i) {
445         Rect *a = screen_physical_area_monitor(i);
446         if (x == RECT_LEFT(*a)) dir = OB_DIRECTION_WEST;
447         if (x == RECT_RIGHT(*a)) dir = OB_DIRECTION_EAST;
448         if (y == RECT_TOP(*a)) dir = OB_DIRECTION_NORTH;
449         if (y == RECT_BOTTOM(*a)) dir = OB_DIRECTION_SOUTH;
450
451         /* try check for xinerama boundaries */
452         if ((x + 1 == RECT_LEFT(*a) || x - 1 == RECT_RIGHT(*a)) &&
453             (dir == OB_DIRECTION_WEST || dir == OB_DIRECTION_EAST))
454         {
455             dir = -1;
456         }
457         if ((y + 1 == RECT_TOP(*a) || y - 1 == RECT_BOTTOM(*a)) &&
458             (dir == OB_DIRECTION_NORTH || dir == OB_DIRECTION_SOUTH))
459         {
460             dir = -1;
461         }
462         g_free(a);
463     }
464
465     if (dir != edge_warp_dir) {
466         if (dir == (ObDirection)-1)
467             cancel_edge_warp();
468         else
469             ob_main_loop_timeout_add(ob_main_loop,
470                                      config_mouse_screenedgetime * 1000,
471                                      edge_warp_delay_func,
472                                      NULL, NULL, NULL);
473         edge_warp_dir = dir;
474     }
475 }
476
477 static void cancel_edge_warp()
478 {
479     ob_main_loop_timeout_remove(ob_main_loop, edge_warp_delay_func);
480 }
481
482 gboolean moveresize_event(XEvent *e)
483 {
484     gboolean used = FALSE;
485
486     if (!moveresize_in_progress) return FALSE;
487
488     if (e->type == ButtonPress) {
489         if (!button) {
490             start_x = e->xbutton.x_root;
491             start_y = e->xbutton.y_root;
492             button = e->xbutton.button; /* this will end it now */
493         }
494         used = e->xbutton.button == button;
495     } else if (e->type == ButtonRelease) {
496         if (!button || e->xbutton.button == button) {
497             moveresize_end(FALSE);
498             used = TRUE;
499         }
500     } else if (e->type == MotionNotify) {
501         if (moving) {
502             cur_x = start_cx + e->xmotion.x_root - start_x;
503             cur_y = start_cy + e->xmotion.y_root - start_y;
504             do_move(FALSE);
505             do_edge_warp(e->xmotion.x_root, e->xmotion.y_root);
506         } else {
507             if (corner == prop_atoms.net_wm_moveresize_size_topleft) {
508                 cur_x = start_cw - (e->xmotion.x_root - start_x);
509                 cur_y = start_ch - (e->xmotion.y_root - start_y);
510                 lockcorner = OB_CORNER_BOTTOMRIGHT;
511             } else if (corner == prop_atoms.net_wm_moveresize_size_top) {
512                 cur_x = start_cw;
513                 cur_y = start_ch - (e->xmotion.y_root - start_y);
514                 lockcorner = OB_CORNER_BOTTOMRIGHT;
515             } else if (corner == prop_atoms.net_wm_moveresize_size_topright) {
516                 cur_x = start_cw + (e->xmotion.x_root - start_x);
517                 cur_y = start_ch - (e->xmotion.y_root - start_y);
518                 lockcorner = OB_CORNER_BOTTOMLEFT;
519             } else if (corner == prop_atoms.net_wm_moveresize_size_right) { 
520                 cur_x = start_cw + (e->xmotion.x_root - start_x);
521                 cur_y = start_ch;
522                 lockcorner = OB_CORNER_BOTTOMLEFT;
523             } else if (corner ==
524                        prop_atoms.net_wm_moveresize_size_bottomright) {
525                 cur_x = start_cw + (e->xmotion.x_root - start_x);
526                 cur_y = start_ch + (e->xmotion.y_root - start_y);
527                 lockcorner = OB_CORNER_TOPLEFT;
528             } else if (corner == prop_atoms.net_wm_moveresize_size_bottom) {
529                 cur_x = start_cw;
530                 cur_y = start_ch + (e->xmotion.y_root - start_y);
531                 lockcorner = OB_CORNER_TOPLEFT;
532             } else if (corner ==
533                        prop_atoms.net_wm_moveresize_size_bottomleft) {
534                 cur_x = start_cw - (e->xmotion.x_root - start_x);
535                 cur_y = start_ch + (e->xmotion.y_root - start_y);
536                 lockcorner = OB_CORNER_TOPRIGHT;
537             } else if (corner == prop_atoms.net_wm_moveresize_size_left) {
538                 cur_x = start_cw - (e->xmotion.x_root - start_x);
539                 cur_y = start_ch;
540                 lockcorner = OB_CORNER_TOPRIGHT;
541             } else if (corner == prop_atoms.net_wm_moveresize_size_keyboard) {
542                 cur_x = start_cw + (e->xmotion.x_root - start_x);
543                 cur_y = start_ch + (e->xmotion.y_root - start_y);
544                 lockcorner = OB_CORNER_TOPLEFT;
545             } else
546                 g_assert_not_reached();
547
548             calc_resize(FALSE);
549             do_resize();
550         }
551         used = TRUE;
552     } else if (e->type == KeyPress) {
553         if (e->xkey.keycode == ob_keycode(OB_KEY_ESCAPE)) {
554             moveresize_end(TRUE);
555             used = TRUE;
556         } else if (e->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
557             moveresize_end(FALSE);
558             used = TRUE;
559         } else if (e->xkey.keycode == ob_keycode(OB_KEY_RIGHT) ||
560                    e->xkey.keycode == ob_keycode(OB_KEY_LEFT) ||
561                    e->xkey.keycode == ob_keycode(OB_KEY_DOWN) ||
562                    e->xkey.keycode == ob_keycode(OB_KEY_UP))
563         {
564             if (corner == prop_atoms.net_wm_moveresize_size_keyboard) {
565                 gint dx = 0, dy = 0, ox = cur_x, oy = cur_y;
566
567                 if (e->xkey.keycode == ob_keycode(OB_KEY_RIGHT))
568                     dx = MAX(KEY_DIST, moveresize_client->size_inc.width);
569                 else if (e->xkey.keycode == ob_keycode(OB_KEY_LEFT))
570                     dx = -MAX(KEY_DIST, moveresize_client->size_inc.width);
571                 else if (e->xkey.keycode == ob_keycode(OB_KEY_DOWN))
572                     dy = MAX(KEY_DIST, moveresize_client->size_inc.height);
573                 else /* if (e->xkey.keycode == ob_keycode(OB_KEY_UP)) */
574                     dy = -MAX(KEY_DIST, moveresize_client->size_inc.height);
575
576                 cur_x += dx;
577                 cur_y += dy;
578                 XWarpPointer(ob_display, None, None, 0, 0, 0, 0, dx, dy);
579                 /* steal the motion events this causes */
580                 XSync(ob_display, FALSE);
581                 {
582                     XEvent ce;
583                     while (XCheckTypedEvent(ob_display, MotionNotify, &ce));
584                 }
585
586                 calc_resize(TRUE);
587                 do_resize();
588
589                 /* because the cursor moves even though the window does
590                    not nessesarily (resistance), this adjusts where the curor
591                    thinks it started so that it keeps up with where the window
592                    actually is */
593                 start_x += dx - (cur_x - ox);
594                 start_y += dy - (cur_y - oy);
595
596                 used = TRUE;
597             } else if (corner == prop_atoms.net_wm_moveresize_move_keyboard) {
598                 gint dx = 0, dy = 0, ox = cur_x, oy = cur_y;
599                 gint opx, px, opy, py;
600
601                 if (e->xkey.keycode == ob_keycode(OB_KEY_RIGHT))
602                     dx = KEY_DIST;
603                 else if (e->xkey.keycode == ob_keycode(OB_KEY_LEFT))
604                     dx = -KEY_DIST;
605                 else if (e->xkey.keycode == ob_keycode(OB_KEY_DOWN))
606                     dy = KEY_DIST;
607                 else /* if (e->xkey.keycode == ob_keycode(OB_KEY_UP)) */
608                     dy = -KEY_DIST;
609
610                 cur_x += dx;
611                 cur_y += dy;
612                 screen_pointer_pos(&opx, &opy);
613                 XWarpPointer(ob_display, None, None, 0, 0, 0, 0, dx, dy);
614                 /* steal the motion events this causes */
615                 XSync(ob_display, FALSE);
616                 {
617                     XEvent ce;
618                     while (XCheckTypedEvent(ob_display, MotionNotify, &ce));
619                 }
620                 screen_pointer_pos(&px, &py);
621
622                 do_move(TRUE);
623
624                 /* because the cursor moves even though the window does
625                    not nessesarily (resistance), this adjusts where the curor
626                    thinks it started so that it keeps up with where the window
627                    actually is */
628                 start_x += (px - opx) - (cur_x - ox);
629                 start_y += (py - opy) - (cur_y - oy);
630
631                 used = TRUE;
632             }
633         }
634     }
635 #ifdef SYNC
636     else if (e->type == extensions_sync_event_basep + XSyncAlarmNotify)
637     {
638         waiting_for_sync = FALSE; /* we got our sync... */
639         do_resize(); /* ...so try resize if there is more change pending */
640         used = TRUE;
641     }
642 #endif
643     return used;
644 }