Compare commits
51 Commits
jsparber/a
...
wip/frame-
Author | SHA1 | Date | |
---|---|---|---|
|
96829e4f40 | ||
|
eee365964a | ||
|
11b8600a15 | ||
|
7bd133c9b8 | ||
|
9db6a6aea0 | ||
|
0d0d587fd1 | ||
|
3e79f7cbfa | ||
|
0e75a5cabb | ||
|
2d0505968c | ||
|
ff395d35a7 | ||
|
7f59f34003 | ||
|
bc390a7e1a | ||
|
fcb7822a65 | ||
|
b0c18211c8 | ||
|
f2ab3ad474 | ||
|
b9837bb8f9 | ||
|
4faf94d677 | ||
|
05dc350048 | ||
|
63bc508071 | ||
|
d69dc91f41 | ||
|
74aaa1ddc8 | ||
|
517344f56e | ||
|
11302c12ff | ||
|
2ddf61ab6f | ||
|
43679422a5 | ||
|
459944ef7e | ||
|
90eb0a9195 | ||
|
c6966f00ce | ||
|
439f52937c | ||
|
fb3facb0b2 | ||
|
91cb469af2 | ||
|
3591cc0ece | ||
|
7a0db9999e | ||
|
ffba7c0161 | ||
|
5431431931 | ||
|
ecd953efa0 | ||
|
d4fd6820ed | ||
|
5e1ec6dd5b | ||
|
08ed370d1c | ||
|
4dfe25aa35 | ||
|
59bb3d3a8c | ||
|
ed835e738d | ||
|
377fbd1d84 | ||
|
04088e7b81 | ||
|
5f9fb32b0c | ||
|
5fda1e49bf | ||
|
30f5599163 | ||
|
48e82e8b12 | ||
|
4856eb0c90 | ||
|
68559ee790 | ||
|
1a0f5e8aaa |
@@ -75,11 +75,13 @@ gdk_public_h_sources = \
|
||||
gdkdisplaymanager.h \
|
||||
gdkdnd.h \
|
||||
gdkevents.h \
|
||||
gdkframetimings.h \
|
||||
gdkkeys.h \
|
||||
gdkkeysyms.h \
|
||||
gdkkeysyms-compat.h \
|
||||
gdkmain.h \
|
||||
gdkpango.h \
|
||||
gdkframeclock.h \
|
||||
gdkpixbuf.h \
|
||||
gdkprivate.h \
|
||||
gdkproperty.h \
|
||||
@@ -101,6 +103,8 @@ gdk_private_headers = \
|
||||
gdkdisplaymanagerprivate.h \
|
||||
gdkdisplayprivate.h \
|
||||
gdkdndprivate.h \
|
||||
gdkframeclockidle.h \
|
||||
gdkframeclockprivate.h \
|
||||
gdkscreenprivate.h \
|
||||
gdkinternals.h \
|
||||
gdkintl.h \
|
||||
@@ -121,10 +125,13 @@ gdk_c_sources = \
|
||||
gdkdisplaymanager.c \
|
||||
gdkdnd.c \
|
||||
gdkevents.c \
|
||||
gdkframetimings.c \
|
||||
gdkglobals.c \
|
||||
gdkkeys.c \
|
||||
gdkkeyuni.c \
|
||||
gdkoffscreenwindow.c \
|
||||
gdkframeclock.c \
|
||||
gdkframeclockidle.c \
|
||||
gdkpango.c \
|
||||
gdkpixbuf-drawable.c \
|
||||
gdkrectangle.c \
|
||||
|
@@ -1294,20 +1294,6 @@ gdk_broadway_window_begin_move_drag (GdkWindow *window,
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_broadway_window_enable_synchronized_configure (GdkWindow *window)
|
||||
{
|
||||
if (!GDK_IS_WINDOW_IMPL_BROADWAY (window->impl))
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_broadway_window_configure_finished (GdkWindow *window)
|
||||
{
|
||||
if (!WINDOW_IS_TOPLEVEL (window))
|
||||
return;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gdk_broadway_window_beep (GdkWindow *window)
|
||||
{
|
||||
@@ -1499,8 +1485,6 @@ gdk_window_impl_broadway_class_init (GdkWindowImplBroadwayClass *klass)
|
||||
impl_class->set_functions = gdk_broadway_window_set_functions;
|
||||
impl_class->begin_resize_drag = gdk_broadway_window_begin_resize_drag;
|
||||
impl_class->begin_move_drag = gdk_broadway_window_begin_move_drag;
|
||||
impl_class->enable_synchronized_configure = gdk_broadway_window_enable_synchronized_configure;
|
||||
impl_class->configure_finished = gdk_broadway_window_configure_finished;
|
||||
impl_class->set_opacity = gdk_broadway_window_set_opacity;
|
||||
impl_class->set_composited = gdk_broadway_window_set_composited;
|
||||
impl_class->destroy_notify = gdk_broadway_window_destroy_notify;
|
||||
|
@@ -138,7 +138,8 @@ static const GDebugKey gdk_debug_keys[] = {
|
||||
{"multihead", GDK_DEBUG_MULTIHEAD},
|
||||
{"xinerama", GDK_DEBUG_XINERAMA},
|
||||
{"draw", GDK_DEBUG_DRAW},
|
||||
{"eventloop", GDK_DEBUG_EVENTLOOP}
|
||||
{"eventloop", GDK_DEBUG_EVENTLOOP},
|
||||
{"frames", GDK_DEBUG_FRAMES}
|
||||
};
|
||||
|
||||
static gboolean
|
||||
|
@@ -307,7 +307,11 @@ gdk_display_get_event (GdkDisplay *display)
|
||||
{
|
||||
g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
|
||||
|
||||
if (display->event_pause_count > 0)
|
||||
return NULL;
|
||||
|
||||
GDK_DISPLAY_GET_CLASS (display)->queue_events (display);
|
||||
|
||||
return _gdk_event_unqueue (display);
|
||||
}
|
||||
|
||||
@@ -2003,6 +2007,38 @@ gdk_display_notify_startup_complete (GdkDisplay *display,
|
||||
GDK_DISPLAY_GET_CLASS (display)->notify_startup_complete (display, startup_id);
|
||||
}
|
||||
|
||||
void
|
||||
_gdk_display_pause_events (GdkDisplay *display)
|
||||
{
|
||||
display->event_pause_count++;
|
||||
}
|
||||
|
||||
void
|
||||
_gdk_display_unpause_events (GdkDisplay *display)
|
||||
{
|
||||
g_return_if_fail (display->event_pause_count > 0);
|
||||
|
||||
display->event_pause_count--;
|
||||
}
|
||||
|
||||
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,
|
||||
|
@@ -113,7 +113,10 @@ struct _GdkDisplay
|
||||
guint double_click_time; /* Maximum time between clicks in msecs */
|
||||
GdkDevice *core_pointer; /* Core pointer device */
|
||||
|
||||
guint event_pause_count; /* How many times events are blocked */
|
||||
|
||||
guint closed : 1; /* Whether this display has been closed */
|
||||
guint flushing_events : 1; /* Inside gdk_display_flush_events */
|
||||
|
||||
GArray *touch_implicit_grabs;
|
||||
GHashTable *device_grabs;
|
||||
@@ -296,6 +299,9 @@ void _gdk_display_pointer_info_foreach (GdkDisplay *display
|
||||
GdkDisplayPointerInfoForeach func,
|
||||
gpointer user_data);
|
||||
gulong _gdk_display_get_next_serial (GdkDisplay *display);
|
||||
void _gdk_display_pause_events (GdkDisplay *display);
|
||||
void _gdk_display_unpause_events (GdkDisplay *display);
|
||||
void _gdk_display_flush_events (GdkDisplay *display);
|
||||
void _gdk_display_event_data_copy (GdkDisplay *display,
|
||||
const GdkEvent *event,
|
||||
GdkEvent *new_event);
|
||||
|
@@ -85,13 +85,27 @@ _gdk_event_emit (GdkEvent *event)
|
||||
GList*
|
||||
_gdk_event_queue_find_first (GdkDisplay *display)
|
||||
{
|
||||
GList *tmp_list = display->queued_events;
|
||||
GList *tmp_list;
|
||||
GList *pending_motion = NULL;
|
||||
|
||||
if (display->event_pause_count > 0)
|
||||
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);
|
||||
}
|
||||
@@ -248,6 +262,61 @@ _gdk_event_unqueue (GdkDisplay *display)
|
||||
return event;
|
||||
}
|
||||
|
||||
void
|
||||
_gdk_event_queue_handle_motion_compression (GdkDisplay *display)
|
||||
{
|
||||
GList *tmp_list;
|
||||
GList *pending_motions = NULL;
|
||||
GdkWindow *pending_motion_window = NULL;
|
||||
GdkDevice *pending_motion_device = 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;
|
||||
|
||||
if (pending_motion_device != NULL &&
|
||||
pending_motion_device != event->event.motion.device)
|
||||
break;
|
||||
|
||||
pending_motion_window = event->event.motion.window;
|
||||
pending_motion_device = event->event.motion.device;
|
||||
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.
|
||||
|
563
gdk/gdkframeclock.c
Normal file
563
gdk/gdkframeclock.c
Normal file
@@ -0,0 +1,563 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Modified by the GTK+ Team and others 1997-2010. See the AUTHORS
|
||||
* file for a list of people on the GTK+ Team. See the ChangeLog
|
||||
* files for a list of changes. These files are distributed with
|
||||
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gdkframeclockprivate.h"
|
||||
#include "gdkinternals.h"
|
||||
|
||||
/**
|
||||
* SECTION:frameclock
|
||||
* @Short_description: Frame clock syncs painting to a window or display
|
||||
* @Title: Frame clock
|
||||
*
|
||||
* A #GdkFrameClock tells the application when to update and repaint a
|
||||
* window. This may be synced to the vertical refresh rate of the
|
||||
* monitor, for example. Even when the frame clock uses a simple timer
|
||||
* rather than a hardware-based vertical sync, the frame clock helps
|
||||
* because it ensures everything paints at the same time (reducing the
|
||||
* total number of frames). The frame clock can also automatically
|
||||
* stop painting when it knows the frames will not be visible, or
|
||||
* scale back animation framerates.
|
||||
*
|
||||
* #GdkFrameClock is designed to be compatible with an OpenGL-based
|
||||
* implementation or with mozRequestAnimationFrame in Firefox,
|
||||
* for example.
|
||||
*
|
||||
* A frame clock is idle until someone requests a frame with
|
||||
* gdk_frame_clock_request_phase(). At some later point that makes
|
||||
* sense for the synchronization being implemented, the clock will
|
||||
* process a frame and emit signals for each phase that has been
|
||||
* requested. (See the signals of the #GdkFrameClock class for
|
||||
* documentation of the phases. GDK_FRAME_CLOCK_PHASE_UPDATE and the
|
||||
* ::update signal are most interesting for application writers, and
|
||||
* are used to update the animations, using the frame time given by
|
||||
* gdk_frame_clock_get_frame_time().
|
||||
*
|
||||
* The frame time is reported in microseconds and generally in the same
|
||||
* timescale as g_get_monotonic_time(), however, it is not the same
|
||||
* as g_get_monotonic_time(). The frame time does not advance during
|
||||
* the time a frame is being painted, and outside of a frame, an attempt
|
||||
* is made so that all calls to gdk_frame_clock_get_frame_time() that
|
||||
* are called at a "similar" time get the same value. This means that
|
||||
* if different animations are timed by looking at the difference in
|
||||
* time between an initial value from gdk_frame_clock_get_frame_time()
|
||||
* and the value inside the ::update signal of the clock, they will
|
||||
* stay exactly synchronized.
|
||||
*/
|
||||
|
||||
G_DEFINE_ABSTRACT_TYPE (GdkFrameClock, gdk_frame_clock, G_TYPE_OBJECT)
|
||||
|
||||
enum {
|
||||
FLUSH_EVENTS,
|
||||
BEFORE_PAINT,
|
||||
UPDATE,
|
||||
LAYOUT,
|
||||
PAINT,
|
||||
AFTER_PAINT,
|
||||
RESUME_EVENTS,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint signals[LAST_SIGNAL];
|
||||
|
||||
#define FRAME_HISTORY_MAX_LENGTH 16
|
||||
|
||||
struct _GdkFrameClockPrivate
|
||||
{
|
||||
gint64 frame_counter;
|
||||
gint n_timings;
|
||||
gint current;
|
||||
GdkFrameTimings *timings[FRAME_HISTORY_MAX_LENGTH];
|
||||
};
|
||||
|
||||
static void
|
||||
gdk_frame_clock_finalize (GObject *object)
|
||||
{
|
||||
GdkFrameClockPrivate *priv = GDK_FRAME_CLOCK (object)->priv;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < FRAME_HISTORY_MAX_LENGTH; i++)
|
||||
if (priv->timings[i] != 0)
|
||||
gdk_frame_timings_unref (priv->timings[i]);
|
||||
|
||||
G_OBJECT_CLASS (gdk_frame_clock_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_frame_clock_class_init (GdkFrameClockClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = (GObjectClass*) klass;
|
||||
|
||||
gobject_class->finalize = gdk_frame_clock_finalize;
|
||||
|
||||
/**
|
||||
* GdkFrameClock::flush-events:
|
||||
* @clock: the frame clock emitting the signal
|
||||
*
|
||||
* This signal is used to flush pending motion events that
|
||||
* are being batched up and compressed together. Applications
|
||||
* should not handle this signal.
|
||||
*/
|
||||
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
|
||||
*
|
||||
* This signal begins processing of the frame. Applications
|
||||
* should generally not handle this signal.
|
||||
*/
|
||||
signals[BEFORE_PAINT] =
|
||||
g_signal_new (g_intern_static_string ("before-paint"),
|
||||
GDK_TYPE_FRAME_CLOCK,
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE, 0);
|
||||
|
||||
/**
|
||||
* GdkFrameClock::update:
|
||||
* @clock: the frame clock emitting the signal
|
||||
*
|
||||
* This signal is emitted as the first step of toolkit and
|
||||
* application processing of the frame. Animations should
|
||||
* be updated using gdk_frame_clock_get_frame_time().
|
||||
* Applications can connect directly to this signal, or
|
||||
* use gtk_widget_add_tick_callback() as a more convenient
|
||||
* interface.
|
||||
*/
|
||||
signals[UPDATE] =
|
||||
g_signal_new (g_intern_static_string ("update"),
|
||||
GDK_TYPE_FRAME_CLOCK,
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE, 0);
|
||||
|
||||
/**
|
||||
* GdkFrameClock::layout:
|
||||
* @clock: the frame clock emitting the signal
|
||||
*
|
||||
* This signal is emitted as the second step of toolkit and
|
||||
* application processing of the frame. Any work to update
|
||||
* sizes and positions of application elements should be
|
||||
* performed. GTK normally handles this internally.
|
||||
*/
|
||||
signals[LAYOUT] =
|
||||
g_signal_new (g_intern_static_string ("layout"),
|
||||
GDK_TYPE_FRAME_CLOCK,
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE, 0);
|
||||
|
||||
/**
|
||||
* GdkFrameClock::layout:
|
||||
* @clock: the frame clock emitting the signal
|
||||
*
|
||||
* This signal is emitted as the third step of toolkit and
|
||||
* application processing of the frame. The frame is
|
||||
* repainted. GDK normally handles this internally and
|
||||
* produce expose events, which are turned into GTK
|
||||
* GtkWidget::draw signals.
|
||||
*/
|
||||
signals[PAINT] =
|
||||
g_signal_new (g_intern_static_string ("paint"),
|
||||
GDK_TYPE_FRAME_CLOCK,
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE, 0);
|
||||
|
||||
/**
|
||||
* GdkFrameClock::after-paint:
|
||||
* @clock: the frame clock emitting the signal
|
||||
*
|
||||
* This signal ends processing of the frame. Applications
|
||||
* should generally not handle this signal.
|
||||
*/
|
||||
signals[AFTER_PAINT] =
|
||||
g_signal_new (g_intern_static_string ("after-paint"),
|
||||
GDK_TYPE_FRAME_CLOCK,
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE, 0);
|
||||
|
||||
/**
|
||||
* GdkFrameClock::resume-events:
|
||||
* @clock: the frame clock emitting the signal
|
||||
*
|
||||
* This signal is emitted after processing of the frame is
|
||||
* finished, and is handled internally by GTK+ to resume normal
|
||||
* event processing. Applications should not handle this signal.
|
||||
*/
|
||||
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);
|
||||
|
||||
g_type_class_add_private (klass, sizeof (GdkFrameClockPrivate));
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_frame_clock_init (GdkFrameClock *clock)
|
||||
{
|
||||
GdkFrameClockPrivate *priv;
|
||||
|
||||
clock->priv = G_TYPE_INSTANCE_GET_PRIVATE (clock,
|
||||
GDK_TYPE_FRAME_CLOCK,
|
||||
GdkFrameClockPrivate);
|
||||
priv = clock->priv;
|
||||
|
||||
priv->frame_counter = -1;
|
||||
priv->current = FRAME_HISTORY_MAX_LENGTH - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_frame_clock_get_frame_time:
|
||||
* @frame_clock: a #GdkFrameClock
|
||||
*
|
||||
* Gets the time that should currently be used for animations. Inside
|
||||
* the processing of a frame, it's the time used to compute the
|
||||
* animation position of everything in a frame. Outside of a frame, it's
|
||||
* the time of the conceptual "previous frame," which may be either
|
||||
* the actual previous frame time, or if that's too old, an updated
|
||||
* time.
|
||||
*
|
||||
* Since: 3.8
|
||||
* Return value: a timestamp in microseconds, in the timescale of
|
||||
* of g_get_monotonic_time().
|
||||
*/
|
||||
gint64
|
||||
gdk_frame_clock_get_frame_time (GdkFrameClock *frame_clock)
|
||||
{
|
||||
g_return_val_if_fail (GDK_IS_FRAME_CLOCK (frame_clock), 0);
|
||||
|
||||
return GDK_FRAME_CLOCK_GET_CLASS (frame_clock)->get_frame_time (frame_clock);
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_frame_clock_request_phase:
|
||||
* @frame_clock: a #GdkFrameClock
|
||||
*
|
||||
* Asks the frame clock to run a particular phase. The signal
|
||||
* corresponding the requested phase will be emitted the next
|
||||
* time the frame clock processes. Multiple calls to
|
||||
* gdk_frame_clock_request_phase() will be combined togethe
|
||||
* and only one frame processed.
|
||||
*
|
||||
* Since: 3.8
|
||||
*/
|
||||
void
|
||||
gdk_frame_clock_request_phase (GdkFrameClock *frame_clock,
|
||||
GdkFrameClockPhase phase)
|
||||
{
|
||||
g_return_if_fail (GDK_IS_FRAME_CLOCK (frame_clock));
|
||||
|
||||
GDK_FRAME_CLOCK_GET_CLASS (frame_clock)->request_phase (frame_clock, phase);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
_gdk_frame_clock_freeze (GdkFrameClock *clock)
|
||||
{
|
||||
g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
|
||||
|
||||
GDK_FRAME_CLOCK_GET_CLASS (clock)->freeze (clock);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
_gdk_frame_clock_thaw (GdkFrameClock *clock)
|
||||
{
|
||||
g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
|
||||
|
||||
GDK_FRAME_CLOCK_GET_CLASS (clock)->thaw (clock);
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_frame_clock_get_frame_counter:
|
||||
* @frame_clock: a #GdkFrameClock
|
||||
*
|
||||
* A #GdkFrameClock maintains a 64-bit counter that increments for
|
||||
* each frame drawn.
|
||||
*
|
||||
* Returns: inside frame processing, the value of the frame counter
|
||||
* for the current frame. Outside of frame processing, the frame
|
||||
* counter for the last frame.
|
||||
* Since: 3.8
|
||||
*/
|
||||
gint64
|
||||
gdk_frame_clock_get_frame_counter (GdkFrameClock *frame_clock)
|
||||
{
|
||||
GdkFrameClockPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (GDK_IS_FRAME_CLOCK (frame_clock), 0);
|
||||
|
||||
priv = frame_clock->priv;
|
||||
|
||||
return priv->frame_counter;
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_frame_clock_get_history_start:
|
||||
* @frame_clock: a #GdkFrameClock
|
||||
*
|
||||
* #GdkFrameClock internally keeps a history of #GdkFrameTiming
|
||||
* objects for recent frames that can be retrieved with
|
||||
* gdk_frame_clock_get_timings(). The set of stored frames
|
||||
* is the set from the counter values given by
|
||||
* gdk_frame_clock_get_history_start() and
|
||||
* gdk_frame_clock_get_frame_counter(), inclusive.
|
||||
*
|
||||
* Return value: the frame counter value for the oldest frame
|
||||
* that is available in the internal frame history of the
|
||||
* #GdkFrameClock.
|
||||
* Since: 3.8
|
||||
*/
|
||||
gint64
|
||||
gdk_frame_clock_get_history_start (GdkFrameClock *frame_clock)
|
||||
{
|
||||
GdkFrameClockPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (GDK_IS_FRAME_CLOCK (frame_clock), 0);
|
||||
|
||||
priv = frame_clock->priv;
|
||||
|
||||
return priv->frame_counter + 1 - priv->n_timings;
|
||||
}
|
||||
|
||||
void
|
||||
_gdk_frame_clock_begin_frame (GdkFrameClock *frame_clock)
|
||||
{
|
||||
GdkFrameClockPrivate *priv;
|
||||
|
||||
g_return_if_fail (GDK_IS_FRAME_CLOCK (frame_clock));
|
||||
|
||||
priv = frame_clock->priv;
|
||||
|
||||
priv->frame_counter++;
|
||||
priv->current = (priv->current + 1) % FRAME_HISTORY_MAX_LENGTH;
|
||||
|
||||
if (priv->n_timings < FRAME_HISTORY_MAX_LENGTH)
|
||||
priv->n_timings++;
|
||||
else
|
||||
{
|
||||
gdk_frame_timings_unref(priv->timings[priv->current]);
|
||||
}
|
||||
|
||||
priv->timings[priv->current] = _gdk_frame_timings_new (priv->frame_counter);
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_frame_clock_get_timings:
|
||||
* @frame_clock: a #GdkFrameClock
|
||||
* @frame_counter: the frame counter value identifying the frame to
|
||||
* be received.
|
||||
*
|
||||
* Retrieves a #GdkFrameTimings object holding timing information
|
||||
* for the current frame or a recent frame. The #GdkFrameTimings
|
||||
* object may not yet be complete: see gdk_frame_timings_get_complete().
|
||||
*
|
||||
* Return value: the #GdkFrameTimings object for the specified
|
||||
* frame, or %NULL if it is not available. See
|
||||
* gdk_frame_clock_get_history_start().
|
||||
* Since: 3.8
|
||||
*/
|
||||
GdkFrameTimings *
|
||||
gdk_frame_clock_get_timings (GdkFrameClock *frame_clock,
|
||||
gint64 frame_counter)
|
||||
{
|
||||
GdkFrameClockPrivate *priv;
|
||||
gint pos;
|
||||
|
||||
g_return_val_if_fail (GDK_IS_FRAME_CLOCK (frame_clock), NULL);
|
||||
|
||||
priv = frame_clock->priv;
|
||||
|
||||
if (frame_counter > priv->frame_counter)
|
||||
return NULL;
|
||||
|
||||
if (frame_counter <= priv->frame_counter - priv->n_timings)
|
||||
return NULL;
|
||||
|
||||
pos = (priv->current - (priv->frame_counter - frame_counter) + FRAME_HISTORY_MAX_LENGTH) % FRAME_HISTORY_MAX_LENGTH;
|
||||
|
||||
return priv->timings[pos];
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_frame_clock_get_current_timings:
|
||||
* @frame_clock: a #GdkFrameClock
|
||||
*
|
||||
* Gets the frame timings for the current frame.
|
||||
*
|
||||
* Returns: the #GdkFrameTimings for the frame currently being
|
||||
* processed, or even no frame is being processed, for the
|
||||
* previous frame. Before any frames have been procesed,
|
||||
* returns %NULL.
|
||||
* Since: 3.8
|
||||
*/
|
||||
GdkFrameTimings *
|
||||
gdk_frame_clock_get_current_timings (GdkFrameClock *frame_clock)
|
||||
{
|
||||
GdkFrameClockPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (GDK_IS_FRAME_CLOCK (frame_clock), 0);
|
||||
|
||||
priv = frame_clock->priv;
|
||||
|
||||
return gdk_frame_clock_get_timings (frame_clock, priv->frame_counter);
|
||||
}
|
||||
|
||||
|
||||
#ifdef G_ENABLE_DEBUG
|
||||
void
|
||||
_gdk_frame_clock_debug_print_timings (GdkFrameClock *clock,
|
||||
GdkFrameTimings *timings)
|
||||
{
|
||||
gint64 previous_frame_time = 0;
|
||||
GdkFrameTimings *previous_timings = gdk_frame_clock_get_timings (clock,
|
||||
timings->frame_counter - 1);
|
||||
|
||||
if (previous_timings != NULL)
|
||||
previous_frame_time = previous_timings->frame_time;
|
||||
|
||||
g_print ("%5" G_GINT64_FORMAT ":", timings->frame_counter);
|
||||
if (previous_frame_time != 0)
|
||||
{
|
||||
g_print (" interval=%-4.1f", (timings->frame_time - previous_frame_time) / 1000.);
|
||||
g_print (timings->slept_before ? " (sleep)" : " ");
|
||||
}
|
||||
if (timings->layout_start_time != 0)
|
||||
g_print (" layout_start=%-4.1f", (timings->layout_start_time - timings->frame_time) / 1000.);
|
||||
if (timings->paint_start_time != 0)
|
||||
g_print (" paint_start=%-4.1f", (timings->paint_start_time - timings->frame_time) / 1000.);
|
||||
if (timings->frame_end_time != 0)
|
||||
g_print (" frame_end=%-4.1f", (timings->frame_end_time - timings->frame_time) / 1000.);
|
||||
if (timings->presentation_time != 0)
|
||||
g_print (" present=%-4.1f", (timings->presentation_time - timings->frame_time) / 1000.);
|
||||
if (timings->predicted_presentation_time != 0)
|
||||
g_print (" predicted=%-4.1f", (timings->predicted_presentation_time - timings->frame_time) / 1000.);
|
||||
if (timings->refresh_interval != 0)
|
||||
g_print (" refresh_interval=%-4.1f", timings->refresh_interval / 1000.);
|
||||
g_print ("\n");
|
||||
}
|
||||
#endif /* G_ENABLE_DEBUG */
|
||||
|
||||
#define DEFAULT_REFRESH_INTERVAL 16667 /* 16.7ms (1/60th second) */
|
||||
#define MAX_HISTORY_AGE 150000 /* 150ms */
|
||||
|
||||
/**
|
||||
* gdk_frame_clock_get_refresh_info:
|
||||
* @frame_clock: a #GdkFrameClock
|
||||
* @base_time: base time for determining a presentaton time
|
||||
* @refresh_interval_return: a location to store the determined refresh
|
||||
* interval, or %NULL. A default refresh interval of 1/60th of
|
||||
* a second will be stored if no history is present.
|
||||
* @presentation_time_return: a location to store the next
|
||||
* candidate presentation time after the given base time.
|
||||
* 0 will be will be stored if no history is present.
|
||||
*
|
||||
* Using the frame history stored in the frame clock, finds the last
|
||||
* known presentation time and refresh interval, and assuming that
|
||||
* presentation times are separated by the refresh interval,
|
||||
* predicts a presentation time that is a multiple of the refresh
|
||||
* interval after the last presentation time, and later than @base_time.
|
||||
*
|
||||
* Since: 3.8
|
||||
*/
|
||||
void
|
||||
gdk_frame_clock_get_refresh_info (GdkFrameClock *frame_clock,
|
||||
gint64 base_time,
|
||||
gint64 *refresh_interval_return,
|
||||
gint64 *presentation_time_return)
|
||||
{
|
||||
gint64 frame_counter;
|
||||
|
||||
g_return_if_fail (GDK_IS_FRAME_CLOCK (frame_clock));
|
||||
|
||||
frame_counter = gdk_frame_clock_get_frame_counter (frame_clock);
|
||||
|
||||
if (presentation_time_return)
|
||||
*presentation_time_return = 0;
|
||||
if (refresh_interval_return)
|
||||
*refresh_interval_return = DEFAULT_REFRESH_INTERVAL;
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
GdkFrameTimings *timings = gdk_frame_clock_get_timings (frame_clock, frame_counter);
|
||||
gint64 presentation_time;
|
||||
gint64 refresh_interval;
|
||||
|
||||
if (timings == NULL)
|
||||
return;
|
||||
|
||||
refresh_interval = timings->refresh_interval;
|
||||
presentation_time = timings->presentation_time;
|
||||
|
||||
if (presentation_time != 0)
|
||||
{
|
||||
if (presentation_time > base_time - MAX_HISTORY_AGE &&
|
||||
presentation_time_return)
|
||||
{
|
||||
if (refresh_interval == 0)
|
||||
refresh_interval = DEFAULT_REFRESH_INTERVAL;
|
||||
|
||||
if (refresh_interval_return)
|
||||
*refresh_interval_return = refresh_interval;
|
||||
|
||||
while (presentation_time < base_time)
|
||||
presentation_time += refresh_interval;
|
||||
|
||||
if (presentation_time_return)
|
||||
*presentation_time_return = presentation_time;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
frame_counter--;
|
||||
}
|
||||
}
|
105
gdk/gdkframeclock.h
Normal file
105
gdk/gdkframeclock.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Modified by the GTK+ Team and others 1997-2010. See the AUTHORS
|
||||
* file for a list of people on the GTK+ Team. See the ChangeLog
|
||||
* files for a list of changes. These files are distributed with
|
||||
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
|
||||
*/
|
||||
|
||||
#if !defined (__GDK_H_INSIDE__) && !defined (GDK_COMPILATION)
|
||||
#error "Only <gdk/gdk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#ifndef __GDK_FRAME_CLOCK_H__
|
||||
#define __GDK_FRAME_CLOCK_H__
|
||||
|
||||
#include <gdk/gdkframetimings.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GDK_TYPE_FRAME_CLOCK (gdk_frame_clock_get_type ())
|
||||
#define GDK_FRAME_CLOCK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_FRAME_CLOCK, GdkFrameClock))
|
||||
#define GDK_FRAME_CLOCK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_FRAME_CLOCK, GdkFrameClockClass))
|
||||
#define GDK_IS_FRAME_CLOCK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_FRAME_CLOCK))
|
||||
#define GDK_IS_FRAME_CLOCK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_FRAME_CLOCK))
|
||||
#define GDK_FRAME_CLOCK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_FRAME_CLOCK, GdkFrameClockClass))
|
||||
|
||||
typedef struct _GdkFrameClock GdkFrameClock;
|
||||
typedef struct _GdkFrameClockPrivate GdkFrameClockPrivate;
|
||||
typedef struct _GdkFrameClockClass GdkFrameClockClass;
|
||||
|
||||
/**
|
||||
* GdkFrameClockPhase:
|
||||
* @GDK_FRAME_CLOCK_PHASE_NONE: no phase
|
||||
* @GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS: corresponds to GdkFrameClock::flush-events. Should not be handled by applications.
|
||||
* @GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT: corresponds to GdkFrameClock::before-paint. Should not be handled by applications.
|
||||
* @GDK_FRAME_CLOCK_PHASE_UPDATE: corresponds to GdkFrameClock::update.
|
||||
* @GDK_FRAME_CLOCK_PHASE_LAYOUT: corresponds to GdkFrameClock::layout.
|
||||
* @GDK_FRAME_CLOCK_PHASE_PAINT: corresponds to GdkFrameClock::paint.
|
||||
* @GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS: corresponds to GdkFrameClock::resume-events. Should not be handled by applications.
|
||||
* @GDK_FRAME_CLOCK_PHASE_AFTER_PAINT: corresponds to GdkFrameClock::after-paint. Should not be handled by applications.
|
||||
*
|
||||
* #GdkFrameClockPhase is used to represent the different paint clock
|
||||
* phases that can be requested. The element of the enumeration
|
||||
* correspond to the signals of #GdkPaintClock.
|
||||
*
|
||||
* Since: 3.8
|
||||
**/
|
||||
typedef enum {
|
||||
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_RESUME_EVENTS = 1 << 5,
|
||||
GDK_FRAME_CLOCK_PHASE_AFTER_PAINT = 1 << 6
|
||||
} GdkFrameClockPhase;
|
||||
|
||||
GType gdk_frame_clock_get_type (void) G_GNUC_CONST;
|
||||
|
||||
gint64 gdk_frame_clock_get_frame_time (GdkFrameClock *frame_clock);
|
||||
|
||||
GDK_AVAILABLE_IN_3_8
|
||||
void gdk_frame_clock_request_phase (GdkFrameClock *frame_clock,
|
||||
GdkFrameClockPhase phase);
|
||||
|
||||
/* Frame history */
|
||||
GDK_AVAILABLE_IN_3_8
|
||||
gint64 gdk_frame_clock_get_frame_counter (GdkFrameClock *frame_clock);
|
||||
GDK_AVAILABLE_IN_3_8
|
||||
gint64 gdk_frame_clock_get_history_start (GdkFrameClock *frame_clock);
|
||||
GDK_AVAILABLE_IN_3_8
|
||||
GdkFrameTimings *gdk_frame_clock_get_timings (GdkFrameClock *frame_clock,
|
||||
gint64 frame_counter);
|
||||
|
||||
GDK_AVAILABLE_IN_3_8
|
||||
GdkFrameTimings *gdk_frame_clock_get_current_timings (GdkFrameClock *frame_clock);
|
||||
|
||||
GDK_AVAILABLE_IN_3_8
|
||||
void gdk_frame_clock_get_refresh_info (GdkFrameClock *frame_clock,
|
||||
gint64 base_time,
|
||||
gint64 *refresh_interval_return,
|
||||
gint64 *presentation_time_return);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GDK_FRAME_CLOCK_H__ */
|
499
gdk/gdkframeclockidle.c
Normal file
499
gdk/gdkframeclockidle.c
Normal file
@@ -0,0 +1,499 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Modified by the GTK+ Team and others 1997-2010. See the AUTHORS
|
||||
* file for a list of people on the GTK+ Team. See the ChangeLog
|
||||
* files for a list of changes. These files are distributed with
|
||||
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gdkinternals.h"
|
||||
#include "gdkframeclockprivate.h"
|
||||
#include "gdkframeclockidle.h"
|
||||
#include "gdk.h"
|
||||
|
||||
#define FRAME_INTERVAL 16667 // microseconds
|
||||
|
||||
struct _GdkFrameClockIdlePrivate
|
||||
{
|
||||
GTimer *timer;
|
||||
/* timer_base is used to avoid ever going backward */
|
||||
gint64 timer_base;
|
||||
gint64 frame_time;
|
||||
gint64 min_next_frame_time;
|
||||
gint64 sleep_serial;
|
||||
|
||||
guint flush_idle_id;
|
||||
guint paint_idle_id;
|
||||
guint freeze_count;
|
||||
|
||||
GdkFrameClockPhase requested;
|
||||
GdkFrameClockPhase phase;
|
||||
|
||||
guint in_paint_idle : 1;
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
G_DEFINE_TYPE (GdkFrameClockIdle, gdk_frame_clock_idle, GDK_TYPE_FRAME_CLOCK)
|
||||
|
||||
static gint64 sleep_serial;
|
||||
static gint64 sleep_source_prepare_time;
|
||||
static GSource *sleep_source;
|
||||
|
||||
gboolean
|
||||
sleep_source_prepare (GSource *source,
|
||||
gint *timeout)
|
||||
{
|
||||
sleep_source_prepare_time = g_source_get_time (source);
|
||||
*timeout = -1;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
sleep_source_check (GSource *source)
|
||||
{
|
||||
if (g_source_get_time (source) != sleep_source_prepare_time)
|
||||
sleep_serial++;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
sleep_source_dispatch (GSource *source,
|
||||
GSourceFunc callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GSourceFuncs sleep_source_funcs = {
|
||||
sleep_source_prepare,
|
||||
sleep_source_check,
|
||||
sleep_source_dispatch,
|
||||
NULL /* finalize */
|
||||
};
|
||||
|
||||
static gint64
|
||||
get_sleep_serial (void)
|
||||
{
|
||||
if (sleep_source == NULL)
|
||||
{
|
||||
sleep_source = g_source_new (&sleep_source_funcs, sizeof (GSource));
|
||||
|
||||
g_source_set_priority (sleep_source, G_PRIORITY_HIGH);
|
||||
g_source_attach (sleep_source, NULL);
|
||||
g_source_unref (sleep_source);
|
||||
}
|
||||
|
||||
return sleep_serial;
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_frame_clock_idle_init (GdkFrameClockIdle *frame_clock_idle)
|
||||
{
|
||||
GdkFrameClockIdlePrivate *priv;
|
||||
|
||||
frame_clock_idle->priv = G_TYPE_INSTANCE_GET_PRIVATE (frame_clock_idle,
|
||||
GDK_TYPE_FRAME_CLOCK_IDLE,
|
||||
GdkFrameClockIdlePrivate);
|
||||
priv = frame_clock_idle->priv;
|
||||
|
||||
priv->timer = g_timer_new ();
|
||||
priv->freeze_count = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_frame_clock_idle_finalize (GObject *object)
|
||||
{
|
||||
GdkFrameClockIdlePrivate *priv = GDK_FRAME_CLOCK_IDLE (object)->priv;
|
||||
|
||||
g_timer_destroy (priv->timer);
|
||||
|
||||
G_OBJECT_CLASS (gdk_frame_clock_idle_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static gint64
|
||||
compute_frame_time (GdkFrameClockIdle *idle)
|
||||
{
|
||||
GdkFrameClockIdlePrivate *priv = idle->priv;
|
||||
gint64 computed_frame_time;
|
||||
gint64 elapsed;
|
||||
|
||||
elapsed = g_get_monotonic_time () + priv->timer_base;
|
||||
if (elapsed < priv->frame_time)
|
||||
{
|
||||
/* clock went backward. adapt to that by forevermore increasing
|
||||
* timer_base. For now, assume we've gone forward in time 1ms.
|
||||
*/
|
||||
/* hmm. just fix GTimer? */
|
||||
computed_frame_time = priv->frame_time + 1;
|
||||
priv->timer_base += (priv->frame_time - elapsed) + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
computed_frame_time = elapsed;
|
||||
}
|
||||
|
||||
return computed_frame_time;
|
||||
}
|
||||
|
||||
static gint64
|
||||
gdk_frame_clock_idle_get_frame_time (GdkFrameClock *clock)
|
||||
{
|
||||
GdkFrameClockIdlePrivate *priv = GDK_FRAME_CLOCK_IDLE (clock)->priv;
|
||||
gint64 computed_frame_time;
|
||||
|
||||
/* can't change frame time during a paint */
|
||||
if (priv->phase != GDK_FRAME_CLOCK_PHASE_NONE &&
|
||||
priv->phase != GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS)
|
||||
return priv->frame_time;
|
||||
|
||||
/* Outside a paint, pick something close to "now" */
|
||||
computed_frame_time = compute_frame_time (GDK_FRAME_CLOCK_IDLE (clock));
|
||||
|
||||
/* 16ms is 60fps. We only update frame time that often because we'd
|
||||
* like to try to keep animations on the same start times.
|
||||
* get_frame_time() would normally be used outside of a paint to
|
||||
* record an animation start time for example.
|
||||
*/
|
||||
if ((computed_frame_time - priv->frame_time) > FRAME_INTERVAL)
|
||||
priv->frame_time = computed_frame_time;
|
||||
|
||||
return priv->frame_time;
|
||||
}
|
||||
|
||||
static void
|
||||
maybe_start_idle (GdkFrameClockIdle *clock_idle)
|
||||
{
|
||||
GdkFrameClockIdlePrivate *priv = clock_idle->priv;
|
||||
|
||||
if (priv->freeze_count == 0 && priv->requested != 0)
|
||||
{
|
||||
guint min_interval = 0;
|
||||
|
||||
if (priv->min_next_frame_time != 0)
|
||||
{
|
||||
gint64 now = compute_frame_time (clock_idle);
|
||||
gint64 min_interval_us = MAX (priv->min_next_frame_time, now) - now;
|
||||
min_interval = (min_interval_us + 500) / 1000;
|
||||
}
|
||||
|
||||
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->in_paint_idle &&
|
||||
(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static gint64
|
||||
compute_min_next_frame_time (GdkFrameClockIdle *clock_idle,
|
||||
gint64 last_frame_time)
|
||||
{
|
||||
gint64 presentation_time;
|
||||
gint64 refresh_interval;
|
||||
|
||||
gdk_frame_clock_get_refresh_info (GDK_FRAME_CLOCK (clock_idle),
|
||||
last_frame_time,
|
||||
&refresh_interval, &presentation_time);
|
||||
|
||||
if (presentation_time == 0)
|
||||
return last_frame_time + refresh_interval;
|
||||
else
|
||||
return presentation_time + refresh_interval / 2;
|
||||
}
|
||||
|
||||
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;
|
||||
GdkFrameTimings *timings = NULL;
|
||||
|
||||
priv->paint_idle_id = 0;
|
||||
priv->in_paint_idle = TRUE;
|
||||
priv->min_next_frame_time = 0;
|
||||
|
||||
skip_to_resume_events =
|
||||
(priv->requested & ~(GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS | GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS)) == 0;
|
||||
|
||||
if (priv->phase > GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT)
|
||||
{
|
||||
timings = gdk_frame_clock_get_current_timings (clock);
|
||||
}
|
||||
|
||||
if (!skip_to_resume_events)
|
||||
{
|
||||
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);
|
||||
|
||||
_gdk_frame_clock_begin_frame (clock);
|
||||
timings = gdk_frame_clock_get_current_timings (clock);
|
||||
|
||||
timings->frame_time = priv->frame_time;
|
||||
timings->slept_before = priv->sleep_serial != get_sleep_serial ();
|
||||
|
||||
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. */
|
||||
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->requested & GDK_FRAME_CLOCK_PHASE_UPDATE)
|
||||
{
|
||||
priv->requested &= ~GDK_FRAME_CLOCK_PHASE_UPDATE;
|
||||
g_signal_emit_by_name (G_OBJECT (clock), "update");
|
||||
}
|
||||
}
|
||||
case GDK_FRAME_CLOCK_PHASE_LAYOUT:
|
||||
if (priv->freeze_count == 0)
|
||||
{
|
||||
#ifdef G_ENABLE_DEBUG
|
||||
if ((_gdk_debug_flags & GDK_DEBUG_FRAMES) != 0)
|
||||
{
|
||||
if (priv->phase != GDK_FRAME_CLOCK_PHASE_LAYOUT &&
|
||||
(priv->requested & GDK_FRAME_CLOCK_PHASE_LAYOUT))
|
||||
timings->layout_start_time = g_get_monotonic_time ();
|
||||
}
|
||||
#endif /* G_ENABLE_DEBUG */
|
||||
|
||||
priv->phase = GDK_FRAME_CLOCK_PHASE_LAYOUT;
|
||||
if (priv->requested & GDK_FRAME_CLOCK_PHASE_LAYOUT)
|
||||
{
|
||||
priv->requested &= ~GDK_FRAME_CLOCK_PHASE_LAYOUT;
|
||||
g_signal_emit_by_name (G_OBJECT (clock), "layout");
|
||||
}
|
||||
}
|
||||
case GDK_FRAME_CLOCK_PHASE_PAINT:
|
||||
if (priv->freeze_count == 0)
|
||||
{
|
||||
#ifdef G_ENABLE_DEBUG
|
||||
if ((_gdk_debug_flags & GDK_DEBUG_FRAMES) != 0)
|
||||
{
|
||||
if (priv->phase != GDK_FRAME_CLOCK_PHASE_PAINT &&
|
||||
(priv->requested & GDK_FRAME_CLOCK_PHASE_PAINT))
|
||||
timings->paint_start_time = g_get_monotonic_time ();
|
||||
}
|
||||
#endif /* G_ENABLE_DEBUG */
|
||||
|
||||
priv->phase = GDK_FRAME_CLOCK_PHASE_PAINT;
|
||||
if (priv->requested & GDK_FRAME_CLOCK_PHASE_PAINT)
|
||||
{
|
||||
priv->requested &= ~GDK_FRAME_CLOCK_PHASE_PAINT;
|
||||
g_signal_emit_by_name (G_OBJECT (clock), "paint");
|
||||
}
|
||||
}
|
||||
case GDK_FRAME_CLOCK_PHASE_AFTER_PAINT:
|
||||
if (priv->freeze_count == 0)
|
||||
{
|
||||
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_NONE;
|
||||
|
||||
#ifdef G_ENABLE_DEBUG
|
||||
if ((_gdk_debug_flags & GDK_DEBUG_FRAMES) != 0)
|
||||
timings->frame_end_time = g_get_monotonic_time ();
|
||||
#endif /* G_ENABLE_DEBUG */
|
||||
}
|
||||
case GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS:
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef G_ENABLE_DEBUG
|
||||
if ((_gdk_debug_flags & GDK_DEBUG_FRAMES) != 0)
|
||||
{
|
||||
if (timings->complete)
|
||||
_gdk_frame_clock_debug_print_timings (clock, timings);
|
||||
}
|
||||
#endif /* G_ENABLE_DEBUG */
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
if (priv->freeze_count == 0)
|
||||
priv->phase = GDK_FRAME_CLOCK_PHASE_NONE;
|
||||
|
||||
priv->in_paint_idle = FALSE;
|
||||
|
||||
/* If there is throttling in the backend layer, then we'll do another
|
||||
* update as soon as the backend unthrottles (if there is work to do),
|
||||
* otherwise we need to figure when the next frame should be.
|
||||
*/
|
||||
if (priv->freeze_count == 0)
|
||||
{
|
||||
priv->min_next_frame_time = compute_min_next_frame_time (clock_idle,
|
||||
priv->frame_time);
|
||||
maybe_start_idle (clock_idle);
|
||||
}
|
||||
|
||||
if (priv->freeze_count == 0)
|
||||
priv->sleep_serial = get_sleep_serial ();
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_frame_clock_idle_request_phase (GdkFrameClock *clock,
|
||||
GdkFrameClockPhase phase)
|
||||
{
|
||||
GdkFrameClockIdle *clock_idle = GDK_FRAME_CLOCK_IDLE (clock);
|
||||
GdkFrameClockIdlePrivate *priv = clock_idle->priv;
|
||||
|
||||
priv->requested |= phase;
|
||||
maybe_start_idle (clock_idle);
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_frame_clock_idle_freeze (GdkFrameClock *clock)
|
||||
{
|
||||
GdkFrameClockIdlePrivate *priv = GDK_FRAME_CLOCK_IDLE (clock)->priv;
|
||||
|
||||
priv->freeze_count++;
|
||||
|
||||
if (priv->freeze_count == 1)
|
||||
{
|
||||
if (priv->flush_idle_id)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_frame_clock_idle_thaw (GdkFrameClock *clock)
|
||||
{
|
||||
GdkFrameClockIdle *clock_idle = GDK_FRAME_CLOCK_IDLE (clock);
|
||||
GdkFrameClockIdlePrivate *priv = clock_idle->priv;
|
||||
|
||||
g_return_if_fail (priv->freeze_count > 0);
|
||||
|
||||
priv->freeze_count--;
|
||||
if (priv->freeze_count == 0)
|
||||
{
|
||||
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;
|
||||
|
||||
priv->sleep_serial = get_sleep_serial ();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_frame_clock_idle_class_init (GdkFrameClockIdleClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = (GObjectClass*) klass;
|
||||
GdkFrameClockClass *frame_clock_class = (GdkFrameClockClass *)klass;
|
||||
|
||||
gobject_class->finalize = gdk_frame_clock_idle_finalize;
|
||||
|
||||
frame_clock_class->get_frame_time = gdk_frame_clock_idle_get_frame_time;
|
||||
frame_clock_class->request_phase = gdk_frame_clock_idle_request_phase;
|
||||
frame_clock_class->freeze = gdk_frame_clock_idle_freeze;
|
||||
frame_clock_class->thaw = gdk_frame_clock_idle_thaw;
|
||||
|
||||
g_type_class_add_private (klass, sizeof (GdkFrameClockIdlePrivate));
|
||||
}
|
||||
|
||||
GdkFrameClock *
|
||||
_gdk_frame_clock_idle_new (void)
|
||||
{
|
||||
GdkFrameClockIdle *clock;
|
||||
|
||||
clock = g_object_new (GDK_TYPE_FRAME_CLOCK_IDLE, NULL);
|
||||
|
||||
return GDK_FRAME_CLOCK (clock);
|
||||
}
|
68
gdk/gdkframeclockidle.h
Normal file
68
gdk/gdkframeclockidle.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Modified by the GTK+ Team and others 1997-2010. See the AUTHORS
|
||||
* file for a list of people on the GTK+ Team. See the ChangeLog
|
||||
* files for a list of changes. These files are distributed with
|
||||
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
|
||||
*/
|
||||
|
||||
/* Uninstalled header, internal to GDK */
|
||||
|
||||
#ifndef __GDK_FRAME_CLOCK_IDLE_H__
|
||||
#define __GDK_FRAME_CLOCK_IDLE_H__
|
||||
|
||||
#include "gdkframeclockprivate.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GDK_TYPE_FRAME_CLOCK_IDLE (gdk_frame_clock_idle_get_type ())
|
||||
#define GDK_FRAME_CLOCK_IDLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_FRAME_CLOCK_IDLE, GdkFrameClockIdle))
|
||||
#define GDK_FRAME_CLOCK_IDLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_FRAME_CLOCK_IDLE, GdkFrameClockIdleClass))
|
||||
#define GDK_IS_FRAME_CLOCK_IDLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_FRAME_CLOCK_IDLE))
|
||||
#define GDK_IS_FRAME_CLOCK_IDLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_FRAME_CLOCK_IDLE))
|
||||
#define GDK_FRAME_CLOCK_IDLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_FRAME_CLOCK_IDLE, GdkFrameClockIdleClass))
|
||||
|
||||
|
||||
typedef struct _GdkFrameClockIdle GdkFrameClockIdle;
|
||||
typedef struct _GdkFrameClockIdlePrivate GdkFrameClockIdlePrivate;
|
||||
typedef struct _GdkFrameClockIdleClass GdkFrameClockIdleClass;
|
||||
|
||||
struct _GdkFrameClockIdle
|
||||
{
|
||||
GdkFrameClock parent_instance;
|
||||
|
||||
/*< private >*/
|
||||
GdkFrameClockIdlePrivate *priv;
|
||||
};
|
||||
|
||||
struct _GdkFrameClockIdleClass
|
||||
{
|
||||
GdkFrameClockClass parent_class;
|
||||
};
|
||||
|
||||
GType gdk_frame_clock_idle_get_type (void) G_GNUC_CONST;
|
||||
|
||||
void _gdk_frame_clock_idle_freeze_updates (GdkFrameClockIdle *clock_idle);
|
||||
void _gdk_frame_clock_idle_thaw_updates (GdkFrameClockIdle *clock_idle);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GDK_FRAME_CLOCK_IDLE_H__ */
|
101
gdk/gdkframeclockprivate.h
Normal file
101
gdk/gdkframeclockprivate.h
Normal file
@@ -0,0 +1,101 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Modified by the GTK+ Team and others 1997-2010. See the AUTHORS
|
||||
* file for a list of people on the GTK+ Team. See the ChangeLog
|
||||
* files for a list of changes. These files are distributed with
|
||||
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
|
||||
*/
|
||||
|
||||
/* Uninstalled header, internal to GDK */
|
||||
|
||||
#ifndef __GDK_FRAME_CLOCK_PRIVATE_H__
|
||||
#define __GDK_FRAME_CLOCK_PRIVATE_H__
|
||||
|
||||
#include <gdk/gdkframeclock.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GdkFrameClockIdlePrivate GdkFrameClockIdlePrivate;
|
||||
|
||||
struct _GdkFrameClock
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
/*< private >*/
|
||||
GdkFrameClockPrivate *priv;
|
||||
};
|
||||
|
||||
struct _GdkFrameClockClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
|
||||
gint64 (* get_frame_time) (GdkFrameClock *clock);
|
||||
|
||||
void (* request_phase) (GdkFrameClock *clock,
|
||||
GdkFrameClockPhase phase);
|
||||
|
||||
void (* freeze) (GdkFrameClock *clock);
|
||||
void (* thaw) (GdkFrameClock *clock);
|
||||
|
||||
/* signals */
|
||||
/* 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); */
|
||||
};
|
||||
|
||||
struct _GdkFrameTimings
|
||||
{
|
||||
guint ref_count;
|
||||
|
||||
gint64 frame_counter;
|
||||
guint64 cookie;
|
||||
gint64 frame_time;
|
||||
gint64 drawn_time;
|
||||
gint64 presentation_time;
|
||||
gint64 refresh_interval;
|
||||
gint64 predicted_presentation_time;
|
||||
|
||||
#ifdef G_ENABLE_DEBUG
|
||||
gint64 layout_start_time;
|
||||
gint64 paint_start_time;
|
||||
gint64 frame_end_time;
|
||||
#endif /* G_ENABLE_DEBUG */
|
||||
|
||||
guint complete : 1;
|
||||
guint slept_before : 1;
|
||||
};
|
||||
|
||||
void _gdk_frame_clock_freeze (GdkFrameClock *clock);
|
||||
void _gdk_frame_clock_thaw (GdkFrameClock *clock);
|
||||
|
||||
void _gdk_frame_clock_begin_frame (GdkFrameClock *clock);
|
||||
void _gdk_frame_clock_debug_print_timings (GdkFrameClock *clock,
|
||||
GdkFrameTimings *timings);
|
||||
|
||||
GdkFrameTimings *_gdk_frame_timings_new (gint64 frame_counter);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GDK_FRAME_CLOCK_PRIVATE_H__ */
|
219
gdk/gdkframetimings.c
Normal file
219
gdk/gdkframetimings.c
Normal file
@@ -0,0 +1,219 @@
|
||||
/* GDK - The GIMP Drawing Kit
|
||||
* Copyright (C) 2012 Red Hat, Inc.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gdkframeclockprivate.h"
|
||||
|
||||
/**
|
||||
* SECTION:frametimings
|
||||
* @Short_description: Object holding timing information for a single frame
|
||||
* @Title: Frame timings
|
||||
*
|
||||
* A #GdkFrameTimings object holds timing information for a single frame
|
||||
* of the application's displays. To retrieve #GdkFrameTimings objects,
|
||||
* use gdk_frame_clock_get_timings() or gdk_frame_clock_get_current_timings().
|
||||
* The information in #GdkFrameTimings is useful for precise synchronization
|
||||
* of video with the event or audio streams, and for measuring
|
||||
* quality metrics for the application's display, such as latency and jitter.
|
||||
*/
|
||||
|
||||
G_DEFINE_BOXED_TYPE (GdkFrameTimings, gdk_frame_timings,
|
||||
gdk_frame_timings_ref,
|
||||
gdk_frame_timings_unref)
|
||||
|
||||
GdkFrameTimings *
|
||||
_gdk_frame_timings_new (gint64 frame_counter)
|
||||
{
|
||||
GdkFrameTimings *timings;
|
||||
|
||||
timings = g_slice_new0 (GdkFrameTimings);
|
||||
timings->ref_count = 1;
|
||||
timings->frame_counter = frame_counter;
|
||||
|
||||
return timings;
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_frame_timings_ref:
|
||||
* @timings: a #GdkFrameTimings
|
||||
*
|
||||
* Increases the reference count of @timings.
|
||||
*
|
||||
* Returns: @timings
|
||||
* Since: 3.8
|
||||
*/
|
||||
GdkFrameTimings *
|
||||
gdk_frame_timings_ref (GdkFrameTimings *timings)
|
||||
{
|
||||
g_return_val_if_fail (timings != NULL, NULL);
|
||||
|
||||
timings->ref_count++;
|
||||
|
||||
return timings;
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_frame_timings_unref:
|
||||
* @timings: a #GdkFrameTimings
|
||||
*
|
||||
* Decreases the reference count of @timings. If @timings
|
||||
* is no longer referenced, it will be freed.
|
||||
*
|
||||
* Since: 3.8
|
||||
*/
|
||||
void
|
||||
gdk_frame_timings_unref (GdkFrameTimings *timings)
|
||||
{
|
||||
g_return_if_fail (timings != NULL);
|
||||
g_return_if_fail (timings->ref_count > 0);
|
||||
|
||||
timings->ref_count--;
|
||||
if (timings->ref_count == 0)
|
||||
{
|
||||
g_slice_free (GdkFrameTimings, timings);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_frame_timings_get_frame_counter:
|
||||
* @timings: a #GdkFrameTimings
|
||||
*
|
||||
* Gets the frame counter value of the #GdkFrameClock when this
|
||||
* this frame was drawn.
|
||||
*
|
||||
* Returns: the frame counter value for this frame
|
||||
* Since: 3.8
|
||||
*/
|
||||
gint64
|
||||
gdk_frame_timings_get_frame_counter (GdkFrameTimings *timings)
|
||||
{
|
||||
return timings->frame_counter;
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_frame_timings_complete:
|
||||
* @timings: a #GdkFrameTimings
|
||||
*
|
||||
* The timing information in a #GdkFrameTimings is filled in
|
||||
* incrementally as the frame as drawn and passed off to the
|
||||
* window system for processing and display to the user. The
|
||||
* accessor functions for #GdkFrameTimings can return 0 to
|
||||
* indicate an unavailable value for two reasons: either because
|
||||
* the information is not yet available, or because it isn't
|
||||
* available at all. Once gdk_frame_timings_complete() returns
|
||||
* %TRUE for a frame, you can be certain that no further values
|
||||
* will become available and be stored in the #GdkFrameTimings.
|
||||
*
|
||||
* Returns: %TRUE if all information that will be available
|
||||
* for the frame has been filled in.
|
||||
* Since: 3.8
|
||||
*/
|
||||
gboolean
|
||||
gdk_frame_timings_get_complete (GdkFrameTimings *timings)
|
||||
{
|
||||
g_return_val_if_fail (timings != NULL, FALSE);
|
||||
|
||||
return timings->complete;
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_frame_timings_get_frame_time:
|
||||
* @timings: A #GdkFrameTimings
|
||||
*
|
||||
* Returns the frame time for the frame. This is the time value
|
||||
* that is typically used to time animations for the frame. See
|
||||
* gdk_frame_clock_get_frame_time().
|
||||
*
|
||||
* Returns: the frame time for the frame, in the timescale
|
||||
* of g_get_monotonic_time()
|
||||
*/
|
||||
gint64
|
||||
gdk_frame_timings_get_frame_time (GdkFrameTimings *timings)
|
||||
{
|
||||
g_return_val_if_fail (timings != NULL, 0);
|
||||
|
||||
return timings->frame_time;
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_frame_timings_get_presentation_time:
|
||||
* @timings: a #GdkFrameTimings
|
||||
*
|
||||
* Reurns the presentation time. This is the time at which the frame
|
||||
* became visible to the user.
|
||||
*
|
||||
* Returns: the time the frame was displayed to the user, in the
|
||||
* timescale of g_get_monotonic_time(), or 0 if no presentation
|
||||
* time is available. See gdk_frame_timings_get_complete()
|
||||
* Since: 3.8
|
||||
*/
|
||||
gint64
|
||||
gdk_frame_timings_get_presentation_time (GdkFrameTimings *timings)
|
||||
{
|
||||
g_return_val_if_fail (timings != NULL, 0);
|
||||
|
||||
return timings->presentation_time;
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_frame_timings_get_predicted_presentation_time:
|
||||
* @timings: a #GdkFrameTimings
|
||||
*
|
||||
* Gets the predicted time at which this frame will be displayed. Although
|
||||
* no predicted time may be available, if one is available, it will
|
||||
* be available while the frame is being generated, in contrast to
|
||||
* gdk_frame_timings_get_presentation_time(), which is only available
|
||||
* after the frame has been presented. In general, if you are simply
|
||||
* animating, you should use gdk_frame_clock_get_frame_time() rather
|
||||
* than this function, but this function is useful for applications
|
||||
* that want exact control over latency. For example, a movie player
|
||||
* may want this information for Audio/Video synchronization.
|
||||
*
|
||||
* Returns: The predicted time at which the frame will be presented,
|
||||
* in the timescale of g_get_monotonic_time(), or 0 if no predicted
|
||||
* presentation time is available.
|
||||
* Since: 3.8
|
||||
*/
|
||||
gint64
|
||||
gdk_frame_timings_get_predicted_presentation_time (GdkFrameTimings *timings)
|
||||
{
|
||||
g_return_val_if_fail (timings != NULL, 0);
|
||||
|
||||
return timings->predicted_presentation_time;
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_frame_timings_get_refresh_interval:
|
||||
* @timings: a #GdkFrameTimings
|
||||
*
|
||||
* Gets the natural interval between presentation times for
|
||||
* the display that this frame was displayed on. Frame presentation
|
||||
* usually happens during the "vertical blanking interval".
|
||||
*
|
||||
* Returns: the refresh interval of the display, in microseconds,
|
||||
* or 0 if the refresh interval is not available.
|
||||
* See gdk_frame_timings_get_complete().
|
||||
* Since: 3.8
|
||||
*/
|
||||
gint64
|
||||
gdk_frame_timings_get_refresh_interval (GdkFrameTimings *timings)
|
||||
{
|
||||
g_return_val_if_fail (timings != NULL, 0);
|
||||
|
||||
return timings->refresh_interval;
|
||||
}
|
56
gdk/gdkframetimings.h
Normal file
56
gdk/gdkframetimings.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/* GDK - The GIMP Drawing Kit
|
||||
* Copyright (C) 2012 Red Hat, Inc.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if !defined (__GDK_H_INSIDE__) && !defined (GDK_COMPILATION)
|
||||
#error "Only <gdk/gdk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#ifndef __GDK_FRAME_TIMINGS_H__
|
||||
#define __GDK_FRAME_TIMINGS_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <gdk/gdkversionmacros.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GdkFrameTimings GdkFrameTimings;
|
||||
|
||||
GDK_AVAILABLE_IN_3_8
|
||||
GType gdk_frame_timings_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GDK_AVAILABLE_IN_3_8
|
||||
GdkFrameTimings *gdk_frame_timings_ref (GdkFrameTimings *timings);
|
||||
GDK_AVAILABLE_IN_3_8
|
||||
void gdk_frame_timings_unref (GdkFrameTimings *timings);
|
||||
|
||||
GDK_AVAILABLE_IN_3_8
|
||||
gint64 gdk_frame_timings_get_frame_counter (GdkFrameTimings *timings);
|
||||
GDK_AVAILABLE_IN_3_8
|
||||
gboolean gdk_frame_timings_get_complete (GdkFrameTimings *timings);
|
||||
GDK_AVAILABLE_IN_3_8
|
||||
gint64 gdk_frame_timings_get_frame_time (GdkFrameTimings *timings);
|
||||
GDK_AVAILABLE_IN_3_8
|
||||
gint64 gdk_frame_timings_get_presentation_time (GdkFrameTimings *timings);
|
||||
GDK_AVAILABLE_IN_3_8
|
||||
gint64 gdk_frame_timings_get_refresh_interval (GdkFrameTimings *timings);
|
||||
|
||||
GDK_AVAILABLE_IN_3_8
|
||||
gint64 gdk_frame_timings_get_predicted_presentation_time (GdkFrameTimings *timings);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GDK_FRAME_TIMINGS_H__ */
|
@@ -82,7 +82,8 @@ typedef enum {
|
||||
GDK_DEBUG_MULTIHEAD = 1 << 7,
|
||||
GDK_DEBUG_XINERAMA = 1 << 8,
|
||||
GDK_DEBUG_DRAW = 1 << 9,
|
||||
GDK_DEBUG_EVENTLOOP = 1 << 10
|
||||
GDK_DEBUG_EVENTLOOP = 1 << 10,
|
||||
GDK_DEBUG_FRAMES = 1 << 11
|
||||
} GdkDebugFlag;
|
||||
|
||||
typedef enum {
|
||||
@@ -265,6 +266,8 @@ struct _GdkWindow
|
||||
gulong device_changed_handler_id;
|
||||
|
||||
guint num_offscreen_children;
|
||||
|
||||
GdkFrameClock *frame_clock; /* NULL to use from parent or default */
|
||||
};
|
||||
|
||||
#define GDK_WINDOW_TYPE(d) (((GDK_WINDOW (d)))->window_type)
|
||||
@@ -298,6 +301,9 @@ GList* _gdk_event_queue_insert_after (GdkDisplay *display,
|
||||
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);
|
||||
|
||||
@@ -423,7 +429,6 @@ cairo_surface_t * _gdk_offscreen_window_create_surface (GdkWindow *window,
|
||||
gint width,
|
||||
gint height);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GDK_INTERNALS_H__ */
|
||||
|
291
gdk/gdkwindow.c
291
gdk/gdkwindow.c
@@ -37,6 +37,7 @@
|
||||
#include "gdkdeviceprivate.h"
|
||||
#include "gdkvisualprivate.h"
|
||||
#include "gdkmarshalers.h"
|
||||
#include "gdkframeclockidle.h"
|
||||
#include "gdkwindowimpl.h"
|
||||
|
||||
#include <math.h>
|
||||
@@ -238,6 +239,9 @@ static void gdk_window_invalidate_rect_full (GdkWindow *window,
|
||||
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_set_frame_clock (GdkWindow *window,
|
||||
GdkFrameClock *clock);
|
||||
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
static gpointer parent_class = NULL;
|
||||
@@ -1465,6 +1469,12 @@ gdk_window_new (GdkWindow *parent,
|
||||
if (window->parent)
|
||||
window->parent->children = g_list_prepend (window->parent->children, window);
|
||||
|
||||
if (window->parent->window_type == GDK_WINDOW_ROOT)
|
||||
{
|
||||
GdkFrameClock *frame_clock = g_object_new (GDK_TYPE_FRAME_CLOCK_IDLE, NULL);
|
||||
gdk_window_set_frame_clock (window, frame_clock);
|
||||
}
|
||||
|
||||
native = FALSE;
|
||||
if (window->parent->window_type == GDK_WINDOW_ROOT)
|
||||
native = TRUE; /* Always use native windows for toplevels */
|
||||
@@ -1715,6 +1725,27 @@ gdk_window_reparent (GdkWindow *window,
|
||||
}
|
||||
}
|
||||
|
||||
/* If we changed the window type, we might have to set or
|
||||
* unset the frame clock on the window
|
||||
*/
|
||||
if (GDK_WINDOW_TYPE (new_parent) == GDK_WINDOW_ROOT &&
|
||||
GDK_WINDOW_TYPE (window) != GDK_WINDOW_FOREIGN)
|
||||
{
|
||||
if (window->frame_clock == NULL)
|
||||
{
|
||||
GdkFrameClock *frame_clock = g_object_new (GDK_TYPE_FRAME_CLOCK_IDLE, NULL);
|
||||
gdk_window_set_frame_clock (window, frame_clock);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (window->frame_clock != NULL)
|
||||
{
|
||||
g_object_run_dispose (G_OBJECT (window->frame_clock));
|
||||
gdk_window_set_frame_clock (window, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* We might have changed window type for a native windows, so we
|
||||
need to change the event mask too. */
|
||||
if (gdk_window_has_impl (window))
|
||||
@@ -2034,6 +2065,12 @@ _gdk_window_destroy_hierarchy (GdkWindow *window,
|
||||
}
|
||||
}
|
||||
|
||||
if (window->frame_clock)
|
||||
{
|
||||
g_object_run_dispose (G_OBJECT (window->frame_clock));
|
||||
gdk_window_set_frame_clock (window, NULL);
|
||||
}
|
||||
|
||||
gdk_window_free_paint_stack (window);
|
||||
|
||||
if (window->background)
|
||||
@@ -3778,7 +3815,6 @@ gdk_cairo_create (GdkWindow *window)
|
||||
/* Code for dirty-region queueing
|
||||
*/
|
||||
static GSList *update_windows = NULL;
|
||||
static guint update_idle = 0;
|
||||
static gboolean debug_updates = FALSE;
|
||||
|
||||
static inline gboolean
|
||||
@@ -3877,14 +3913,6 @@ gdk_window_remove_update_window (GdkWindow *window)
|
||||
update_windows = g_slist_remove (update_windows, window);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gdk_window_update_idle (gpointer data)
|
||||
{
|
||||
gdk_window_process_all_updates ();
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gdk_window_is_toplevel_frozen (GdkWindow *window)
|
||||
{
|
||||
@@ -3898,16 +3926,20 @@ gdk_window_is_toplevel_frozen (GdkWindow *window)
|
||||
static void
|
||||
gdk_window_schedule_update (GdkWindow *window)
|
||||
{
|
||||
GdkFrameClock *frame_clock;
|
||||
|
||||
if (window &&
|
||||
(window->update_freeze_count ||
|
||||
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);
|
||||
/* If there's no frame clock (a foreign window), then the invalid
|
||||
* region will just stick around unless gdk_window_process_updates()
|
||||
* is called. */
|
||||
frame_clock = gdk_window_get_frame_clock (window);
|
||||
if (frame_clock)
|
||||
gdk_frame_clock_request_phase (gdk_window_get_frame_clock (window),
|
||||
GDK_FRAME_CLOCK_PHASE_PAINT);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -4250,18 +4282,13 @@ gdk_window_process_all_updates (void)
|
||||
/* 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 ();
|
||||
|
||||
@@ -4296,31 +4323,20 @@ gdk_window_process_all_updates (void)
|
||||
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;
|
||||
|
||||
@@ -4346,7 +4362,7 @@ gdk_window_process_updates (GdkWindow *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
|
||||
@@ -4358,8 +4374,14 @@ gdk_window_process_updates (GdkWindow *window,
|
||||
|
||||
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);
|
||||
@@ -4368,6 +4390,33 @@ gdk_window_process_updates (GdkWindow *window,
|
||||
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,
|
||||
@@ -4854,6 +4903,7 @@ gdk_window_freeze_toplevel_updates_libgtk_only (GdkWindow *window)
|
||||
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));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4874,6 +4924,7 @@ gdk_window_thaw_toplevel_updates_libgtk_only (GdkWindow *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);
|
||||
}
|
||||
@@ -9971,7 +10022,7 @@ _gdk_windowing_got_event (GdkDisplay *display,
|
||||
{
|
||||
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;
|
||||
@@ -10014,7 +10065,7 @@ _gdk_windowing_got_event (GdkDisplay *display,
|
||||
|
||||
event_window = event->any.window;
|
||||
if (!event_window)
|
||||
return;
|
||||
goto out;
|
||||
|
||||
#ifdef DEBUG_WINDOW_PRINTING
|
||||
if (event->type == GDK_KEY_PRESS &&
|
||||
@@ -10029,13 +10080,13 @@ _gdk_windowing_got_event (GdkDisplay *display,
|
||||
{
|
||||
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);
|
||||
|
||||
@@ -10128,7 +10179,6 @@ _gdk_windowing_got_event (GdkDisplay *display,
|
||||
_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))
|
||||
@@ -10170,6 +10220,13 @@ _gdk_windowing_got_event (GdkDisplay *display,
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -11142,44 +11199,28 @@ gdk_window_begin_move_drag (GdkWindow *window,
|
||||
* gdk_window_enable_synchronized_configure:
|
||||
* @window: a toplevel #GdkWindow
|
||||
*
|
||||
* Indicates that the application will cooperate with the window
|
||||
* system in synchronizing the window repaint with the window
|
||||
* manager during resizing operations. After an application calls
|
||||
* this function, it must call gdk_window_configure_finished() every
|
||||
* time it has finished all processing associated with a set of
|
||||
* Configure events. Toplevel GTK+ windows automatically use this
|
||||
* protocol.
|
||||
*
|
||||
* On X, calling this function makes @window participate in the
|
||||
* _NET_WM_SYNC_REQUEST window manager protocol.
|
||||
* Does nothing, present only for compatiblity.
|
||||
*
|
||||
* Since: 2.6
|
||||
* Deprecated: 3.8: this function is no longer needed
|
||||
**/
|
||||
void
|
||||
gdk_window_enable_synchronized_configure (GdkWindow *window)
|
||||
{
|
||||
GDK_WINDOW_IMPL_GET_CLASS (window->impl)->enable_synchronized_configure (window);
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_window_configure_finished:
|
||||
* @window: a toplevel #GdkWindow
|
||||
*
|
||||
* Signal to the window system that the application has finished
|
||||
* handling Configure events it has received. Window Managers can
|
||||
* use this to better synchronize the frame repaint with the
|
||||
* application. GTK+ applications will automatically call this
|
||||
* function when appropriate.
|
||||
*
|
||||
* This function can only be called if gdk_window_enable_synchronized_configure()
|
||||
* was called previously.
|
||||
* Does nothing, present only for compatiblity.
|
||||
*
|
||||
* Since: 2.6
|
||||
* Deprecated: 3.8: this function is no longer needed
|
||||
**/
|
||||
void
|
||||
gdk_window_configure_finished (GdkWindow *window)
|
||||
{
|
||||
GDK_WINDOW_IMPL_GET_CLASS (window->impl)->configure_finished (window);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -11535,3 +11576,113 @@ gdk_property_delete (GdkWindow *window,
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
static 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 (clock == NULL || gdk_window_is_toplevel (window));
|
||||
|
||||
if (clock == window->frame_clock)
|
||||
return;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 unless the window is reparented to a new toplevel
|
||||
* window.
|
||||
*
|
||||
* Since: 3.8
|
||||
* Return value: (transfer none): the frame clock
|
||||
*/
|
||||
GdkFrameClock*
|
||||
gdk_window_get_frame_clock (GdkWindow *window)
|
||||
{
|
||||
GdkWindow *toplevel;
|
||||
|
||||
g_return_val_if_fail (GDK_IS_WINDOW (window), NULL);
|
||||
|
||||
toplevel = gdk_window_get_toplevel (window);
|
||||
|
||||
return toplevel->frame_clock;
|
||||
}
|
||||
|
@@ -32,6 +32,7 @@
|
||||
#include <gdk/gdkversionmacros.h>
|
||||
#include <gdk/gdktypes.h>
|
||||
#include <gdk/gdkevents.h>
|
||||
#include <gdk/gdkframeclock.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
@@ -883,7 +884,9 @@ void gdk_window_constrain_size (GdkGeometry *geometry,
|
||||
gint *new_width,
|
||||
gint *new_height);
|
||||
|
||||
GDK_DEPRECATED_IN_3_8
|
||||
void gdk_window_enable_synchronized_configure (GdkWindow *window);
|
||||
GDK_DEPRECATED_IN_3_8
|
||||
void gdk_window_configure_finished (GdkWindow *window);
|
||||
|
||||
GdkWindow *gdk_get_default_root_window (void);
|
||||
@@ -901,6 +904,10 @@ void gdk_window_set_support_multidevice (GdkWindow *window,
|
||||
gboolean support_multidevice);
|
||||
gboolean gdk_window_get_support_multidevice (GdkWindow *window);
|
||||
|
||||
/* Frame clock */
|
||||
GDK_AVAILABLE_IN_3_8
|
||||
GdkFrameClock* gdk_window_get_frame_clock (GdkWindow *window);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GDK_WINDOW_H__ */
|
||||
|
@@ -3002,16 +3002,6 @@ gdk_quartz_window_set_group (GdkWindow *window,
|
||||
/* FIXME: Implement */
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_quartz_window_enable_synchronized_configure (GdkWindow *window)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_quartz_window_configure_finished (GdkWindow *window)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_quartz_window_destroy_notify (GdkWindow *window)
|
||||
{
|
||||
@@ -3129,8 +3119,6 @@ gdk_window_impl_quartz_class_init (GdkWindowImplQuartzClass *klass)
|
||||
impl_class->set_functions = gdk_quartz_window_set_functions;
|
||||
impl_class->begin_resize_drag = gdk_quartz_window_begin_resize_drag;
|
||||
impl_class->begin_move_drag = gdk_quartz_window_begin_move_drag;
|
||||
impl_class->enable_synchronized_configure = gdk_quartz_window_enable_synchronized_configure;
|
||||
impl_class->configure_finished = gdk_quartz_window_configure_finished;
|
||||
impl_class->set_opacity = gdk_quartz_window_set_opacity;
|
||||
impl_class->destroy_notify = gdk_quartz_window_destroy_notify;
|
||||
impl_class->register_dnd = _gdk_quartz_window_register_dnd;
|
||||
|
@@ -1468,21 +1468,6 @@ gdk_wayland_window_begin_move_drag (GdkWindow *window,
|
||||
gdk_device_ungrab (device, timestamp);
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_wayland_window_enable_synchronized_configure (GdkWindow *window)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_wayland_window_configure_finished (GdkWindow *window)
|
||||
{
|
||||
if (!WINDOW_IS_TOPLEVEL (window))
|
||||
return;
|
||||
|
||||
if (!GDK_IS_WINDOW_IMPL_WAYLAND (window->impl))
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_wayland_window_set_opacity (GdkWindow *window,
|
||||
gdouble opacity)
|
||||
@@ -1666,8 +1651,6 @@ _gdk_window_impl_wayland_class_init (GdkWindowImplWaylandClass *klass)
|
||||
impl_class->set_functions = gdk_wayland_window_set_functions;
|
||||
impl_class->begin_resize_drag = gdk_wayland_window_begin_resize_drag;
|
||||
impl_class->begin_move_drag = gdk_wayland_window_begin_move_drag;
|
||||
impl_class->enable_synchronized_configure = gdk_wayland_window_enable_synchronized_configure;
|
||||
impl_class->configure_finished = gdk_wayland_window_configure_finished;
|
||||
impl_class->set_opacity = gdk_wayland_window_set_opacity;
|
||||
impl_class->set_composited = gdk_wayland_window_set_composited;
|
||||
impl_class->destroy_notify = gdk_wayland_window_destroy_notify;
|
||||
|
@@ -3248,18 +3248,6 @@ gdk_win32_window_lookup_for_display (GdkDisplay *display,
|
||||
return (GdkWindow*) gdk_win32_handle_table_lookup (anid);
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_win32_window_enable_synchronized_configure (GdkWindow *window)
|
||||
{
|
||||
/* nothing - no window manager to cooperate with */
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_win32_window_configure_finished (GdkWindow *window)
|
||||
{
|
||||
/* nothing - no window manager to cooperate with */
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_win32_window_set_opacity (GdkWindow *window,
|
||||
gdouble opacity)
|
||||
@@ -3618,8 +3606,6 @@ gdk_window_impl_win32_class_init (GdkWindowImplWin32Class *klass)
|
||||
|
||||
impl_class->begin_resize_drag = gdk_win32_window_begin_resize_drag;
|
||||
impl_class->begin_move_drag = gdk_win32_window_begin_move_drag;
|
||||
impl_class->enable_synchronized_configure = gdk_win32_window_enable_synchronized_configure;
|
||||
impl_class->configure_finished = gdk_win32_window_configure_finished;
|
||||
impl_class->set_opacity = gdk_win32_window_set_opacity;
|
||||
//impl_class->set_composited = gdk_win32_window_set_composited;
|
||||
impl_class->destroy_notify = gdk_win32_window_destroy_notify;
|
||||
|
@@ -1569,22 +1569,25 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
|
||||
case XI_FocusIn:
|
||||
case XI_FocusOut:
|
||||
{
|
||||
XIEnterEvent *xev = (XIEnterEvent *) ev;
|
||||
GdkDevice *device, *source_device;
|
||||
if (window)
|
||||
{
|
||||
XIEnterEvent *xev = (XIEnterEvent *) ev;
|
||||
GdkDevice *device, *source_device;
|
||||
|
||||
device = g_hash_table_lookup (device_manager->id_table,
|
||||
GINT_TO_POINTER (xev->deviceid));
|
||||
device = g_hash_table_lookup (device_manager->id_table,
|
||||
GINT_TO_POINTER (xev->deviceid));
|
||||
|
||||
source_device = g_hash_table_lookup (device_manager->id_table,
|
||||
GUINT_TO_POINTER (xev->sourceid));
|
||||
source_device = g_hash_table_lookup (device_manager->id_table,
|
||||
GUINT_TO_POINTER (xev->sourceid));
|
||||
|
||||
_gdk_device_manager_core_handle_focus (window,
|
||||
xev->event,
|
||||
device,
|
||||
source_device,
|
||||
(ev->evtype == XI_FocusIn) ? TRUE : FALSE,
|
||||
xev->detail,
|
||||
xev->mode);
|
||||
_gdk_device_manager_core_handle_focus (window,
|
||||
xev->event,
|
||||
device,
|
||||
source_device,
|
||||
(ev->evtype == XI_FocusIn) ? TRUE : FALSE,
|
||||
xev->detail,
|
||||
xev->mode);
|
||||
}
|
||||
|
||||
return_val = FALSE;
|
||||
}
|
||||
|
@@ -26,6 +26,7 @@
|
||||
#include "gdkdisplay.h"
|
||||
#include "gdkeventsource.h"
|
||||
#include "gdkeventtranslator.h"
|
||||
#include "gdkframeclockprivate.h"
|
||||
#include "gdkinternals.h"
|
||||
#include "gdkscreen.h"
|
||||
#include "gdkinternals.h"
|
||||
@@ -382,15 +383,11 @@ gdk_check_wm_state_changed (GdkWindow *window)
|
||||
do_net_wm_state_changes (window);
|
||||
}
|
||||
|
||||
static GdkWindow *
|
||||
get_event_window (GdkEventTranslator *translator,
|
||||
XEvent *xevent)
|
||||
static Window
|
||||
get_event_xwindow (XEvent *xevent)
|
||||
{
|
||||
GdkDisplay *display;
|
||||
Window xwindow;
|
||||
|
||||
display = (GdkDisplay *) translator;
|
||||
|
||||
switch (xevent->type)
|
||||
{
|
||||
case DestroyNotify:
|
||||
@@ -405,11 +402,20 @@ get_event_window (GdkEventTranslator *translator,
|
||||
case ConfigureNotify:
|
||||
xwindow = xevent->xconfigure.window;
|
||||
break;
|
||||
case ReparentNotify:
|
||||
xwindow = xevent->xreparent.window;
|
||||
break;
|
||||
case GravityNotify:
|
||||
xwindow = xevent->xgravity.window;
|
||||
break;
|
||||
case CirculateNotify:
|
||||
xwindow = xevent->xcirculate.window;
|
||||
break;
|
||||
default:
|
||||
xwindow = xevent->xany.window;
|
||||
}
|
||||
|
||||
return gdk_x11_window_lookup_for_display (display, xwindow);
|
||||
return xwindow;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@@ -418,7 +424,9 @@ gdk_x11_display_translate_event (GdkEventTranslator *translator,
|
||||
GdkEvent *event,
|
||||
XEvent *xevent)
|
||||
{
|
||||
Window xwindow;
|
||||
GdkWindow *window;
|
||||
gboolean is_substructure;
|
||||
GdkWindowImplX11 *window_impl = NULL;
|
||||
GdkScreen *screen = NULL;
|
||||
GdkX11Screen *x11_screen = NULL;
|
||||
@@ -426,12 +434,20 @@ gdk_x11_display_translate_event (GdkEventTranslator *translator,
|
||||
GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
|
||||
gboolean return_val;
|
||||
|
||||
/* Find the GdkWindow that this event relates to.
|
||||
* Basically this means substructure events
|
||||
* are reported same as structure events
|
||||
/* Find the GdkWindow that this event relates to. If that's
|
||||
* not the same as the window that the event was sent to,
|
||||
* we are getting an event from SubstructureNotifyMask.
|
||||
* We ignore such events for internal operation, but we
|
||||
* need to report them to the application because of
|
||||
* GDK_SUBSTRUCTURE_MASK (which should be removed at next
|
||||
* opportunity.) The most likely reason for getting these
|
||||
* events is when we are used in the Metacity or Mutter
|
||||
* window managers.
|
||||
*/
|
||||
window = get_event_window (translator, xevent);
|
||||
xwindow = get_event_xwindow (xevent);
|
||||
is_substructure = xwindow != xevent->xany.window;
|
||||
|
||||
window = gdk_x11_window_lookup_for_display (display, xwindow);
|
||||
if (window)
|
||||
{
|
||||
/* We may receive events such as NoExpose/GraphicsExpose
|
||||
@@ -460,7 +476,7 @@ gdk_x11_display_translate_event (GdkEventTranslator *translator,
|
||||
}
|
||||
}
|
||||
|
||||
if (xevent->type == DestroyNotify)
|
||||
if (xevent->type == DestroyNotify && !is_substructure)
|
||||
{
|
||||
int i, n;
|
||||
|
||||
@@ -622,8 +638,7 @@ gdk_x11_display_translate_event (GdkEventTranslator *translator,
|
||||
g_message ("destroy notify:\twindow: %ld",
|
||||
xevent->xdestroywindow.window));
|
||||
|
||||
/* Ignore DestroyNotify from SubstructureNotifyMask */
|
||||
if (xevent->xdestroywindow.window == xevent->xdestroywindow.event)
|
||||
if (!is_substructure)
|
||||
{
|
||||
event->any.type = GDK_DESTROY;
|
||||
event->any.window = window;
|
||||
@@ -646,27 +661,39 @@ gdk_x11_display_translate_event (GdkEventTranslator *translator,
|
||||
event->any.type = GDK_UNMAP;
|
||||
event->any.window = window;
|
||||
|
||||
/* If the WM supports the _NET_WM_STATE_HIDDEN hint, we do not want to
|
||||
* interpret UnmapNotify events as implying iconic state.
|
||||
* http://bugzilla.gnome.org/show_bug.cgi?id=590726.
|
||||
*/
|
||||
if (screen &&
|
||||
!gdk_x11_screen_supports_net_wm_hint (screen,
|
||||
gdk_atom_intern_static_string ("_NET_WM_STATE_HIDDEN")))
|
||||
{
|
||||
/* If we are shown (not withdrawn) and get an unmap, it means we were
|
||||
* iconified in the X sense. If we are withdrawn, and get an unmap, it
|
||||
* means we hid the window ourselves, so we will have already flipped
|
||||
* the iconified bit off.
|
||||
if (window && !is_substructure)
|
||||
{
|
||||
/* If the WM supports the _NET_WM_STATE_HIDDEN hint, we do not want to
|
||||
* interpret UnmapNotify events as implying iconic state.
|
||||
* http://bugzilla.gnome.org/show_bug.cgi?id=590726.
|
||||
*/
|
||||
if (window && GDK_WINDOW_IS_MAPPED (window))
|
||||
gdk_synthesize_window_state (window,
|
||||
0,
|
||||
GDK_WINDOW_STATE_ICONIFIED);
|
||||
}
|
||||
if (screen &&
|
||||
!gdk_x11_screen_supports_net_wm_hint (screen,
|
||||
gdk_atom_intern_static_string ("_NET_WM_STATE_HIDDEN")))
|
||||
{
|
||||
/* If we are shown (not withdrawn) and get an unmap, it means we were
|
||||
* iconified in the X sense. If we are withdrawn, and get an unmap, it
|
||||
* means we hid the window ourselves, so we will have already flipped
|
||||
* the iconified bit off.
|
||||
*/
|
||||
if (GDK_WINDOW_IS_MAPPED (window))
|
||||
gdk_synthesize_window_state (window,
|
||||
0,
|
||||
GDK_WINDOW_STATE_ICONIFIED);
|
||||
}
|
||||
|
||||
if (window)
|
||||
_gdk_x11_window_grab_check_unmap (window, xevent->xany.serial);
|
||||
if (window_impl->toplevel &&
|
||||
window_impl->toplevel->frame_pending)
|
||||
{
|
||||
window_impl->toplevel->frame_pending = FALSE;
|
||||
_gdk_frame_clock_thaw (gdk_window_get_frame_clock (event->any.window));
|
||||
}
|
||||
|
||||
if (toplevel)
|
||||
gdk_window_freeze_toplevel_updates_libgtk_only (window);
|
||||
|
||||
_gdk_x11_window_grab_check_unmap (window, xevent->xany.serial);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@@ -678,11 +705,17 @@ gdk_x11_display_translate_event (GdkEventTranslator *translator,
|
||||
event->any.type = GDK_MAP;
|
||||
event->any.window = window;
|
||||
|
||||
/* Unset iconified if it was set */
|
||||
if (window && (window->state & GDK_WINDOW_STATE_ICONIFIED))
|
||||
gdk_synthesize_window_state (window,
|
||||
GDK_WINDOW_STATE_ICONIFIED,
|
||||
0);
|
||||
if (window && !is_substructure)
|
||||
{
|
||||
/* Unset iconified if it was set */
|
||||
if (window->state & GDK_WINDOW_STATE_ICONIFIED)
|
||||
gdk_synthesize_window_state (window,
|
||||
GDK_WINDOW_STATE_ICONIFIED,
|
||||
0);
|
||||
|
||||
if (toplevel)
|
||||
gdk_window_thaw_toplevel_updates_libgtk_only (window);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@@ -728,10 +761,11 @@ gdk_x11_display_translate_event (GdkEventTranslator *translator,
|
||||
}
|
||||
|
||||
#ifdef HAVE_XSYNC
|
||||
if (toplevel && display_x11->use_sync && !XSyncValueIsZero (toplevel->pending_counter_value))
|
||||
if (!is_substructure && toplevel && display_x11->use_sync && toplevel->pending_counter_value != 0)
|
||||
{
|
||||
toplevel->current_counter_value = toplevel->pending_counter_value;
|
||||
XSyncIntToValue (&toplevel->pending_counter_value, 0);
|
||||
toplevel->configure_counter_value = toplevel->pending_counter_value;
|
||||
toplevel->configure_counter_value_is_extended = toplevel->pending_counter_value_is_extended;
|
||||
toplevel->pending_counter_value = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -773,21 +807,24 @@ gdk_x11_display_translate_event (GdkEventTranslator *translator,
|
||||
event->configure.x = xevent->xconfigure.x;
|
||||
event->configure.y = xevent->xconfigure.y;
|
||||
}
|
||||
window->x = event->configure.x;
|
||||
window->y = event->configure.y;
|
||||
window->width = xevent->xconfigure.width;
|
||||
window->height = xevent->xconfigure.height;
|
||||
if (!is_substructure)
|
||||
{
|
||||
window->x = event->configure.x;
|
||||
window->y = event->configure.y;
|
||||
window->width = xevent->xconfigure.width;
|
||||
window->height = xevent->xconfigure.height;
|
||||
|
||||
_gdk_window_update_size (window);
|
||||
_gdk_x11_window_update_size (GDK_WINDOW_IMPL_X11 (window->impl));
|
||||
_gdk_window_update_size (window);
|
||||
_gdk_x11_window_update_size (GDK_WINDOW_IMPL_X11 (window->impl));
|
||||
|
||||
if (window->resize_count >= 1)
|
||||
{
|
||||
window->resize_count -= 1;
|
||||
if (window->resize_count >= 1)
|
||||
{
|
||||
window->resize_count -= 1;
|
||||
|
||||
if (window->resize_count == 0)
|
||||
_gdk_x11_moveresize_configure_done (display, window);
|
||||
}
|
||||
if (window->resize_count == 0)
|
||||
_gdk_x11_moveresize_configure_done (display, window);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1021,6 +1058,25 @@ gdk_x11_display_translate_event (GdkEventTranslator *translator,
|
||||
return return_val;
|
||||
}
|
||||
|
||||
static GdkFrameTimings *
|
||||
find_frame_timings (GdkFrameClock *clock,
|
||||
guint64 serial)
|
||||
{
|
||||
gint64 start_frame, end_frame, i;
|
||||
|
||||
start_frame = gdk_frame_clock_get_history_start (clock);
|
||||
end_frame = gdk_frame_clock_get_frame_counter (clock);
|
||||
for (i = end_frame; i >= start_frame; i--)
|
||||
{
|
||||
GdkFrameTimings *timings = gdk_frame_clock_get_timings (clock, i);
|
||||
|
||||
if (timings->cookie == serial)
|
||||
return timings;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GdkFilterReturn
|
||||
_gdk_wm_protocols_filter (GdkXEvent *xev,
|
||||
GdkEvent *event,
|
||||
@@ -1031,7 +1087,7 @@ _gdk_wm_protocols_filter (GdkXEvent *xev,
|
||||
GdkDisplay *display;
|
||||
Atom atom;
|
||||
|
||||
if (!GDK_IS_X11_WINDOW (win))
|
||||
if (!GDK_IS_X11_WINDOW (win) || GDK_WINDOW_DESTROYED (win))
|
||||
return GDK_FILTER_CONTINUE;
|
||||
|
||||
if (xevent->type != ClientMessage)
|
||||
@@ -1039,6 +1095,82 @@ _gdk_wm_protocols_filter (GdkXEvent *xev,
|
||||
|
||||
display = GDK_WINDOW_DISPLAY (win);
|
||||
|
||||
/* This isn't actually WM_PROTOCOLS because that wouldn't leave enough space
|
||||
* in the message for everything that gets stuffed in */
|
||||
if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_FRAME_DRAWN"))
|
||||
{
|
||||
GdkWindowImplX11 *window_impl;
|
||||
window_impl = GDK_WINDOW_IMPL_X11 (win->impl);
|
||||
if (window_impl->toplevel)
|
||||
{
|
||||
guint32 d0 = xevent->xclient.data.l[0];
|
||||
guint32 d1 = xevent->xclient.data.l[1];
|
||||
guint32 d2 = xevent->xclient.data.l[2];
|
||||
guint32 d3 = xevent->xclient.data.l[3];
|
||||
|
||||
guint64 serial = ((guint64)d1 << 32) | d0;
|
||||
gint64 frame_drawn_time = ((guint64)d3 << 32) | d2;
|
||||
gint64 refresh_interval, presentation_time;
|
||||
|
||||
GdkFrameClock *clock = gdk_window_get_frame_clock (win);
|
||||
GdkFrameTimings *timings = find_frame_timings (clock, serial);
|
||||
|
||||
if (timings)
|
||||
timings->drawn_time = frame_drawn_time;
|
||||
|
||||
if (window_impl->toplevel->frame_pending)
|
||||
{
|
||||
window_impl->toplevel->frame_pending = FALSE;
|
||||
_gdk_frame_clock_thaw (clock);
|
||||
}
|
||||
|
||||
gdk_frame_clock_get_refresh_info (clock,
|
||||
frame_drawn_time,
|
||||
&refresh_interval,
|
||||
&presentation_time);
|
||||
if (presentation_time != 0)
|
||||
window_impl->toplevel->throttled_presentation_time = presentation_time + refresh_interval;
|
||||
}
|
||||
|
||||
return GDK_FILTER_REMOVE;
|
||||
}
|
||||
|
||||
if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_FRAME_TIMINGS"))
|
||||
{
|
||||
GdkWindowImplX11 *window_impl;
|
||||
window_impl = GDK_WINDOW_IMPL_X11 (win->impl);
|
||||
if (window_impl->toplevel)
|
||||
{
|
||||
guint32 d0 = xevent->xclient.data.l[0];
|
||||
guint32 d1 = xevent->xclient.data.l[1];
|
||||
guint32 d2 = xevent->xclient.data.l[2];
|
||||
guint32 d3 = xevent->xclient.data.l[3];
|
||||
|
||||
guint64 serial = ((guint64)d1 << 32) | d0;
|
||||
|
||||
GdkFrameClock *clock = gdk_window_get_frame_clock (win);
|
||||
GdkFrameTimings *timings = find_frame_timings (clock, serial);
|
||||
|
||||
if (timings)
|
||||
{
|
||||
gint32 presentation_time_offset = (gint32)d2;
|
||||
gint32 refresh_interval = d3;
|
||||
|
||||
if (timings->drawn_time && presentation_time_offset)
|
||||
timings->presentation_time = timings->drawn_time + presentation_time_offset;
|
||||
|
||||
if (refresh_interval)
|
||||
timings->refresh_interval = refresh_interval;
|
||||
|
||||
timings->complete = TRUE;
|
||||
#ifdef G_ENABLE_DEBUG
|
||||
if ((_gdk_debug_flags & GDK_DEBUG_FRAMES) != 0)
|
||||
_gdk_frame_clock_debug_print_timings (clock, timings);
|
||||
#endif /* G_ENABLE_DEBUG */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (xevent->xclient.message_type != gdk_x11_get_xatom_by_name_for_display (display, "WM_PROTOCOLS"))
|
||||
return GDK_FILTER_CONTINUE;
|
||||
|
||||
@@ -1066,7 +1198,7 @@ _gdk_wm_protocols_filter (GdkXEvent *xev,
|
||||
}
|
||||
else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "WM_TAKE_FOCUS"))
|
||||
{
|
||||
GdkToplevelX11 *toplevel = _gdk_x11_window_get_toplevel (event->any.window);
|
||||
GdkToplevelX11 *toplevel = _gdk_x11_window_get_toplevel (win);
|
||||
|
||||
/* There is no way of knowing reliably whether we are viewable;
|
||||
* so trap errors asynchronously around the XSetInputFocus call
|
||||
@@ -1100,13 +1232,12 @@ _gdk_wm_protocols_filter (GdkXEvent *xev,
|
||||
else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_SYNC_REQUEST") &&
|
||||
GDK_X11_DISPLAY (display)->use_sync)
|
||||
{
|
||||
GdkToplevelX11 *toplevel = _gdk_x11_window_get_toplevel (event->any.window);
|
||||
GdkToplevelX11 *toplevel = _gdk_x11_window_get_toplevel (win);
|
||||
if (toplevel)
|
||||
{
|
||||
#ifdef HAVE_XSYNC
|
||||
XSyncIntsToValue (&toplevel->pending_counter_value,
|
||||
xevent->xclient.data.l[2],
|
||||
xevent->xclient.data.l[3]);
|
||||
toplevel->pending_counter_value = xevent->xclient.data.l[2] + ((gint64)xevent->xclient.data.l[3] << 32);
|
||||
toplevel->pending_counter_value_is_extended = xevent->xclient.data.l[4] != 0;
|
||||
#endif
|
||||
}
|
||||
return GDK_FILTER_REMOVE;
|
||||
|
@@ -276,8 +276,12 @@ gdk_event_source_prepare (GSource *source,
|
||||
gdk_threads_enter ();
|
||||
|
||||
*timeout = -1;
|
||||
retval = (_gdk_event_queue_find_first (display) != NULL ||
|
||||
gdk_check_xpending (display));
|
||||
|
||||
if (display->event_pause_count > 0)
|
||||
retval = FALSE;
|
||||
else
|
||||
retval = (_gdk_event_queue_find_first (display) != NULL ||
|
||||
gdk_check_xpending (display));
|
||||
|
||||
gdk_threads_leave ();
|
||||
|
||||
@@ -292,7 +296,9 @@ gdk_event_source_check (GSource *source)
|
||||
|
||||
gdk_threads_enter ();
|
||||
|
||||
if (event_source->event_poll_fd.revents & G_IO_IN)
|
||||
if (event_source->display->event_pause_count > 0)
|
||||
retval = FALSE;
|
||||
else if (event_source->event_poll_fd.revents & G_IO_IN)
|
||||
retval = (_gdk_event_queue_find_first (event_source->display) != NULL ||
|
||||
gdk_check_xpending (event_source->display));
|
||||
else
|
||||
|
@@ -32,6 +32,7 @@
|
||||
#include "gdkvisualprivate.h"
|
||||
#include "gdkinternals.h"
|
||||
#include "gdkdeviceprivate.h"
|
||||
#include "gdkframeclockprivate.h"
|
||||
#include "gdkasync.h"
|
||||
#include "gdkeventsource.h"
|
||||
#include "gdkdisplay-x11.h"
|
||||
@@ -199,6 +200,234 @@ _gdk_x11_window_update_size (GdkWindowImplX11 *impl)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
set_sync_counter(Display *display,
|
||||
XSyncCounter counter,
|
||||
gint64 value)
|
||||
{
|
||||
XSyncValue sync_value;
|
||||
|
||||
XSyncIntsToValue(&sync_value,
|
||||
value & G_GINT64_CONSTANT(0xFFFFFFFF),
|
||||
value >> 32);
|
||||
XSyncSetCounter(display, counter, sync_value);
|
||||
}
|
||||
|
||||
static void
|
||||
window_pre_damage (GdkWindow *window)
|
||||
{
|
||||
GdkWindow *toplevel_window = gdk_window_get_toplevel (window);
|
||||
GdkWindowImplX11 *impl;
|
||||
|
||||
if (!toplevel_window || !WINDOW_IS_TOPLEVEL (toplevel_window))
|
||||
return;
|
||||
|
||||
impl = GDK_WINDOW_IMPL_X11 (toplevel_window->impl);
|
||||
|
||||
if (impl->toplevel->in_frame &&
|
||||
impl->toplevel->current_counter_value % 2 == 0)
|
||||
{
|
||||
impl->toplevel->current_counter_value += 1;
|
||||
set_sync_counter(GDK_WINDOW_XDISPLAY (impl->wrapper),
|
||||
impl->toplevel->extended_update_counter,
|
||||
impl->toplevel->current_counter_value);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_surface_changed (void *data)
|
||||
{
|
||||
GdkWindow *window = data;
|
||||
|
||||
window_pre_damage (window);
|
||||
}
|
||||
|
||||
/* We want to know when cairo drawing causes damage to the window,
|
||||
* so we engage in the _NET_WM_FRAME_DRAWN protocol with the
|
||||
* window only when there actually is drawing. To do that we use
|
||||
* a technique (hack) suggested by Uli Schlachter - if we set
|
||||
* a dummy "mime data" on the cairo surface (this facility is
|
||||
* used to attach JPEG data to an imager), then cairo wil flush
|
||||
* and remove the mime data before making any changes to the window.
|
||||
*/
|
||||
|
||||
static void
|
||||
hook_surface_changed (GdkWindow *window)
|
||||
{
|
||||
GdkWindowImplX11 *impl = GDK_WINDOW_IMPL_X11 (window->impl);
|
||||
|
||||
if (impl->cairo_surface)
|
||||
cairo_surface_set_mime_data (impl->cairo_surface,
|
||||
"x-gdk/change-notify",
|
||||
(unsigned char *)"X",
|
||||
1,
|
||||
on_surface_changed,
|
||||
window);
|
||||
}
|
||||
|
||||
static void
|
||||
unhook_surface_changed (GdkWindow *window)
|
||||
{
|
||||
GdkWindowImplX11 *impl = GDK_WINDOW_IMPL_X11 (window->impl);
|
||||
|
||||
if (impl->cairo_surface)
|
||||
cairo_surface_set_mime_data (impl->cairo_surface,
|
||||
"x-gdk/change-notify",
|
||||
NULL, 0,
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_x11_window_predict_presentation_time (GdkWindow *window)
|
||||
{
|
||||
GdkWindowImplX11 *impl = GDK_WINDOW_IMPL_X11 (window->impl);
|
||||
GdkFrameClock *clock;
|
||||
GdkFrameTimings *timings;
|
||||
gint64 presentation_time;
|
||||
gint64 refresh_interval;
|
||||
|
||||
if (!WINDOW_IS_TOPLEVEL (window))
|
||||
return;
|
||||
|
||||
clock = gdk_window_get_frame_clock (window);
|
||||
|
||||
timings = gdk_frame_clock_get_current_timings (clock);
|
||||
|
||||
gdk_frame_clock_get_refresh_info (clock,
|
||||
timings->frame_time,
|
||||
&refresh_interval, &presentation_time);
|
||||
|
||||
if (presentation_time != 0)
|
||||
{
|
||||
if (timings->slept_before)
|
||||
{
|
||||
presentation_time += refresh_interval;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (presentation_time < timings->frame_time + refresh_interval / 2)
|
||||
presentation_time += refresh_interval;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (timings->slept_before)
|
||||
presentation_time = timings->frame_time + refresh_interval + refresh_interval / 2;
|
||||
else
|
||||
presentation_time = timings->frame_time + refresh_interval;
|
||||
}
|
||||
|
||||
if (presentation_time < impl->toplevel->throttled_presentation_time)
|
||||
presentation_time = impl->toplevel->throttled_presentation_time;
|
||||
|
||||
timings->predicted_presentation_time = presentation_time;
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_x11_window_begin_frame (GdkWindow *window)
|
||||
{
|
||||
GdkWindowImplX11 *impl;
|
||||
|
||||
g_return_if_fail (GDK_IS_WINDOW (window));
|
||||
|
||||
impl = GDK_WINDOW_IMPL_X11 (window->impl);
|
||||
|
||||
if (!WINDOW_IS_TOPLEVEL (window) ||
|
||||
impl->toplevel->extended_update_counter == None)
|
||||
return;
|
||||
|
||||
impl->toplevel->in_frame = TRUE;
|
||||
|
||||
if (impl->toplevel->configure_counter_value != 0 &&
|
||||
impl->toplevel->configure_counter_value_is_extended)
|
||||
{
|
||||
impl->toplevel->current_counter_value = impl->toplevel->configure_counter_value;
|
||||
if ((impl->toplevel->current_counter_value % 2) == 1)
|
||||
impl->toplevel->current_counter_value += 1;
|
||||
|
||||
impl->toplevel->configure_counter_value = 0;
|
||||
|
||||
window_pre_damage (window);
|
||||
}
|
||||
else
|
||||
{
|
||||
hook_surface_changed (window);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_x11_window_end_frame (GdkWindow *window)
|
||||
{
|
||||
GdkFrameClock *clock;
|
||||
GdkFrameTimings *timings;
|
||||
GdkWindowImplX11 *impl;
|
||||
|
||||
g_return_if_fail (GDK_IS_WINDOW (window));
|
||||
|
||||
impl = GDK_WINDOW_IMPL_X11 (window->impl);
|
||||
|
||||
if (!WINDOW_IS_TOPLEVEL (window) ||
|
||||
impl->toplevel->extended_update_counter == None ||
|
||||
!impl->toplevel->in_frame)
|
||||
return;
|
||||
|
||||
clock = gdk_window_get_frame_clock (window);
|
||||
timings = gdk_frame_clock_get_current_timings (clock);
|
||||
|
||||
impl->toplevel->in_frame = FALSE;
|
||||
|
||||
if (impl->toplevel->current_counter_value % 2 == 1)
|
||||
{
|
||||
#ifdef G_ENABLE_DEBUG
|
||||
if ((_gdk_debug_flags & GDK_DEBUG_FRAMES) != 0)
|
||||
{
|
||||
XImage *image = XGetImage (GDK_WINDOW_XDISPLAY (window),
|
||||
GDK_WINDOW_XID (window),
|
||||
0, 0, 1, 1,
|
||||
(1 << 24) - 1,
|
||||
ZPixmap);
|
||||
XDestroyImage (image);
|
||||
}
|
||||
#endif /* G_ENABLE_DEBUG */
|
||||
|
||||
/* An increment of 3 means that the frame was not drawn as fast as possible,
|
||||
* but rather at a particular time. This can trigger different handling from
|
||||
* the compositor.
|
||||
*/
|
||||
if (timings->slept_before)
|
||||
impl->toplevel->current_counter_value += 3;
|
||||
else
|
||||
impl->toplevel->current_counter_value += 1;
|
||||
|
||||
set_sync_counter(GDK_WINDOW_XDISPLAY (impl->wrapper),
|
||||
impl->toplevel->extended_update_counter,
|
||||
impl->toplevel->current_counter_value);
|
||||
|
||||
if (gdk_x11_screen_supports_net_wm_hint (gdk_window_get_screen (window),
|
||||
gdk_atom_intern_static_string ("_NET_WM_FRAME_DRAWN")))
|
||||
{
|
||||
impl->toplevel->frame_pending = TRUE;
|
||||
_gdk_frame_clock_freeze (gdk_window_get_frame_clock (window));
|
||||
timings->cookie = impl->toplevel->current_counter_value;
|
||||
}
|
||||
}
|
||||
|
||||
unhook_surface_changed (window);
|
||||
|
||||
if (impl->toplevel->configure_counter_value != 0 &&
|
||||
!impl->toplevel->configure_counter_value_is_extended)
|
||||
{
|
||||
set_sync_counter (GDK_WINDOW_XDISPLAY (window),
|
||||
impl->toplevel->update_counter,
|
||||
impl->toplevel->configure_counter_value);
|
||||
|
||||
impl->toplevel->configure_counter_value = 0;
|
||||
}
|
||||
|
||||
if (!impl->toplevel->frame_pending)
|
||||
timings->complete = TRUE;
|
||||
}
|
||||
|
||||
/*****************************************************
|
||||
* X11 specific implementations of generic functions *
|
||||
*****************************************************/
|
||||
@@ -242,6 +471,9 @@ gdk_x11_ref_cairo_surface (GdkWindow *window)
|
||||
if (impl->cairo_surface)
|
||||
cairo_surface_set_user_data (impl->cairo_surface, &gdk_x11_cairo_key,
|
||||
impl, gdk_x11_cairo_surface_destroy);
|
||||
|
||||
if (WINDOW_IS_TOPLEVEL (window) && impl->toplevel->in_frame)
|
||||
hook_surface_changed (window);
|
||||
}
|
||||
else
|
||||
cairo_surface_reference (impl->cairo_surface);
|
||||
@@ -261,6 +493,9 @@ gdk_window_impl_x11_finalize (GObject *object)
|
||||
|
||||
wrapper = impl->wrapper;
|
||||
|
||||
if (WINDOW_IS_TOPLEVEL (wrapper) && impl->toplevel->in_frame)
|
||||
unhook_surface_changed (wrapper);
|
||||
|
||||
_gdk_x11_window_grab_check_destroy (wrapper);
|
||||
|
||||
if (!GDK_WINDOW_DESTROYED (wrapper))
|
||||
@@ -602,29 +837,32 @@ ensure_sync_counter (GdkWindow *window)
|
||||
{
|
||||
GdkDisplay *display = GDK_WINDOW_DISPLAY (window);
|
||||
GdkToplevelX11 *toplevel = _gdk_x11_window_get_toplevel (window);
|
||||
GdkWindowImplX11 *impl = GDK_WINDOW_IMPL_X11 (window->impl);
|
||||
|
||||
if (toplevel && impl->use_synchronized_configure &&
|
||||
if (toplevel &&
|
||||
toplevel->update_counter == None &&
|
||||
GDK_X11_DISPLAY (display)->use_sync)
|
||||
{
|
||||
Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
|
||||
XSyncValue value;
|
||||
Atom atom;
|
||||
XID counters[2];
|
||||
|
||||
XSyncIntToValue (&value, 0);
|
||||
|
||||
toplevel->update_counter = XSyncCreateCounter (xdisplay, value);
|
||||
toplevel->extended_update_counter = XSyncCreateCounter (xdisplay, value);
|
||||
|
||||
atom = gdk_x11_get_xatom_by_name_for_display (display,
|
||||
"_NET_WM_SYNC_REQUEST_COUNTER");
|
||||
|
||||
|
||||
counters[0] = toplevel->update_counter;
|
||||
counters[1] = toplevel->extended_update_counter;
|
||||
XChangeProperty (xdisplay, GDK_WINDOW_XID (window),
|
||||
atom, XA_CARDINAL,
|
||||
32, PropModeReplace,
|
||||
(guchar *)&toplevel->update_counter, 1);
|
||||
(guchar *)counters, 2);
|
||||
|
||||
XSyncIntToValue (&toplevel->current_counter_value, 0);
|
||||
toplevel->current_counter_value = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -698,6 +936,44 @@ setup_toplevel_window (GdkWindow *window,
|
||||
gdk_x11_window_set_user_time (window, GDK_X11_DISPLAY (x11_screen->display)->user_time);
|
||||
|
||||
ensure_sync_counter (window);
|
||||
|
||||
/* Start off in a frozen state - we'll finish this when we first paint */
|
||||
gdk_x11_window_begin_frame (window);
|
||||
}
|
||||
|
||||
static void
|
||||
on_frame_clock_before_paint (GdkFrameClock *clock,
|
||||
GdkWindow *window)
|
||||
{
|
||||
gdk_x11_window_predict_presentation_time (window);
|
||||
gdk_x11_window_begin_frame (window);
|
||||
}
|
||||
|
||||
static void
|
||||
on_frame_clock_after_paint (GdkFrameClock *clock,
|
||||
GdkWindow *window)
|
||||
{
|
||||
gdk_x11_window_end_frame (window);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
connect_frame_clock (GdkWindow *window)
|
||||
{
|
||||
GdkWindowImplX11 *impl;
|
||||
|
||||
impl = GDK_WINDOW_IMPL_X11 (window->impl);
|
||||
if (WINDOW_IS_TOPLEVEL (window) && !impl->frame_clock_connected)
|
||||
{
|
||||
GdkFrameClock *frame_clock = gdk_window_get_frame_clock (window);
|
||||
|
||||
g_signal_connect (frame_clock, "before-paint",
|
||||
G_CALLBACK (on_frame_clock_before_paint), window);
|
||||
g_signal_connect (frame_clock, "after-paint",
|
||||
G_CALLBACK (on_frame_clock_after_paint), window);
|
||||
|
||||
impl->frame_clock_connected = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -856,6 +1132,11 @@ _gdk_x11_display_create_window_impl (GdkDisplay *display,
|
||||
gdk_x11_event_source_select_events ((GdkEventSource *) display_x11->event_source,
|
||||
GDK_WINDOW_XID (window), event_mask,
|
||||
StructureNotifyMask | PropertyChangeMask);
|
||||
|
||||
connect_frame_clock (window);
|
||||
|
||||
if (GDK_WINDOW_TYPE (window) != GDK_WINDOW_CHILD)
|
||||
gdk_window_freeze_toplevel_updates_libgtk_only (window);
|
||||
}
|
||||
|
||||
static GdkEventMask
|
||||
@@ -1001,7 +1282,7 @@ gdk_toplevel_x11_free_contents (GdkDisplay *display,
|
||||
toplevel->update_counter);
|
||||
toplevel->update_counter = None;
|
||||
|
||||
XSyncIntToValue (&toplevel->current_counter_value, 0);
|
||||
toplevel->current_counter_value = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -1473,6 +1754,8 @@ window_x11_move (GdkWindow *window,
|
||||
|
||||
if (GDK_WINDOW_TYPE (window) == GDK_WINDOW_CHILD)
|
||||
{
|
||||
/* The window isn't actually damaged, but it's parent is */
|
||||
window_pre_damage (window);
|
||||
_gdk_x11_window_move_resize_child (window,
|
||||
x, y,
|
||||
window->width, window->height);
|
||||
@@ -1502,6 +1785,8 @@ window_x11_resize (GdkWindow *window,
|
||||
if (height < 1)
|
||||
height = 1;
|
||||
|
||||
window_pre_damage (window);
|
||||
|
||||
if (GDK_WINDOW_TYPE (window) == GDK_WINDOW_CHILD)
|
||||
{
|
||||
_gdk_x11_window_move_resize_child (window,
|
||||
@@ -1545,6 +1830,8 @@ window_x11_move_resize (GdkWindow *window,
|
||||
if (height < 1)
|
||||
height = 1;
|
||||
|
||||
window_pre_damage (window);
|
||||
|
||||
if (GDK_WINDOW_TYPE (window) == GDK_WINDOW_CHILD)
|
||||
{
|
||||
_gdk_x11_window_move_resize_child (window, x, y, width, height);
|
||||
@@ -1614,6 +1901,12 @@ gdk_window_x11_reparent (GdkWindow *window,
|
||||
_gdk_x11_window_tmp_reset_parent_bg (window);
|
||||
_gdk_x11_window_tmp_reset_bg (window, TRUE);
|
||||
|
||||
if (WINDOW_IS_TOPLEVEL (window))
|
||||
connect_frame_clock (window);
|
||||
else
|
||||
/* old frame clock was disposed, our signal handlers removed */
|
||||
impl->frame_clock_connected = FALSE;
|
||||
|
||||
if (GDK_WINDOW_TYPE (new_parent) == GDK_WINDOW_FOREIGN)
|
||||
new_parent = gdk_screen_get_root_window (GDK_WINDOW_SCREEN (window));
|
||||
|
||||
@@ -4817,59 +5110,6 @@ gdk_x11_window_begin_move_drag (GdkWindow *window,
|
||||
emulate_move_drag (window, device, button, root_x, root_y, timestamp);
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_x11_window_enable_synchronized_configure (GdkWindow *window)
|
||||
{
|
||||
GdkWindowImplX11 *impl;
|
||||
|
||||
if (!GDK_IS_WINDOW_IMPL_X11 (window->impl))
|
||||
return;
|
||||
|
||||
impl = GDK_WINDOW_IMPL_X11 (window->impl);
|
||||
|
||||
if (!impl->use_synchronized_configure)
|
||||
{
|
||||
/* This basically means you want to do fancy X specific stuff, so
|
||||
ensure we have a native window */
|
||||
gdk_window_ensure_native (window);
|
||||
|
||||
impl->use_synchronized_configure = TRUE;
|
||||
ensure_sync_counter (window);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_x11_window_configure_finished (GdkWindow *window)
|
||||
{
|
||||
GdkWindowImplX11 *impl;
|
||||
|
||||
if (!WINDOW_IS_TOPLEVEL (window))
|
||||
return;
|
||||
|
||||
impl = GDK_WINDOW_IMPL_X11 (window->impl);
|
||||
if (!impl->use_synchronized_configure)
|
||||
return;
|
||||
|
||||
#ifdef HAVE_XSYNC
|
||||
if (!GDK_WINDOW_DESTROYED (window))
|
||||
{
|
||||
GdkDisplay *display = GDK_WINDOW_DISPLAY (window);
|
||||
GdkToplevelX11 *toplevel = _gdk_x11_window_get_toplevel (window);
|
||||
|
||||
if (toplevel && toplevel->update_counter != None &&
|
||||
GDK_X11_DISPLAY (display)->use_sync &&
|
||||
!XSyncValueIsZero (toplevel->current_counter_value))
|
||||
{
|
||||
XSyncSetCounter (GDK_WINDOW_XDISPLAY (window),
|
||||
toplevel->update_counter,
|
||||
toplevel->current_counter_value);
|
||||
|
||||
XSyncIntToValue (&toplevel->current_counter_value, 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gdk_x11_window_beep (GdkWindow *window)
|
||||
{
|
||||
@@ -5141,8 +5381,6 @@ gdk_window_impl_x11_class_init (GdkWindowImplX11Class *klass)
|
||||
impl_class->set_functions = gdk_x11_window_set_functions;
|
||||
impl_class->begin_resize_drag = gdk_x11_window_begin_resize_drag;
|
||||
impl_class->begin_move_drag = gdk_x11_window_begin_move_drag;
|
||||
impl_class->enable_synchronized_configure = gdk_x11_window_enable_synchronized_configure;
|
||||
impl_class->configure_finished = gdk_x11_window_configure_finished;
|
||||
impl_class->set_opacity = gdk_x11_window_set_opacity;
|
||||
impl_class->set_composited = gdk_x11_window_set_composited;
|
||||
impl_class->destroy_notify = gdk_x11_window_destroy_notify;
|
||||
|
@@ -72,7 +72,7 @@ struct _GdkWindowImplX11
|
||||
guint no_bg : 1; /* Set when the window background is temporarily
|
||||
* unset during resizing and scaling */
|
||||
guint override_redirect : 1;
|
||||
guint use_synchronized_configure : 1;
|
||||
guint frame_clock_connected : 1;
|
||||
|
||||
cairo_surface_t *cairo_surface;
|
||||
|
||||
@@ -126,7 +126,17 @@ struct _GdkToplevelX11
|
||||
/* Set if the WM is presenting us as focused, i.e. with active decorations
|
||||
*/
|
||||
guint have_focused : 1;
|
||||
|
||||
|
||||
guint in_frame : 1;
|
||||
|
||||
/* If we're expecting a response from the compositor after painting a frame */
|
||||
guint frame_pending : 1;
|
||||
|
||||
/* Whether pending_counter_value/configure_counter_value are updates
|
||||
* to the extended update counter */
|
||||
guint pending_counter_value_is_extended : 1;
|
||||
guint configure_counter_value_is_extended : 1;
|
||||
|
||||
gulong map_serial; /* Serial of last transition from unmapped */
|
||||
|
||||
cairo_surface_t *icon_pixmap;
|
||||
@@ -144,11 +154,17 @@ struct _GdkToplevelX11
|
||||
|
||||
#ifdef HAVE_XSYNC
|
||||
XID update_counter;
|
||||
XSyncValue pending_counter_value; /* latest _NET_WM_SYNC_REQUEST value received */
|
||||
XSyncValue current_counter_value; /* Latest _NET_WM_SYNC_REQUEST value received
|
||||
* where we have also seen the corresponding
|
||||
* ConfigureNotify
|
||||
*/
|
||||
XID extended_update_counter;
|
||||
gint64 pending_counter_value; /* latest _NET_WM_SYNC_REQUEST value received */
|
||||
gint64 configure_counter_value; /* Latest _NET_WM_SYNC_REQUEST value received
|
||||
* where we have also seen the corresponding
|
||||
* ConfigureNotify
|
||||
*/
|
||||
gint64 current_counter_value;
|
||||
|
||||
/* After a _NET_WM_FRAME_DRAWN message, this is the soonest that we think
|
||||
* frame after will be presented */
|
||||
gint64 throttled_presentation_time;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@@ -236,6 +236,9 @@ struct _GtkContainerPrivate
|
||||
{
|
||||
GtkWidget *focus_child;
|
||||
|
||||
guint resize_handler;
|
||||
GdkFrameClock *resize_clock;
|
||||
|
||||
guint border_width : 16;
|
||||
|
||||
guint has_focus_chain : 1;
|
||||
@@ -344,8 +347,6 @@ static const gchar vadjustment_key[] = "gtk-vadjustment";
|
||||
static guint vadjustment_key_id = 0;
|
||||
static const gchar hadjustment_key[] = "gtk-hadjustment";
|
||||
static guint hadjustment_key_id = 0;
|
||||
static GSList *container_resize_queue = NULL;
|
||||
static GSList *container_restyle_queue = NULL;
|
||||
static guint container_signals[LAST_SIGNAL] = { 0 };
|
||||
static GtkWidgetClass *parent_class = NULL;
|
||||
extern GParamSpecPool *_gtk_widget_child_property_pool;
|
||||
@@ -1357,11 +1358,9 @@ gtk_container_destroy (GtkWidget *widget)
|
||||
|
||||
if (priv->resize_pending)
|
||||
_gtk_container_dequeue_resize_handler (container);
|
||||
|
||||
if (priv->restyle_pending)
|
||||
{
|
||||
container_restyle_queue = g_slist_remove (container_restyle_queue, container);
|
||||
priv->restyle_pending = FALSE;
|
||||
}
|
||||
priv->restyle_pending = FALSE;
|
||||
|
||||
if (priv->focus_child)
|
||||
{
|
||||
@@ -1553,7 +1552,6 @@ _gtk_container_dequeue_resize_handler (GtkContainer *container)
|
||||
g_return_if_fail (GTK_IS_CONTAINER (container));
|
||||
g_return_if_fail (container->priv->resize_pending);
|
||||
|
||||
container_resize_queue = g_slist_remove (container_resize_queue, container);
|
||||
container->priv->resize_pending = FALSE;
|
||||
}
|
||||
|
||||
@@ -1630,12 +1628,10 @@ gtk_container_set_reallocate_redraws (GtkContainer *container,
|
||||
container->priv->reallocate_redraws = needs_redraws ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_container_idle_sizer (gpointer data)
|
||||
static void
|
||||
gtk_container_idle_sizer (GdkFrameClock *clock,
|
||||
GtkContainer *container)
|
||||
{
|
||||
GSList *slist;
|
||||
gint64 current_time;
|
||||
|
||||
/* We validate the style contexts in a single loop before even trying
|
||||
* to handle resizes instead of doing validations inline.
|
||||
* This is mostly necessary for compatibility reasons with old code,
|
||||
@@ -1646,16 +1642,13 @@ gtk_container_idle_sizer (gpointer data)
|
||||
* sane values. So the result of an invalid style context will never be
|
||||
* a program crash, but only a wrong layout or rendering.
|
||||
*/
|
||||
current_time = g_get_monotonic_time ();
|
||||
slist = container_restyle_queue;
|
||||
container_restyle_queue = NULL;
|
||||
while (slist)
|
||||
if (container->priv->restyle_pending)
|
||||
{
|
||||
GSList *next = slist->next;
|
||||
GtkContainer *container = slist->data;
|
||||
GtkBitmask *empty;
|
||||
gint64 current_time;
|
||||
|
||||
empty = _gtk_bitmask_new ();
|
||||
current_time = g_get_monotonic_time ();
|
||||
|
||||
container->priv->restyle_pending = FALSE;
|
||||
_gtk_style_context_validate (gtk_widget_get_style_context (GTK_WIDGET (container)),
|
||||
@@ -1663,8 +1656,6 @@ gtk_container_idle_sizer (gpointer data)
|
||||
0,
|
||||
empty);
|
||||
|
||||
g_slist_free_1 (slist);
|
||||
slist = next;
|
||||
_gtk_bitmask_free (empty);
|
||||
}
|
||||
|
||||
@@ -1674,35 +1665,52 @@ gtk_container_idle_sizer (gpointer data)
|
||||
* than trying to explicitely work around them with some extra flags,
|
||||
* since it doesn't cause any actual harm.
|
||||
*/
|
||||
while (container_resize_queue)
|
||||
if (container->priv->resize_pending)
|
||||
{
|
||||
GtkContainer *container;
|
||||
|
||||
slist = container_resize_queue;
|
||||
container_resize_queue = slist->next;
|
||||
container = slist->data;
|
||||
g_slist_free_1 (slist);
|
||||
|
||||
container->priv->resize_pending = FALSE;
|
||||
gtk_container_check_resize (container);
|
||||
}
|
||||
|
||||
gdk_window_process_all_updates ();
|
||||
|
||||
return container_resize_queue != NULL || container_restyle_queue != NULL;
|
||||
if (!container->priv->restyle_pending && !container->priv->resize_pending)
|
||||
{
|
||||
_gtk_container_stop_idle_sizer (container);
|
||||
}
|
||||
else
|
||||
{
|
||||
gdk_frame_clock_request_phase (clock,
|
||||
GDK_FRAME_CLOCK_PHASE_LAYOUT);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_container_start_idle_sizer (GtkContainer *container)
|
||||
{
|
||||
/* already started */
|
||||
if (container_resize_queue != NULL ||
|
||||
container_restyle_queue != NULL)
|
||||
GdkFrameClock *clock;
|
||||
|
||||
if (container->priv->resize_handler != 0)
|
||||
return;
|
||||
|
||||
gdk_threads_add_idle_full (GTK_PRIORITY_RESIZE,
|
||||
gtk_container_idle_sizer,
|
||||
NULL, NULL);
|
||||
clock = gtk_widget_get_frame_clock (GTK_WIDGET (container));
|
||||
if (clock == NULL)
|
||||
return;
|
||||
|
||||
container->priv->resize_clock = clock;
|
||||
container->priv->resize_handler = g_signal_connect (clock, "layout",
|
||||
G_CALLBACK (gtk_container_idle_sizer), container);
|
||||
gdk_frame_clock_request_phase (clock,
|
||||
GDK_FRAME_CLOCK_PHASE_LAYOUT);
|
||||
}
|
||||
|
||||
void
|
||||
_gtk_container_stop_idle_sizer (GtkContainer *container)
|
||||
{
|
||||
if (container->priv->resize_handler == 0)
|
||||
return;
|
||||
|
||||
g_signal_handler_disconnect (container->priv->resize_clock,
|
||||
container->priv->resize_handler);
|
||||
container->priv->resize_handler = 0;
|
||||
container->priv->resize_clock = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1725,7 +1733,6 @@ gtk_container_queue_resize_handler (GtkContainer *container)
|
||||
{
|
||||
container->priv->resize_pending = TRUE;
|
||||
gtk_container_start_idle_sizer (container);
|
||||
container_resize_queue = g_slist_prepend (container_resize_queue, container);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1780,8 +1787,6 @@ _gtk_container_queue_restyle (GtkContainer *container)
|
||||
return;
|
||||
|
||||
gtk_container_start_idle_sizer (container);
|
||||
|
||||
container_restyle_queue = g_slist_prepend (container_restyle_queue, container);
|
||||
priv->restyle_pending = TRUE;
|
||||
}
|
||||
|
||||
@@ -1815,6 +1820,13 @@ _gtk_container_resize_invalidate (GtkContainer *container)
|
||||
_gtk_container_queue_resize_internal (container, TRUE);
|
||||
}
|
||||
|
||||
void
|
||||
_gtk_container_maybe_start_idle_sizer (GtkContainer *container)
|
||||
{
|
||||
if (container->priv->restyle_pending || container->priv->resize_pending)
|
||||
gtk_container_start_idle_sizer (container);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_container_check_resize (GtkContainer *container)
|
||||
{
|
||||
|
@@ -39,6 +39,8 @@ GList * _gtk_container_focus_sort (GtkContainer *container,
|
||||
GtkWidget *old_focus);
|
||||
gboolean _gtk_container_get_reallocate_redraws (GtkContainer *container);
|
||||
|
||||
void _gtk_container_stop_idle_sizer (GtkContainer *container);
|
||||
void _gtk_container_maybe_start_idle_sizer (GtkContainer *container);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
@@ -126,7 +126,6 @@
|
||||
#define TOUCH_BYPASS_CAPTURED_THRESHOLD 30
|
||||
|
||||
/* Kinetic scrolling */
|
||||
#define FRAME_INTERVAL (1000 / 60)
|
||||
#define MAX_OVERSHOOT_DISTANCE 50
|
||||
#define FRICTION_DECELERATION 0.003
|
||||
#define OVERSHOOT_INVERSE_ACCELERATION 0.003
|
||||
@@ -284,6 +283,8 @@ static gboolean _gtk_scrolled_window_set_adjustment_value (GtkScrolledWindo
|
||||
gboolean allow_overshooting,
|
||||
gboolean snap_to_border);
|
||||
|
||||
static void gtk_scrolled_window_cancel_deceleration (GtkScrolledWindow *scrolled_window);
|
||||
|
||||
static guint signals[LAST_SIGNAL] = {0};
|
||||
|
||||
G_DEFINE_TYPE (GtkScrolledWindow, gtk_scrolled_window, GTK_TYPE_BIN)
|
||||
@@ -2383,10 +2384,10 @@ _gtk_scrolled_window_set_adjustment_value (GtkScrolledWindow *scrolled_window,
|
||||
return (*prev_value != value);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
scrolled_window_deceleration_cb (gpointer user_data)
|
||||
static void
|
||||
scrolled_window_deceleration_cb (GdkFrameClock *frame_clock,
|
||||
KineticScrollData *data)
|
||||
{
|
||||
KineticScrollData *data = user_data;
|
||||
GtkScrolledWindow *scrolled_window = data->scrolled_window;
|
||||
GtkScrolledWindowPrivate *priv = scrolled_window->priv;
|
||||
GtkAdjustment *hadjustment, *vadjustment;
|
||||
@@ -2401,7 +2402,7 @@ scrolled_window_deceleration_cb (gpointer user_data)
|
||||
_gtk_scrolled_window_get_overshoot (scrolled_window,
|
||||
&old_overshoot_x, &old_overshoot_y);
|
||||
|
||||
current_time = g_get_monotonic_time ();
|
||||
current_time = gdk_frame_clock_get_frame_time (frame_clock);
|
||||
elapsed = (current_time - data->last_deceleration_time) / 1000;
|
||||
data->last_deceleration_time = current_time;
|
||||
|
||||
@@ -2495,12 +2496,9 @@ scrolled_window_deceleration_cb (gpointer user_data)
|
||||
|
||||
if (overshoot_x != 0 || overshoot_y != 0 ||
|
||||
data->x_velocity != 0 || data->y_velocity != 0)
|
||||
return TRUE;
|
||||
gdk_frame_clock_request_phase (frame_clock, GDK_FRAME_CLOCK_PHASE_UPDATE);
|
||||
else
|
||||
{
|
||||
priv->deceleration_id = 0;
|
||||
return FALSE;
|
||||
}
|
||||
gtk_scrolled_window_cancel_deceleration (scrolled_window);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -2510,7 +2508,11 @@ gtk_scrolled_window_cancel_deceleration (GtkScrolledWindow *scrolled_window)
|
||||
|
||||
if (priv->deceleration_id)
|
||||
{
|
||||
g_source_remove (priv->deceleration_id);
|
||||
GdkFrameClock *frame_clock;
|
||||
|
||||
frame_clock = gtk_widget_get_frame_clock (GTK_WIDGET (scrolled_window));
|
||||
g_signal_handler_disconnect (frame_clock,
|
||||
priv->deceleration_id);
|
||||
priv->deceleration_id = 0;
|
||||
}
|
||||
}
|
||||
@@ -2519,12 +2521,15 @@ static void
|
||||
gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window)
|
||||
{
|
||||
GtkScrolledWindowPrivate *priv = scrolled_window->priv;
|
||||
GdkFrameClock *frame_clock;
|
||||
KineticScrollData *data;
|
||||
gdouble angle;
|
||||
|
||||
frame_clock = gtk_widget_get_frame_clock (GTK_WIDGET (scrolled_window));
|
||||
|
||||
data = g_new0 (KineticScrollData, 1);
|
||||
data->scrolled_window = scrolled_window;
|
||||
data->last_deceleration_time = g_get_monotonic_time ();
|
||||
data->last_deceleration_time = gdk_frame_clock_get_frame_time (frame_clock);
|
||||
data->x_velocity = priv->x_velocity;
|
||||
data->y_velocity = priv->y_velocity;
|
||||
|
||||
@@ -2536,10 +2541,12 @@ gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window)
|
||||
data->vel_sine = sin (angle);
|
||||
|
||||
scrolled_window->priv->deceleration_id =
|
||||
gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT,
|
||||
FRAME_INTERVAL,
|
||||
scrolled_window_deceleration_cb,
|
||||
data, (GDestroyNotify) g_free);
|
||||
g_signal_connect_data (frame_clock, "update",
|
||||
G_CALLBACK (scrolled_window_deceleration_cb),
|
||||
data,
|
||||
(GClosureNotify) g_free,
|
||||
0);
|
||||
gdk_frame_clock_request_phase (frame_clock, GDK_FRAME_CLOCK_PHASE_UPDATE);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
|
@@ -358,20 +358,21 @@ struct _GtkStyleContextPrivate
|
||||
|
||||
GtkStyleCascade *cascade;
|
||||
|
||||
GtkStyleContext *animation_list_prev;
|
||||
GtkStyleContext *animation_list_next;
|
||||
|
||||
GtkStyleContext *parent;
|
||||
GSList *children;
|
||||
GtkWidget *widget;
|
||||
GtkWidget *widget;
|
||||
GtkWidgetPath *widget_path;
|
||||
GHashTable *style_data;
|
||||
GtkStyleInfo *info;
|
||||
|
||||
GdkFrameClock *frame_clock;
|
||||
guint frame_clock_update_id;
|
||||
|
||||
GtkCssChange relevant_changes;
|
||||
GtkCssChange pending_changes;
|
||||
|
||||
const GtkBitmask *invalidating_context;
|
||||
guint animating : 1;
|
||||
guint invalid : 1;
|
||||
};
|
||||
|
||||
@@ -379,6 +380,7 @@ enum {
|
||||
PROP_0,
|
||||
PROP_SCREEN,
|
||||
PROP_DIRECTION,
|
||||
PROP_FRAME_CLOCK,
|
||||
PROP_PARENT
|
||||
};
|
||||
|
||||
@@ -388,8 +390,6 @@ enum {
|
||||
};
|
||||
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
static GtkStyleContext *_running_animations = NULL;
|
||||
guint _running_animations_timer_id = 0;
|
||||
|
||||
static void gtk_style_context_finalize (GObject *object);
|
||||
|
||||
@@ -404,6 +404,9 @@ static void gtk_style_context_impl_get_property (GObject *object,
|
||||
static StyleData *style_data_lookup (GtkStyleContext *context);
|
||||
|
||||
|
||||
static void gtk_style_context_disconnect_update (GtkStyleContext *context);
|
||||
static void gtk_style_context_connect_update (GtkStyleContext *context);
|
||||
|
||||
G_DEFINE_TYPE (GtkStyleContext, gtk_style_context, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
@@ -442,6 +445,13 @@ gtk_style_context_class_init (GtkStyleContextClass *klass)
|
||||
P_("The associated GdkScreen"),
|
||||
GDK_TYPE_SCREEN,
|
||||
GTK_PARAM_READWRITE));
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_FRAME_CLOCK,
|
||||
g_param_spec_object ("paint-clock",
|
||||
P_("FrameClock"),
|
||||
P_("The associated GdkFrameClock"),
|
||||
GDK_TYPE_FRAME_CLOCK,
|
||||
GTK_PARAM_READWRITE));
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_DIRECTION,
|
||||
g_param_spec_enum ("direction",
|
||||
@@ -724,19 +734,14 @@ gtk_style_context_init (GtkStyleContext *style_context)
|
||||
_gtk_style_cascade_get_for_screen (priv->screen));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_style_context_do_animations (gpointer unused)
|
||||
static void
|
||||
gtk_style_context_update (GdkFrameClock *clock,
|
||||
GtkStyleContext *context)
|
||||
{
|
||||
GtkStyleContext *context;
|
||||
_gtk_style_context_queue_invalidate (context, GTK_CSS_CHANGE_ANIMATE);
|
||||
|
||||
for (context = _running_animations;
|
||||
context != NULL;
|
||||
context = context->priv->animation_list_next)
|
||||
{
|
||||
_gtk_style_context_queue_invalidate (context, GTK_CSS_CHANGE_ANIMATE);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
/* A little blech to request one more than we need */
|
||||
gdk_frame_clock_request_phase (clock, GDK_FRAME_CLOCK_PHASE_UPDATE);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@@ -744,8 +749,35 @@ gtk_style_context_is_animating (GtkStyleContext *context)
|
||||
{
|
||||
GtkStyleContextPrivate *priv = context->priv;
|
||||
|
||||
return priv->animation_list_prev != NULL
|
||||
|| _running_animations == context;
|
||||
return priv->animating;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_style_context_disconnect_update (GtkStyleContext *context)
|
||||
{
|
||||
GtkStyleContextPrivate *priv = context->priv;
|
||||
|
||||
if (priv->frame_clock && priv->frame_clock_update_id)
|
||||
{
|
||||
g_signal_handler_disconnect (priv->frame_clock,
|
||||
priv->frame_clock_update_id);
|
||||
priv->frame_clock_update_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_style_context_connect_update (GtkStyleContext *context)
|
||||
{
|
||||
GtkStyleContextPrivate *priv = context->priv;
|
||||
|
||||
if (priv->frame_clock && priv->frame_clock_update_id == 0)
|
||||
{
|
||||
priv->frame_clock_update_id = g_signal_connect (priv->frame_clock,
|
||||
"update",
|
||||
G_CALLBACK (gtk_style_context_update),
|
||||
context);
|
||||
gdk_frame_clock_request_phase (priv->frame_clock, GDK_FRAME_CLOCK_PHASE_UPDATE);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -756,25 +788,9 @@ gtk_style_context_stop_animating (GtkStyleContext *context)
|
||||
if (!gtk_style_context_is_animating (context))
|
||||
return;
|
||||
|
||||
if (priv->animation_list_prev == NULL)
|
||||
{
|
||||
_running_animations = priv->animation_list_next;
|
||||
priv->animating = FALSE;
|
||||
|
||||
if (_running_animations == NULL)
|
||||
{
|
||||
/* we were the last animation */
|
||||
g_source_remove (_running_animations_timer_id);
|
||||
_running_animations_timer_id = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
priv->animation_list_prev->priv->animation_list_next = priv->animation_list_next;
|
||||
|
||||
if (priv->animation_list_next)
|
||||
priv->animation_list_next->priv->animation_list_prev = priv->animation_list_prev;
|
||||
|
||||
priv->animation_list_next = NULL;
|
||||
priv->animation_list_prev = NULL;
|
||||
gtk_style_context_disconnect_update (context);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -785,19 +801,9 @@ gtk_style_context_start_animating (GtkStyleContext *context)
|
||||
if (gtk_style_context_is_animating (context))
|
||||
return;
|
||||
|
||||
if (_running_animations == NULL)
|
||||
{
|
||||
_running_animations_timer_id = gdk_threads_add_timeout (25,
|
||||
gtk_style_context_do_animations,
|
||||
NULL);
|
||||
_running_animations = context;
|
||||
}
|
||||
else
|
||||
{
|
||||
priv->animation_list_next = _running_animations;
|
||||
_running_animations->priv->animation_list_prev = context;
|
||||
_running_animations = context;
|
||||
}
|
||||
priv->animating = TRUE;
|
||||
|
||||
gtk_style_context_connect_update (context);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@@ -886,6 +892,10 @@ gtk_style_context_impl_set_property (GObject *object,
|
||||
g_value_get_enum (value));
|
||||
G_GNUC_END_IGNORE_DEPRECATIONS;
|
||||
break;
|
||||
case PROP_FRAME_CLOCK:
|
||||
gtk_style_context_set_frame_clock (style_context,
|
||||
g_value_get_object (value));
|
||||
break;
|
||||
case PROP_PARENT:
|
||||
gtk_style_context_set_parent (style_context,
|
||||
g_value_get_object (value));
|
||||
@@ -918,6 +928,9 @@ gtk_style_context_impl_get_property (GObject *object,
|
||||
g_value_set_enum (value, gtk_style_context_get_direction (style_context));
|
||||
G_GNUC_END_IGNORE_DEPRECATIONS;
|
||||
break;
|
||||
case PROP_FRAME_CLOCK:
|
||||
g_value_set_object (value, priv->frame_clock);
|
||||
break;
|
||||
case PROP_PARENT:
|
||||
g_value_set_object (value, priv->parent);
|
||||
break;
|
||||
@@ -2614,6 +2627,70 @@ gtk_style_context_get_screen (GtkStyleContext *context)
|
||||
return priv->screen;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_style_context_set_frame_clock:
|
||||
* @context: a #GdkFrameClock
|
||||
* @frame_clock: a #GdkFrameClock
|
||||
*
|
||||
* Attaches @context to the given frame clock.
|
||||
*
|
||||
* The frame clock is used for the timing of animations.
|
||||
*
|
||||
* If you are using a #GtkStyleContext returned from
|
||||
* gtk_widget_get_style_context(), you do not need to
|
||||
* call this yourself.
|
||||
*
|
||||
* Since: 3.8
|
||||
**/
|
||||
void
|
||||
gtk_style_context_set_frame_clock (GtkStyleContext *context,
|
||||
GdkFrameClock *frame_clock)
|
||||
{
|
||||
GtkStyleContextPrivate *priv;
|
||||
|
||||
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
|
||||
g_return_if_fail (frame_clock == NULL || GDK_IS_FRAME_CLOCK (frame_clock));
|
||||
|
||||
priv = context->priv;
|
||||
if (priv->frame_clock == frame_clock)
|
||||
return;
|
||||
|
||||
if (priv->animating)
|
||||
gtk_style_context_disconnect_update (context);
|
||||
|
||||
if (priv->frame_clock)
|
||||
g_object_unref (priv->frame_clock);
|
||||
priv->frame_clock = frame_clock;
|
||||
if (priv->frame_clock)
|
||||
g_object_ref (priv->frame_clock);
|
||||
|
||||
if (priv->animating)
|
||||
gtk_style_context_connect_update (context);
|
||||
|
||||
g_object_notify (G_OBJECT (context), "paint-clock");
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_style_context_get_frame_clock:
|
||||
* @context: a #GtkStyleContext
|
||||
*
|
||||
* Returns the #GdkFrameClock to which @context is attached.
|
||||
*
|
||||
* Returns: (transfer none): a #GdkFrameClock, or %NULL
|
||||
* if @context does not have an attached frame clock.
|
||||
* Since: 3.8
|
||||
**/
|
||||
GdkFrameClock *
|
||||
gtk_style_context_get_frame_clock (GtkStyleContext *context)
|
||||
{
|
||||
GtkStyleContextPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
|
||||
|
||||
priv = context->priv;
|
||||
return priv->frame_clock;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_style_context_set_direction:
|
||||
* @context: a #GtkStyleContext
|
||||
|
@@ -836,6 +836,13 @@ void gtk_style_context_set_screen (GtkStyleContext *context,
|
||||
GdkScreen *screen);
|
||||
GdkScreen * gtk_style_context_get_screen (GtkStyleContext *context);
|
||||
|
||||
GDK_AVAILABLE_IN_3_8
|
||||
void gtk_style_context_set_frame_clock (GtkStyleContext *context,
|
||||
GdkFrameClock *frame_clock);
|
||||
GDK_AVAILABLE_IN_3_8
|
||||
GdkFrameClock *gtk_style_context_get_frame_clock (GtkStyleContext *context);
|
||||
|
||||
|
||||
GDK_DEPRECATED_IN_3_8_FOR(gtk_style_context_set_state)
|
||||
void gtk_style_context_set_direction (GtkStyleContext *context,
|
||||
GtkTextDirection direction);
|
||||
|
307
gtk/gtkwidget.c
307
gtk/gtkwidget.c
@@ -409,6 +409,9 @@ struct _GtkWidgetPrivate
|
||||
/* The widget's parent */
|
||||
GtkWidget *parent;
|
||||
|
||||
/* Animations and other things to update on clock ticks */
|
||||
GList *tick_callbacks;
|
||||
|
||||
#ifdef G_ENABLE_DEBUG
|
||||
/* Number of gtk_widget_push_verify_invariants () */
|
||||
guint verifying_invariants_count;
|
||||
@@ -710,6 +713,10 @@ static void gtk_widget_set_device_enabled_internal (GtkWidget *widget,
|
||||
GdkDevice *device,
|
||||
gboolean recurse,
|
||||
gboolean enabled);
|
||||
|
||||
static void gtk_widget_on_frame_clock_update (GdkFrameClock *frame_clock,
|
||||
GtkWidget *widget);
|
||||
|
||||
static gboolean event_window_is_still_viewable (GdkEvent *event);
|
||||
static void gtk_cairo_set_event (cairo_t *cr,
|
||||
GdkEventExpose *event);
|
||||
@@ -4505,6 +4512,222 @@ gtk_widget_update_devices_mask (GtkWidget *widget,
|
||||
gtk_widget_set_device_enabled_internal (widget, GDK_DEVICE (l->data), recurse, TRUE);
|
||||
}
|
||||
|
||||
typedef struct _GtkTickCallbackInfo GtkTickCallbackInfo;
|
||||
|
||||
struct _GtkTickCallbackInfo
|
||||
{
|
||||
guint refcount;
|
||||
|
||||
guint id;
|
||||
GtkTickCallback callback;
|
||||
gpointer user_data;
|
||||
GDestroyNotify notify;
|
||||
|
||||
guint destroyed : 1;
|
||||
};
|
||||
|
||||
static void
|
||||
ref_tick_callback_info (GtkTickCallbackInfo *info)
|
||||
{
|
||||
info->refcount++;
|
||||
}
|
||||
|
||||
static void
|
||||
unref_tick_callback_info (GtkWidget *widget,
|
||||
GtkTickCallbackInfo *info,
|
||||
GList *link)
|
||||
{
|
||||
GtkWidgetPrivate *priv = widget->priv;
|
||||
|
||||
info->refcount--;
|
||||
if (info->refcount == 0)
|
||||
{
|
||||
priv->tick_callbacks = g_list_delete_link (priv->tick_callbacks, link);
|
||||
if (info->notify)
|
||||
info->notify (info->user_data);
|
||||
g_slice_free (GtkTickCallbackInfo, info);
|
||||
}
|
||||
|
||||
if (priv->tick_callbacks == NULL && priv->realized)
|
||||
{
|
||||
GdkFrameClock *frame_clock = gtk_widget_get_frame_clock (widget);
|
||||
g_signal_handlers_disconnect_by_func (frame_clock,
|
||||
(gpointer) gtk_widget_on_frame_clock_update,
|
||||
widget);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_tick_callback_info (GtkWidget *widget,
|
||||
GtkTickCallbackInfo *info,
|
||||
GList *link)
|
||||
{
|
||||
if (!info->destroyed)
|
||||
{
|
||||
info->destroyed = TRUE;
|
||||
unref_tick_callback_info (widget, info, link);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_widget_on_frame_clock_update (GdkFrameClock *frame_clock,
|
||||
GtkWidget *widget)
|
||||
{
|
||||
GtkWidgetPrivate *priv = widget->priv;
|
||||
GList *l;
|
||||
|
||||
for (l = priv->tick_callbacks; l;)
|
||||
{
|
||||
GtkTickCallbackInfo *info = l->data;
|
||||
GList *next;
|
||||
|
||||
ref_tick_callback_info (info);
|
||||
if (!info->destroyed)
|
||||
{
|
||||
if (info->callback (widget,
|
||||
frame_clock,
|
||||
info->user_data) == G_SOURCE_REMOVE)
|
||||
{
|
||||
destroy_tick_callback_info (widget, info, l);
|
||||
}
|
||||
}
|
||||
|
||||
next = l->next;
|
||||
unref_tick_callback_info (widget, info, l);
|
||||
l = next;
|
||||
}
|
||||
|
||||
if (priv->tick_callbacks != NULL)
|
||||
gdk_frame_clock_request_phase (frame_clock,
|
||||
GDK_FRAME_CLOCK_PHASE_UPDATE);
|
||||
}
|
||||
|
||||
static guint tick_callback_id;
|
||||
|
||||
/**
|
||||
* gtk_widget_add_tick_callback:
|
||||
* @widget: a #GtkWidget
|
||||
* @callback: function to call for updating animations
|
||||
* @user_data: data to pass to @callback
|
||||
* @notify: function to call to free @user_data when the callback is removed.
|
||||
*
|
||||
* Queues a animation frame update and adds a callback to be called
|
||||
* before each frame. Until the tick callback is removed, it will be
|
||||
* called frequently (usually at the frame rate of the output device
|
||||
* or as quickly as the application an be repainted, whichever is
|
||||
* slower). For this reason, is most suitable for handling graphics
|
||||
* that change every frame or every few frames. The tick callback does
|
||||
* not automatically imply a relayout or repaint. If you want a
|
||||
* repaint or relayout, and aren't changing widget properties that
|
||||
* would trigger that (for example, changing the text of a #GtkLabel),
|
||||
* then you will have to call gtk_widget_queue_resize() or
|
||||
* gtk_widget_queue_draw_area() yourself.
|
||||
*
|
||||
* gtk_frame_clock_get_frame_time() should generally be used for timing
|
||||
* continuous animations and
|
||||
* gtk_frame_timings_get_predicted_presentation_time() if you are
|
||||
* trying to display isolated frames particular times.
|
||||
*
|
||||
* This is a more convenient alternative to connecting directly to the
|
||||
* ::update signal of GdkFrameClock, since you don't have to worry about
|
||||
* when a #GdkFrameClock is assigned to a widget.
|
||||
*/
|
||||
guint
|
||||
gtk_widget_add_tick_callback (GtkWidget *widget,
|
||||
GtkTickCallback callback,
|
||||
gpointer user_data,
|
||||
GDestroyNotify notify)
|
||||
{
|
||||
GtkWidgetPrivate *priv;
|
||||
GtkTickCallbackInfo *info;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
|
||||
|
||||
priv = widget->priv;
|
||||
|
||||
if (priv->tick_callbacks == NULL && priv->realized)
|
||||
{
|
||||
GdkFrameClock *frame_clock = gtk_widget_get_frame_clock (widget);
|
||||
g_signal_connect (frame_clock, "update",
|
||||
G_CALLBACK (gtk_widget_on_frame_clock_update),
|
||||
widget);
|
||||
gdk_frame_clock_request_phase (frame_clock,
|
||||
GDK_FRAME_CLOCK_PHASE_UPDATE);
|
||||
}
|
||||
|
||||
info = g_slice_new (GtkTickCallbackInfo);
|
||||
|
||||
info->refcount = 1;
|
||||
info->id = ++tick_callback_id;
|
||||
info->callback = callback;
|
||||
info->user_data = user_data;
|
||||
info->notify = notify;
|
||||
|
||||
priv->tick_callbacks = g_list_prepend (priv->tick_callbacks,
|
||||
info);
|
||||
|
||||
return info->id;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_widget_remove_tick_callback (GtkWidget *widget,
|
||||
guint id)
|
||||
{
|
||||
GtkWidgetPrivate *priv;
|
||||
GList *l;
|
||||
|
||||
g_return_if_fail (GTK_IS_WIDGET (widget));
|
||||
|
||||
priv = widget->priv;
|
||||
|
||||
for (l = priv->tick_callbacks; l; l = l->next)
|
||||
{
|
||||
GtkTickCallbackInfo *info = l->data;
|
||||
if (info->id == id)
|
||||
destroy_tick_callback_info (widget, info, l);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_widget_connect_frame_clock (GtkWidget *widget,
|
||||
GdkFrameClock *frame_clock)
|
||||
{
|
||||
GtkWidgetPrivate *priv = widget->priv;
|
||||
|
||||
if (GTK_IS_CONTAINER (widget))
|
||||
_gtk_container_maybe_start_idle_sizer (GTK_CONTAINER (widget));
|
||||
|
||||
if (priv->tick_callbacks != NULL)
|
||||
{
|
||||
g_signal_connect (frame_clock, "update",
|
||||
G_CALLBACK (gtk_widget_on_frame_clock_update),
|
||||
widget);
|
||||
gdk_frame_clock_request_phase (frame_clock,
|
||||
GDK_FRAME_CLOCK_PHASE_UPDATE);
|
||||
}
|
||||
|
||||
if (priv->context)
|
||||
gtk_style_context_set_frame_clock (priv->context, frame_clock);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_widget_disconnect_frame_clock (GtkWidget *widget,
|
||||
GdkFrameClock *frame_clock)
|
||||
{
|
||||
GtkWidgetPrivate *priv = widget->priv;
|
||||
|
||||
if (GTK_IS_CONTAINER (widget))
|
||||
_gtk_container_stop_idle_sizer (GTK_CONTAINER (widget));
|
||||
|
||||
if (priv->tick_callbacks)
|
||||
g_signal_handlers_disconnect_by_func (frame_clock,
|
||||
(gpointer) gtk_widget_on_frame_clock_update,
|
||||
widget);
|
||||
|
||||
if (priv->context)
|
||||
gtk_style_context_set_frame_clock (priv->context, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_widget_realize:
|
||||
* @widget: a #GtkWidget
|
||||
@@ -4585,6 +4808,9 @@ gtk_widget_realize (GtkWidget *widget)
|
||||
_gtk_widget_enable_device_events (widget);
|
||||
gtk_widget_update_devices_mask (widget, TRUE);
|
||||
|
||||
gtk_widget_connect_frame_clock (widget,
|
||||
gtk_widget_get_frame_clock (widget));
|
||||
|
||||
gtk_widget_pop_verify_invariants (widget);
|
||||
}
|
||||
}
|
||||
@@ -4617,6 +4843,9 @@ gtk_widget_unrealize (GtkWidget *widget)
|
||||
if (widget->priv->mapped)
|
||||
gtk_widget_unmap (widget);
|
||||
|
||||
gtk_widget_disconnect_frame_clock (widget,
|
||||
gtk_widget_get_frame_clock (widget));
|
||||
|
||||
g_signal_emit (widget, widget_signals[UNREALIZE], 0);
|
||||
g_assert (!widget->priv->mapped);
|
||||
gtk_widget_set_realized (widget, FALSE);
|
||||
@@ -4777,6 +5006,60 @@ gtk_widget_queue_resize_no_redraw (GtkWidget *widget)
|
||||
_gtk_size_group_queue_resize (widget, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_widget_get_frame_clock:
|
||||
* @widget: a #GtkWidget
|
||||
*
|
||||
* Obtains the frame clock for a widget. The frame clock is a global
|
||||
* "ticker" that can be used to drive animations and repaints. The
|
||||
* most common reason to get the frame clock is to call
|
||||
* gdk_frame_clock_get_frame_time(), in order to get a time to use for
|
||||
* animating. For example you might record the start of the animation
|
||||
* with an initial value from gdk_frame_clock_get_frame_time(), and
|
||||
* then update the animation by calling
|
||||
* gdk_frame_clock_get_frame_time() again during each repaint.
|
||||
*
|
||||
* gdk_frame_clock_request_phase() will result in a new frame on the
|
||||
* clock, but won't necessarily repaint any widgets. To repaint a
|
||||
* widget, you have to use gtk_widget_queue_draw() which invalidates
|
||||
* the widget (thus scheduling it to receive a draw on the next
|
||||
* frame). gtk_widget_queue_draw() will also end up requesting a frame
|
||||
* on the appropriate frame clock.
|
||||
*
|
||||
* A widget's frame clock will not change while the widget is
|
||||
* mapped. Reparenting a widget (which implies a temporary unmap) can
|
||||
* change the widget's frame clock.
|
||||
*
|
||||
* Unrealized widgets do not have a frame clock.
|
||||
*
|
||||
* Since: 3.0
|
||||
* Return value: (transfer none): a #GdkFrameClock (or #NULL if widget is unrealized)
|
||||
*/
|
||||
GdkFrameClock*
|
||||
gtk_widget_get_frame_clock (GtkWidget *widget)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
|
||||
|
||||
if (widget->priv->realized)
|
||||
{
|
||||
/* We use gtk_widget_get_toplevel() here to make it explicit that
|
||||
* the frame clock is a property of the toplevel that a widget
|
||||
* is anchored to; gdk_window_get_toplevel() will go up the
|
||||
* hierarchy anyways, but should squash any funny business with
|
||||
* reparenting windows and widgets.
|
||||
*/
|
||||
GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
|
||||
GdkWindow *window = gtk_widget_get_window (toplevel);
|
||||
g_assert (window != NULL);
|
||||
|
||||
return gdk_window_get_frame_clock (window);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_widget_size_request:
|
||||
* @widget: a #GtkWidget
|
||||
@@ -8453,6 +8736,17 @@ gtk_widget_propagate_hierarchy_changed_recurse (GtkWidget *widget,
|
||||
|
||||
priv->anchored = new_anchored;
|
||||
|
||||
/* This can only happen with gtk_widget_reparent() */
|
||||
if (priv->realized)
|
||||
{
|
||||
if (new_anchored)
|
||||
gtk_widget_connect_frame_clock (widget,
|
||||
gtk_widget_get_frame_clock (widget));
|
||||
else
|
||||
gtk_widget_disconnect_frame_clock (widget,
|
||||
gtk_widget_get_frame_clock (info->previous_toplevel));
|
||||
}
|
||||
|
||||
g_signal_emit (widget, widget_signals[HIERARCHY_CHANGED], 0, info->previous_toplevel);
|
||||
do_screen_change (widget, info->previous_screen, info->new_screen);
|
||||
|
||||
@@ -10461,6 +10755,7 @@ gtk_widget_real_destroy (GtkWidget *object)
|
||||
/* gtk_object_destroy() will already hold a refcount on object */
|
||||
GtkWidget *widget = GTK_WIDGET (object);
|
||||
GtkWidgetPrivate *priv = widget->priv;
|
||||
GList *l;
|
||||
|
||||
if (GTK_WIDGET_GET_CLASS (widget)->priv->accessible_type != GTK_TYPE_ACCESSIBLE)
|
||||
{
|
||||
@@ -10482,6 +10777,13 @@ gtk_widget_real_destroy (GtkWidget *object)
|
||||
|
||||
gtk_grab_remove (widget);
|
||||
|
||||
for (l = priv->tick_callbacks; l;)
|
||||
{
|
||||
GList *next = l->next;
|
||||
destroy_tick_callback_info (widget, l->data, l);
|
||||
l = next;
|
||||
}
|
||||
|
||||
if (priv->style)
|
||||
g_object_unref (priv->style);
|
||||
priv->style = gtk_widget_get_default_style ();
|
||||
@@ -14396,6 +14698,7 @@ gtk_widget_get_style_context (GtkWidget *widget)
|
||||
if (G_UNLIKELY (priv->context == NULL))
|
||||
{
|
||||
GdkScreen *screen;
|
||||
GdkFrameClock *frame_clock;
|
||||
|
||||
priv->context = gtk_style_context_new ();
|
||||
|
||||
@@ -14405,6 +14708,10 @@ gtk_widget_get_style_context (GtkWidget *widget)
|
||||
if (screen)
|
||||
gtk_style_context_set_screen (priv->context, screen);
|
||||
|
||||
frame_clock = gtk_widget_get_frame_clock (widget);
|
||||
if (frame_clock)
|
||||
gtk_style_context_set_frame_clock (priv->context, frame_clock);
|
||||
|
||||
if (priv->parent)
|
||||
gtk_style_context_set_parent (priv->context,
|
||||
gtk_widget_get_style_context (priv->parent));
|
||||
|
@@ -86,6 +86,21 @@ typedef GdkRectangle GtkAllocation;
|
||||
typedef void (*GtkCallback) (GtkWidget *widget,
|
||||
gpointer data);
|
||||
|
||||
/**
|
||||
* GtkTickCallback:
|
||||
* @widget: the widget
|
||||
* @frame_clock: the frame clock for the widget (same as calling gtk_widget_get_frame_clock())
|
||||
* @user_data: user data passed to gtk_widget_add_tick_callback().
|
||||
*
|
||||
* Callback type for adding a function to update animations. See gtk_widget_add_tick_callback().
|
||||
*
|
||||
* Returns: %G_SOURCE_CONTINUE if the tick callback should continue to be called,
|
||||
* %G_SOURCE_REMOVE if the tick callback should be removed.
|
||||
*/
|
||||
typedef gboolean (*GtkTickCallback) (GtkWidget *widget,
|
||||
GdkFrameClock *frame_clock,
|
||||
gpointer user_data);
|
||||
|
||||
/**
|
||||
* GtkRequisition:
|
||||
* @width: the widget's desired width
|
||||
@@ -473,6 +488,8 @@ void gtk_widget_queue_draw_region (GtkWidget *widget,
|
||||
const cairo_region_t*region);
|
||||
void gtk_widget_queue_resize (GtkWidget *widget);
|
||||
void gtk_widget_queue_resize_no_redraw (GtkWidget *widget);
|
||||
GdkFrameClock* gtk_widget_get_frame_clock (GtkWidget *widget);
|
||||
|
||||
GDK_DEPRECATED_IN_3_0_FOR(gtk_widget_get_preferred_size)
|
||||
void gtk_widget_size_request (GtkWidget *widget,
|
||||
GtkRequisition *requisition);
|
||||
@@ -903,6 +920,18 @@ void gtk_widget_insert_action_group (GtkWidg
|
||||
const gchar *name,
|
||||
GActionGroup *group);
|
||||
|
||||
|
||||
|
||||
GDK_AVAILABLE_IN_3_8
|
||||
guint gtk_widget_add_tick_callback (GtkWidget *widget,
|
||||
GtkTickCallback callback,
|
||||
gpointer user_data,
|
||||
GDestroyNotify notify);
|
||||
|
||||
GDK_AVAILABLE_IN_3_8
|
||||
void gtk_widget_remove_tick_callback (GtkWidget *widget,
|
||||
guint id);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_WIDGET_H__ */
|
||||
|
@@ -5164,7 +5164,6 @@ gtk_window_realize (GtkWidget *widget)
|
||||
|
||||
gtk_style_context_set_background (gtk_widget_get_style_context (widget), gdk_window);
|
||||
|
||||
gdk_window_enable_synchronized_configure (gdk_window);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -5238,8 +5237,6 @@ gtk_window_realize (GtkWidget *widget)
|
||||
gdk_window = gdk_window_new (parent_window, &attributes, attributes_mask);
|
||||
gtk_widget_set_window (widget, gdk_window);
|
||||
|
||||
gdk_window_enable_synchronized_configure (gdk_window);
|
||||
|
||||
gtk_widget_register_window (widget, gdk_window);
|
||||
|
||||
context = gtk_widget_get_style_context (widget);
|
||||
@@ -5580,7 +5577,6 @@ gtk_window_configure_event (GtkWidget *widget,
|
||||
if (GTK_WIDGET_CLASS (gtk_window_parent_class)->configure_event)
|
||||
return GTK_WIDGET_CLASS (gtk_window_parent_class)->configure_event (widget, event);
|
||||
|
||||
gdk_window_configure_finished (gtk_widget_get_window (widget));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -5614,7 +5610,6 @@ gtk_window_configure_event (GtkWidget *widget,
|
||||
(allocation.width == event->width &&
|
||||
allocation.height == event->height))
|
||||
{
|
||||
gdk_window_configure_finished (gtk_widget_get_window (widget));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -7153,10 +7148,6 @@ gtk_window_move_resize (GtkWindow *window)
|
||||
set_grip_position (window);
|
||||
update_grip_visibility (window);
|
||||
|
||||
gdk_window_process_updates (gdk_window, TRUE);
|
||||
|
||||
gdk_window_configure_finished (gdk_window);
|
||||
|
||||
/* If the configure request changed, it means that
|
||||
* we either:
|
||||
* 1) coincidentally changed hints or widget properties
|
||||
|
@@ -25,9 +25,12 @@ testsocket_programs = testsocket testsocket_child
|
||||
endif
|
||||
|
||||
noinst_PROGRAMS = $(TEST_PROGS) \
|
||||
animated-resizing \
|
||||
motion-compression \
|
||||
simple \
|
||||
flicker \
|
||||
print-editor \
|
||||
video-timer \
|
||||
testaccel \
|
||||
testadjustsize \
|
||||
testappchooser \
|
||||
@@ -149,9 +152,12 @@ endif
|
||||
|
||||
endif
|
||||
|
||||
animated_resizing_DEPENDENCIES = $(TEST_DEPS)
|
||||
flicker_DEPENDENCIES = $(TEST_DEPS)
|
||||
motion_compression_DEPENDENCIES = $(TEST_DEPS)
|
||||
simple_DEPENDENCIES = $(TEST_DEPS)
|
||||
print_editor_DEPENDENCIES = $(TEST_DEPS)
|
||||
video_timer_DEPENDENCIES = $(TEST_DEPS)
|
||||
testheightforwidth_DEPENDENCIES = $(TEST_DEPS)
|
||||
testicontheme_DEPENDENCIES = $(TEST_DEPS)
|
||||
testiconview_DEPENDENCIES = $(TEST_DEPS)
|
||||
@@ -249,6 +255,16 @@ testpixbuf_scale_DEPENDENCIES = $(TEST_DEPS)
|
||||
testgmenu_DEPENDENCIES = $(TEST_DEPS)
|
||||
testlogout_DEPENDENCIES = $(TEST_DEPS)
|
||||
|
||||
animated_resizing_SOURCES = \
|
||||
animated-resizing.c \
|
||||
variable.c \
|
||||
variable.h
|
||||
|
||||
video_timer_SOURCES = \
|
||||
video-timer.c \
|
||||
variable.c \
|
||||
variable.h
|
||||
|
||||
testboxcss_SOURCES = \
|
||||
testboxcss.c \
|
||||
prop-editor.c
|
||||
|
324
tests/animated-resizing.c
Normal file
324
tests/animated-resizing.c
Normal file
@@ -0,0 +1,324 @@
|
||||
/* -*- mode: C; c-basic-offset: 2; indent-tabs-mode: nil; -*- */
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "variable.h"
|
||||
|
||||
#define RADIUS 64
|
||||
#define DIAMETER (2*RADIUS)
|
||||
#define WIDTH 600
|
||||
#define HEIGHT 600
|
||||
#define WINDOW_SIZE_JITTER 200
|
||||
#define CYCLE_TIME 5.
|
||||
|
||||
static GtkWidget *window;
|
||||
static int window_width = WIDTH, window_height = HEIGHT;
|
||||
|
||||
gint64 start_frame_time;
|
||||
static double angle;
|
||||
|
||||
static int max_stats = -1;
|
||||
static double statistics_time = 5.;
|
||||
static double load_factor = 1.0;
|
||||
static double cb_no_resize = FALSE;
|
||||
static gboolean machine_readable = FALSE;
|
||||
|
||||
static cairo_surface_t *source_surface;
|
||||
|
||||
static void
|
||||
ensure_resources(cairo_surface_t *target)
|
||||
{
|
||||
cairo_t *cr;
|
||||
int i, j;
|
||||
|
||||
if (source_surface != NULL)
|
||||
return;
|
||||
|
||||
source_surface = cairo_surface_create_similar (target, CAIRO_CONTENT_COLOR_ALPHA,
|
||||
16 * DIAMETER, 16 * DIAMETER);
|
||||
cr = cairo_create(source_surface);
|
||||
|
||||
cairo_save(cr);
|
||||
cairo_set_source_rgba(cr, 0, 0, 0, 0);
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_paint(cr);
|
||||
cairo_restore(cr);
|
||||
|
||||
cairo_set_line_width(cr, 1.0);
|
||||
|
||||
for (j = 0; j < 16; j++)
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
cairo_set_source_rgba(cr,
|
||||
((i * 41) % 16) / 15.,
|
||||
((i * 31) % 16) / 15.,
|
||||
((i * 23) % 16) / 15.,
|
||||
0.25);
|
||||
cairo_arc(cr,
|
||||
i * DIAMETER + RADIUS, j * DIAMETER + RADIUS,
|
||||
RADIUS - 0.5, 0, 2 * M_PI);
|
||||
cairo_fill_preserve(cr);
|
||||
cairo_set_source_rgba(cr,
|
||||
((i * 41) % 16) / 15.,
|
||||
((i * 31) % 16) / 15.,
|
||||
((i * 23) % 16) / 15.,
|
||||
1.0);
|
||||
cairo_stroke(cr);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_window_draw (GtkWidget *widget,
|
||||
cairo_t *cr)
|
||||
|
||||
{
|
||||
GRand *rand = g_rand_new_with_seed(0);
|
||||
int i;
|
||||
int width, height;
|
||||
|
||||
width = gtk_widget_get_allocated_width (widget);
|
||||
height = gtk_widget_get_allocated_height (widget);
|
||||
|
||||
ensure_resources (cairo_get_target (cr));
|
||||
|
||||
cairo_set_source_rgb(cr, 1, 1, 1);
|
||||
cairo_paint(cr);
|
||||
|
||||
cairo_set_source_rgb(cr, 0, 0, 0);
|
||||
cairo_set_line_width(cr, 1.0);
|
||||
cairo_rectangle (cr, 0.5, 0.5, width - 1, height - 1);
|
||||
cairo_stroke (cr);
|
||||
|
||||
for(i = 0; i < load_factor * 150; i++)
|
||||
{
|
||||
int source = g_rand_int_range(rand, 0, 255);
|
||||
double phi = g_rand_double_range(rand, 0, 2 * M_PI) + angle;
|
||||
double r = g_rand_double_range(rand, 0, width / 2 - RADIUS);
|
||||
int x, y;
|
||||
|
||||
int source_x = (source % 16) * DIAMETER;
|
||||
int source_y = (source / 16) * DIAMETER;
|
||||
|
||||
x = round(width / 2 + r * cos(phi) - RADIUS);
|
||||
y = round(height / 2 - r * sin(phi) - RADIUS);
|
||||
|
||||
cairo_set_source_surface(cr, source_surface,
|
||||
x - source_x, y - source_y);
|
||||
cairo_rectangle(cr, x, y, DIAMETER, DIAMETER);
|
||||
cairo_fill(cr);
|
||||
}
|
||||
|
||||
g_rand_free(rand);
|
||||
}
|
||||
|
||||
static void
|
||||
print_double (const char *description,
|
||||
double value)
|
||||
{
|
||||
if (machine_readable)
|
||||
g_print ("%g\t", value);
|
||||
else
|
||||
g_print ("%s: %g\n", description, value);
|
||||
}
|
||||
|
||||
static void
|
||||
print_variable (const char *description,
|
||||
Variable *variable)
|
||||
{
|
||||
if (variable->weight != 0)
|
||||
{
|
||||
if (machine_readable)
|
||||
g_print ("%g\t%g\t",
|
||||
variable_mean (variable),
|
||||
variable_standard_deviation (variable));
|
||||
else
|
||||
g_print ("%s: %g +/- %g\n", description,
|
||||
variable_mean (variable),
|
||||
variable_standard_deviation (variable));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (machine_readable)
|
||||
g_print ("-\t-\t");
|
||||
else
|
||||
g_print ("%s: <n/a>\n", description);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
handle_frame_stats (GdkFrameClock *frame_clock)
|
||||
{
|
||||
static int num_stats = 0;
|
||||
static double last_print_time = 0;
|
||||
static int frames_since_last_print = 0;
|
||||
static gint64 frame_counter;
|
||||
static gint64 last_handled_frame = -1;
|
||||
|
||||
static Variable latency = VARIABLE_INIT;
|
||||
|
||||
double current_time;
|
||||
|
||||
current_time = g_get_monotonic_time ();
|
||||
if (current_time >= last_print_time + 1000000 * statistics_time)
|
||||
{
|
||||
if (frames_since_last_print)
|
||||
{
|
||||
if (num_stats == 0 && machine_readable)
|
||||
{
|
||||
g_print ("# load_factor frame_rate latency\n");
|
||||
}
|
||||
|
||||
num_stats++;
|
||||
if (machine_readable)
|
||||
g_print ("%g ", load_factor);
|
||||
print_double ("Frame rate ",
|
||||
frames_since_last_print / ((current_time - last_print_time) / 1000000.));
|
||||
|
||||
print_variable ("Latency", &latency);
|
||||
|
||||
g_print ("\n");
|
||||
}
|
||||
|
||||
last_print_time = current_time;
|
||||
frames_since_last_print = 0;
|
||||
variable_reset (&latency);
|
||||
|
||||
if (num_stats == max_stats)
|
||||
gtk_main_quit ();
|
||||
}
|
||||
|
||||
frames_since_last_print++;
|
||||
|
||||
for (frame_counter = last_handled_frame;
|
||||
frame_counter < gdk_frame_clock_get_frame_counter (frame_clock);
|
||||
frame_counter++)
|
||||
{
|
||||
GdkFrameTimings *timings = gdk_frame_clock_get_timings (frame_clock, frame_counter);
|
||||
GdkFrameTimings *previous_timings = gdk_frame_clock_get_timings (frame_clock, frame_counter - 1);
|
||||
|
||||
if (!timings || gdk_frame_timings_get_complete (timings))
|
||||
last_handled_frame = frame_counter;
|
||||
|
||||
if (timings && gdk_frame_timings_get_complete (timings) && previous_timings &&
|
||||
gdk_frame_timings_get_presentation_time (timings) != 0 &&
|
||||
gdk_frame_timings_get_presentation_time (previous_timings) != 0)
|
||||
{
|
||||
double display_time = (gdk_frame_timings_get_presentation_time (timings) - gdk_frame_timings_get_presentation_time (previous_timings)) / 1000.;
|
||||
double frame_latency = (gdk_frame_timings_get_presentation_time (previous_timings) - gdk_frame_timings_get_frame_time (previous_timings)) / 1000. + display_time / 2;
|
||||
|
||||
variable_add_weighted (&latency, frame_latency, display_time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_frame (double progress)
|
||||
{
|
||||
GdkFrameClock *frame_clock = gtk_widget_get_frame_clock (window);
|
||||
int jitter;
|
||||
|
||||
if (frame_clock)
|
||||
handle_frame_stats (frame_clock);
|
||||
|
||||
angle = 2 * M_PI * progress;
|
||||
jitter = WINDOW_SIZE_JITTER * sin(angle);
|
||||
|
||||
if (!cb_no_resize)
|
||||
{
|
||||
window_width = WIDTH + jitter;
|
||||
window_height = HEIGHT + jitter;
|
||||
}
|
||||
|
||||
gtk_window_resize (GTK_WINDOW (window),
|
||||
window_width, window_height);
|
||||
|
||||
gtk_widget_queue_draw (window);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
tick_callback (GtkWidget *widget,
|
||||
GdkFrameClock *frame_clock,
|
||||
gpointer user_data)
|
||||
{
|
||||
gint64 frame_time = gdk_frame_clock_get_frame_time (frame_clock);
|
||||
double scaled_time;
|
||||
|
||||
if (start_frame_time == 0)
|
||||
start_frame_time = frame_time;
|
||||
|
||||
scaled_time = (frame_time - start_frame_time) / (CYCLE_TIME * 1000000);
|
||||
on_frame (scaled_time - floor (scaled_time));
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
on_map_event (GtkWidget *widget,
|
||||
GdkEventAny *event)
|
||||
{
|
||||
gtk_widget_add_tick_callback (window, tick_callback, NULL, NULL);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static GOptionEntry options[] = {
|
||||
{ "factor", 'f', 0, G_OPTION_ARG_DOUBLE, &load_factor, "Load factor", "FACTOR" },
|
||||
{ "max-statistics", 'm', 0, G_OPTION_ARG_INT, &max_stats, "Maximum statistics printed", NULL },
|
||||
{ "machine-readable", 0, 0, G_OPTION_ARG_NONE, &machine_readable, "Print statistics in columns", NULL },
|
||||
{ "no-resize", 'n', 0, G_OPTION_ARG_NONE, &cb_no_resize, "No Resize", NULL },
|
||||
{ "statistics-time", 's', 0, G_OPTION_ARG_DOUBLE, &statistics_time, "Statistics accumulation time", "TIME" },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
GError *error = NULL;
|
||||
GdkScreen *screen;
|
||||
GdkRectangle monitor_bounds;
|
||||
|
||||
if (!gtk_init_with_args (&argc, &argv, "",
|
||||
options, NULL, &error))
|
||||
{
|
||||
g_printerr ("Option parsing failed: %s\n", error->message);
|
||||
return 1;
|
||||
}
|
||||
|
||||
g_print ("%sLoad factor: %g\n",
|
||||
machine_readable ? "# " : "",
|
||||
load_factor);
|
||||
g_print ("%sResizing?: %s\n",
|
||||
machine_readable ? "# " : "",
|
||||
cb_no_resize ? "no" : "yes");
|
||||
|
||||
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||||
gtk_window_set_keep_above (GTK_WINDOW (window), TRUE);
|
||||
gtk_window_set_gravity (GTK_WINDOW (window), GDK_GRAVITY_CENTER);
|
||||
gtk_widget_set_app_paintable (window, TRUE);
|
||||
|
||||
g_signal_connect (window, "draw",
|
||||
G_CALLBACK (on_window_draw), NULL);
|
||||
g_signal_connect (window, "destroy",
|
||||
G_CALLBACK (gtk_main_quit), NULL);
|
||||
|
||||
g_signal_connect (window, "map-event",
|
||||
G_CALLBACK (on_map_event), NULL);
|
||||
on_frame (0.);
|
||||
|
||||
screen = gtk_widget_get_screen (window);
|
||||
gdk_screen_get_monitor_geometry (screen,
|
||||
gdk_screen_get_primary_monitor (screen),
|
||||
&monitor_bounds);
|
||||
|
||||
gtk_window_move (GTK_WINDOW (window),
|
||||
monitor_bounds.x + (monitor_bounds.width - window_width) / 2,
|
||||
monitor_bounds.y + (monitor_bounds.height - window_height) / 2);
|
||||
|
||||
gtk_widget_show (window);
|
||||
|
||||
gtk_main ();
|
||||
|
||||
return 0;
|
||||
}
|
72
tests/motion-compression.c
Normal file
72
tests/motion-compression.c
Normal file
@@ -0,0 +1,72 @@
|
||||
#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;
|
||||
}
|
43
tests/variable.c
Normal file
43
tests/variable.c
Normal file
@@ -0,0 +1,43 @@
|
||||
/* -*- mode: C; c-basic-offset: 2; indent-tabs-mode: nil; -*- */
|
||||
|
||||
#include <math.h>
|
||||
#include "variable.h"
|
||||
|
||||
void
|
||||
variable_add_weighted (Variable *variable,
|
||||
double value,
|
||||
double weight)
|
||||
{
|
||||
variable->weight += weight;
|
||||
variable->sum += weight * value;
|
||||
variable->sum2 += weight * value * value;
|
||||
}
|
||||
|
||||
void
|
||||
variable_add (Variable *variable,
|
||||
double value)
|
||||
{
|
||||
variable_add_weighted (variable, value, 1.);
|
||||
}
|
||||
|
||||
double
|
||||
variable_mean (Variable *variable)
|
||||
{
|
||||
return variable->sum / variable->weight;
|
||||
}
|
||||
|
||||
double
|
||||
variable_standard_deviation (Variable *variable)
|
||||
{
|
||||
double mean = variable_mean (variable);
|
||||
return sqrt (variable->sum2 / variable->weight - mean * mean);
|
||||
}
|
||||
|
||||
void
|
||||
variable_reset (Variable *variable)
|
||||
{
|
||||
variable->weight = 0;
|
||||
variable->sum = 0;
|
||||
variable->sum2 = 0;
|
||||
}
|
||||
|
23
tests/variable.h
Normal file
23
tests/variable.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#ifndef __VARIABLE_H__
|
||||
#define __VARAIBLE_H__
|
||||
|
||||
typedef struct
|
||||
{
|
||||
double weight;
|
||||
double sum;
|
||||
double sum2;
|
||||
} Variable;
|
||||
|
||||
#define VARIABLE_INIT { 0, 0.0, 0.0 }
|
||||
|
||||
void variable_add_weighted (Variable *variable,
|
||||
double value,
|
||||
double weight);
|
||||
void variable_add (Variable *variable,
|
||||
double value);
|
||||
double variable_mean (Variable *variable);
|
||||
double variable_standard_deviation (Variable *variable);
|
||||
void variable_reset (Variable *variable);
|
||||
|
||||
#endif /* __VARAIBLE_H__ */
|
||||
|
401
tests/video-timer.c
Normal file
401
tests/video-timer.c
Normal file
@@ -0,0 +1,401 @@
|
||||
#include <math.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "variable.h"
|
||||
|
||||
typedef struct {
|
||||
gdouble angle;
|
||||
gint64 stream_time;
|
||||
gint64 clock_time;
|
||||
gint64 frame_counter;
|
||||
} FrameData;
|
||||
|
||||
static FrameData *displayed_frame;
|
||||
static GtkWidget *window;
|
||||
static GList *past_frames;
|
||||
static Variable latency_error = VARIABLE_INIT;
|
||||
static Variable time_factor_stats = VARIABLE_INIT;
|
||||
static int dropped_frames = 0;
|
||||
static int n_frames = 0;
|
||||
|
||||
static gboolean pll;
|
||||
static int fps = 24;
|
||||
|
||||
/* Thread-safe frame queue */
|
||||
|
||||
#define MAX_QUEUE_LENGTH 5
|
||||
|
||||
static GQueue *frame_queue;
|
||||
static GMutex frame_mutex;
|
||||
static GCond frame_cond;
|
||||
|
||||
static void
|
||||
queue_frame (FrameData *frame_data)
|
||||
{
|
||||
g_mutex_lock (&frame_mutex);
|
||||
|
||||
while (frame_queue->length == MAX_QUEUE_LENGTH)
|
||||
g_cond_wait (&frame_cond, &frame_mutex);
|
||||
|
||||
g_queue_push_tail (frame_queue, frame_data);
|
||||
|
||||
g_mutex_unlock (&frame_mutex);
|
||||
}
|
||||
|
||||
static FrameData *
|
||||
unqueue_frame (void)
|
||||
{
|
||||
FrameData *frame_data;
|
||||
|
||||
g_mutex_lock (&frame_mutex);
|
||||
|
||||
if (frame_queue->length > 0)
|
||||
{
|
||||
frame_data = g_queue_pop_head (frame_queue);
|
||||
g_cond_signal (&frame_cond);
|
||||
}
|
||||
else
|
||||
{
|
||||
frame_data = NULL;
|
||||
}
|
||||
|
||||
g_mutex_unlock (&frame_mutex);
|
||||
|
||||
return frame_data;
|
||||
}
|
||||
|
||||
static FrameData *
|
||||
peek_pending_frame (void)
|
||||
{
|
||||
FrameData *frame_data;
|
||||
|
||||
g_mutex_lock (&frame_mutex);
|
||||
|
||||
if (frame_queue->head)
|
||||
frame_data = frame_queue->head->data;
|
||||
else
|
||||
frame_data = NULL;
|
||||
|
||||
g_mutex_unlock (&frame_mutex);
|
||||
|
||||
return frame_data;
|
||||
}
|
||||
|
||||
static FrameData *
|
||||
peek_next_frame (void)
|
||||
{
|
||||
FrameData *frame_data;
|
||||
|
||||
g_mutex_lock (&frame_mutex);
|
||||
|
||||
if (frame_queue->head && frame_queue->head->next)
|
||||
frame_data = frame_queue->head->next->data;
|
||||
else
|
||||
frame_data = NULL;
|
||||
|
||||
g_mutex_unlock (&frame_mutex);
|
||||
|
||||
return frame_data;
|
||||
}
|
||||
|
||||
/* Frame producer thread */
|
||||
|
||||
static gpointer
|
||||
create_frames_thread (gpointer data)
|
||||
{
|
||||
int frame_count = 0;
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
FrameData *frame_data = g_slice_new0 (FrameData);
|
||||
frame_data->angle = 2 * M_PI * (frame_count % fps) / (double)fps;
|
||||
frame_data->stream_time = (G_GINT64_CONSTANT (1000000) * frame_count) / fps;
|
||||
|
||||
queue_frame (frame_data);
|
||||
frame_count++;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Clock management:
|
||||
*
|
||||
* The logic here, which is activated by the --pll argument
|
||||
* demonstrates adjusting the playback rate so that the frames exactly match
|
||||
* when they are displayed both frequency and phase. If there was an
|
||||
* accompanying audio track, you would need to resample the audio to match
|
||||
* the clock.
|
||||
*
|
||||
* The algorithm isn't exactly a PLL - I wrote it first that way, but
|
||||
* it oscillicated before coming into sync and this approach was easier than
|
||||
* fine-tuning the PLL filter.
|
||||
*
|
||||
* A more complicated algorithm could also establish sync when the playback
|
||||
* rate isn't exactly an integral divisor of the VBlank rate, such as 24fps
|
||||
* video on a 60fps display.
|
||||
*/
|
||||
#define PRE_BUFFER_TIME 500000
|
||||
|
||||
static gint64 stream_time_base;
|
||||
static gint64 clock_time_base;
|
||||
static double time_factor = 1.0;
|
||||
static double frequency_time_factor = 1.0;
|
||||
static double phase_time_factor = 1.0;
|
||||
|
||||
static gint64
|
||||
stream_time_to_clock_time (gint64 stream_time)
|
||||
{
|
||||
return clock_time_base + (stream_time - stream_time_base) * time_factor;
|
||||
}
|
||||
|
||||
static void
|
||||
adjust_clock_for_phase (gint64 frame_clock_time,
|
||||
gint64 presentation_time)
|
||||
{
|
||||
static gint count = 0;
|
||||
static gint64 previous_frame_clock_time;
|
||||
static gint64 previous_presentation_time;
|
||||
gint64 phase = presentation_time - frame_clock_time;
|
||||
|
||||
count++;
|
||||
if (count >= fps) /* Give a second of warmup */
|
||||
{
|
||||
gint64 time_delta = frame_clock_time - previous_frame_clock_time;
|
||||
gint64 previous_phase = previous_presentation_time - previous_frame_clock_time;
|
||||
|
||||
double expected_phase_delta;
|
||||
|
||||
stream_time_base += (frame_clock_time - clock_time_base) / time_factor;
|
||||
clock_time_base = frame_clock_time;
|
||||
|
||||
expected_phase_delta = time_delta * (1 - phase_time_factor);
|
||||
|
||||
/* If the phase is increasing that means the computed clock times are
|
||||
* increasing too slowly. We increase the frequency time factor to compensate,
|
||||
* but decrease the compensation so that it takes effect over 1 second to
|
||||
* avoid jitter */
|
||||
frequency_time_factor += (phase - previous_phase - expected_phase_delta) / (double)time_delta / fps;
|
||||
|
||||
/* We also want to increase or decrease the frequency to bring the phase
|
||||
* into sync. We do that again so that the phase should sync up over 1 seconds
|
||||
*/
|
||||
phase_time_factor = 1 + phase / 2000000.;
|
||||
|
||||
time_factor = frequency_time_factor * phase_time_factor;
|
||||
}
|
||||
|
||||
previous_frame_clock_time = frame_clock_time;
|
||||
previous_presentation_time = presentation_time;
|
||||
}
|
||||
|
||||
/* Drawing */
|
||||
|
||||
static void
|
||||
on_window_draw (GtkWidget *widget,
|
||||
cairo_t *cr)
|
||||
{
|
||||
GdkRectangle allocation;
|
||||
double cx, cy, r;
|
||||
|
||||
cairo_set_source_rgb (cr, 1., 1., 1.);
|
||||
cairo_paint (cr);
|
||||
|
||||
cairo_set_source_rgb (cr, 0., 0., 0.);
|
||||
gtk_widget_get_allocation (widget, &allocation);
|
||||
|
||||
cx = allocation.width / 2.;
|
||||
cy = allocation.height / 2.;
|
||||
r = MIN (allocation.width, allocation.height) / 2.;
|
||||
|
||||
cairo_arc (cr, cx, cy, r,
|
||||
0, 2 * M_PI);
|
||||
cairo_stroke (cr);
|
||||
if (displayed_frame)
|
||||
{
|
||||
cairo_move_to (cr, cx, cy);
|
||||
cairo_line_to (cr,
|
||||
cx + r * cos(displayed_frame->angle - M_PI / 2),
|
||||
cy + r * sin(displayed_frame->angle - M_PI / 2));
|
||||
cairo_stroke (cr);
|
||||
|
||||
if (displayed_frame->frame_counter == 0)
|
||||
{
|
||||
GdkFrameClock *frame_clock = gtk_widget_get_frame_clock (window);
|
||||
displayed_frame->frame_counter = gdk_frame_clock_get_frame_counter (frame_clock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
collect_old_frames (void)
|
||||
{
|
||||
GdkFrameClock *frame_clock = gtk_widget_get_frame_clock (window);
|
||||
GList *l, *l_next;
|
||||
|
||||
for (l = past_frames; l; l = l_next)
|
||||
{
|
||||
FrameData *frame_data = l->data;
|
||||
gboolean remove = FALSE;
|
||||
l_next = l->next;
|
||||
|
||||
GdkFrameTimings *timings = gdk_frame_clock_get_timings (frame_clock,
|
||||
frame_data->frame_counter);
|
||||
if (timings == NULL)
|
||||
{
|
||||
remove = TRUE;
|
||||
}
|
||||
else if (gdk_frame_timings_get_complete (timings))
|
||||
{
|
||||
gint64 presentation_time = gdk_frame_timings_get_predicted_presentation_time (timings);
|
||||
gint64 refresh_interval = gdk_frame_timings_get_refresh_interval (timings);
|
||||
|
||||
if (pll &&
|
||||
presentation_time && refresh_interval &&
|
||||
presentation_time > frame_data->clock_time - refresh_interval / 2 &&
|
||||
presentation_time < frame_data->clock_time + refresh_interval / 2)
|
||||
adjust_clock_for_phase (frame_data->clock_time, presentation_time);
|
||||
|
||||
if (presentation_time)
|
||||
variable_add (&latency_error,
|
||||
presentation_time - frame_data->clock_time);
|
||||
|
||||
remove = TRUE;
|
||||
}
|
||||
|
||||
if (remove)
|
||||
{
|
||||
past_frames = g_list_delete_link (past_frames, l);
|
||||
g_slice_free (FrameData, frame_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
print_statistics (void)
|
||||
{
|
||||
gint64 now = g_get_monotonic_time ();
|
||||
static gint64 last_print_time = 0;
|
||||
|
||||
if (last_print_time == 0)
|
||||
last_print_time = now;
|
||||
else if (now -last_print_time > 5000000)
|
||||
{
|
||||
g_print ("dropped_frames: %d/%d\n",
|
||||
dropped_frames, n_frames);
|
||||
g_print ("collected_frames: %g/%d\n",
|
||||
latency_error.weight, n_frames);
|
||||
g_print ("latency_error: %g +/- %g\n",
|
||||
variable_mean (&latency_error),
|
||||
variable_standard_deviation (&latency_error));
|
||||
if (pll)
|
||||
g_print ("playback rate adjustment: %g +/- %g %%\n",
|
||||
(variable_mean (&time_factor_stats) - 1) * 100,
|
||||
variable_standard_deviation (&time_factor_stats) * 100);
|
||||
variable_reset (&latency_error);
|
||||
variable_reset (&time_factor_stats);
|
||||
dropped_frames = 0;
|
||||
n_frames = 0;
|
||||
last_print_time = now;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_update (GdkFrameClock *frame_clock,
|
||||
gpointer data)
|
||||
{
|
||||
GdkFrameTimings *timings = gdk_frame_clock_get_current_timings (frame_clock);
|
||||
gint64 frame_time = gdk_frame_timings_get_frame_time (timings);
|
||||
gint64 predicted_presentation_time = gdk_frame_timings_get_predicted_presentation_time (timings);
|
||||
gint64 refresh_interval;
|
||||
FrameData *pending_frame;
|
||||
|
||||
if (clock_time_base == 0)
|
||||
clock_time_base = frame_time + PRE_BUFFER_TIME;
|
||||
|
||||
gdk_frame_clock_get_refresh_info (frame_clock, frame_time,
|
||||
&refresh_interval, NULL);
|
||||
|
||||
pending_frame = peek_pending_frame ();
|
||||
if (stream_time_to_clock_time (pending_frame->stream_time)
|
||||
< predicted_presentation_time + refresh_interval / 2)
|
||||
{
|
||||
while (TRUE)
|
||||
{
|
||||
FrameData *next_frame = peek_next_frame ();
|
||||
if (next_frame &&
|
||||
stream_time_to_clock_time (next_frame->stream_time)
|
||||
< predicted_presentation_time + refresh_interval / 2)
|
||||
{
|
||||
g_slice_free (FrameData, unqueue_frame ());
|
||||
n_frames++;
|
||||
dropped_frames++;
|
||||
pending_frame = next_frame;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (displayed_frame)
|
||||
past_frames = g_list_prepend (past_frames, displayed_frame);
|
||||
|
||||
n_frames++;
|
||||
displayed_frame = unqueue_frame ();
|
||||
displayed_frame->clock_time = stream_time_to_clock_time (displayed_frame->stream_time);
|
||||
|
||||
displayed_frame->frame_counter = gdk_frame_timings_get_frame_counter (timings);
|
||||
variable_add (&time_factor_stats, time_factor);
|
||||
|
||||
collect_old_frames ();
|
||||
print_statistics ();
|
||||
|
||||
gtk_widget_queue_draw (window);
|
||||
}
|
||||
|
||||
gdk_frame_clock_request_phase (frame_clock, GDK_FRAME_CLOCK_PHASE_UPDATE);
|
||||
}
|
||||
|
||||
static GOptionEntry options[] = {
|
||||
{ "pll", 'p', 0, G_OPTION_ARG_NONE, &pll, "Sync frame rate to refresh", NULL },
|
||||
{ "fps", 'f', 0, G_OPTION_ARG_INT, &fps, "Frame rate", "FPS" },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
GError *error = NULL;
|
||||
GdkFrameClock *frame_clock;
|
||||
|
||||
if (!gtk_init_with_args (&argc, &argv, "",
|
||||
options, NULL, &error))
|
||||
{
|
||||
g_printerr ("Option parsing failed: %s\n", error->message);
|
||||
return 1;
|
||||
}
|
||||
|
||||
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||||
gtk_widget_set_app_paintable (window, TRUE);
|
||||
gtk_window_set_default_size (GTK_WINDOW (window), 300, 300);
|
||||
|
||||
g_signal_connect (window, "draw",
|
||||
G_CALLBACK (on_window_draw), NULL);
|
||||
g_signal_connect (window, "destroy",
|
||||
G_CALLBACK (gtk_main_quit), NULL);
|
||||
|
||||
gtk_widget_show (window);
|
||||
|
||||
frame_queue = g_queue_new ();
|
||||
g_mutex_init (&frame_mutex);
|
||||
g_cond_init (&frame_cond);
|
||||
|
||||
g_thread_new ("Create Frames", create_frames_thread, NULL);
|
||||
|
||||
frame_clock = gtk_widget_get_frame_clock (window);
|
||||
g_signal_connect (frame_clock, "update",
|
||||
G_CALLBACK (on_update), NULL);
|
||||
gdk_frame_clock_request_phase (frame_clock, GDK_FRAME_CLOCK_PHASE_UPDATE);
|
||||
|
||||
gtk_main ();
|
||||
|
||||
return 0;
|
||||
}
|
Reference in New Issue
Block a user