#include "gdkdeviceprivate.h"
#include "gdkvisualprivate.h"
#include "gdkmarshalers.h"
+#include "gdkframeclockidle.h"
#include "gdkwindowimpl.h"
#include <math.h>
enum {
PROP_0,
- PROP_CURSOR
+ PROP_CURSOR,
+ PROP_FRAME_CLOCK
};
typedef enum {
cairo_region_t *region;
cairo_surface_t *surface;
cairo_region_t *flushed;
+ guint8 alpha;
guint uses_implicit : 1;
};
static void _gdk_window_propagate_has_alpha_background (GdkWindow *window);
static cairo_surface_t *gdk_window_ref_impl_surface (GdkWindow *window);
+static void gdk_window_process_all_updates_internal (gboolean default_clock_only);
+
+static void gdk_ensure_default_frame_clock (void);
+
static guint signals[LAST_SIGNAL] = { 0 };
static gpointer parent_class = NULL;
GDK_TYPE_CURSOR,
G_PARAM_READWRITE));
+ /**
+ * GdkWindow:paint-clock:
+ *
+ * The frame clock for a #GdkWindow, see #GdkFrameClock
+ *
+ * The frame clock remains the same for the lifetime of the window.
+ *
+ * Since: 3.0
+ */
+ g_object_class_install_property (object_class,
+ PROP_FRAME_CLOCK,
+ g_param_spec_object ("paint-clock",
+ P_("Frame clock"),
+ P_("Frame clock"),
+ GDK_TYPE_FRAME_CLOCK,
+ G_PARAM_READWRITE));
+
/**
* GdkWindow::pick-embedded-child:
* @window: the window on which the signal is emitted
gdk_window_set_cursor (window, g_value_get_object (value));
break;
+ case PROP_FRAME_CLOCK:
+ gdk_window_set_frame_clock (window, g_value_get_object (value));
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
g_value_set_object (value, gdk_window_get_cursor (window));
break;
+ case PROP_FRAME_CLOCK:
+ g_value_set_object (value, gdk_window_get_frame_clock (window));
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
gdk_window_has_alpha (GdkWindow *window)
{
return !gdk_window_has_impl (window) &&
- window->has_alpha_background;
+ (window->has_alpha_background || window->alpha != 255);
}
static void
window->y = y;
window->width = (attributes->width > 1) ? (attributes->width) : (1);
window->height = (attributes->height > 1) ? (attributes->height) : (1);
+ window->alpha = 255;
if (attributes->wclass == GDK_INPUT_ONLY)
{
* directly to the window or a child window.
*/
static gboolean
-gdk_window_begin_implicit_paint (GdkWindow *window, GdkRectangle *rect)
+gdk_window_begin_implicit_paint (GdkWindow *window, GdkRectangle *rect,
+ gboolean with_alpha, guint8 alpha)
{
GdkWindowPaint *paint;
if (GDK_IS_PAINTABLE (window->impl))
return FALSE; /* Implementation does double buffering */
- if (window->paint_stack != NULL ||
- window->implicit_paint != NULL)
+ if (window->paint_stack != NULL)
return FALSE; /* Don't stack implicit paints */
/* Never do implicit paints for foreign windows, they don't need
paint->region = cairo_region_create (); /* Empty */
paint->uses_implicit = FALSE;
paint->flushed = NULL;
+ paint->alpha = alpha;
paint->surface = gdk_window_create_similar_surface (window,
- gdk_window_get_content (window),
+ with_alpha ? CAIRO_CONTENT_COLOR_ALPHA : gdk_window_get_content (window),
MAX (rect->width, 1),
MAX (rect->height, 1));
cairo_surface_set_device_offset (paint->surface, -rect->x, -rect->y);
GdkWindow *impl_window;
GdkWindowPaint *paint;
cairo_region_t *region;
+ GSList *l;
impl_window = gdk_window_get_impl_window (window);
if (impl_window->implicit_paint == NULL)
return;
- paint = impl_window->implicit_paint->data;
+ /* There is no proper way to flush any non-toplevel implicit
+ paint, because direct rendering is not compatible with
+ opacity rendering, as it requires an opacity group.
+
+ We warn and flush just the outermost paint in this case.
+ */
+ l = g_slist_last (impl_window->implicit_paint);
+ if (l != impl_window->implicit_paint)
+ g_warning ("Non double buffered drawing not supported inside transparent windows");
+
+ paint = l->data;
region = cairo_region_copy (window->clip_region_with_children);
cairo_region_translate (region, window->abs_x, window->abs_y);
window->implicit_paint = g_slist_delete_link (window->implicit_paint,
window->implicit_paint);
- if (!GDK_WINDOW_DESTROYED (window) && !cairo_region_is_empty (paint->region))
+ if (!GDK_WINDOW_DESTROYED (window) && !cairo_region_is_empty (paint->region) && paint->alpha > 0)
{
+ GdkWindowPaint *parent_paint = NULL;
cairo_t *cr;
/* Some regions are valid, push these to window now */
- cr = gdk_cairo_create_for_impl (window);
+ if (window->implicit_paint)
+ {
+ parent_paint = window->implicit_paint->data;
+
+ /* If the toplevel implicit paint was flushed, restore it now
+ before we blend over it. */
+ if (parent_paint->flushed != NULL &&
+ !cairo_region_is_empty (parent_paint->flushed))
+ {
+ cairo_surface_t *source_surface = gdk_window_ref_impl_surface (window);
+ cairo_region_t *flushed = cairo_region_copy (parent_paint->flushed);
+
+ cr = cairo_create (parent_paint->surface);
+ cairo_set_source_surface (cr, source_surface, 0, 0);
+ cairo_surface_destroy (source_surface);
+
+ cairo_region_intersect (flushed, paint->region);
+ gdk_cairo_region (cr, flushed);
+ cairo_clip (cr);
+
+ cairo_region_subtract (parent_paint->flushed, flushed);
+ cairo_region_destroy (flushed);
+
+ cairo_paint (cr);
+ cairo_destroy (cr);
+ }
+
+ cr = cairo_create (parent_paint->surface);
+ }
+ else
+ cr = gdk_cairo_create_for_impl (window);
+
gdk_cairo_region (cr, paint->region);
cairo_clip (cr);
cairo_set_source_surface (cr, paint->surface, 0, 0);
- cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
- cairo_paint (cr);
+ if (paint->alpha == 255)
+ {
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+ cairo_paint (cr);
+ }
+ else
+ {
+ cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+ cairo_paint_with_alpha (cr, paint->alpha / 255.0);
+ }
+
cairo_destroy (cr);
+
+ if (parent_paint)
+ cairo_region_union (parent_paint->region, paint->region);
}
-
+
cairo_region_destroy (paint->region);
if (paint->flushed)
cairo_region_destroy (paint->flushed);
/* Code for dirty-region queueing
*/
static GSList *update_windows = NULL;
-static guint update_idle = 0;
+static GdkFrameClock *_gdk_default_frame_clock = NULL;
static gboolean debug_updates = FALSE;
static inline gboolean
update_windows = g_slist_remove (update_windows, window);
}
-static gboolean
-gdk_window_update_idle (gpointer data)
+static void
+gdk_window_paint_default_clock_updates (gpointer data)
{
- gdk_window_process_all_updates ();
+ gdk_window_process_all_updates_internal (TRUE);
+}
- return FALSE;
+static void
+gdk_ensure_default_frame_clock (void)
+{
+ if (_gdk_default_frame_clock == NULL)
+ {
+ _gdk_default_frame_clock = g_object_new (GDK_TYPE_FRAME_CLOCK_IDLE,
+ NULL);
+
+ g_signal_connect (G_OBJECT (_gdk_default_frame_clock),
+ "paint",
+ G_CALLBACK (gdk_window_paint_default_clock_updates),
+ NULL);
+ }
}
static gboolean
gdk_window_is_toplevel_frozen (window)))
return;
- if (!update_idle)
- update_idle =
- gdk_threads_add_idle_full (GDK_PRIORITY_REDRAW,
- gdk_window_update_idle,
- NULL, NULL);
+ gdk_frame_clock_request_phase (gdk_window_get_frame_clock (window),
+ GDK_FRAME_CLOCK_PHASE_PAINT);
}
void
{
GdkWindow *child;
cairo_region_t *clipped_expose_region;
+ GdkRectangle clip_box;
GList *l, *children;
+ gboolean end_implicit;
if (cairo_region_is_empty (expose_region))
return;
gdk_window_has_impl (window))
_gdk_window_add_damage ((GdkWindow *) window->impl_window, expose_region);
+ end_implicit = FALSE;
+ if (window->alpha != 255 && !gdk_window_has_impl (window))
+ {
+ if (window->alpha == 0)
+ return;
+
+ cairo_region_get_extents (expose_region, &clip_box);
+ clip_box.x += window->abs_x;
+ clip_box.y += window->abs_y;
+ end_implicit = gdk_window_begin_implicit_paint (window->impl_window, &clip_box, TRUE, window->alpha);
+ }
/* Paint the window before the children, clipped to the window region
with visible child windows removed */
g_list_free_full (children, g_object_unref);
+ if (end_implicit)
+ gdk_window_end_implicit_paint (window->impl_window);
}
/* Process and remove any invalid area on the native window by creating
*/
cairo_region_get_extents (update_area, &clip_box);
- end_implicit = gdk_window_begin_implicit_paint (window, &clip_box);
+ end_implicit = gdk_window_begin_implicit_paint (window, &clip_box, FALSE, 255);
expose_region = cairo_region_copy (update_area);
impl_class = GDK_WINDOW_IMPL_GET_CLASS (window->impl);
if (!end_implicit)
g_slist_free (displays);
}
+/**
+ * gdk_window_process_all_updates:
+ *
+ * Calls gdk_window_process_updates() for all windows (see #GdkWindow)
+ * in the application.
+ *
+ **/
+void
+gdk_window_process_all_updates (void)
+{
+ gdk_window_process_all_updates_internal (FALSE);
+}
+
/* Currently it is not possible to override
* gdk_window_process_all_updates in the same manner as
* gdk_window_process_updates and gdk_window_invalidate_maybe_recurse
* displays and call the mehod.
*/
-/**
- * gdk_window_process_all_updates:
- *
- * Calls gdk_window_process_updates() for all windows (see #GdkWindow)
- * in the application.
- *
- **/
-void
-gdk_window_process_all_updates (void)
+static void
+gdk_window_process_all_updates_internal (gboolean default_clock_only)
{
GSList *old_update_windows = update_windows;
GSList *tmp_list = update_windows;
/* We can't do this now since that would recurse, so
delay it until after the recursion is done. */
got_recursive_update = TRUE;
- update_idle = 0;
return;
}
in_process_all_updates = TRUE;
got_recursive_update = FALSE;
- if (update_idle)
- g_source_remove (update_idle);
-
update_windows = NULL;
- update_idle = 0;
before_process_all_updates ();
if (!GDK_WINDOW_DESTROYED (window))
{
if (window->update_freeze_count ||
- gdk_window_is_toplevel_frozen (window))
+ gdk_window_is_toplevel_frozen (window) ||
+ (default_clock_only && window->frame_clock != NULL))
gdk_window_add_update_window (window);
else
gdk_window_process_updates_internal (window);
redraw now so that it eventually happens,
otherwise we could miss an update if nothing
else schedules an update. */
- if (got_recursive_update && !update_idle)
- update_idle =
- gdk_threads_add_idle_full (GDK_PRIORITY_REDRAW,
- gdk_window_update_idle,
- NULL, NULL);
+ if (got_recursive_update)
+ gdk_window_schedule_update (NULL);
}
-/**
- * gdk_window_process_updates:
- * @window: a #GdkWindow
- * @update_children: whether to also process updates for child windows
- *
- * Sends one or more expose events to @window. The areas in each
- * expose event will cover the entire update area for the window (see
- * gdk_window_invalidate_region() for details). Normally GDK calls
- * gdk_window_process_all_updates() on your behalf, so there's no
- * need to call this function unless you want to force expose events
- * to be delivered immediately and synchronously (vs. the usual
- * case, where GDK delivers them in an idle handler). Occasionally
- * this is useful to produce nicer scrolling behavior, for example.
- *
- **/
-void
-gdk_window_process_updates (GdkWindow *window,
- gboolean update_children)
+
+enum {
+ PROCESS_UPDATES_NO_RECURSE,
+ PROCESS_UPDATES_WITH_ALL_CHILDREN,
+ PROCESS_UPDATES_WITH_SAME_CLOCK_CHILDREN
+};
+
+static void
+gdk_window_process_updates_with_mode (GdkWindow *window,
+ int recurse_mode)
{
GdkWindow *impl_window;
gdk_window_remove_update_window ((GdkWindow *)impl_window);
}
- if (update_children)
+ if (recurse_mode != PROCESS_UPDATES_NO_RECURSE)
{
/* process updates in reverse stacking order so composition or
* painting over achieves the desired effect for offscreen windows
for (node = g_list_last (children); node; node = node->prev)
{
- gdk_window_process_updates (node->data, TRUE);
- g_object_unref (node->data);
+ GdkWindow *child = node->data;
+ if (recurse_mode == PROCESS_UPDATES_WITH_ALL_CHILDREN ||
+ (recurse_mode == PROCESS_UPDATES_WITH_SAME_CLOCK_CHILDREN &&
+ child->frame_clock == NULL))
+ {
+ gdk_window_process_updates (child, TRUE);
+ }
+ g_object_unref (child);
}
g_list_free (children);
g_object_unref (window);
}
+/**
+ * gdk_window_process_updates:
+ * @window: a #GdkWindow
+ * @update_children: whether to also process updates for child windows
+ *
+ * Sends one or more expose events to @window. The areas in each
+ * expose event will cover the entire update area for the window (see
+ * gdk_window_invalidate_region() for details). Normally GDK calls
+ * gdk_window_process_all_updates() on your behalf, so there's no
+ * need to call this function unless you want to force expose events
+ * to be delivered immediately and synchronously (vs. the usual
+ * case, where GDK delivers them in an idle handler). Occasionally
+ * this is useful to produce nicer scrolling behavior, for example.
+ *
+ **/
+void
+gdk_window_process_updates (GdkWindow *window,
+ gboolean update_children)
+{
+ g_return_if_fail (GDK_IS_WINDOW (window));
+
+ return gdk_window_process_updates_with_mode (window,
+ update_children ?
+ PROCESS_UPDATES_WITH_ALL_CHILDREN :
+ PROCESS_UPDATES_NO_RECURSE);
+}
+
static void
gdk_window_invalidate_rect_full (GdkWindow *window,
const GdkRectangle *rect,
g_return_if_fail (window->window_type != GDK_WINDOW_CHILD);
window->update_and_descendants_freeze_count++;
+ gdk_frame_clock_freeze (gdk_window_get_frame_clock (window));
}
/**
g_return_if_fail (window->update_and_descendants_freeze_count > 0);
window->update_and_descendants_freeze_count--;
+ gdk_frame_clock_thaw (gdk_window_get_frame_clock (window));
gdk_window_schedule_update (window);
}
serial);
if (new_window_under_pointer != pointer_info->window_under_pointer)
{
+ GdkDevice *source_device;
+
+ if (pointer_info->last_slave)
+ source_device = pointer_info->last_slave;
+ else
+ source_device = device;
+
_gdk_synthesize_crossing_events (display,
pointer_info->window_under_pointer,
new_window_under_pointer,
- device, pointer_info->last_slave,
+ device, source_device,
GDK_CROSSING_NORMAL,
pointer_info->toplevel_x,
pointer_info->toplevel_y,
{
GdkWindow *event_window;
gdouble x, y;
- gboolean unlink_event;
+ gboolean unlink_event = FALSE;
GdkDeviceGrabInfo *button_release_grab;
GdkPointerWindowInfo *pointer_info = NULL;
GdkDevice *device, *source_device;
event_window = event->any.window;
if (!event_window)
- return;
+ goto out;
#ifdef DEBUG_WINDOW_PRINTING
if (event->type == GDK_KEY_PRESS &&
{
event_window->native_visibility = event->visibility.state;
gdk_window_update_visibility_recursively (event_window, event_window);
- return;
+ goto out;
}
if (!(is_button_type (event->type) ||
is_motion_type (event->type)) ||
event_window->window_type == GDK_WINDOW_ROOT)
- return;
+ goto out;
is_toplevel = gdk_window_is_toplevel (event_window);
_gdk_display_enable_motion_hints (display, device);
}
- unlink_event = FALSE;
if (is_motion_type (event->type))
unlink_event = proxy_pointer_event (display, event, serial);
else if (is_button_type (event->type))
g_list_free_1 (event_link);
gdk_event_free (event);
}
+
+ /* This does two things - first it sees if there are motions at the
+ * end of the queue that can be compressed. Second, if there is just
+ * a single motion that won't be dispatched because it is a compression
+ * candidate it queues up flushing the event queue.
+ */
+ _gdk_event_queue_handle_motion_compression (display);
}
/**
/**
* gdk_window_set_opacity:
- * @window: a top-level #GdkWindow
+ * @window: a top-level or non-native #GdkWindow
* @opacity: opacity
*
- * Request the windowing system to make @window partially transparent,
+ * Set @window to render as partially transparent,
* with opacity 0 being fully transparent and 1 fully opaque. (Values
* of the opacity parameter are clamped to the [0,1] range.)
*
- * On X11, this works only on X screens with a compositing manager
- * running.
+ * For toplevel windows this depends on support from the windowing system
+ * that may not always be there. For instance, On X11, this works only on
+ * X screens with a compositing manager running.
+ *
+ * For child windows this function only works for non-native windows.
+ *
+ * For setting up per-pixel alpha topelevels, see gdk_screen_get_rgba_visual(),
+ * and for non-toplevels, see gdk_window_set_composited().
*
- * For setting up per-pixel alpha, see gdk_screen_get_rgba_visual().
- * For making non-toplevel windows translucent, see
- * gdk_window_set_composited().
+ * Support for non-toplevel windows was added in 3.8.
*
* Since: 2.12
*/
gdk_window_set_opacity (GdkWindow *window,
gdouble opacity)
{
- GDK_WINDOW_IMPL_GET_CLASS (window->impl)->set_opacity (window, opacity);
+ if (opacity < 0)
+ opacity = 0;
+ else if (opacity > 1)
+ opacity = 1;
+
+ window->alpha = round (opacity * 255);
+
+ if (window->destroyed)
+ return;
+
+ if (gdk_window_has_impl (window))
+ GDK_WINDOW_IMPL_GET_CLASS (window->impl)->set_opacity (window, opacity);
+ else
+ {
+ recompute_visible_regions (window, TRUE, FALSE);
+ gdk_window_invalidate_rect_full (window, NULL, TRUE, CLEAR_BG_ALL);
+ }
}
/* This function is called when the XWindow is really gone.
{
GDK_WINDOW_IMPL_GET_CLASS (window->impl)->delete_property (window, property);
}
+
+static void
+gdk_window_flush_events (GdkFrameClock *clock,
+ void *data)
+{
+ GdkWindow *window;
+ GdkDisplay *display;
+
+ window = GDK_WINDOW (data);
+
+ display = gdk_window_get_display (window);
+ _gdk_display_flush_events (display);
+ _gdk_display_pause_events (display);
+
+ gdk_frame_clock_request_phase (clock, GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS);
+}
+
+static void
+gdk_window_paint_on_clock (GdkFrameClock *clock,
+ void *data)
+{
+ GdkWindow *window;
+
+ window = GDK_WINDOW (data);
+
+ /* Update window and any children on the same clock.
+ */
+ gdk_window_process_updates_with_mode (window, PROCESS_UPDATES_WITH_SAME_CLOCK_CHILDREN);
+}
+
+static void
+gdk_window_resume_events (GdkFrameClock *clock,
+ void *data)
+{
+ GdkWindow *window;
+ GdkDisplay *display;
+
+ window = GDK_WINDOW (data);
+
+ display = gdk_window_get_display (window);
+ _gdk_display_unpause_events (display);
+}
+
+/**
+ * gdk_window_set_frame_clock:
+ * @window: window to set frame clock on
+ * @clock: the clock
+ *
+ * Sets the frame clock for the window. The frame clock for a window
+ * cannot be changed while the window is mapped. Set the frame
+ * clock to #NULL to use the default frame clock. (By default the
+ * frame clock comes from the window's parent or is a global default
+ * frame clock.)
+ *
+ * Since: 3.0
+ */
+void
+gdk_window_set_frame_clock (GdkWindow *window,
+ GdkFrameClock *clock)
+{
+ g_return_if_fail (GDK_IS_WINDOW (window));
+ g_return_if_fail (clock == NULL || GDK_IS_FRAME_CLOCK (clock));
+ g_return_if_fail (!GDK_WINDOW_IS_MAPPED (window));
+
+ if (clock == window->frame_clock)
+ return;
+
+ /* If we are using our parent's clock, then the parent will repaint
+ * us when that clock fires. If we are using the default clock, then
+ * it does a gdk_window_process_all_updates() which will repaint us
+ * when the clock fires. If we are using our own clock, then we have
+ * to connect to "paint" on it ourselves and paint ourselves and
+ * any child windows.
+ */
+
+ if (clock)
+ {
+ g_object_ref (clock);
+ g_signal_connect (G_OBJECT (clock),
+ "flush-events",
+ G_CALLBACK (gdk_window_flush_events),
+ window);
+ g_signal_connect (G_OBJECT (clock),
+ "paint",
+ G_CALLBACK (gdk_window_paint_on_clock),
+ window);
+ g_signal_connect (G_OBJECT (clock),
+ "resume-events",
+ G_CALLBACK (gdk_window_resume_events),
+ window);
+ }
+
+ if (window->frame_clock)
+ {
+ g_signal_handlers_disconnect_by_func (G_OBJECT (window->frame_clock),
+ G_CALLBACK (gdk_window_flush_events),
+ window);
+ g_signal_handlers_disconnect_by_func (G_OBJECT (window->frame_clock),
+ G_CALLBACK (gdk_window_paint_on_clock),
+ window);
+ g_signal_handlers_disconnect_by_func (G_OBJECT (window->frame_clock),
+ G_CALLBACK (gdk_window_resume_events),
+ window);
+ g_object_unref (window->frame_clock);
+ }
+
+ window->frame_clock = clock;
+ g_object_notify (G_OBJECT (window), "paint-clock");
+
+ /* We probably should recurse child windows and emit notify on their
+ * paint-clock properties also, and we should emit notify when a
+ * window is first parented.
+ */
+}
+
+/**
+ * gdk_window_get_frame_clock:
+ * @window: window to get frame clock for
+ *
+ * Gets the frame clock for the window. The frame clock for a window
+ * never changes while the window is mapped. It may be changed at
+ * other times.
+ *
+ * Since: 3.0
+ * Return value: (transfer none): the frame clock
+ */
+GdkFrameClock*
+gdk_window_get_frame_clock (GdkWindow *window)
+{
+ g_return_val_if_fail (GDK_IS_WINDOW (window), NULL);
+
+ if (window->frame_clock != NULL)
+ {
+ /* Frame clock set explicitly on this window */
+ return window->frame_clock;
+ }
+ else
+ {
+ GdkWindow *parent;
+
+ /* parent's frame clock or default */
+ parent = gdk_window_get_effective_parent (window);
+ if (parent != NULL)
+ {
+ return gdk_window_get_frame_clock (parent);
+ }
+ else
+ {
+ gdk_ensure_default_frame_clock ();
+ return _gdk_default_frame_clock;
+ }
+ }
+}