]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkgeometry-x11.c
Fix serial number comparison regression introduced recently. (#474897)
[~andy/gtk] / gdk / x11 / gdkgeometry-x11.c
1 /* GDK - The GIMP Drawing Kit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /* gdkgeometry-x11.c: emulation of 32 bit coordinates within the
21  * limits of X. 
22  *
23  * By Owen Taylor <otaylor@redhat.com>
24  * Copyright Red Hat, Inc. 2000
25  *
26  * The algorithms implemented in this file are an extension of the
27  * idea of guffaw scrolling, a technique (and name) taken from the classic
28  * Netscape source code. The basic idea of guffaw scrolling is a trick
29  * to get around a limitation of X: there is no way of scrolling the
30  * contents of a window. Guffaw scrolling exploits the X concepts of
31  * window gravity and bit gravity:
32  *
33  *  window gravity: the window gravity of a window affects what happens
34  *   to a windows position when _its parent_ is resized, or
35  *   moved and resized simultaneously.
36  *
37  *  bit gravity: the bit gravity of a window affects what happens to
38  *  the pixels of a window when _it_ is is resized, or moved and
39  *  resized simultaneously.
40  *
41  * These were basically intended to do things like have right
42  * justified widgets in a window automatically stay right justified
43  * when the window was resized, but there is also the special
44  * "StaticGravity" which means "do nothing." We can exploit
45  * StaticGravity to scroll a window:
46  *
47  *     |  VISIBLE  |
48  * 
49  *     |abcdefghijk|
50  *     |abcdefghijk    |   (1) Resize bigger
51  * |    efghijk    |       (2) Move  
52  *     |efghijk    |       (3) Move-resize back to the original size
53  *
54  * Or, going the other way:
55
56  *     |abcdefghijk|
57  * |    abcdefghijk|       (1) Move-resize bigger
58  *     |    abcdefghijk|   (2) Move  
59  *     |    abcdefg|       (4) Resize back to the original size
60  *
61  * By using this technique, we can simulate scrolling around in a
62  * large virtual space without having to actually have windows that
63  * big; for the pixels of the window, this is all we have to do.  For
64  * subwindows, we have to take care of one other detail - since
65  * coordinates in X are limited to 16 bits, subwindows scrolled off
66  * will wrap around and come back eventually. So, we have to take care
67  * to unmap windows that go outside the 16-bit range and remap them as
68  * they come back in.
69  *
70  * Since we are temporarily making the window bigger, this only looks
71  * good if the edges of the window are obscured. Typically, we do
72  * this by making the window we are scrolling the immediate child
73  * of a "clip window".
74  *
75  * But, this isn't a perfect API for applications for several reasons:
76  *
77  *  - We have to use this inefficient technique even for small windows
78  *    if the window _could_ be big.
79  *  - Applications have to use a special scrolling API.
80  *
81  * What we'd like is to simply have windows with 32 bit coordinates
82  * so applications could scroll in the classic way - just move a big
83  * window around.
84  *
85  * It turns out that StaticGravity can also be used to achieve emulation
86  * of 32 bit coordinates with only 16 bit coordinates if we expand
87  * our horizons just a bit; what guffaw scrolling really is is a way
88  * to move the contents of a window a different amount than we move
89  * the borders of of the window. In the above example pictures we
90  * ended up with the borders of the window not moving at all, but
91  * that isn't necessary.
92  *
93  * So, what we do is set up a mapping from virtual 32 bit window position/size
94  * to:
95  *
96  *  - Real window position/size
97  *  - Offset between virtual coordinates and real coordinates for the window
98  *  - Map state (mapped or unmapped)
99  *
100  * By the following rules:
101  *
102  *  - If the window is less than 32767 pixels in width (resp. height), we use it's
103  *    virtual width and position.
104  *  - Otherwise, we use a width of 32767 and determine the position of the window
105  *    so that the portion of the real window [16384, 16383] in _toplevel window
106  *    coordinates_ is the same as the portion of the real window 
107  *
108  * This is implemented in gdk_window_compute_position(). Then the algorithm
109  * for a moving a window (_window_move_resize_child ()) is:
110  * 
111  *  - Compute the new window mappings for the window and all subwindows
112  *  - Expand out the boundary of the window and all subwindows by the amount
113  *    that the real/virtual offset changes for each window. 
114  *    (compute_intermediate_position() computes expanded boundary)
115  *  - Move the toplevel by the amount that it's contents need to translate.
116  *  - Move/resize the window and all subwindows to the newly computed
117  *    positions.
118  *
119  * If we just are scrolling (gdk_window_guffaw_scroll()), then things
120  * are similar, except that the final mappings for the toplevel are
121  * the same as the initial mappings, but we act as if it moved by the
122  * amount we are scrolling by.
123  *
124  * Note that we don't have to worry about a clip window in
125  * _gdk_window_move_resize() since we have set up our translation so
126  * that things in the range [16384,16383] in toplevel window
127  * coordinates look exactly as they would if we were simply moving the
128  * windows, and nothing outside this range is going to be visible
129  * unless the user has a _really_ huge screen.
130  */
131
132 #include <config.h>
133 #include "gdk.h"                /* For gdk_rectangle_intersect */
134 #include "gdkprivate-x11.h"
135 #include "gdkx.h"
136 #include "gdkregion.h"
137 #include "gdkinternals.h"
138 #include "gdkscreen-x11.h"
139 #include "gdkdisplay-x11.h"
140 #include "gdkwindow-x11.h"
141 #include "gdkalias.h"
142
143 typedef struct _GdkWindowQueueItem GdkWindowQueueItem;
144 typedef struct _GdkWindowParentPos GdkWindowParentPos;
145
146 typedef enum {
147   GDK_WINDOW_QUEUE_TRANSLATE,
148   GDK_WINDOW_QUEUE_ANTIEXPOSE
149 } GdkWindowQueueType;
150
151 struct _GdkWindowQueueItem
152 {
153   GdkWindow *window;
154   gulong serial;
155   GdkWindowQueueType type;
156   union {
157     struct {
158       GdkRegion *area;
159       gint dx;
160       gint dy;
161     } translate;
162     struct {
163       GdkRegion *area;
164     } antiexpose;
165   } u;
166 };
167
168 struct _GdkWindowParentPos
169 {
170   gint x;
171   gint y;
172   gint x11_x;
173   gint x11_y;
174   GdkRectangle clip_rect;
175 };
176
177 static void gdk_window_compute_position   (GdkWindowImplX11      *window,
178                                            GdkWindowParentPos *parent_pos,
179                                            GdkXPositionInfo   *info);
180 static void gdk_window_compute_parent_pos (GdkWindowImplX11      *window,
181                                            GdkWindowParentPos *parent_pos);
182 static void gdk_window_premove            (GdkWindow          *window,
183                                            GdkWindowParentPos *parent_pos);
184 static void gdk_window_postmove           (GdkWindow          *window,
185                                            GdkWindowParentPos *parent_pos);
186 static void gdk_window_queue_translation  (GdkWindow          *window,
187                                            GdkRegion          *area,
188                                            gint                dx,
189                                            gint                dy);
190 static void gdk_window_clip_changed       (GdkWindow          *window,
191                                            GdkRectangle       *old_clip,
192                                            GdkRectangle       *new_clip);
193
194 void
195 _gdk_windowing_window_get_offsets (GdkWindow *window,
196                                    gint      *x_offset,
197                                    gint      *y_offset)
198 {
199   GdkWindowImplX11 *impl =
200     GDK_WINDOW_IMPL_X11 (GDK_WINDOW_OBJECT (window)->impl);
201
202   *x_offset = impl->position_info.x_offset;
203   *y_offset = impl->position_info.y_offset;
204 }
205
206 void
207 _gdk_window_init_position (GdkWindow *window)
208 {
209   GdkWindowParentPos parent_pos;
210   GdkWindowImplX11 *impl;
211   
212   g_return_if_fail (GDK_IS_WINDOW (window));
213   
214   impl =
215     GDK_WINDOW_IMPL_X11 (GDK_WINDOW_OBJECT (window)->impl);
216   
217   gdk_window_compute_parent_pos (impl, &parent_pos);
218   gdk_window_compute_position (impl, &parent_pos, &impl->position_info);
219 }
220
221 static void
222 gdk_window_copy_area_scroll (GdkWindow    *window,
223                              GdkRectangle *dest_rect,
224                              gint          dx,
225                              gint          dy)
226 {
227   GdkWindowObject *obj = GDK_WINDOW_OBJECT (window);
228   GList *l;
229
230   if (dest_rect->width > 0 && dest_rect->height > 0)
231     {
232       GdkGC *gc;
233
234       gc = _gdk_drawable_get_scratch_gc (window, TRUE);
235       
236       gdk_window_queue_translation (window, NULL, dx, dy);
237    
238       XCopyArea (GDK_WINDOW_XDISPLAY (window),
239                  GDK_WINDOW_XID (window),
240                  GDK_WINDOW_XID (window),
241                  gdk_x11_gc_get_xgc (gc),
242                  dest_rect->x - dx, dest_rect->y - dy,
243                  dest_rect->width, dest_rect->height,
244                  dest_rect->x, dest_rect->y);
245     }
246
247   for (l = obj->children; l; l = l->next)
248     {
249       GdkWindow *child = (GdkWindow*) l->data;
250       GdkWindowObject *child_obj = GDK_WINDOW_OBJECT (child);
251
252       gdk_window_move (child, child_obj->x + dx, child_obj->y + dy);
253     }
254 }
255
256 static void
257 compute_intermediate_position (GdkXPositionInfo *position_info,
258                                GdkXPositionInfo *new_info,
259                                gint              d_xoffset,
260                                gint              d_yoffset,
261                                GdkRectangle     *new_position)
262 {
263   gint new_x0, new_x1, new_y0, new_y1;
264   
265   /* Wrap d_xoffset, d_yoffset into [-32768,32767] range. For the
266    * purposes of subwindow movement, it doesn't matter if we are
267    * off by a factor of 65536, and if we don't do this range
268    * reduction, we'll end up with invalid widths.
269    */
270   d_xoffset = (gint16)d_xoffset;
271   d_yoffset = (gint16)d_yoffset;
272   
273   if (d_xoffset < 0)
274     {
275       new_x0 = position_info->x + d_xoffset;
276       new_x1 = position_info->x + position_info->width;
277     }
278   else
279     {
280       new_x0 = position_info->x;
281       new_x1 = position_info->x + new_info->width + d_xoffset;
282     }
283
284   new_position->x = new_x0;
285   new_position->width = new_x1 - new_x0;
286   
287   if (d_yoffset < 0)
288     {
289       new_y0 = position_info->y + d_yoffset;
290       new_y1 = position_info->y + position_info->height;
291     }
292   else
293     {
294       new_y0 = position_info->y;
295       new_y1 = position_info->y + new_info->height + d_yoffset;
296     }
297   
298   new_position->y = new_y0;
299   new_position->height = new_y1 - new_y0;
300 }
301
302 static void
303 translate_pos (GdkWindowParentPos *dest, GdkWindowParentPos *src,
304                GdkWindowObject *obj, GdkXPositionInfo *pos_info,
305                gboolean set_clip)
306 {
307   dest->x = src->x + obj->x;
308   dest->y = src->y + obj->y;
309   dest->x11_x = src->x11_x + pos_info->x;
310   dest->x11_y = src->x11_y + pos_info->y;
311
312   if (set_clip)
313       dest->clip_rect = pos_info->clip_rect;
314 }
315
316 static void
317 move (GdkWindow *window, GdkXPositionInfo *pos)
318 {
319   XMoveWindow (GDK_WINDOW_XDISPLAY (window),
320                GDK_WINDOW_XID (window), pos->x, pos->y);
321 }
322
323 static void
324 move_relative (GdkWindow *window, GdkRectangle *rect,
325                gint dx, gint dy)
326 {
327   XMoveWindow (GDK_WINDOW_XDISPLAY (window),
328                GDK_WINDOW_XID (window),
329                rect->x + dx, rect->y + dy);
330 }
331
332 static void
333 move_resize (GdkWindow *window, GdkRectangle *pos)
334 {
335   XMoveResizeWindow (GDK_WINDOW_XDISPLAY (window),
336                      GDK_WINDOW_XID (window),
337                      pos->x, pos->y, pos->width, pos->height);
338 }
339
340 static void
341 gdk_window_guffaw_scroll (GdkWindow    *window,
342                           gint          dx,
343                           gint          dy)
344 {
345   GdkWindowObject *obj = GDK_WINDOW_OBJECT (window);
346   GdkWindowImplX11 *impl = GDK_WINDOW_IMPL_X11 (obj->impl);
347
348   gint d_xoffset = -dx;
349   gint d_yoffset = -dy;
350   GdkRectangle new_position;
351   GdkXPositionInfo new_info;
352   GdkWindowParentPos parent_pos;
353   GList *l;
354   
355   gdk_window_compute_parent_pos (impl, &parent_pos);
356   gdk_window_compute_position (impl, &parent_pos, &new_info);
357
358   translate_pos (&parent_pos, &parent_pos, obj, &new_info, TRUE);
359
360   _gdk_x11_window_tmp_unset_bg (window, FALSE);;
361
362   if (dx > 0 || dy > 0)
363     gdk_window_queue_translation (window, NULL, MAX (dx, 0), MAX (dy, 0));
364         
365   gdk_window_set_static_gravities (window, TRUE);
366
367   compute_intermediate_position (&impl->position_info, &new_info, d_xoffset, d_yoffset,
368                                  &new_position);
369   
370   move_resize (window, &new_position);
371   
372   for (l = obj->children; l; l = l->next)
373     {
374       GdkWindow *child = (GdkWindow*) l->data;
375       GdkWindowObject *child_obj = GDK_WINDOW_OBJECT (child);
376
377       child_obj->x -= d_xoffset;
378       child_obj->y -= d_yoffset;
379
380       gdk_window_premove (child, &parent_pos);
381     }
382   
383   move_relative (window, &new_position, -d_xoffset, -d_yoffset);
384   
385   if (dx < 0 || dy < 0)
386     gdk_window_queue_translation (window, NULL, MIN (dx, 0), MIN (dy, 0));
387   
388   move_resize (window, (GdkRectangle *) &impl->position_info);
389   
390   if (impl->position_info.no_bg)
391     _gdk_x11_window_tmp_reset_bg (window, FALSE);
392   
393   impl->position_info = new_info;
394   
395   g_list_foreach (obj->children, (GFunc) gdk_window_postmove, &parent_pos);
396 }
397
398 /**
399  * gdk_window_scroll:
400  * @window: a #GdkWindow
401  * @dx: Amount to scroll in the X direction
402  * @dy: Amount to scroll in the Y direction
403  * 
404  * Scroll the contents of @window, both pixels and children, by the given
405  * amount. @window itself does not move.  Portions of the window that the scroll
406  * operation brings in from offscreen areas are invalidated. The invalidated
407  * region may be bigger than what would strictly be necessary.  (For X11, a
408  * minimum area will be invalidated if the window has no subwindows, or if the
409  * edges of the window's parent do not extend beyond the edges of the window. In
410  * other cases, a multi-step process is used to scroll the window which may
411  * produce temporary visual artifacts and unnecessary invalidations.)
412  **/
413 void
414 gdk_window_scroll (GdkWindow *window,
415                    gint       dx,
416                    gint       dy)
417 {
418   gboolean can_guffaw_scroll = FALSE;
419   GdkRegion *invalidate_region;
420   GdkWindowImplX11 *impl;
421   GdkWindowObject *obj;
422   GdkRectangle src_rect, dest_rect;
423   
424   g_return_if_fail (GDK_IS_WINDOW (window));
425
426   if (GDK_WINDOW_DESTROYED (window))
427     return;
428   
429   obj = GDK_WINDOW_OBJECT (window);
430   impl = GDK_WINDOW_IMPL_X11 (obj->impl);  
431
432   if (dx == 0 && dy == 0)
433     return;
434   
435   /* Move the current invalid region */
436   if (obj->update_area)
437     gdk_region_offset (obj->update_area, dx, dy);
438   
439   /* impl->position_info.clip_rect isn't meaningful for toplevels */
440   if (GDK_WINDOW_TYPE (window) == GDK_WINDOW_CHILD)
441     src_rect = impl->position_info.clip_rect;
442   else
443     {
444       src_rect.x = 0;
445       src_rect.y = 0;
446       src_rect.width = impl->width;
447       src_rect.height = impl->height;
448     }
449   
450   invalidate_region = gdk_region_rectangle (&src_rect);
451
452   dest_rect = src_rect;
453   dest_rect.x += dx;
454   dest_rect.y += dy;
455   gdk_rectangle_intersect (&dest_rect, &src_rect, &dest_rect);
456
457   if (dest_rect.width > 0 && dest_rect.height > 0)
458     {
459       GdkRegion *tmp_region;
460
461       tmp_region = gdk_region_rectangle (&dest_rect);
462       gdk_region_subtract (invalidate_region, tmp_region);
463       gdk_region_destroy (tmp_region);
464     }
465   
466   gdk_window_invalidate_region (window, invalidate_region, TRUE);
467   gdk_region_destroy (invalidate_region);
468
469   /* We can guffaw scroll if we are a child window, and the parent
470    * does not extend beyond our edges. Otherwise, we use XCopyArea, then
471    * move any children later
472    */
473   if (GDK_WINDOW_TYPE (window) == GDK_WINDOW_CHILD)
474     {
475       GdkWindowImplX11 *parent_impl = GDK_WINDOW_IMPL_X11 (obj->parent->impl);  
476       can_guffaw_scroll = ((dx == 0 || (obj->x <= 0 && obj->x + impl->width >= parent_impl->width)) &&
477                            (dy == 0 || (obj->y <= 0 && obj->y + impl->height >= parent_impl->height)));
478     }
479
480   if (!obj->children || !can_guffaw_scroll)
481     gdk_window_copy_area_scroll (window, &dest_rect, dx, dy);
482   else
483     gdk_window_guffaw_scroll (window, dx, dy);
484 }
485
486 /**
487  * gdk_window_move_region:
488  * @window: a #GdkWindow
489  * @region: The #GdkRegion to move
490  * @dx: Amount to move in the X direction
491  * @dy: Amount to move in the Y direction
492  * 
493  * Move the part of @window indicated by @region by @dy pixels in the Y 
494  * direction and @dx pixels in the X direction. The portions of @region 
495  * that not covered by the new position of @region are invalidated.
496  * 
497  * Child windows are not moved.
498  * 
499  * Since: 2.8
500  **/
501 void
502 gdk_window_move_region (GdkWindow *window,
503                         GdkRegion *region,
504                         gint       dx,
505                         gint       dy)
506 {
507   GdkWindowImplX11 *impl;
508   GdkWindowObject *private;
509   GdkRegion *window_clip;
510   GdkRegion *src_region;
511   GdkRegion *brought_in;
512   GdkRegion *dest_region;
513   GdkRegion *moving_invalid_region;
514   GdkRectangle dest_extents;
515   GdkGC *gc;
516   
517   g_return_if_fail (GDK_IS_WINDOW (window));
518   g_return_if_fail (region != NULL);
519   
520   if (GDK_WINDOW_DESTROYED (window))
521     return;
522   
523   private = GDK_WINDOW_OBJECT (window);
524   impl = GDK_WINDOW_IMPL_X11 (private->impl);  
525
526   if (dx == 0 && dy == 0)
527     return;
528
529   window_clip = gdk_region_rectangle (&impl->position_info.clip_rect);
530
531   /* compute source regions */
532   src_region = gdk_region_copy (region);
533   brought_in = gdk_region_copy (region);
534   gdk_region_intersect (src_region, window_clip);
535
536   gdk_region_subtract (brought_in, src_region);
537   gdk_region_offset (brought_in, dx, dy);
538
539   /* compute destination regions */
540   dest_region = gdk_region_copy (src_region);
541   gdk_region_offset (dest_region, dx, dy);
542   gdk_region_intersect (dest_region, window_clip);
543   gdk_region_get_clipbox (dest_region, &dest_extents);
544
545   gdk_region_destroy (window_clip);
546
547   /* calculating moving part of current invalid area */
548   moving_invalid_region = NULL;
549   if (private->update_area)
550     {
551       moving_invalid_region = gdk_region_copy (private->update_area);
552       gdk_region_intersect (moving_invalid_region, src_region);
553       gdk_region_offset (moving_invalid_region, dx, dy);
554     }
555   
556   /* invalidate all of the src region */
557   gdk_window_invalidate_region (window, src_region, FALSE);
558
559   /* un-invalidate destination region */
560   if (private->update_area)
561     gdk_region_subtract (private->update_area, dest_region);
562   
563   /* invalidate moving parts of existing update area */
564   if (moving_invalid_region)
565     {
566       gdk_window_invalidate_region (window, moving_invalid_region, FALSE);
567       gdk_region_destroy (moving_invalid_region);
568     }
569
570   /* invalidate area brought in from off-screen */
571   gdk_window_invalidate_region (window, brought_in, FALSE);
572   gdk_region_destroy (brought_in);
573
574   /* Actually do the moving */
575   gdk_window_queue_translation (window, src_region, dx, dy);
576
577   gc = _gdk_drawable_get_scratch_gc (window, TRUE);
578   gdk_gc_set_clip_region (gc, dest_region);
579   
580   XCopyArea (GDK_WINDOW_XDISPLAY (window),
581              GDK_WINDOW_XID (window),
582              GDK_WINDOW_XID (window),
583              GDK_GC_XGC (gc),
584              dest_extents.x - dx, dest_extents.y - dy,
585              dest_extents.width, dest_extents.height,
586              dest_extents.x, dest_extents.y);
587
588   /* Unset clip region of cached GC */
589   gdk_gc_set_clip_region (gc, NULL);
590   
591   gdk_region_destroy (src_region);
592   gdk_region_destroy (dest_region);
593 }
594
595 static void
596 reset_backgrounds (GdkWindow *window)
597 {
598   GdkWindowObject *obj = (GdkWindowObject *)window;
599
600   _gdk_x11_window_tmp_reset_bg (window, FALSE);
601   
602   if (obj->parent)
603     _gdk_x11_window_tmp_reset_bg ((GdkWindow *)obj->parent, FALSE);
604 }
605
606 static void
607 map_if_needed (GdkWindow *window, GdkXPositionInfo *pos_info)
608 {
609   GdkWindowObject *obj = (GdkWindowObject *) window;
610   GdkWindowImplX11 *impl = GDK_WINDOW_IMPL_X11 (obj->impl);
611
612   if (!impl->position_info.mapped && pos_info->mapped && GDK_WINDOW_IS_MAPPED (obj))
613     XMapWindow (GDK_DRAWABLE_XDISPLAY (window), GDK_DRAWABLE_XID (window));
614 }
615
616 static void
617 unmap_if_needed (GdkWindow *window, GdkXPositionInfo *pos_info)
618 {
619   GdkWindowObject *obj = (GdkWindowObject *) window;
620   GdkWindowImplX11 *impl = GDK_WINDOW_IMPL_X11 (obj->impl);
621
622   if (impl->position_info.mapped && !pos_info->mapped)
623     XUnmapWindow (GDK_DRAWABLE_XDISPLAY (window), GDK_DRAWABLE_XID (window));
624 }
625
626 void
627 _gdk_window_move_resize_child (GdkWindow *window,
628                                gint       x,
629                                gint       y,
630                                gint       width,
631                                gint       height)
632 {
633   GdkWindowImplX11 *impl;
634   GdkWindowObject *obj;
635   GdkXPositionInfo new_info;
636   GdkWindowParentPos parent_pos;
637   
638   gint d_xoffset, d_yoffset;
639   gint dx, dy;
640   gboolean is_move;
641   gboolean is_resize;
642
643   GdkRectangle old_pos;
644   
645   g_return_if_fail (window != NULL);
646   g_return_if_fail (GDK_IS_WINDOW (window)); 
647
648   impl = GDK_WINDOW_IMPL_X11 (GDK_WINDOW_OBJECT (window)->impl);
649   obj = GDK_WINDOW_OBJECT (window);
650
651   dx = x - obj->x;
652   dy = y - obj->y;
653   
654   is_move = dx != 0 || dy != 0;
655   is_resize = impl->width != width || impl->height != height;
656
657   if (!is_move && !is_resize)
658     return;
659   
660   old_pos.x = obj->x;
661   old_pos.y = obj->y;
662   old_pos.width = impl->width;
663   old_pos.height = impl->height;
664
665   obj->x = x;
666   obj->y = y;
667   impl->width = width;
668   impl->height = height;
669
670   gdk_window_compute_parent_pos (impl, &parent_pos);
671   gdk_window_compute_position (impl, &parent_pos, &new_info);
672
673   gdk_window_clip_changed (window, &impl->position_info.clip_rect, &new_info.clip_rect);
674
675   translate_pos (&parent_pos, &parent_pos, obj, &new_info, TRUE);
676
677   d_xoffset = new_info.x_offset - impl->position_info.x_offset;
678   d_yoffset = new_info.y_offset - impl->position_info.y_offset;
679   
680   if (d_xoffset != 0 || d_yoffset != 0)
681     {
682       GdkRectangle new_position;
683
684       gdk_window_set_static_gravities (window, TRUE);
685
686       if (d_xoffset < 0 || d_yoffset < 0)
687         gdk_window_queue_translation (window, NULL, MIN (d_xoffset, 0), MIN (d_yoffset, 0));
688
689       compute_intermediate_position (&impl->position_info, &new_info, d_xoffset, d_yoffset,
690                                      &new_position);
691       
692       move_resize (window, &new_position);
693       
694       g_list_foreach (obj->children, (GFunc) gdk_window_premove, &parent_pos);
695
696       move_relative (window, &new_position, dx, dy);
697       
698       if (d_xoffset > 0 || d_yoffset > 0)
699         gdk_window_queue_translation (window, NULL, MAX (d_xoffset, 0), MAX (d_yoffset, 0));
700       
701       move_resize (window, (GdkRectangle *) &new_info);
702
703       reset_backgrounds (window);
704
705       map_if_needed (window, &new_info);
706       
707       impl->position_info = new_info;
708       
709       g_list_foreach (obj->children, (GFunc) gdk_window_postmove, &parent_pos);
710     }
711   else
712     {
713       if (is_move && is_resize)
714         gdk_window_set_static_gravities (window, FALSE);
715
716       unmap_if_needed (window, &new_info);
717       
718       g_list_foreach (obj->children, (GFunc) gdk_window_premove, &parent_pos);
719
720       if (is_resize)
721           move_resize (window, (GdkRectangle *) &new_info);
722       else
723           move (window, &new_info);
724
725       g_list_foreach (obj->children, (GFunc) gdk_window_postmove, &parent_pos);
726
727       reset_backgrounds (window);
728       
729       map_if_needed (window, &new_info);
730
731       impl->position_info = new_info;
732     }
733
734   if (GDK_WINDOW_IS_MAPPED (obj) && obj->parent)
735     gdk_window_invalidate_rect ((GdkWindow *)obj->parent, &old_pos, FALSE);
736 }
737
738 static void
739 gdk_window_compute_position (GdkWindowImplX11   *window,
740                              GdkWindowParentPos *parent_pos,
741                              GdkXPositionInfo   *info)
742 {
743   GdkWindowObject *wrapper;
744   int parent_x_offset;
745   int parent_y_offset;
746
747   g_return_if_fail (GDK_IS_WINDOW_IMPL_X11 (window));
748
749   wrapper = GDK_WINDOW_OBJECT (GDK_DRAWABLE_IMPL_X11 (window)->wrapper);
750   
751   info->big = FALSE;
752   
753   if (window->width <= 32767)
754     {
755       info->width = window->width;
756       info->x = parent_pos->x + wrapper->x - parent_pos->x11_x;
757     }
758   else
759     {
760       info->big = TRUE;
761       info->width = 32767;
762       if (parent_pos->x + wrapper->x < -16384)
763         {
764           if (parent_pos->x + wrapper->x + window->width < 16384)
765             info->x = parent_pos->x + wrapper->x + window->width - info->width - parent_pos->x11_x;
766           else
767             info->x = -16384 - parent_pos->x11_x;
768         }
769       else
770         info->x = parent_pos->x + wrapper->x - parent_pos->x11_x;
771     }
772
773   if (window->height <= 32767)
774     {
775       info->height = window->height;
776       info->y = parent_pos->y + wrapper->y - parent_pos->x11_y;
777     }
778   else
779     {
780       info->big = TRUE;
781       info->height = 32767;
782       if (parent_pos->y + wrapper->y < -16384)
783         {
784           if (parent_pos->y + wrapper->y + window->height < 16384)
785             info->y = parent_pos->y + wrapper->y + window->height - info->height - parent_pos->x11_y;
786           else
787             info->y = -16384 - parent_pos->x11_y;
788         }
789       else
790         info->y = parent_pos->y + wrapper->y - parent_pos->x11_y;
791     }
792
793   parent_x_offset = parent_pos->x11_x - parent_pos->x;
794   parent_y_offset = parent_pos->x11_y - parent_pos->y;
795   
796   info->x_offset = parent_x_offset + info->x - wrapper->x;
797   info->y_offset = parent_y_offset + info->y - wrapper->y;
798
799   /* We don't considering the clipping of toplevel windows and their immediate children
800    * by their parents, and simply always map those windows.
801    */
802   if (parent_pos->clip_rect.width == G_MAXINT)
803     info->mapped = TRUE;
804   /* Check if the window would wrap around into the visible space in either direction */
805   else if (info->x + parent_x_offset < parent_pos->clip_rect.x + parent_pos->clip_rect.width - 65536 ||
806       info->x + info->width + parent_x_offset > parent_pos->clip_rect.x + 65536 ||
807       info->y + parent_y_offset < parent_pos->clip_rect.y + parent_pos->clip_rect.height - 65536 ||
808       info->y + info->height + parent_y_offset  > parent_pos->clip_rect.y + 65536)
809     info->mapped = FALSE;
810   else
811     info->mapped = TRUE;
812
813   info->no_bg = FALSE;
814
815   if (GDK_WINDOW_TYPE (wrapper) == GDK_WINDOW_CHILD)
816     {
817       info->clip_rect.x = wrapper->x;
818       info->clip_rect.y = wrapper->y;
819       info->clip_rect.width = window->width;
820       info->clip_rect.height = window->height;
821       
822       gdk_rectangle_intersect (&info->clip_rect, &parent_pos->clip_rect, &info->clip_rect);
823
824       info->clip_rect.x -= wrapper->x;
825       info->clip_rect.y -= wrapper->y;
826     }
827   else
828     {
829       info->clip_rect.x = 0;
830       info->clip_rect.y = 0;
831       info->clip_rect.width = G_MAXINT;
832       info->clip_rect.height = G_MAXINT;
833     }
834 }
835
836 static void
837 gdk_window_compute_parent_pos (GdkWindowImplX11      *window,
838                                GdkWindowParentPos *parent_pos)
839 {
840   GdkWindowObject *wrapper;
841   GdkWindowObject *parent;
842   GdkRectangle tmp_clip;
843   
844   int clip_xoffset = 0;
845   int clip_yoffset = 0;
846
847   g_return_if_fail (GDK_IS_WINDOW_IMPL_X11 (window));
848
849   wrapper =
850     GDK_WINDOW_OBJECT (GDK_DRAWABLE_IMPL_X11 (window)->wrapper);
851   
852   parent_pos->x = 0;
853   parent_pos->y = 0;
854   parent_pos->x11_x = 0;
855   parent_pos->x11_y = 0;
856
857   /* We take a simple approach here and simply consider toplevel
858    * windows not to clip their children on the right/bottom, since the
859    * size of toplevel windows is not directly under our
860    * control. Clipping only really matters when scrolling and
861    * generally we aren't going to be moving the immediate child of a
862    * toplevel beyond the bounds of that toplevel.
863    *
864    * We could go ahead and recompute the clips of toplevel windows and
865    * their descendents when we receive size notification, but it would
866    * probably not be an improvement in most cases.
867    */
868   parent_pos->clip_rect.x = 0;
869   parent_pos->clip_rect.y = 0;
870   parent_pos->clip_rect.width = G_MAXINT;
871   parent_pos->clip_rect.height = G_MAXINT;
872
873   parent = (GdkWindowObject *)wrapper->parent;
874   while (parent && parent->window_type == GDK_WINDOW_CHILD)
875     {
876       GdkWindowImplX11 *impl = GDK_WINDOW_IMPL_X11 (parent->impl);
877       
878       tmp_clip.x = - clip_xoffset;
879       tmp_clip.y = - clip_yoffset;
880       tmp_clip.width = impl->width;
881       tmp_clip.height = impl->height;
882
883       gdk_rectangle_intersect (&parent_pos->clip_rect, &tmp_clip, &parent_pos->clip_rect);
884
885       translate_pos (parent_pos, parent_pos, parent,
886                      &impl->position_info, FALSE);
887
888       clip_xoffset += parent->x;
889       clip_yoffset += parent->y;
890
891       parent = (GdkWindowObject *)parent->parent;
892     }
893 }
894
895 static void
896 gdk_window_premove (GdkWindow          *window,
897                     GdkWindowParentPos *parent_pos)
898 {
899   GdkWindowImplX11 *impl;
900   GdkWindowObject *obj;
901   GdkXPositionInfo new_info;
902   gint d_xoffset, d_yoffset;
903   GdkWindowParentPos this_pos;
904
905   obj = (GdkWindowObject *) window;
906   impl = GDK_WINDOW_IMPL_X11 (obj->impl);
907   
908   gdk_window_compute_position (impl, parent_pos, &new_info);
909
910   gdk_window_clip_changed (window, &impl->position_info.clip_rect, &new_info.clip_rect);
911
912   translate_pos (&this_pos, parent_pos, obj, &new_info, TRUE);
913
914   unmap_if_needed (window, &new_info);
915
916   d_xoffset = new_info.x_offset - impl->position_info.x_offset;
917   d_yoffset = new_info.y_offset - impl->position_info.y_offset;
918   
919   if (d_xoffset != 0 || d_yoffset != 0)
920     {
921       GdkRectangle new_position;
922
923       if (d_xoffset < 0 || d_yoffset < 0)
924         gdk_window_queue_translation (window, NULL, MIN (d_xoffset, 0), MIN (d_yoffset, 0));
925
926       compute_intermediate_position (&impl->position_info, &new_info, d_xoffset, d_yoffset,
927                                      &new_position);
928
929       move_resize (window, &new_position);
930     }
931
932   g_list_foreach (obj->children, (GFunc) gdk_window_premove, &this_pos);
933 }
934
935 static void
936 gdk_window_postmove (GdkWindow          *window,
937                      GdkWindowParentPos *parent_pos)
938 {
939   GdkWindowImplX11 *impl;
940   GdkWindowObject *obj;
941   GdkXPositionInfo new_info;
942   gint d_xoffset, d_yoffset;
943   GdkWindowParentPos this_pos;
944
945   obj = (GdkWindowObject *) window;
946   impl = GDK_WINDOW_IMPL_X11 (obj->impl);
947   
948   gdk_window_compute_position (impl, parent_pos, &new_info);
949
950   translate_pos (&this_pos, parent_pos, obj, &new_info, TRUE);
951
952   d_xoffset = new_info.x_offset - impl->position_info.x_offset;
953   d_yoffset = new_info.y_offset - impl->position_info.y_offset;
954   
955   if (d_xoffset != 0 || d_yoffset != 0)
956     {
957       if (d_xoffset > 0 || d_yoffset > 0)
958         gdk_window_queue_translation (window, NULL, MAX (d_xoffset, 0), MAX (d_yoffset, 0));
959         
960       move_resize (window, (GdkRectangle *) &new_info);
961     }
962
963   map_if_needed (window, &new_info);
964
965   reset_backgrounds (window);
966
967   impl->position_info = new_info;
968
969   g_list_foreach (obj->children, (GFunc) gdk_window_postmove, &this_pos);
970 }
971
972 static Bool
973 expose_serial_predicate (Display *xdisplay,
974                          XEvent  *xev,
975                          XPointer arg)
976 {
977   gulong *serial = (gulong *)arg;
978
979   if (xev->xany.type == Expose)
980     *serial = MIN (*serial, xev->xany.serial);
981
982   return False;
983 }
984
985 /* Find oldest possible serial for an outstanding expose event
986  */
987 static gulong
988 find_current_serial (Display *xdisplay)
989 {
990   XEvent xev;
991   gulong serial = NextRequest (xdisplay);
992   
993   XSync (xdisplay, False);
994
995   XCheckIfEvent (xdisplay, &xev, expose_serial_predicate, (XPointer)&serial);
996
997   return serial;
998 }
999
1000 static void
1001 queue_delete_link (GQueue *queue,
1002                    GList  *link)
1003 {
1004   if (queue->tail == link)
1005     queue->tail = link->prev;
1006   
1007   queue->head = g_list_remove_link (queue->head, link);
1008   g_list_free_1 (link);
1009   queue->length--;
1010 }
1011
1012 static void
1013 queue_item_free (GdkWindowQueueItem *item)
1014 {
1015   if (item->window)
1016     {
1017       g_object_remove_weak_pointer (G_OBJECT (item->window),
1018                                     (gpointer *)&(item->window));
1019     }
1020   
1021   if (item->type == GDK_WINDOW_QUEUE_ANTIEXPOSE)
1022     gdk_region_destroy (item->u.antiexpose.area);
1023   else
1024     {
1025       if (item->u.translate.area)
1026         gdk_region_destroy (item->u.translate.area);
1027     }
1028   
1029   g_free (item);
1030 }
1031
1032 static void
1033 gdk_window_queue (GdkWindow          *window,
1034                   GdkWindowQueueItem *item)
1035 {
1036   GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (GDK_WINDOW_DISPLAY (window));
1037   
1038   if (!display_x11->translate_queue)
1039     display_x11->translate_queue = g_queue_new ();
1040
1041   /* Keep length of queue finite by, if it grows too long,
1042    * figuring out the latest relevant serial and discarding
1043    * irrelevant queue items.
1044    */
1045   if (display_x11->translate_queue->length >= 64)
1046     {
1047       gulong serial = find_current_serial (GDK_WINDOW_XDISPLAY (window));
1048       GList *tmp_list = display_x11->translate_queue->head;
1049       
1050       while (tmp_list)
1051         {
1052           GdkWindowQueueItem *item = tmp_list->data;
1053           GList *next = tmp_list->next;
1054           
1055           /* an overflow-safe (item->serial < serial) */
1056           if (item->serial - serial > (gulong) G_MAXLONG)
1057             {
1058               queue_delete_link (display_x11->translate_queue, tmp_list);
1059               queue_item_free (item);
1060             }
1061
1062           tmp_list = next;
1063         }
1064     }
1065
1066   /* Catch the case where someone isn't processing events and there
1067    * is an event stuck in the event queue with an old serial:
1068    * If we can't reduce the queue length by the above method,
1069    * discard anti-expose items. (We can't discard translate
1070    * items 
1071    */
1072   if (display_x11->translate_queue->length >= 64)
1073     {
1074       GList *tmp_list = display_x11->translate_queue->head;
1075       
1076       while (tmp_list)
1077         {
1078           GdkWindowQueueItem *item = tmp_list->data;
1079           GList *next = tmp_list->next;
1080           
1081           if (item->type == GDK_WINDOW_QUEUE_ANTIEXPOSE)
1082             {
1083               queue_delete_link (display_x11->translate_queue, tmp_list);
1084               queue_item_free (item);
1085             }
1086
1087           tmp_list = next;
1088         }
1089     }
1090
1091   item->window = window;
1092   item->serial = NextRequest (GDK_WINDOW_XDISPLAY (window));
1093   
1094   g_object_add_weak_pointer (G_OBJECT (window),
1095                              (gpointer *)&(item->window));
1096
1097   g_queue_push_tail (display_x11->translate_queue, item);
1098 }
1099
1100 static void
1101 gdk_window_queue_translation (GdkWindow *window,
1102                               GdkRegion *area,
1103                               gint       dx,
1104                               gint       dy)
1105 {
1106   GdkWindowQueueItem *item = g_new (GdkWindowQueueItem, 1);
1107   item->type = GDK_WINDOW_QUEUE_TRANSLATE;
1108   item->u.translate.area = area ? gdk_region_copy (area) : NULL;
1109   item->u.translate.dx = dx;
1110   item->u.translate.dy = dy;
1111
1112   gdk_window_queue (window, item);
1113 }
1114
1115 gboolean
1116 _gdk_windowing_window_queue_antiexpose (GdkWindow *window,
1117                                         GdkRegion *area)
1118 {
1119   GdkWindowQueueItem *item = g_new (GdkWindowQueueItem, 1);
1120   item->type = GDK_WINDOW_QUEUE_ANTIEXPOSE;
1121   item->u.antiexpose.area = area;
1122
1123   gdk_window_queue (window, item);
1124
1125   return TRUE;
1126 }
1127
1128 void
1129 _gdk_window_process_expose (GdkWindow    *window,
1130                             gulong        serial,
1131                             GdkRectangle *area)
1132 {
1133   GdkWindowImplX11 *impl;
1134   GdkRegion *invalidate_region = gdk_region_rectangle (area);
1135   GdkRegion *clip_region;
1136   GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (GDK_WINDOW_DISPLAY (window));
1137   impl = GDK_WINDOW_IMPL_X11 (GDK_WINDOW_OBJECT (window)->impl);
1138
1139   if (display_x11->translate_queue)
1140     {
1141       GList *tmp_list = display_x11->translate_queue->head;
1142       
1143       while (tmp_list)
1144         {
1145           GdkWindowQueueItem *item = tmp_list->data;
1146           GList *next = tmp_list->next;
1147           
1148           /* an overflow-safe (serial < item->serial) */
1149           if (serial - item->serial > (gulong) G_MAXLONG)
1150             {
1151               if (item->window == window)
1152                 {
1153                   if (item->type == GDK_WINDOW_QUEUE_TRANSLATE)
1154                     {
1155                       if (item->u.translate.area)
1156                         {
1157                           GdkRegion *intersection;
1158                           
1159                           intersection = gdk_region_copy (invalidate_region);
1160                           gdk_region_intersect (intersection, item->u.translate.area);
1161                           gdk_region_subtract (invalidate_region, intersection);
1162                           gdk_region_offset (intersection, item->u.translate.dx, item->u.translate.dy);
1163                           gdk_region_union (invalidate_region, intersection);
1164                           gdk_region_destroy (intersection);
1165                         }
1166                       else
1167                         gdk_region_offset (invalidate_region, item->u.translate.dx, item->u.translate.dy);
1168                     }
1169                   else          /* anti-expose */
1170                     {
1171                       gdk_region_subtract (invalidate_region, item->u.antiexpose.area);
1172                     }
1173                 }
1174             }
1175           else
1176             {
1177               queue_delete_link (display_x11->translate_queue, tmp_list);
1178               queue_item_free (item);
1179             }
1180           tmp_list = next;
1181         }
1182     }
1183
1184   clip_region = gdk_region_rectangle (&impl->position_info.clip_rect);
1185   gdk_region_intersect (invalidate_region, clip_region);
1186
1187   if (!gdk_region_empty (invalidate_region))
1188     gdk_window_invalidate_region (window, invalidate_region, FALSE);
1189   
1190   gdk_region_destroy (invalidate_region);
1191   gdk_region_destroy (clip_region);
1192 }
1193
1194 static void
1195 gdk_window_clip_changed (GdkWindow *window, GdkRectangle *old_clip, GdkRectangle *new_clip)
1196 {
1197   GdkWindowImplX11 *impl;
1198   GdkWindowObject *obj;
1199   GdkRegion *old_clip_region;
1200   GdkRegion *new_clip_region;
1201   
1202   if (((GdkWindowObject *)window)->input_only)
1203     return;
1204
1205   obj = (GdkWindowObject *) window;
1206   impl = GDK_WINDOW_IMPL_X11 (obj->impl);
1207   
1208   old_clip_region = gdk_region_rectangle (old_clip);
1209   new_clip_region = gdk_region_rectangle (new_clip);
1210
1211   /* We need to update this here because gdk_window_invalidate_region makes
1212    * use if it (through gdk_drawable_get_visible_region
1213    */
1214   impl->position_info.clip_rect = *new_clip;
1215   
1216   /* Trim invalid region of window to new clip rectangle
1217    */
1218   if (obj->update_area)
1219     gdk_region_intersect (obj->update_area, new_clip_region);
1220
1221   /* Invalidate newly exposed portion of window
1222    */
1223   gdk_region_subtract (new_clip_region, old_clip_region);
1224   if (!gdk_region_empty (new_clip_region))
1225     {
1226       _gdk_x11_window_tmp_unset_bg (window, FALSE);;
1227       gdk_window_invalidate_region (window, new_clip_region, FALSE);
1228     }
1229
1230   if (obj->parent)
1231     _gdk_x11_window_tmp_unset_bg ((GdkWindow *)obj->parent, FALSE);
1232   
1233   gdk_region_destroy (new_clip_region);
1234   gdk_region_destroy (old_clip_region);
1235 }
1236
1237 #define __GDK_GEOMETRY_X11_C__
1238 #include "gdkaliasdef.c"