1 /* GDK - The GIMP Drawing Kit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
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.
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.
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.
20 /* gdkgeometry-x11.c: emulation of 32 bit coordinates within the
23 * By Owen Taylor <otaylor@redhat.com>
24 * Copyright Red Hat, Inc. 2000
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:
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.
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.
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:
50 * |abcdefghijk | (1) Resize bigger
51 * | efghijk | (2) Move
52 * |efghijk | (3) Move-resize back to the original size
54 * Or, going the other way:
57 * | abcdefghijk| (1) Move-resize bigger
58 * | abcdefghijk| (2) Move
59 * | abcdefg| (4) Resize back to the original size
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
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
75 * But, this isn't a perfect API for applications for several reasons:
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.
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
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.
93 * So, what we do is set up a mapping from virtual 32 bit window position/size
96 * - Real window position/size
97 * - Offset between virtual coordinates and real coordinates for the window
98 * - Map state (mapped or unmapped)
100 * By the following rules:
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
108 * This is implemented in gdk_window_compute_position(). Then the algorithm
109 * for a moving a window (_window_move_resize_child ()) is:
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
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.
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.
133 #include "gdk.h" /* For gdk_rectangle_intersect */
134 #include "gdkprivate-x11.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"
143 typedef struct _GdkWindowQueueItem GdkWindowQueueItem;
144 typedef struct _GdkWindowParentPos GdkWindowParentPos;
147 GDK_WINDOW_QUEUE_TRANSLATE,
148 GDK_WINDOW_QUEUE_ANTIEXPOSE
149 } GdkWindowQueueType;
151 struct _GdkWindowQueueItem
155 GdkWindowQueueType type;
169 move (GdkWindow *window, GdkRectangle *pos)
171 XMoveWindow (GDK_WINDOW_XDISPLAY (window),
172 GDK_WINDOW_XID (window), pos->x, pos->y);
176 move_resize (GdkWindow *window, GdkRectangle *pos)
178 XMoveResizeWindow (GDK_WINDOW_XDISPLAY (window),
179 GDK_WINDOW_XID (window),
180 pos->x, pos->y, pos->width, pos->height);
184 _gdk_window_move_resize_child (GdkWindow *window,
190 GdkWindowImplX11 *impl;
191 GdkWindowObject *obj;
192 GdkRectangle new_info;
195 g_return_if_fail (window != NULL);
196 g_return_if_fail (GDK_IS_WINDOW (window));
198 impl = GDK_WINDOW_IMPL_X11 (GDK_WINDOW_OBJECT (window)->impl);
199 obj = GDK_WINDOW_OBJECT (window);
202 width != obj->width ||
203 height != obj->height;
208 obj->height = height;
210 new_info.x = obj->x + obj->parent->abs_x;
211 new_info.y = obj->y + obj->parent->abs_y;
212 new_info.width = obj->width;
213 new_info.height = obj->height;
215 _gdk_x11_window_tmp_unset_bg (window, TRUE);
216 _gdk_x11_window_tmp_unset_bg (obj->parent, FALSE);
218 move_resize (window, &new_info);
220 move (window, &new_info);
221 _gdk_x11_window_tmp_reset_bg (obj->parent, FALSE);
222 _gdk_x11_window_tmp_reset_bg (window, TRUE);
226 expose_serial_predicate (Display *xdisplay,
230 gulong *serial = (gulong *)arg;
232 if (xev->xany.type == Expose)
233 *serial = MIN (*serial, xev->xany.serial);
238 /* Find oldest possible serial for an outstanding expose event
241 find_current_serial (Display *xdisplay)
244 gulong serial = NextRequest (xdisplay);
246 XSync (xdisplay, False);
248 XCheckIfEvent (xdisplay, &xev, expose_serial_predicate, (XPointer)&serial);
254 queue_delete_link (GQueue *queue,
257 if (queue->tail == link)
258 queue->tail = link->prev;
260 queue->head = g_list_remove_link (queue->head, link);
261 g_list_free_1 (link);
266 queue_item_free (GdkWindowQueueItem *item)
270 g_object_remove_weak_pointer (G_OBJECT (item->window),
271 (gpointer *)&(item->window));
274 if (item->type == GDK_WINDOW_QUEUE_ANTIEXPOSE)
275 gdk_region_destroy (item->u.antiexpose.area);
278 if (item->u.translate.area)
279 gdk_region_destroy (item->u.translate.area);
286 gdk_window_queue (GdkWindow *window,
287 GdkWindowQueueItem *item)
289 GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (GDK_WINDOW_DISPLAY (window));
291 if (!display_x11->translate_queue)
292 display_x11->translate_queue = g_queue_new ();
294 /* Keep length of queue finite by, if it grows too long,
295 * figuring out the latest relevant serial and discarding
296 * irrelevant queue items.
298 if (display_x11->translate_queue->length >= 64)
300 gulong serial = find_current_serial (GDK_WINDOW_XDISPLAY (window));
301 GList *tmp_list = display_x11->translate_queue->head;
305 GdkWindowQueueItem *item = tmp_list->data;
306 GList *next = tmp_list->next;
308 /* an overflow-safe (item->serial < serial) */
309 if (item->serial - serial > (gulong) G_MAXLONG)
311 queue_delete_link (display_x11->translate_queue, tmp_list);
312 queue_item_free (item);
319 /* Catch the case where someone isn't processing events and there
320 * is an event stuck in the event queue with an old serial:
321 * If we can't reduce the queue length by the above method,
322 * discard anti-expose items. (We can't discard translate
325 if (display_x11->translate_queue->length >= 64)
327 GList *tmp_list = display_x11->translate_queue->head;
331 GdkWindowQueueItem *item = tmp_list->data;
332 GList *next = tmp_list->next;
334 if (item->type == GDK_WINDOW_QUEUE_ANTIEXPOSE)
336 queue_delete_link (display_x11->translate_queue, tmp_list);
337 queue_item_free (item);
344 item->window = window;
345 item->serial = NextRequest (GDK_WINDOW_XDISPLAY (window));
347 g_object_add_weak_pointer (G_OBJECT (window),
348 (gpointer *)&(item->window));
350 g_queue_push_tail (display_x11->translate_queue, item);
354 _gdk_x11_window_queue_translation (GdkWindow *window,
359 GdkWindowQueueItem *item = g_new (GdkWindowQueueItem, 1);
360 item->type = GDK_WINDOW_QUEUE_TRANSLATE;
361 item->u.translate.area = area ? gdk_region_copy (area) : NULL;
362 item->u.translate.dx = dx;
363 item->u.translate.dy = dy;
365 gdk_window_queue (window, item);
369 _gdk_x11_window_queue_antiexpose (GdkWindow *window,
372 GdkWindowQueueItem *item = g_new (GdkWindowQueueItem, 1);
373 item->type = GDK_WINDOW_QUEUE_ANTIEXPOSE;
374 item->u.antiexpose.area = area;
376 gdk_window_queue (window, item);
382 _gdk_window_process_expose (GdkWindow *window,
386 GdkWindowImplX11 *impl;
387 GdkRegion *invalidate_region = gdk_region_rectangle (area);
388 GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (GDK_WINDOW_DISPLAY (window));
389 impl = GDK_WINDOW_IMPL_X11 (GDK_WINDOW_OBJECT (window)->impl);
391 if (display_x11->translate_queue)
393 GList *tmp_list = display_x11->translate_queue->head;
397 GdkWindowQueueItem *item = tmp_list->data;
398 GList *next = tmp_list->next;
400 /* an overflow-safe (serial < item->serial) */
401 if (serial - item->serial > (gulong) G_MAXLONG)
403 if (item->window == window)
405 if (item->type == GDK_WINDOW_QUEUE_TRANSLATE)
407 if (item->u.translate.area)
409 GdkRegion *intersection;
411 intersection = gdk_region_copy (invalidate_region);
412 gdk_region_intersect (intersection, item->u.translate.area);
413 gdk_region_subtract (invalidate_region, intersection);
414 gdk_region_offset (intersection, item->u.translate.dx, item->u.translate.dy);
415 gdk_region_union (invalidate_region, intersection);
416 gdk_region_destroy (intersection);
419 gdk_region_offset (invalidate_region, item->u.translate.dx, item->u.translate.dy);
421 else /* anti-expose */
423 gdk_region_subtract (invalidate_region, item->u.antiexpose.area);
429 queue_delete_link (display_x11->translate_queue, tmp_list);
430 queue_item_free (item);
436 if (!gdk_region_empty (invalidate_region))
437 _gdk_window_invalidate_for_expose (window, invalidate_region);
439 gdk_region_destroy (invalidate_region);
442 #define __GDK_GEOMETRY_X11_C__
443 #include "gdkaliasdef.c"