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