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