forked from AuroraMiddleware/gtk
8d1b7362b3
This code is a relic from GTK2 days and should not be necessary anymore, as code now makes sure to only copy those parts of the window that are not overlapped by parent windows. By deleting it we fix potential issues with composited and translucent windows copying the wrong data. https://bugzilla.gnome.org/show_bug.cgi?id=643416
368 lines
10 KiB
C
368 lines
10 KiB
C
/* GDK - The GIMP Drawing Kit
|
|
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "gdkinternals.h"
|
|
#include "gdkrectangle.h"
|
|
#include "gdkprivate-x11.h"
|
|
#include "gdkscreen-x11.h"
|
|
#include "gdkdisplay-x11.h"
|
|
#include "gdkwindow-x11.h"
|
|
|
|
|
|
typedef struct _GdkWindowQueueItem GdkWindowQueueItem;
|
|
typedef struct _GdkWindowParentPos GdkWindowParentPos;
|
|
|
|
typedef enum {
|
|
GDK_WINDOW_QUEUE_TRANSLATE,
|
|
GDK_WINDOW_QUEUE_ANTIEXPOSE
|
|
} GdkWindowQueueType;
|
|
|
|
struct _GdkWindowQueueItem
|
|
{
|
|
GdkWindow *window;
|
|
gulong serial;
|
|
GdkWindowQueueType type;
|
|
union {
|
|
struct {
|
|
cairo_region_t *area;
|
|
gint dx;
|
|
gint dy;
|
|
} translate;
|
|
struct {
|
|
cairo_region_t *area;
|
|
} antiexpose;
|
|
} u;
|
|
};
|
|
|
|
void
|
|
_gdk_x11_window_move_resize_child (GdkWindow *window,
|
|
gint x,
|
|
gint y,
|
|
gint width,
|
|
gint height)
|
|
{
|
|
g_return_if_fail (window != NULL);
|
|
g_return_if_fail (GDK_IS_WINDOW (window));
|
|
|
|
if (width > 65535 ||
|
|
height > 65535)
|
|
{
|
|
g_warning ("Native children wider or taller than 65535 pixels are not supported");
|
|
|
|
if (width > 65535)
|
|
width = 65535;
|
|
if (height > 65535)
|
|
height = 65535;
|
|
}
|
|
|
|
window->x = x;
|
|
window->y = y;
|
|
window->width = width;
|
|
window->height = height;
|
|
|
|
/* We don't really care about origin overflow, because on overflow
|
|
* the window won't be visible anyway and thus it will be shaped
|
|
* to nothing
|
|
*/
|
|
_gdk_x11_window_tmp_unset_parent_bg (window);
|
|
_gdk_x11_window_tmp_unset_bg (window, TRUE);
|
|
XMoveResizeWindow (GDK_WINDOW_XDISPLAY (window),
|
|
GDK_WINDOW_XID (window),
|
|
window->x + window->parent->abs_x,
|
|
window->y + window->parent->abs_y,
|
|
width, height);
|
|
_gdk_x11_window_tmp_reset_parent_bg (window);
|
|
_gdk_x11_window_tmp_reset_bg (window, TRUE);
|
|
}
|
|
|
|
static Bool
|
|
expose_serial_predicate (Display *xdisplay,
|
|
XEvent *xev,
|
|
XPointer arg)
|
|
{
|
|
gulong *serial = (gulong *)arg;
|
|
|
|
if (xev->xany.type == Expose || xev->xany.type == GraphicsExpose)
|
|
*serial = MIN (*serial, xev->xany.serial);
|
|
|
|
return False;
|
|
}
|
|
|
|
/* Find oldest possible serial for an outstanding expose event
|
|
*/
|
|
static gulong
|
|
find_current_serial (Display *xdisplay)
|
|
{
|
|
XEvent xev;
|
|
gulong serial = NextRequest (xdisplay);
|
|
|
|
XSync (xdisplay, False);
|
|
|
|
XCheckIfEvent (xdisplay, &xev, expose_serial_predicate, (XPointer)&serial);
|
|
|
|
return serial;
|
|
}
|
|
|
|
static void
|
|
queue_delete_link (GQueue *queue,
|
|
GList *link)
|
|
{
|
|
if (queue->tail == link)
|
|
queue->tail = link->prev;
|
|
|
|
queue->head = g_list_remove_link (queue->head, link);
|
|
g_list_free_1 (link);
|
|
queue->length--;
|
|
}
|
|
|
|
static void
|
|
queue_item_free (GdkWindowQueueItem *item)
|
|
{
|
|
if (item->window)
|
|
{
|
|
g_object_remove_weak_pointer (G_OBJECT (item->window),
|
|
(gpointer *)&(item->window));
|
|
}
|
|
|
|
if (item->type == GDK_WINDOW_QUEUE_ANTIEXPOSE)
|
|
cairo_region_destroy (item->u.antiexpose.area);
|
|
else
|
|
{
|
|
if (item->u.translate.area)
|
|
cairo_region_destroy (item->u.translate.area);
|
|
}
|
|
|
|
g_free (item);
|
|
}
|
|
|
|
static void
|
|
gdk_window_queue (GdkWindow *window,
|
|
GdkWindowQueueItem *item)
|
|
{
|
|
GdkX11Display *display_x11 = GDK_X11_DISPLAY (GDK_WINDOW_DISPLAY (window));
|
|
|
|
if (!display_x11->translate_queue)
|
|
display_x11->translate_queue = g_queue_new ();
|
|
|
|
/* Keep length of queue finite by, if it grows too long,
|
|
* figuring out the latest relevant serial and discarding
|
|
* irrelevant queue items.
|
|
*/
|
|
if (display_x11->translate_queue->length >= 64)
|
|
{
|
|
gulong serial = find_current_serial (GDK_WINDOW_XDISPLAY (window));
|
|
GList *tmp_list = display_x11->translate_queue->head;
|
|
|
|
while (tmp_list)
|
|
{
|
|
GdkWindowQueueItem *item = tmp_list->data;
|
|
GList *next = tmp_list->next;
|
|
|
|
/* an overflow-safe (item->serial < serial) */
|
|
if (item->serial - serial > (gulong) G_MAXLONG)
|
|
{
|
|
queue_delete_link (display_x11->translate_queue, tmp_list);
|
|
queue_item_free (item);
|
|
}
|
|
|
|
tmp_list = next;
|
|
}
|
|
}
|
|
|
|
/* Catch the case where someone isn't processing events and there
|
|
* is an event stuck in the event queue with an old serial:
|
|
* If we can't reduce the queue length by the above method,
|
|
* discard anti-expose items. (We can't discard translate
|
|
* items
|
|
*/
|
|
if (display_x11->translate_queue->length >= 64)
|
|
{
|
|
GList *tmp_list = display_x11->translate_queue->head;
|
|
|
|
while (tmp_list)
|
|
{
|
|
GdkWindowQueueItem *item = tmp_list->data;
|
|
GList *next = tmp_list->next;
|
|
|
|
if (item->type == GDK_WINDOW_QUEUE_ANTIEXPOSE)
|
|
{
|
|
queue_delete_link (display_x11->translate_queue, tmp_list);
|
|
queue_item_free (item);
|
|
}
|
|
|
|
tmp_list = next;
|
|
}
|
|
}
|
|
|
|
item->window = window;
|
|
item->serial = NextRequest (GDK_WINDOW_XDISPLAY (window));
|
|
|
|
g_object_add_weak_pointer (G_OBJECT (window),
|
|
(gpointer *)&(item->window));
|
|
|
|
g_queue_push_tail (display_x11->translate_queue, item);
|
|
}
|
|
|
|
static GC
|
|
_get_scratch_gc (GdkWindow *window, cairo_region_t *clip_region)
|
|
{
|
|
GdkX11Screen *screen;
|
|
XRectangle *rectangles;
|
|
gint n_rects;
|
|
gint depth;
|
|
|
|
screen = GDK_X11_SCREEN (gdk_window_get_screen (window));
|
|
depth = gdk_visual_get_depth (gdk_window_get_visual (window)) - 1;
|
|
|
|
if (!screen->subwindow_gcs[depth])
|
|
{
|
|
XGCValues values;
|
|
|
|
values.graphics_exposures = True;
|
|
values.subwindow_mode = IncludeInferiors;
|
|
|
|
screen->subwindow_gcs[depth] = XCreateGC (screen->xdisplay,
|
|
GDK_WINDOW_XID (window),
|
|
GCSubwindowMode | GCGraphicsExposures,
|
|
&values);
|
|
}
|
|
|
|
_gdk_x11_region_get_xrectangles (clip_region,
|
|
0, 0,
|
|
&rectangles,
|
|
&n_rects);
|
|
|
|
XSetClipRectangles (screen->xdisplay,
|
|
screen->subwindow_gcs[depth],
|
|
0, 0,
|
|
rectangles, n_rects,
|
|
YXBanded);
|
|
|
|
g_free (rectangles);
|
|
return screen->subwindow_gcs[depth];
|
|
}
|
|
|
|
|
|
|
|
void
|
|
_gdk_x11_window_translate (GdkWindow *window,
|
|
cairo_region_t *area,
|
|
gint dx,
|
|
gint dy)
|
|
{
|
|
GdkWindowQueueItem *item;
|
|
GC xgc;
|
|
GdkRectangle extents;
|
|
|
|
cairo_region_get_extents (area, &extents);
|
|
|
|
xgc = _get_scratch_gc (window, area);
|
|
|
|
cairo_region_translate (area, -dx, -dy); /* Move to source region */
|
|
|
|
item = g_new (GdkWindowQueueItem, 1);
|
|
item->type = GDK_WINDOW_QUEUE_TRANSLATE;
|
|
item->u.translate.area = cairo_region_copy (area);
|
|
item->u.translate.dx = dx;
|
|
item->u.translate.dy = dy;
|
|
gdk_window_queue (window, item);
|
|
|
|
XCopyArea (GDK_WINDOW_XDISPLAY (window),
|
|
GDK_WINDOW_XID (window),
|
|
GDK_WINDOW_XID (window),
|
|
xgc,
|
|
extents.x - dx, extents.y - dy,
|
|
extents.width, extents.height,
|
|
extents.x, extents.y);
|
|
}
|
|
|
|
gboolean
|
|
_gdk_x11_window_queue_antiexpose (GdkWindow *window,
|
|
cairo_region_t *area)
|
|
{
|
|
GdkWindowQueueItem *item = g_new (GdkWindowQueueItem, 1);
|
|
item->type = GDK_WINDOW_QUEUE_ANTIEXPOSE;
|
|
item->u.antiexpose.area = area;
|
|
|
|
gdk_window_queue (window, item);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
_gdk_x11_window_process_expose (GdkWindow *window,
|
|
gulong serial,
|
|
GdkRectangle *area)
|
|
{
|
|
cairo_region_t *invalidate_region = cairo_region_create_rectangle (area);
|
|
GdkX11Display *display_x11 = GDK_X11_DISPLAY (GDK_WINDOW_DISPLAY (window));
|
|
|
|
if (display_x11->translate_queue)
|
|
{
|
|
GList *tmp_list = display_x11->translate_queue->head;
|
|
|
|
while (tmp_list)
|
|
{
|
|
GdkWindowQueueItem *item = tmp_list->data;
|
|
GList *next = tmp_list->next;
|
|
|
|
/* an overflow-safe (serial < item->serial) */
|
|
if (serial - item->serial > (gulong) G_MAXLONG)
|
|
{
|
|
if (item->window == window)
|
|
{
|
|
if (item->type == GDK_WINDOW_QUEUE_TRANSLATE)
|
|
{
|
|
if (item->u.translate.area)
|
|
{
|
|
cairo_region_t *intersection;
|
|
|
|
intersection = cairo_region_copy (invalidate_region);
|
|
cairo_region_intersect (intersection, item->u.translate.area);
|
|
cairo_region_subtract (invalidate_region, intersection);
|
|
cairo_region_translate (intersection, item->u.translate.dx, item->u.translate.dy);
|
|
cairo_region_union (invalidate_region, intersection);
|
|
cairo_region_destroy (intersection);
|
|
}
|
|
else
|
|
cairo_region_translate (invalidate_region, item->u.translate.dx, item->u.translate.dy);
|
|
}
|
|
else /* anti-expose */
|
|
{
|
|
cairo_region_subtract (invalidate_region, item->u.antiexpose.area);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
queue_delete_link (display_x11->translate_queue, tmp_list);
|
|
queue_item_free (item);
|
|
}
|
|
tmp_list = next;
|
|
}
|
|
}
|
|
|
|
if (!cairo_region_is_empty (invalidate_region))
|
|
_gdk_window_invalidate_for_expose (window, invalidate_region);
|
|
|
|
cairo_region_destroy (invalidate_region);
|
|
}
|