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, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "config.h"
19 
20 #include "gdkinternals.h"
21 #include "gdkrectangle.h"
22 #include "gdkprivate-x11.h"
23 #include "gdkscreen-x11.h"
24 #include "gdkdisplay-x11.h"
25 #include "gdkwindow-x11.h"
26 
27 
28 typedef struct _GdkWindowQueueItem GdkWindowQueueItem;
29 typedef struct _GdkWindowParentPos GdkWindowParentPos;
30 
31 struct _GdkWindowQueueItem
32 {
33   GdkWindow *window;
34   gulong serial;
35   cairo_region_t *antiexpose_area;
36 };
37 
38 void
_gdk_x11_window_move_resize_child(GdkWindow * window,gint x,gint y,gint width,gint height)39 _gdk_x11_window_move_resize_child (GdkWindow *window,
40                                    gint       x,
41                                    gint       y,
42                                    gint       width,
43                                    gint       height)
44 {
45   GdkWindowImplX11 *impl;
46 
47   g_return_if_fail (window != NULL);
48   g_return_if_fail (GDK_IS_WINDOW (window));
49 
50   impl = GDK_WINDOW_IMPL_X11 (window->impl);
51 
52   if (width * impl->window_scale > 65535 ||
53       height * impl->window_scale > 65535)
54     {
55       g_warning ("Native children wider or taller than 65535 pixels are not supported");
56 
57       if (width * impl->window_scale > 65535)
58         width = 65535 / impl->window_scale;
59       if (height * impl->window_scale > 65535)
60         height = 65535 / impl->window_scale;
61     }
62 
63   window->x = x;
64   window->y = y;
65   impl->unscaled_width = width * impl->window_scale;
66   impl->unscaled_height = height * impl->window_scale;
67   window->width = width;
68   window->height = height;
69 
70   /* We don't really care about origin overflow, because on overflow
71    * the window won't be visible anyway and thus it will be shaped
72    * to nothing
73    */
74   _gdk_x11_window_tmp_unset_parent_bg (window);
75   _gdk_x11_window_tmp_unset_bg (window, TRUE);
76   XMoveResizeWindow (GDK_WINDOW_XDISPLAY (window),
77                      GDK_WINDOW_XID (window),
78                      (window->x + window->parent->abs_x) * impl->window_scale,
79                      (window->y + window->parent->abs_y) * impl->window_scale,
80                      width * impl->window_scale,
81                      height * impl->window_scale);
82   _gdk_x11_window_tmp_reset_parent_bg (window);
83   _gdk_x11_window_tmp_reset_bg (window, TRUE);
84 }
85 
86 static Bool
expose_serial_predicate(Display * xdisplay,XEvent * xev,XPointer arg)87 expose_serial_predicate (Display *xdisplay,
88 			 XEvent  *xev,
89 			 XPointer arg)
90 {
91   gulong *serial = (gulong *)arg;
92 
93   if (xev->xany.type == Expose || xev->xany.type == GraphicsExpose)
94     *serial = MIN (*serial, xev->xany.serial);
95 
96   return False;
97 }
98 
99 /* Find oldest possible serial for an outstanding expose event
100  */
101 static gulong
find_current_serial(Display * xdisplay)102 find_current_serial (Display *xdisplay)
103 {
104   XEvent xev;
105   gulong serial = NextRequest (xdisplay);
106 
107   XSync (xdisplay, False);
108 
109   XCheckIfEvent (xdisplay, &xev, expose_serial_predicate, (XPointer)&serial);
110 
111   return serial;
112 }
113 
114 static void
queue_delete_link(GQueue * queue,GList * link)115 queue_delete_link (GQueue *queue,
116 		   GList  *link)
117 {
118   if (queue->tail == link)
119     queue->tail = link->prev;
120 
121   queue->head = g_list_remove_link (queue->head, link);
122   g_list_free_1 (link);
123   queue->length--;
124 }
125 
126 static void
queue_item_free(GdkWindowQueueItem * item)127 queue_item_free (GdkWindowQueueItem *item)
128 {
129   if (item->window)
130     {
131       g_object_remove_weak_pointer (G_OBJECT (item->window),
132 				    (gpointer *)&(item->window));
133     }
134 
135   cairo_region_destroy (item->antiexpose_area);
136   g_free (item);
137 }
138 
139 void
_gdk_x11_display_free_translate_queue(GdkDisplay * display)140 _gdk_x11_display_free_translate_queue (GdkDisplay *display)
141 {
142   GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
143 
144   if (display_x11->translate_queue)
145     {
146       g_queue_foreach (display_x11->translate_queue, (GFunc)queue_item_free, NULL);
147       g_queue_free (display_x11->translate_queue);
148       display_x11->translate_queue = NULL;
149     }
150 }
151 
152 static void
gdk_window_queue(GdkWindow * window,GdkWindowQueueItem * new_item)153 gdk_window_queue (GdkWindow          *window,
154 		  GdkWindowQueueItem *new_item)
155 {
156   GdkX11Display *display_x11 = GDK_X11_DISPLAY (GDK_WINDOW_DISPLAY (window));
157 
158   if (!display_x11->translate_queue)
159     display_x11->translate_queue = g_queue_new ();
160 
161   /* Keep length of queue finite by, if it grows too long,
162    * figuring out the latest relevant serial and discarding
163    * irrelevant queue items.
164    */
165   if (display_x11->translate_queue->length >= 64)
166     {
167       gulong serial = find_current_serial (GDK_WINDOW_XDISPLAY (window));
168       GList *tmp_list = display_x11->translate_queue->head;
169 
170       while (tmp_list)
171 	{
172 	  GdkWindowQueueItem *item = tmp_list->data;
173 	  GList *next = tmp_list->next;
174 
175 	  /* an overflow-safe (item->serial < serial) */
176 	  if (item->serial - serial > (gulong) G_MAXLONG)
177 	    {
178 	      queue_delete_link (display_x11->translate_queue, tmp_list);
179 	      queue_item_free (item);
180 	    }
181 
182 	  tmp_list = next;
183 	}
184     }
185 
186   /* Catch the case where someone isn't processing events and there
187    * is an event stuck in the event queue with an old serial:
188    * If we can't reduce the queue length by the above method,
189    * discard anti-expose items. (We can't discard translate
190    * items
191    */
192   if (display_x11->translate_queue->length >= 64)
193     {
194       GList *tmp_list = display_x11->translate_queue->head;
195 
196       while (tmp_list)
197 	{
198 	  GdkWindowQueueItem *item = tmp_list->data;
199 	  GList *next = tmp_list->next;
200 
201 	  queue_delete_link (display_x11->translate_queue, tmp_list);
202 	  queue_item_free (item);
203 
204 	  tmp_list = next;
205 	}
206     }
207 
208   new_item->window = window;
209   new_item->serial = NextRequest (GDK_WINDOW_XDISPLAY (window));
210 
211   g_object_add_weak_pointer (G_OBJECT (window),
212 			     (gpointer *)&(new_item->window));
213 
214   g_queue_push_tail (display_x11->translate_queue, new_item);
215 }
216 
217 void
_gdk_x11_window_queue_antiexpose(GdkWindow * window,cairo_region_t * area)218 _gdk_x11_window_queue_antiexpose (GdkWindow *window,
219 				  cairo_region_t *area)
220 {
221   GdkWindowQueueItem *item = g_new (GdkWindowQueueItem, 1);
222   item->antiexpose_area = cairo_region_reference (area);
223 
224   gdk_window_queue (window, item);
225 }
226 
227 void
_gdk_x11_window_process_expose(GdkWindow * window,gulong serial,GdkRectangle * area)228 _gdk_x11_window_process_expose (GdkWindow    *window,
229                                 gulong        serial,
230                                 GdkRectangle *area)
231 {
232   cairo_region_t *invalidate_region = cairo_region_create_rectangle (area);
233   GdkX11Display *display_x11 = GDK_X11_DISPLAY (GDK_WINDOW_DISPLAY (window));
234 
235   if (display_x11->translate_queue)
236     {
237       GList *tmp_list = display_x11->translate_queue->head;
238 
239       while (tmp_list)
240         {
241           GdkWindowQueueItem *item = tmp_list->data;
242           GList *next = tmp_list->next;
243 
244           /* an overflow-safe (serial < item->serial) */
245           if (serial - item->serial > (gulong) G_MAXLONG)
246             {
247               if (item->window == window)
248 		cairo_region_subtract (invalidate_region, item->antiexpose_area);
249             }
250           else
251             {
252               queue_delete_link (display_x11->translate_queue, tmp_list);
253               queue_item_free (item);
254             }
255           tmp_list = next;
256         }
257     }
258 
259   if (!cairo_region_is_empty (invalidate_region))
260     _gdk_window_invalidate_for_expose (window, invalidate_region);
261 
262   cairo_region_destroy (invalidate_region);
263 }
264