]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkgeometry-x11.c
GC caching, bug #125645 (based on patch by Brian Cameron)
[~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 "gdk.h"                /* For gdk_rectangle_intersect */
133 #include "gdkprivate-x11.h"
134 #include "gdkx.h"
135 #include "gdkregion.h"
136 #include "gdkinternals.h"
137 #include "gdkscreen-x11.h"
138 #include "gdkdisplay-x11.h"
139
140 typedef struct _GdkWindowQueueItem GdkWindowQueueItem;
141 typedef struct _GdkWindowParentPos GdkWindowParentPos;
142
143 typedef enum {
144   GDK_WINDOW_QUEUE_TRANSLATE,
145   GDK_WINDOW_QUEUE_ANTIEXPOSE
146 } GdkWindowQueueType;
147
148 struct _GdkWindowQueueItem
149 {
150   GdkWindow *window;
151   gulong serial;
152   GdkWindowQueueType type;
153   union {
154     struct {
155       gint dx;
156       gint dy;
157     } translate;
158     struct {
159       GdkRegion *area;
160     } antiexpose;
161   } u;
162 };
163
164 struct _GdkWindowParentPos
165 {
166   gint x;
167   gint y;
168   gint x11_x;
169   gint x11_y;
170   GdkRectangle clip_rect;
171 };
172
173 static void gdk_window_compute_position   (GdkWindowImplX11      *window,
174                                            GdkWindowParentPos *parent_pos,
175                                            GdkXPositionInfo   *info);
176 static void gdk_window_compute_parent_pos (GdkWindowImplX11      *window,
177                                            GdkWindowParentPos *parent_pos);
178 static void gdk_window_premove            (GdkWindow          *window,
179                                            GdkWindowParentPos *parent_pos);
180 static void gdk_window_postmove           (GdkWindow          *window,
181                                            GdkWindowParentPos *parent_pos);
182 static void gdk_window_queue_translation  (GdkWindow          *window,
183                                            gint                dx,
184                                            gint                dy);
185 static void gdk_window_tmp_unset_bg       (GdkWindow          *window);
186 static void gdk_window_tmp_reset_bg       (GdkWindow          *window);
187 static void gdk_window_clip_changed       (GdkWindow          *window,
188                                            GdkRectangle       *old_clip,
189                                            GdkRectangle       *new_clip);
190
191 void
192 _gdk_windowing_window_get_offsets (GdkWindow *window,
193                                    gint      *x_offset,
194                                    gint      *y_offset)
195 {
196   GdkWindowImplX11 *impl =
197     GDK_WINDOW_IMPL_X11 (GDK_WINDOW_OBJECT (window)->impl);
198
199   *x_offset = impl->position_info.x_offset;
200   *y_offset = impl->position_info.y_offset;
201 }
202
203 void
204 _gdk_window_init_position (GdkWindow *window)
205 {
206   GdkWindowParentPos parent_pos;
207   GdkWindowImplX11 *impl;
208   
209   g_return_if_fail (GDK_IS_WINDOW (window));
210   
211   impl =
212     GDK_WINDOW_IMPL_X11 (GDK_WINDOW_OBJECT (window)->impl);
213   
214   gdk_window_compute_parent_pos (impl, &parent_pos);
215   gdk_window_compute_position (impl, &parent_pos, &impl->position_info);
216 }
217
218 static void
219 gdk_window_copy_area_scroll (GdkWindow    *window,
220                              GdkRectangle *dest_rect,
221                              gint          dx,
222                              gint          dy)
223 {
224   GdkWindowObject *obj = GDK_WINDOW_OBJECT (window);
225   GList *tmp_list;
226
227   if (dest_rect->width > 0 && dest_rect->height > 0)
228     {
229       GdkGC *gc;
230
231       gc = _gdk_drawable_get_scratch_gc (window, TRUE);
232       
233       gdk_window_queue_translation (window, dx, dy);
234
235       XCopyArea (GDK_WINDOW_XDISPLAY (window),
236                  GDK_WINDOW_XID (window),
237                  GDK_WINDOW_XID (window),
238                  gdk_x11_gc_get_xgc (gc),
239                  dest_rect->x - dx, dest_rect->y - dy,
240                  dest_rect->width, dest_rect->height,
241                  dest_rect->x, dest_rect->y);
242     }
243
244   tmp_list = obj->children;
245   while (tmp_list)
246     {
247       GdkWindow *child = GDK_WINDOW (tmp_list->data);
248       GdkWindowObject *child_obj = GDK_WINDOW_OBJECT (child);
249           
250       gdk_window_move (child, child_obj->x + dx, child_obj->y + dy);
251       
252       tmp_list = tmp_list->next;
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 gdk_window_guffaw_scroll (GdkWindow    *window,
304                           gint          dx,
305                           gint          dy)
306 {
307   GdkWindowObject *obj = GDK_WINDOW_OBJECT (window);
308   GdkWindowImplX11 *impl = GDK_WINDOW_IMPL_X11 (obj->impl);
309
310   gint d_xoffset = -dx;
311   gint d_yoffset = -dy;
312   GdkRectangle new_position;
313   GdkXPositionInfo new_info;
314   GdkWindowParentPos parent_pos;
315   GList *tmp_list;
316   
317   gdk_window_compute_parent_pos (impl, &parent_pos);
318   gdk_window_compute_position (impl, &parent_pos, &new_info);
319
320   parent_pos.x += obj->x;
321   parent_pos.y += obj->y;
322   parent_pos.x11_x += new_info.x;
323   parent_pos.x11_y += new_info.y;
324   parent_pos.clip_rect = new_info.clip_rect;
325
326   gdk_window_tmp_unset_bg (window);
327
328   if (dx > 0 || dy > 0)
329     gdk_window_queue_translation (window, MAX (dx, 0), MAX (dy, 0));
330         
331   gdk_window_set_static_gravities (window, TRUE);
332
333   compute_intermediate_position (&impl->position_info, &new_info, d_xoffset, d_yoffset,
334                                  &new_position);
335   
336   XMoveResizeWindow (GDK_WINDOW_XDISPLAY (window),
337                      GDK_WINDOW_XID (window),
338                      new_position.x, new_position.y, new_position.width, new_position.height);
339   
340   tmp_list = obj->children;
341   while (tmp_list)
342     {
343       GDK_WINDOW_OBJECT(tmp_list->data)->x -= d_xoffset;
344       GDK_WINDOW_OBJECT(tmp_list->data)->y -= d_yoffset;
345
346       gdk_window_premove (tmp_list->data, &parent_pos);
347       tmp_list = tmp_list->next;
348     }
349   
350   XMoveWindow (GDK_WINDOW_XDISPLAY (window),
351                GDK_WINDOW_XID (window),
352                new_position.x - d_xoffset, new_position.y - d_yoffset);
353   
354   if (dx < 0 || dy < 0)
355     gdk_window_queue_translation (window, MIN (dx, 0), MIN (dy, 0));
356   
357   XMoveResizeWindow (GDK_WINDOW_XDISPLAY (window),
358                      GDK_WINDOW_XID (window),
359                      impl->position_info.x, impl->position_info.y,
360                      impl->position_info.width, impl->position_info.height);
361   
362   if (impl->position_info.no_bg)
363     gdk_window_tmp_reset_bg (window);
364   
365   impl->position_info = new_info;
366   
367   tmp_list = obj->children;
368   while (tmp_list)
369     {
370       gdk_window_postmove (tmp_list->data, &parent_pos);
371       tmp_list = tmp_list->next;
372     }
373 }
374
375 /**
376  * gdk_window_scroll:
377  * @window: a #GdkWindow
378  * @dx: Amount to scroll in the X direction
379  * @dy: Amount to scroll in the Y direction
380  * 
381  * Scroll the contents of @window, both pixels and children, by the given
382  * amount. @window itself does not move.  Portions of the window that the scroll
383  * operation brings in from offscreen areas are invalidated. The invalidated
384  * region may be bigger than what would strictly be necessary.  (For X11, a
385  * minimum area will be invalidated if the window has no subwindows, or if the
386  * edges of the window's parent do not extend beyond the edges of the window. In
387  * other cases, a multi-step process is used to scroll the window which may
388  * produce temporary visual artifacts and unnecessary invalidations.)
389  **/
390 void
391 gdk_window_scroll (GdkWindow *window,
392                    gint       dx,
393                    gint       dy)
394 {
395   gboolean can_guffaw_scroll = FALSE;
396   GdkRegion *invalidate_region;
397   GdkWindowImplX11 *impl;
398   GdkWindowObject *obj;
399   GdkRectangle src_rect, dest_rect;
400   
401   g_return_if_fail (GDK_IS_WINDOW (window));
402
403   if (GDK_WINDOW_DESTROYED (window))
404     return;
405   
406   obj = GDK_WINDOW_OBJECT (window);
407   impl = GDK_WINDOW_IMPL_X11 (obj->impl);  
408
409   if (dx == 0 && dy == 0)
410     return;
411   
412   /* Move the current invalid region */
413   if (obj->update_area)
414     gdk_region_offset (obj->update_area, dx, dy);
415   
416   /* impl->position_info.clip_rect isn't meaningful for toplevels */
417   if (GDK_WINDOW_TYPE (window) == GDK_WINDOW_CHILD)
418     src_rect = impl->position_info.clip_rect;
419   else
420     {
421       src_rect.x = 0;
422       src_rect.y = 0;
423       src_rect.width = impl->width;
424       src_rect.height = impl->height;
425     }
426   
427   invalidate_region = gdk_region_rectangle (&src_rect);
428
429   dest_rect = src_rect;
430   dest_rect.x += dx;
431   dest_rect.y += dy;
432   gdk_rectangle_intersect (&dest_rect, &src_rect, &dest_rect);
433
434   if (dest_rect.width > 0 && dest_rect.height > 0)
435     {
436       GdkRegion *tmp_region;
437
438       tmp_region = gdk_region_rectangle (&dest_rect);
439       gdk_region_subtract (invalidate_region, tmp_region);
440       gdk_region_destroy (tmp_region);
441     }
442   
443   gdk_window_invalidate_region (window, invalidate_region, TRUE);
444   gdk_region_destroy (invalidate_region);
445
446   /* We can guffaw scroll if we are a child window, and the parent
447    * does not extend beyond our edges. Otherwise, we use XCopyArea, then
448    * move any children later
449    */
450   if (GDK_WINDOW_TYPE (window) == GDK_WINDOW_CHILD)
451     {
452       GdkWindowImplX11 *parent_impl = GDK_WINDOW_IMPL_X11 (obj->parent->impl);  
453       can_guffaw_scroll = ((dx == 0 || (obj->x <= 0 && obj->x + impl->width >= parent_impl->width)) &&
454                            (dy == 0 || (obj->y <= 0 && obj->y + impl->height >= parent_impl->height)));
455     }
456
457   if (!obj->children || !can_guffaw_scroll)
458     gdk_window_copy_area_scroll (window, &dest_rect, dx, dy);
459   else
460     gdk_window_guffaw_scroll (window, dx, dy);
461 }
462
463 void
464 _gdk_window_move_resize_child (GdkWindow *window,
465                                gint       x,
466                                gint       y,
467                                gint       width,
468                                gint       height)
469 {
470   GdkWindowImplX11 *impl;
471   GdkWindowObject *obj;
472   GdkXPositionInfo new_info;
473   GdkWindowParentPos parent_pos;
474   GList *tmp_list;
475   
476   gint d_xoffset, d_yoffset;
477   gint dx, dy;
478   gboolean is_move;
479   gboolean is_resize;
480   
481   g_return_if_fail (window != NULL);
482   g_return_if_fail (GDK_IS_WINDOW (window));
483
484   impl = GDK_WINDOW_IMPL_X11 (GDK_WINDOW_OBJECT (window)->impl);
485   obj = GDK_WINDOW_OBJECT (window);
486   
487   dx = x - obj->x;
488   dy = y - obj->y;
489   
490   is_move = dx != 0 || dy != 0;
491   is_resize = impl->width != width || impl->height != height;
492
493   if (!is_move && !is_resize)
494     return;
495   
496   obj->x = x;
497   obj->y = y;
498   impl->width = width;
499   impl->height = height;
500
501   gdk_window_compute_parent_pos (impl, &parent_pos);
502   gdk_window_compute_position (impl, &parent_pos, &new_info);
503
504   gdk_window_clip_changed (window, &impl->position_info.clip_rect, &new_info.clip_rect);
505
506   parent_pos.x += obj->x;
507   parent_pos.y += obj->y;
508   parent_pos.x11_x += new_info.x;
509   parent_pos.x11_y += new_info.y;
510   parent_pos.clip_rect = new_info.clip_rect;
511
512   d_xoffset = new_info.x_offset - impl->position_info.x_offset;
513   d_yoffset = new_info.y_offset - impl->position_info.y_offset;
514   
515   if (d_xoffset != 0 || d_yoffset != 0)
516     {
517       GdkRectangle new_position;
518
519       gdk_window_set_static_gravities (window, TRUE);
520
521       if (d_xoffset < 0 || d_yoffset < 0)
522         gdk_window_queue_translation (window, MIN (d_xoffset, 0), MIN (d_yoffset, 0));
523
524       compute_intermediate_position (&impl->position_info, &new_info, d_xoffset, d_yoffset,
525                                      &new_position);
526       
527       XMoveResizeWindow (GDK_WINDOW_XDISPLAY (window),
528                          GDK_WINDOW_XID (window),
529                          new_position.x, new_position.y, new_position.width, new_position.height);
530       
531       tmp_list = obj->children;
532       while (tmp_list)
533         {
534           gdk_window_premove (tmp_list->data, &parent_pos);
535           tmp_list = tmp_list->next;
536         }
537
538       XMoveWindow (GDK_WINDOW_XDISPLAY (window),
539                    GDK_WINDOW_XID (window),
540                    new_position.x + dx, new_position.y + dy);
541       
542       if (d_xoffset > 0 || d_yoffset > 0)
543         gdk_window_queue_translation (window, MAX (d_xoffset, 0), MAX (d_yoffset, 0));
544       
545       XMoveResizeWindow (GDK_WINDOW_XDISPLAY (window),
546                          GDK_WINDOW_XID (window),
547                          new_info.x, new_info.y, new_info.width, new_info.height);
548       
549       if (impl->position_info.no_bg)
550         gdk_window_tmp_reset_bg (window);
551
552       if (!impl->position_info.mapped && new_info.mapped && GDK_WINDOW_IS_MAPPED (obj))
553         XMapWindow (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XID (window));
554       
555       impl->position_info = new_info;
556       
557       tmp_list = obj->children;
558       while (tmp_list)
559         {
560           gdk_window_postmove (tmp_list->data, &parent_pos);
561           tmp_list = tmp_list->next;
562         }
563     }
564   else
565     {
566       if (is_move && is_resize)
567         gdk_window_set_static_gravities (window, FALSE);
568
569       if (impl->position_info.mapped && !new_info.mapped)
570         XUnmapWindow (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XID (window));
571       
572       tmp_list = obj->children;
573       while (tmp_list)
574         {
575           gdk_window_premove (tmp_list->data, &parent_pos);
576           tmp_list = tmp_list->next;
577         }
578
579       if (is_resize)
580         XMoveResizeWindow (GDK_WINDOW_XDISPLAY (window),
581                            GDK_WINDOW_XID (window),
582                            new_info.x, new_info.y, new_info.width, new_info.height);
583       else
584         XMoveWindow (GDK_WINDOW_XDISPLAY (window),
585                      GDK_WINDOW_XID (window),
586                      new_info.x, new_info.y);
587
588       tmp_list = obj->children;
589       while (tmp_list)
590         {
591           gdk_window_postmove (tmp_list->data, &parent_pos);
592           tmp_list = tmp_list->next;
593         }
594
595       if (impl->position_info.no_bg)
596         gdk_window_tmp_reset_bg (window);
597
598       if (!impl->position_info.mapped && new_info.mapped && GDK_WINDOW_IS_MAPPED (obj))
599         XMapWindow (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XID (window));
600
601       impl->position_info = new_info;
602     }
603 }
604
605 static void
606 gdk_window_compute_position (GdkWindowImplX11   *window,
607                              GdkWindowParentPos *parent_pos,
608                              GdkXPositionInfo   *info)
609 {
610   GdkWindowObject *wrapper;
611   int parent_x_offset;
612   int parent_y_offset;
613
614   g_return_if_fail (GDK_IS_WINDOW_IMPL_X11 (window));
615
616   wrapper = GDK_WINDOW_OBJECT (GDK_DRAWABLE_IMPL_X11 (window)->wrapper);
617   
618   info->big = FALSE;
619   
620   if (window->width <= 32767)
621     {
622       info->width = window->width;
623       info->x = parent_pos->x + wrapper->x - parent_pos->x11_x;
624     }
625   else
626     {
627       info->big = TRUE;
628       info->width = 32767;
629       if (parent_pos->x + wrapper->x < -16384)
630         {
631           if (parent_pos->x + wrapper->x + window->width < 16384)
632             info->x = parent_pos->x + wrapper->x + window->width - info->width - parent_pos->x11_x;
633           else
634             info->x = -16384 - parent_pos->x11_x;
635         }
636       else
637         info->x = parent_pos->x + wrapper->x - parent_pos->x11_x;
638     }
639
640   if (window->height <= 32767)
641     {
642       info->height = window->height;
643       info->y = parent_pos->y + wrapper->y - parent_pos->x11_y;
644     }
645   else
646     {
647       info->big = TRUE;
648       info->height = 32767;
649       if (parent_pos->y + wrapper->y < -16384)
650         {
651           if (parent_pos->y + wrapper->y + window->height < 16384)
652             info->y = parent_pos->y + wrapper->y + window->height - info->height - parent_pos->x11_y;
653           else
654             info->y = -16384 - parent_pos->x11_y;
655         }
656       else
657         info->y = parent_pos->y + wrapper->y - parent_pos->x11_y;
658     }
659
660   parent_x_offset = parent_pos->x11_x - parent_pos->x;
661   parent_y_offset = parent_pos->x11_y - parent_pos->y;
662   
663   info->x_offset = parent_x_offset + info->x - wrapper->x;
664   info->y_offset = parent_y_offset + info->y - wrapper->y;
665
666   /* We don't considering the clipping of toplevel windows and their immediate children
667    * by their parents, and simply always map those windows.
668    */
669   if (parent_pos->clip_rect.width == G_MAXINT)
670     info->mapped = TRUE;
671   /* Check if the window would wrap around into the visible space in either direction */
672   else if (info->x + parent_x_offset < parent_pos->clip_rect.x + parent_pos->clip_rect.width - 65536 ||
673       info->x + info->width + parent_x_offset > parent_pos->clip_rect.x + 65536 ||
674       info->y + parent_y_offset < parent_pos->clip_rect.y + parent_pos->clip_rect.height - 65536 ||
675       info->y + info->height + parent_y_offset  > parent_pos->clip_rect.y + 65536)
676     info->mapped = FALSE;
677   else
678     info->mapped = TRUE;
679
680   info->no_bg = FALSE;
681
682   if (GDK_WINDOW_TYPE (wrapper) == GDK_WINDOW_CHILD)
683     {
684       info->clip_rect.x = wrapper->x;
685       info->clip_rect.y = wrapper->y;
686       info->clip_rect.width = window->width;
687       info->clip_rect.height = window->height;
688       
689       gdk_rectangle_intersect (&info->clip_rect, &parent_pos->clip_rect, &info->clip_rect);
690
691       info->clip_rect.x -= wrapper->x;
692       info->clip_rect.y -= wrapper->y;
693     }
694   else
695     {
696       info->clip_rect.x = 0;
697       info->clip_rect.y = 0;
698       info->clip_rect.width = G_MAXINT;
699       info->clip_rect.height = G_MAXINT;
700     }
701 }
702
703 static void
704 gdk_window_compute_parent_pos (GdkWindowImplX11      *window,
705                                GdkWindowParentPos *parent_pos)
706 {
707   GdkWindowObject *wrapper;
708   GdkWindowObject *parent;
709   GdkRectangle tmp_clip;
710   
711   int clip_xoffset = 0;
712   int clip_yoffset = 0;
713
714   g_return_if_fail (GDK_IS_WINDOW_IMPL_X11 (window));
715
716   wrapper =
717     GDK_WINDOW_OBJECT (GDK_DRAWABLE_IMPL_X11 (window)->wrapper);
718   
719   parent_pos->x = 0;
720   parent_pos->y = 0;
721   parent_pos->x11_x = 0;
722   parent_pos->x11_y = 0;
723
724   /* We take a simple approach here and simply consider toplevel
725    * windows not to clip their children on the right/bottom, since the
726    * size of toplevel windows is not directly under our
727    * control. Clipping only really matters when scrolling and
728    * generally we aren't going to be moving the immediate child of a
729    * toplevel beyond the bounds of that toplevel.
730    *
731    * We could go ahead and recompute the clips of toplevel windows and
732    * their descendents when we receive size notification, but it would
733    * probably not be an improvement in most cases.
734    */
735   parent_pos->clip_rect.x = 0;
736   parent_pos->clip_rect.y = 0;
737   parent_pos->clip_rect.width = G_MAXINT;
738   parent_pos->clip_rect.height = G_MAXINT;
739
740   parent = (GdkWindowObject *)wrapper->parent;
741   while (parent && parent->window_type == GDK_WINDOW_CHILD)
742     {
743       GdkWindowImplX11 *impl = GDK_WINDOW_IMPL_X11 (parent->impl);
744       
745       tmp_clip.x = - clip_xoffset;
746       tmp_clip.y = - clip_yoffset;
747       tmp_clip.width = impl->width;
748       tmp_clip.height = impl->height;
749
750       gdk_rectangle_intersect (&parent_pos->clip_rect, &tmp_clip, &parent_pos->clip_rect);
751
752       parent_pos->x += parent->x;
753       parent_pos->y += parent->y;
754       parent_pos->x11_x += impl->position_info.x;
755       parent_pos->x11_y += impl->position_info.y;
756
757       clip_xoffset += parent->x;
758       clip_yoffset += parent->y;
759
760       parent = (GdkWindowObject *)parent->parent;
761     }
762 }
763
764 static void
765 gdk_window_premove (GdkWindow          *window,
766                     GdkWindowParentPos *parent_pos)
767 {
768   GdkWindowImplX11 *impl;
769   GdkWindowObject *obj;
770   GdkXPositionInfo new_info;
771   GList *tmp_list;
772   gint d_xoffset, d_yoffset;
773   GdkWindowParentPos this_pos;
774
775   obj = (GdkWindowObject *) window;
776   impl = GDK_WINDOW_IMPL_X11 (obj->impl);
777   
778   gdk_window_compute_position (impl, parent_pos, &new_info);
779
780   gdk_window_clip_changed (window, &impl->position_info.clip_rect, &new_info.clip_rect);
781
782   this_pos.x = parent_pos->x + obj->x;
783   this_pos.y = parent_pos->y + obj->y;
784   this_pos.x11_x = parent_pos->x11_x + new_info.x;
785   this_pos.x11_y = parent_pos->x11_y + new_info.y;
786   this_pos.clip_rect = new_info.clip_rect;
787
788   if (impl->position_info.mapped && !new_info.mapped)
789     XUnmapWindow (GDK_DRAWABLE_XDISPLAY (window), GDK_DRAWABLE_XID (window));
790
791   d_xoffset = new_info.x_offset - impl->position_info.x_offset;
792   d_yoffset = new_info.y_offset - impl->position_info.y_offset;
793   
794   if (d_xoffset != 0 || d_yoffset != 0)
795     {
796       GdkRectangle new_position;
797
798       if (d_xoffset < 0 || d_yoffset < 0)
799         gdk_window_queue_translation (window, MIN (d_xoffset, 0), MIN (d_yoffset, 0));
800
801       compute_intermediate_position (&impl->position_info, &new_info, d_xoffset, d_yoffset,
802                                      &new_position);
803
804       XMoveResizeWindow (GDK_DRAWABLE_XDISPLAY (window),
805                          GDK_DRAWABLE_XID (window),
806                          new_position.x, new_position.y, new_position.width, new_position.height);
807     }
808
809   tmp_list = obj->children;
810   while (tmp_list)
811     {
812       gdk_window_premove (tmp_list->data, &this_pos);
813       tmp_list = tmp_list->next;
814     }
815 }
816
817 static void
818 gdk_window_postmove (GdkWindow          *window,
819                      GdkWindowParentPos *parent_pos)
820 {
821   GdkWindowImplX11 *impl;
822   GdkWindowObject *obj;
823   GdkXPositionInfo new_info;
824   GList *tmp_list;
825   gint d_xoffset, d_yoffset;
826   GdkWindowParentPos this_pos;
827
828   obj = (GdkWindowObject *) window;
829   impl = GDK_WINDOW_IMPL_X11 (obj->impl);
830   
831   gdk_window_compute_position (impl, parent_pos, &new_info);
832
833   this_pos.x = parent_pos->x + obj->x;
834   this_pos.y = parent_pos->y + obj->y;
835   this_pos.x11_x = parent_pos->x11_x + new_info.x;
836   this_pos.x11_y = parent_pos->x11_y + new_info.y;
837   this_pos.clip_rect = new_info.clip_rect;
838
839   d_xoffset = new_info.x_offset - impl->position_info.x_offset;
840   d_yoffset = new_info.y_offset - impl->position_info.y_offset;
841   
842   if (d_xoffset != 0 || d_yoffset != 0)
843     {
844       if (d_xoffset > 0 || d_yoffset > 0)
845         gdk_window_queue_translation (window, MAX (d_xoffset, 0), MAX (d_yoffset, 0));
846         
847       XMoveResizeWindow (GDK_DRAWABLE_XDISPLAY (window),
848                          GDK_DRAWABLE_XID (window),
849                          new_info.x, new_info.y, new_info.width, new_info.height);
850     }
851
852   if (!impl->position_info.mapped && new_info.mapped && GDK_WINDOW_IS_MAPPED (obj))
853     XMapWindow (GDK_DRAWABLE_XDISPLAY (window), GDK_DRAWABLE_XID (window));
854
855   if (impl->position_info.no_bg)
856     gdk_window_tmp_reset_bg (window);
857
858   impl->position_info = new_info;
859
860   tmp_list = obj->children;
861   while (tmp_list)
862     {
863       gdk_window_postmove (tmp_list->data, &this_pos);
864       tmp_list = tmp_list->next;
865     }
866 }
867
868 Bool
869 expose_serial_predicate (Display *xdisplay,
870                          XEvent  *xev,
871                          XPointer arg)
872 {
873   gulong *serial = (gulong *)arg;
874
875   if (xev->xany.type == Expose)
876     *serial = MIN (*serial, xev->xany.serial);
877
878   return False;
879 }
880
881 /* Find oldest possible serial for an outstanding expose event
882  */
883 static gulong
884 find_current_serial (Display *xdisplay)
885 {
886   XEvent xev;
887   gulong serial = NextRequest (xdisplay);
888   
889   XSync (xdisplay, False);
890
891   XCheckIfEvent (xdisplay, &xev, expose_serial_predicate, (XPointer)&serial);
892
893   return serial;
894 }
895
896 static void
897 queue_delete_link (GQueue *queue,
898                    GList  *link)
899 {
900   if (queue->tail == link)
901     queue->tail = link->prev;
902   
903   queue->head = g_list_remove_link (queue->head, link);
904   g_list_free_1 (link);
905   queue->length--;
906 }
907
908 static void
909 queue_item_free (GdkWindowQueueItem *item)
910 {
911   if (item->window)
912     {
913       g_object_remove_weak_pointer (G_OBJECT (item->window),
914                                     (gpointer *)&(item->window));
915     }
916   
917   if (item->type == GDK_WINDOW_QUEUE_ANTIEXPOSE)
918     gdk_region_destroy (item->u.antiexpose.area);
919   
920   g_free (item);
921 }
922
923 static void
924 gdk_window_queue (GdkWindow          *window,
925                   GdkWindowQueueItem *item)
926 {
927   GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (GDK_WINDOW_DISPLAY (window));
928   
929   if (!display_x11->translate_queue)
930     display_x11->translate_queue = g_queue_new ();
931
932   /* Keep length of queue finite by, if it grows too long,
933    * figuring out the latest relevant serial and discarding
934    * irrelevant queue items.
935    */
936   if (display_x11->translate_queue->length >= 64)
937     {
938       gulong serial = find_current_serial (GDK_WINDOW_XDISPLAY (window));
939       GList *tmp_list = display_x11->translate_queue->head;
940       
941       while (tmp_list)
942         {
943           GdkWindowQueueItem *item = tmp_list->data;
944           GList *next = tmp_list->next;
945           
946           if (serial > item->serial)
947             {
948               queue_delete_link (display_x11->translate_queue, tmp_list);
949               queue_item_free (item);
950             }
951
952           tmp_list = next;
953         }
954     }
955
956   /* Catch the case where someone isn't processing events and there
957    * is an event stuck in the event queue with an old serial:
958    * If we can't reduce the queue length by the above method,
959    * discard anti-expose items. (We can't discard translate
960    * items 
961    */
962   if (display_x11->translate_queue->length >= 64)
963     {
964       GList *tmp_list = display_x11->translate_queue->head;
965       
966       while (tmp_list)
967         {
968           GdkWindowQueueItem *item = tmp_list->data;
969           GList *next = tmp_list->next;
970           
971           if (item->type == GDK_WINDOW_QUEUE_ANTIEXPOSE)
972             {
973               queue_delete_link (display_x11->translate_queue, tmp_list);
974               queue_item_free (item);
975             }
976
977           tmp_list = next;
978         }
979     }
980
981   item->window = window;
982   item->serial = NextRequest (GDK_WINDOW_XDISPLAY (window));
983   
984   g_object_add_weak_pointer (G_OBJECT (window),
985                              (gpointer *)&(item->window));
986
987   g_queue_push_tail (display_x11->translate_queue, item);
988 }
989
990 static void
991 gdk_window_queue_translation (GdkWindow *window,
992                               gint       dx,
993                               gint       dy)
994 {
995   GdkWindowQueueItem *item = g_new (GdkWindowQueueItem, 1);
996   item->type = GDK_WINDOW_QUEUE_TRANSLATE;
997   item->u.translate.dx = dx;
998   item->u.translate.dy = dy;
999
1000   gdk_window_queue (window, item);
1001 }
1002
1003 gboolean
1004 _gdk_windowing_window_queue_antiexpose (GdkWindow *window,
1005                                         GdkRegion *area)
1006 {
1007   GdkWindowQueueItem *item = g_new (GdkWindowQueueItem, 1);
1008   item->type = GDK_WINDOW_QUEUE_ANTIEXPOSE;
1009   item->u.antiexpose.area = area;
1010
1011   gdk_window_queue (window, item);
1012
1013   return TRUE;
1014 }
1015
1016 void
1017 _gdk_window_process_expose (GdkWindow    *window,
1018                             gulong        serial,
1019                             GdkRectangle *area)
1020 {
1021   GdkWindowImplX11 *impl;
1022   GdkRegion *invalidate_region = gdk_region_rectangle (area);
1023   GdkRegion *clip_region;
1024   GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (GDK_WINDOW_DISPLAY (window));
1025   impl = GDK_WINDOW_IMPL_X11 (GDK_WINDOW_OBJECT (window)->impl);
1026       
1027   if (display_x11->translate_queue)
1028     {
1029       GList *tmp_list = display_x11->translate_queue->head;
1030       
1031       while (tmp_list)
1032         {
1033           GdkWindowQueueItem *item = tmp_list->data;
1034           tmp_list = tmp_list->next;
1035           
1036           if (serial < item->serial)
1037             {
1038               if (item->window == window)
1039                 {
1040                   if (item->type == GDK_WINDOW_QUEUE_TRANSLATE)
1041                     gdk_region_offset (invalidate_region, item->u.translate.dx, item->u.translate.dy);
1042                   else          /* anti-expose */
1043                     gdk_region_subtract (invalidate_region, item->u.antiexpose.area);
1044                 }
1045             }
1046           else
1047             {
1048               queue_delete_link (display_x11->translate_queue, 
1049                                  display_x11->translate_queue->head);
1050               queue_item_free (item);
1051             }
1052         }
1053     }
1054
1055   clip_region = gdk_region_rectangle (&impl->position_info.clip_rect);
1056   gdk_region_intersect (invalidate_region, clip_region);
1057
1058   if (!gdk_region_empty (invalidate_region))
1059     gdk_window_invalidate_region (window, invalidate_region, FALSE);
1060   
1061   gdk_region_destroy (invalidate_region);
1062   gdk_region_destroy (clip_region);
1063 }
1064
1065 static void
1066 gdk_window_tmp_unset_bg (GdkWindow *window)
1067 {
1068   GdkWindowImplX11 *impl;
1069   GdkWindowObject *obj;
1070
1071   obj = (GdkWindowObject *) window;
1072   impl = GDK_WINDOW_IMPL_X11 (obj->impl);
1073
1074   impl->position_info.no_bg = TRUE;
1075
1076   if (obj->bg_pixmap != GDK_NO_BG)
1077     XSetWindowBackgroundPixmap (GDK_DRAWABLE_XDISPLAY (window),
1078                                 GDK_DRAWABLE_XID (window), None);
1079 }
1080
1081 static void
1082 gdk_window_tmp_reset_bg (GdkWindow *window)
1083 {
1084   GdkWindowImplX11 *impl;
1085   GdkWindowObject *obj;
1086
1087   obj = (GdkWindowObject *) window;
1088   impl = GDK_WINDOW_IMPL_X11 (obj->impl);
1089
1090   impl->position_info.no_bg = FALSE;
1091
1092   if (obj->bg_pixmap == GDK_NO_BG)
1093     return;
1094   
1095   if (obj->bg_pixmap)
1096     {
1097       Pixmap xpixmap;
1098
1099       if (obj->bg_pixmap == GDK_PARENT_RELATIVE_BG)
1100         xpixmap = ParentRelative;
1101       else 
1102         xpixmap = GDK_DRAWABLE_XID (obj->bg_pixmap);
1103
1104       XSetWindowBackgroundPixmap (GDK_DRAWABLE_XDISPLAY (window),
1105                                   GDK_DRAWABLE_XID (window), xpixmap);
1106     }
1107   else
1108     {
1109       XSetWindowBackground (GDK_DRAWABLE_XDISPLAY (window),
1110                             GDK_DRAWABLE_XID (window),
1111                             obj->bg_color.pixel);
1112     }
1113 }
1114
1115 static void
1116 gdk_window_clip_changed (GdkWindow *window, GdkRectangle *old_clip, GdkRectangle *new_clip)
1117 {
1118   GdkWindowImplX11 *impl;
1119   GdkWindowObject *obj;
1120   GdkRegion *old_clip_region;
1121   GdkRegion *new_clip_region;
1122   
1123   if (((GdkWindowObject *)window)->input_only)
1124     return;
1125
1126   obj = (GdkWindowObject *) window;
1127   impl = GDK_WINDOW_IMPL_X11 (obj->impl);
1128   
1129   old_clip_region = gdk_region_rectangle (old_clip);
1130   new_clip_region = gdk_region_rectangle (new_clip);
1131
1132   /* We need to update this here because gdk_window_invalidate_region makes
1133    * use if it (through gdk_drawable_get_visible_region
1134    */
1135   impl->position_info.clip_rect = *new_clip;
1136   
1137   /* Trim invalid region of window to new clip rectangle
1138    */
1139   if (obj->update_area)
1140     gdk_region_intersect (obj->update_area, new_clip_region);
1141
1142   /* Invalidate newly exposed portion of window
1143    */
1144   gdk_region_subtract (new_clip_region, old_clip_region);
1145   if (!gdk_region_empty (new_clip_region))
1146     {
1147       gdk_window_tmp_unset_bg (window);
1148       gdk_window_invalidate_region (window, new_clip_region, FALSE);
1149     }
1150
1151   gdk_region_destroy (new_clip_region);
1152   gdk_region_destroy (old_clip_region);
1153 }