g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
GDK_DISPLAY_GET_CLASS (display)->queue_events (display);
- return _gdk_event_unqueue (display);
+
+ if (display->events_paused)
+ return NULL;
+ else
+ return _gdk_event_unqueue (display);
}
/**
GDK_DISPLAY_GET_CLASS (display)->notify_startup_complete (display, startup_id);
}
+void
+_gdk_display_set_events_paused (GdkDisplay *display,
+ gboolean events_paused)
+{
+ display->events_paused = !!events_paused;
+}
+
+void
+_gdk_display_flush_events (GdkDisplay *display)
+{
+ display->flushing_events = TRUE;
+
+ while (TRUE)
+ {
+ GdkEvent *event = _gdk_event_unqueue (display);
+ if (event == NULL)
+ break;
+
+ _gdk_event_emit (event);
+ gdk_event_free (event);
+ }
+
+ display->flushing_events = FALSE;
+}
+
void
_gdk_display_event_data_copy (GdkDisplay *display,
const GdkEvent *event,
GdkDevice *core_pointer; /* Core pointer device */
guint closed : 1; /* Whether this display has been closed */
+ guint events_paused : 1; /* Whether events are blocked */
+ guint flushing_events : 1; /* Inside gdk_display_flush_events */
GArray *touch_implicit_grabs;
GHashTable *device_grabs;
GdkDisplayPointerInfoForeach func,
gpointer user_data);
gulong _gdk_display_get_next_serial (GdkDisplay *display);
+void _gdk_display_set_events_paused (GdkDisplay *display,
+ gboolean events_paused);
+void _gdk_display_flush_events (GdkDisplay *display);
void _gdk_display_event_data_copy (GdkDisplay *display,
const GdkEvent *event,
GdkEvent *new_event);
GList*
_gdk_event_queue_find_first (GdkDisplay *display)
{
- GList *tmp_list = display->queued_events;
+ GList *tmp_list;
+ GList *pending_motion = NULL;
+
+ if (display->events_paused)
+ return NULL;
+ tmp_list = display->queued_events;
while (tmp_list)
{
GdkEventPrivate *event = tmp_list->data;
- if (!(event->flags & GDK_EVENT_PENDING))
- return tmp_list;
+
+ if (event->flags & GDK_EVENT_PENDING)
+ continue;
+
+ if (pending_motion)
+ return pending_motion;
+
+ if (event->event.type == GDK_MOTION_NOTIFY && !display->flushing_events)
+ pending_motion = tmp_list;
+ else
+ return tmp_list;
tmp_list = g_list_next (tmp_list);
}
return event;
}
+void
+_gdk_event_queue_handle_motion_compression (GdkDisplay *display)
+{
+ GList *tmp_list;
+ GList *pending_motions = NULL;
+ GdkWindow *pending_motion_window = NULL;
+
+ /* If the last N events in the event queue are motion notify
+ * events for the same window, drop all but the last */
+
+ tmp_list = display->queued_tail;
+
+ while (tmp_list)
+ {
+ GdkEventPrivate *event = tmp_list->data;
+
+ if (event->flags & GDK_EVENT_PENDING)
+ break;
+
+ if (event->event.type != GDK_MOTION_NOTIFY)
+ break;
+
+ if (pending_motion_window != NULL &&
+ pending_motion_window != event->event.motion.window)
+ break;
+
+ pending_motion_window = event->event.motion.window;
+ pending_motions = tmp_list;
+
+ tmp_list = tmp_list->prev;
+ }
+
+ while (pending_motions && pending_motions->next != NULL)
+ {
+ GList *next = pending_motions->next;
+ display->queued_events = g_list_delete_link (display->queued_events,
+ pending_motions);
+ pending_motions = next;
+ }
+
+ if (pending_motions &&
+ pending_motions == display->queued_events &&
+ pending_motions == display->queued_tail)
+ {
+ GdkFrameClock *clock = gdk_window_get_frame_clock (pending_motion_window);
+ gdk_frame_clock_request_phase (clock, GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS);
+ }
+}
+
/**
* gdk_event_handler_set:
* @func: the function to call to handle events from GDK.
enum {
FRAME_REQUESTED,
+ FLUSH_EVENTS,
BEFORE_PAINT,
UPDATE,
LAYOUT,
PAINT,
AFTER_PAINT,
+ RESUME_EVENTS,
LAST_SIGNAL
};
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
+ /**
+ * GdkFrameClock::flush-events:
+ * @clock: the frame clock emitting the signal
+ *
+ * FIXME.
+ */
+ signals[FLUSH_EVENTS] =
+ g_signal_new (g_intern_static_string ("flush-events"),
+ GDK_TYPE_FRAME_CLOCK,
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
/**
* GdkFrameClock::before-paint:
* @clock: the frame clock emitting the signal
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
+
+ /**
+ * GdkFrameClock::resume-events:
+ * @clock: the frame clock emitting the signal
+ *
+ * FIXME.
+ */
+ signals[RESUME_EVENTS] =
+ g_signal_new (g_intern_static_string ("resume-events"),
+ GDK_TYPE_FRAME_CLOCK,
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
}
/**
#define GDK_FRAME_CLOCK_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), GDK_TYPE_FRAME_CLOCK, GdkFrameClockInterface))
typedef enum {
- GDK_FRAME_CLOCK_PHASE_NONE = 0,
- GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT = 1 << 0,
- GDK_FRAME_CLOCK_PHASE_UPDATE = 1 << 1,
- GDK_FRAME_CLOCK_PHASE_LAYOUT = 1 << 2,
- GDK_FRAME_CLOCK_PHASE_PAINT = 1 << 3,
- GDK_FRAME_CLOCK_PHASE_AFTER_PAINT = 1 << 4
+ GDK_FRAME_CLOCK_PHASE_NONE = 0,
+ GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS = 1 << 0,
+ GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT = 1 << 1,
+ GDK_FRAME_CLOCK_PHASE_UPDATE = 1 << 2,
+ GDK_FRAME_CLOCK_PHASE_LAYOUT = 1 << 3,
+ GDK_FRAME_CLOCK_PHASE_PAINT = 1 << 4,
+ GDK_FRAME_CLOCK_PHASE_AFTER_PAINT = 1 << 5,
+ GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS = 1 << 6
} GdkFrameClockPhase;
struct _GdkFrameClockInterface
/* signals */
/* void (* frame_requested) (GdkFrameClock *clock); */
+ /* void (* flush_events) (GdkFrameClock *clock); */
/* void (* before_paint) (GdkFrameClock *clock); */
/* void (* update) (GdkFrameClock *clock); */
/* void (* layout) (GdkFrameClock *clock); */
/* void (* paint) (GdkFrameClock *clock); */
/* void (* after_paint) (GdkFrameClock *clock); */
+ /* void (* resume_events) (GdkFrameClock *clock); */
};
GType gdk_frame_clock_get_type (void) G_GNUC_CONST;
guint64 frame_time;
guint64 min_next_frame_time;
- guint idle_id;
+ guint flush_idle_id;
+ guint paint_idle_id;
guint freeze_count;
GdkFrameClockPhase requested;
GdkFrameClockPhase phase;
};
+static gboolean gdk_frame_clock_flush_idle (void *data);
static gboolean gdk_frame_clock_paint_idle (void *data);
static void gdk_frame_clock_idle_finalize (GObject *object);
{
GdkFrameClockIdlePrivate *priv = clock_idle->priv;
- if (priv->idle_id == 0 && priv->freeze_count == 0 && priv->requested != 0)
+ if (priv->freeze_count == 0)
{
guint min_interval = 0;
min_interval = (min_interval_us + 500) / 1000;
}
- priv->idle_id = gdk_threads_add_timeout_full (GDK_PRIORITY_REDRAW,
- min_interval,
- gdk_frame_clock_paint_idle,
- g_object_ref (clock_idle),
- (GDestroyNotify) g_object_unref);
+ if (priv->flush_idle_id == 0 &&
+ (priv->requested & GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS) != 0)
+ {
+ priv->flush_idle_id = gdk_threads_add_timeout_full (GDK_PRIORITY_EVENTS + 1,
+ min_interval,
+ gdk_frame_clock_flush_idle,
+ g_object_ref (clock_idle),
+ (GDestroyNotify) g_object_unref);
+ }
+
+ if (priv->paint_idle_id == 0 &&
+ (priv->requested & ~GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS) != 0)
+ {
+ priv->paint_idle_id = gdk_threads_add_timeout_full (GDK_PRIORITY_REDRAW,
+ min_interval,
+ gdk_frame_clock_paint_idle,
+ g_object_ref (clock_idle),
+ (GDestroyNotify) g_object_unref);
- gdk_frame_clock_frame_requested (GDK_FRAME_CLOCK (clock_idle));
+ gdk_frame_clock_frame_requested (GDK_FRAME_CLOCK (clock_idle));
+ }
}
}
+static gboolean
+gdk_frame_clock_flush_idle (void *data)
+{
+ GdkFrameClock *clock = GDK_FRAME_CLOCK (data);
+ GdkFrameClockIdle *clock_idle = GDK_FRAME_CLOCK_IDLE (clock);
+ GdkFrameClockIdlePrivate *priv = clock_idle->priv;
+
+ priv->flush_idle_id = 0;
+
+ if (priv->phase != GDK_FRAME_CLOCK_PHASE_NONE)
+ return FALSE;
+
+ priv->phase = GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS;
+ priv->requested &= ~GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS;
+
+ g_signal_emit_by_name (G_OBJECT (clock), "flush-events");
+
+ if ((priv->requested & ~GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS) != 0)
+ priv->phase = GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT;
+ else
+ priv->phase = GDK_FRAME_CLOCK_PHASE_NONE;
+
+ return FALSE;
+}
+
static gboolean
gdk_frame_clock_paint_idle (void *data)
{
GdkFrameClock *clock = GDK_FRAME_CLOCK (data);
GdkFrameClockIdle *clock_idle = GDK_FRAME_CLOCK_IDLE (clock);
GdkFrameClockIdlePrivate *priv = clock_idle->priv;
+ gboolean skip_to_resume_events;
- priv->idle_id = 0;
+ priv->paint_idle_id = 0;
+
+ skip_to_resume_events =
+ (priv->requested & ~(GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS | GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS)) == 0;
switch (priv->phase)
{
+ case GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS:
+ break;
case GDK_FRAME_CLOCK_PHASE_NONE:
case GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT:
if (priv->freeze_count == 0)
{
priv->frame_time = compute_frame_time (clock_idle);
- priv->phase = GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT;
- priv->requested &= ~GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT;
- /* We always emit ::before-paint and ::after-paint even if
- * not explicitly requested, and unlike other phases,
+ priv->phase = GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT;
+ /* We always emit ::before-paint and ::after-paint if
+ * any of the intermediate phases are requested and
* they don't get repeated if you freeze/thaw while
* in them. */
- g_signal_emit_by_name (G_OBJECT (clock), "before-paint");
- priv->phase = GDK_FRAME_CLOCK_PHASE_UPDATE;
+ if (!skip_to_resume_events)
+ {
+ priv->requested &= ~GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT;
+ g_signal_emit_by_name (G_OBJECT (clock), "before-paint");
+ }
+ priv->phase = GDK_FRAME_CLOCK_PHASE_UPDATE;
}
case GDK_FRAME_CLOCK_PHASE_UPDATE:
if (priv->freeze_count == 0)
if (priv->freeze_count == 0)
{
priv->phase = GDK_FRAME_CLOCK_PHASE_AFTER_PAINT;
- priv->requested &= ~GDK_FRAME_CLOCK_PHASE_AFTER_PAINT;
- g_signal_emit_by_name (G_OBJECT (clock), "after-paint");
- /* the ::after-paint phase doesn't get repeated on freeze/thaw */
+ if (!skip_to_resume_events)
+ {
+ priv->requested &= ~GDK_FRAME_CLOCK_PHASE_AFTER_PAINT;
+ g_signal_emit_by_name (G_OBJECT (clock), "after-paint");
+ }
+ /* the ::after-paint phase doesn't get repeated on freeze/thaw,
+ */
+ priv->phase = GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS;
+ }
+ case GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS:
+ if (priv->freeze_count == 0)
+ {
+ if (priv->requested & GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS)
+ {
+ priv->requested &= ~GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS;
+ g_signal_emit_by_name (G_OBJECT (clock), "resume-events");
+ }
+ /* the ::resume-event phase doesn't get repeated on freeze/thaw */
priv->phase = GDK_FRAME_CLOCK_PHASE_NONE;
}
}
if (priv->freeze_count == 1)
{
- if (priv->idle_id)
+ if (priv->flush_idle_id)
{
- g_source_remove (priv->idle_id);
- priv->idle_id = 0;
+ g_source_remove (priv->flush_idle_id);
+ priv->flush_idle_id = 0;
+ }
+ if (priv->paint_idle_id)
+ {
+ g_source_remove (priv->paint_idle_id);
+ priv->paint_idle_id = 0;
}
}
}
priv->freeze_count--;
if (priv->freeze_count == 0)
- maybe_start_idle (clock_idle);
+ {
+ maybe_start_idle (clock_idle);
+ /* If nothing is requested so we didn't start an idle, we need
+ * to skip to the end of the state chain, since the idle won't
+ * run and do it for us. */
+ if (priv->paint_idle_id == 0)
+ priv->phase = GDK_FRAME_CLOCK_PHASE_NONE;
+ }
}
static void
GList* _gdk_event_queue_insert_before(GdkDisplay *display,
GdkEvent *after_event,
GdkEvent *event);
+
+void _gdk_event_queue_handle_motion_compression (GdkDisplay *display);
+
void _gdk_event_button_generate (GdkDisplay *display,
GdkEvent *event);
{
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_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_set_events_paused (display, TRUE);
+
+ gdk_frame_clock_request_phase (clock, GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS);
+}
+
static void
gdk_window_paint_on_clock (GdkFrameClock *clock,
void *data)
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_set_events_paused (display, FALSE);
+}
+
/**
* gdk_window_set_frame_clock:
* @window: window to set frame clock on
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);
}
noinst_PROGRAMS = $(TEST_PROGS) \
animated-resizing \
+ motion-compression \
simple \
flicker \
print-editor \
animated_resizing_DEPENDENCIES = $(TEST_DEPS)
flicker_DEPENDENCIES = $(TEST_DEPS)
+motion_compression_DEPENDENCIES = $(TEST_DEPS)
simple_DEPENDENCIES = $(TEST_DEPS)
print_editor_DEPENDENCIES = $(TEST_DEPS)
testheightforwidth_DEPENDENCIES = $(TEST_DEPS)
--- /dev/null
+#include <gtk/gtk.h>
+#include <math.h>
+
+GtkAdjustment *adjustment;
+int cursor_x, cursor_y;
+
+static void
+on_motion_notify (GtkWidget *window,
+ GdkEventMotion *event)
+{
+ if (event->window == gtk_widget_get_window (window))
+ {
+ float processing_ms = gtk_adjustment_get_value (adjustment);
+ g_usleep (processing_ms * 1000);
+ cursor_x = event->x;
+ cursor_y = event->y;
+ gtk_widget_queue_draw (window);
+ }
+}
+
+static void
+on_draw (GtkWidget *window,
+ cairo_t *cr)
+{
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_paint (cr);
+
+ cairo_set_source_rgb (cr, 0, 0.5, 0.5);
+
+ cairo_arc (cr, cursor_x, cursor_y, 10, 0, 2 * M_PI);
+ cairo_stroke (cr);
+}
+
+int
+main (int argc, char **argv)
+{
+ GtkWidget *window;
+ GtkWidget *vbox;
+ GtkWidget *label;
+ GtkWidget *scale;
+
+ gtk_init (&argc, &argv);
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_default_size (GTK_WINDOW (window), 300, 300);
+ gtk_widget_set_app_paintable (window, TRUE);
+ gtk_widget_add_events (window, GDK_POINTER_MOTION_MASK);
+ gtk_widget_set_app_paintable (window, TRUE);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_container_add (GTK_CONTAINER (window), vbox);
+
+ adjustment = gtk_adjustment_new (20, 0, 200, 1, 10, 0);
+ scale = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, adjustment);
+ gtk_box_pack_end (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
+
+ label = gtk_label_new ("Event processing time (ms):");
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_box_pack_end (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+ g_signal_connect (window, "motion-notify-event",
+ G_CALLBACK (on_motion_notify), NULL);
+ g_signal_connect (window, "draw",
+ G_CALLBACK (on_draw), NULL);
+ g_signal_connect (window, "destroy",
+ G_CALLBACK (gtk_main_quit), NULL);
+
+ gtk_widget_show_all (window);
+ gtk_main ();
+
+ return 0;
+}