/* 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); }