]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkgeometry-x11.c
05bda6c6d083e32e89bc316150259ec419714d63
[~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 #include "config.h"
21
22 #include "gdkinternals.h"
23 #include "gdkrectangle.h"
24 #include "gdkprivate-x11.h"
25 #include "gdkscreen-x11.h"
26 #include "gdkdisplay-x11.h"
27 #include "gdkwindow-x11.h"
28
29
30 typedef struct _GdkWindowQueueItem GdkWindowQueueItem;
31 typedef struct _GdkWindowParentPos GdkWindowParentPos;
32
33 typedef enum {
34   GDK_WINDOW_QUEUE_TRANSLATE,
35   GDK_WINDOW_QUEUE_ANTIEXPOSE
36 } GdkWindowQueueType;
37
38 struct _GdkWindowQueueItem
39 {
40   GdkWindow *window;
41   gulong serial;
42   GdkWindowQueueType type;
43   union {
44     struct {
45       cairo_region_t *area;
46       gint dx;
47       gint dy;
48     } translate;
49     struct {
50       cairo_region_t *area;
51     } antiexpose;
52   } u;
53 };
54
55 void
56 _gdk_x11_window_move_resize_child (GdkWindow *window,
57                                    gint       x,
58                                    gint       y,
59                                    gint       width,
60                                    gint       height)
61 {
62   g_return_if_fail (window != NULL);
63   g_return_if_fail (GDK_IS_WINDOW (window));
64
65   if (width > 65535 ||
66       height > 65535)
67     {
68       g_warning ("Native children wider or taller than 65535 pixels are not supported");
69
70       if (width > 65535)
71         width = 65535;
72       if (height > 65535)
73         height = 65535;
74     }
75
76   window->x = x;
77   window->y = y;
78   window->width = width;
79   window->height = height;
80
81   /* We don't really care about origin overflow, because on overflow
82    * the window won't be visible anyway and thus it will be shaped
83    * to nothing
84    */
85   _gdk_x11_window_tmp_unset_parent_bg (window);
86   _gdk_x11_window_tmp_unset_bg (window, TRUE);
87   XMoveResizeWindow (GDK_WINDOW_XDISPLAY (window),
88                      GDK_WINDOW_XID (window),
89                      window->x + window->parent->abs_x,
90                      window->y + window->parent->abs_y,
91                      width, height);
92   _gdk_x11_window_tmp_reset_parent_bg (window);
93   _gdk_x11_window_tmp_reset_bg (window, TRUE);
94 }
95
96 static Bool
97 expose_serial_predicate (Display *xdisplay,
98                          XEvent  *xev,
99                          XPointer arg)
100 {
101   gulong *serial = (gulong *)arg;
102
103   if (xev->xany.type == Expose || xev->xany.type == GraphicsExpose)
104     *serial = MIN (*serial, xev->xany.serial);
105
106   return False;
107 }
108
109 /* Find oldest possible serial for an outstanding expose event
110  */
111 static gulong
112 find_current_serial (Display *xdisplay)
113 {
114   XEvent xev;
115   gulong serial = NextRequest (xdisplay);
116   
117   XSync (xdisplay, False);
118
119   XCheckIfEvent (xdisplay, &xev, expose_serial_predicate, (XPointer)&serial);
120
121   return serial;
122 }
123
124 static void
125 queue_delete_link (GQueue *queue,
126                    GList  *link)
127 {
128   if (queue->tail == link)
129     queue->tail = link->prev;
130   
131   queue->head = g_list_remove_link (queue->head, link);
132   g_list_free_1 (link);
133   queue->length--;
134 }
135
136 static void
137 queue_item_free (GdkWindowQueueItem *item)
138 {
139   if (item->window)
140     {
141       g_object_remove_weak_pointer (G_OBJECT (item->window),
142                                     (gpointer *)&(item->window));
143     }
144   
145   if (item->type == GDK_WINDOW_QUEUE_ANTIEXPOSE)
146     cairo_region_destroy (item->u.antiexpose.area);
147   else
148     {
149       if (item->u.translate.area)
150         cairo_region_destroy (item->u.translate.area);
151     }
152   
153   g_free (item);
154 }
155
156 void
157 _gdk_x11_display_free_translate_queue (GdkDisplay *display)
158 {
159   GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
160
161   if (display_x11->translate_queue)
162     {
163       g_queue_foreach (display_x11->translate_queue, (GFunc)queue_item_free, NULL);
164       g_queue_free (display_x11->translate_queue);
165       display_x11->translate_queue = NULL;
166     }
167 }
168
169 static void
170 gdk_window_queue (GdkWindow          *window,
171                   GdkWindowQueueItem *item)
172 {
173   GdkX11Display *display_x11 = GDK_X11_DISPLAY (GDK_WINDOW_DISPLAY (window));
174   
175   if (!display_x11->translate_queue)
176     display_x11->translate_queue = g_queue_new ();
177
178   /* Keep length of queue finite by, if it grows too long,
179    * figuring out the latest relevant serial and discarding
180    * irrelevant queue items.
181    */
182   if (display_x11->translate_queue->length >= 64)
183     {
184       gulong serial = find_current_serial (GDK_WINDOW_XDISPLAY (window));
185       GList *tmp_list = display_x11->translate_queue->head;
186       
187       while (tmp_list)
188         {
189           GdkWindowQueueItem *item = tmp_list->data;
190           GList *next = tmp_list->next;
191           
192           /* an overflow-safe (item->serial < serial) */
193           if (item->serial - serial > (gulong) G_MAXLONG)
194             {
195               queue_delete_link (display_x11->translate_queue, tmp_list);
196               queue_item_free (item);
197             }
198
199           tmp_list = next;
200         }
201     }
202
203   /* Catch the case where someone isn't processing events and there
204    * is an event stuck in the event queue with an old serial:
205    * If we can't reduce the queue length by the above method,
206    * discard anti-expose items. (We can't discard translate
207    * items 
208    */
209   if (display_x11->translate_queue->length >= 64)
210     {
211       GList *tmp_list = display_x11->translate_queue->head;
212       
213       while (tmp_list)
214         {
215           GdkWindowQueueItem *item = tmp_list->data;
216           GList *next = tmp_list->next;
217           
218           if (item->type == GDK_WINDOW_QUEUE_ANTIEXPOSE)
219             {
220               queue_delete_link (display_x11->translate_queue, tmp_list);
221               queue_item_free (item);
222             }
223
224           tmp_list = next;
225         }
226     }
227
228   item->window = window;
229   item->serial = NextRequest (GDK_WINDOW_XDISPLAY (window));
230   
231   g_object_add_weak_pointer (G_OBJECT (window),
232                              (gpointer *)&(item->window));
233
234   g_queue_push_tail (display_x11->translate_queue, item);
235 }
236
237 static GC
238 _get_scratch_gc (GdkWindow *window, cairo_region_t *clip_region)
239 {
240   GdkX11Screen *screen;
241   XRectangle *rectangles;
242   gint n_rects;
243   gint depth;
244
245   screen = GDK_X11_SCREEN (gdk_window_get_screen (window));
246   depth = gdk_visual_get_depth (gdk_window_get_visual (window)) - 1;
247
248   if (!screen->subwindow_gcs[depth])
249     {
250       XGCValues values;
251       
252       values.graphics_exposures = True;
253       values.subwindow_mode = IncludeInferiors;
254       
255       screen->subwindow_gcs[depth] = XCreateGC (screen->xdisplay,
256                                                 GDK_WINDOW_XID (window),
257                                                 GCSubwindowMode | GCGraphicsExposures,
258                                                 &values);
259     }
260   
261   _gdk_x11_region_get_xrectangles (clip_region,
262                                    0, 0,
263                                    &rectangles,
264                                    &n_rects);
265   
266   XSetClipRectangles (screen->xdisplay,
267                       screen->subwindow_gcs[depth],
268                       0, 0,
269                       rectangles, n_rects,
270                       YXBanded);
271   
272   g_free (rectangles);
273   return screen->subwindow_gcs[depth];
274 }
275
276
277
278 void
279 _gdk_x11_window_translate (GdkWindow      *window,
280                            cairo_region_t *area,
281                            gint            dx,
282                            gint            dy)
283 {
284   GdkWindowQueueItem *item;
285   GC xgc;
286   GdkRectangle extents;
287
288   cairo_region_get_extents (area, &extents);
289
290   xgc = _get_scratch_gc (window, area);
291
292   cairo_region_translate (area, -dx, -dy); /* Move to source region */
293
294   item = g_new (GdkWindowQueueItem, 1);
295   item->type = GDK_WINDOW_QUEUE_TRANSLATE;
296   item->u.translate.area = cairo_region_copy (area);
297   item->u.translate.dx = dx;
298   item->u.translate.dy = dy;
299   gdk_window_queue (window, item);
300
301   XCopyArea (GDK_WINDOW_XDISPLAY (window),
302              GDK_WINDOW_XID (window),
303              GDK_WINDOW_XID (window),
304              xgc,
305              extents.x - dx, extents.y - dy,
306              extents.width, extents.height,
307              extents.x, extents.y);
308 }
309
310 gboolean
311 _gdk_x11_window_queue_antiexpose (GdkWindow *window,
312                                   cairo_region_t *area)
313 {
314   GdkWindowQueueItem *item = g_new (GdkWindowQueueItem, 1);
315   item->type = GDK_WINDOW_QUEUE_ANTIEXPOSE;
316   item->u.antiexpose.area = area;
317
318   gdk_window_queue (window, item);
319
320   return TRUE;
321 }
322
323 void
324 _gdk_x11_window_process_expose (GdkWindow    *window,
325                                 gulong        serial,
326                                 GdkRectangle *area)
327 {
328   cairo_region_t *invalidate_region = cairo_region_create_rectangle (area);
329   GdkX11Display *display_x11 = GDK_X11_DISPLAY (GDK_WINDOW_DISPLAY (window));
330
331   if (display_x11->translate_queue)
332     {
333       GList *tmp_list = display_x11->translate_queue->head;
334
335       while (tmp_list)
336         {
337           GdkWindowQueueItem *item = tmp_list->data;
338           GList *next = tmp_list->next;
339
340           /* an overflow-safe (serial < item->serial) */
341           if (serial - item->serial > (gulong) G_MAXLONG)
342             {
343               if (item->window == window)
344                 {
345                   if (item->type == GDK_WINDOW_QUEUE_TRANSLATE)
346                     {
347                       if (item->u.translate.area)
348                         {
349                           cairo_region_t *intersection;
350
351                           intersection = cairo_region_copy (invalidate_region);
352                           cairo_region_intersect (intersection, item->u.translate.area);
353                           cairo_region_subtract (invalidate_region, intersection);
354                           cairo_region_translate (intersection, item->u.translate.dx, item->u.translate.dy);
355                           cairo_region_union (invalidate_region, intersection);
356                           cairo_region_destroy (intersection);
357                         }
358                       else
359                         cairo_region_translate (invalidate_region, item->u.translate.dx, item->u.translate.dy);
360                     }
361                   else /* anti-expose */
362                     {
363                       cairo_region_subtract (invalidate_region, item->u.antiexpose.area);
364                     }
365                 }
366             }
367           else
368             {
369               queue_delete_link (display_x11->translate_queue, tmp_list);
370               queue_item_free (item);
371             }
372           tmp_list = next;
373         }
374     }
375
376   if (!cairo_region_is_empty (invalidate_region))
377     _gdk_window_invalidate_for_expose (window, invalidate_region);
378
379   cairo_region_destroy (invalidate_region);
380 }