Last active
September 3, 2015 09:53
-
-
Save chergert/9a374f1265ac45d00a44 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/gdk/quartz/gdkdisplay-quartz.c b/gdk/quartz/gdkdisplay-quartz.c | |
index 6b4afc8..161f948 100644 | |
--- a/gdk/quartz/gdkdisplay-quartz.c | |
+++ b/gdk/quartz/gdkdisplay-quartz.c | |
@@ -18,8 +18,12 @@ | |
#include "config.h" | |
+#include <mach/mach_time.h> | |
+#include <QuartzCore/QuartzCore.h> | |
+ | |
#include <gdk/gdk.h> | |
#include <gdk/gdkdisplayprivate.h> | |
+#include <gdk/gdkframeclockprivate.h> | |
#include "gdkprivate-quartz.h" | |
#include "gdkquartzscreen.h" | |
@@ -33,6 +37,9 @@ struct _GdkQuartzDisplay | |
GdkDisplay display; | |
GList *input_devices; | |
+ | |
+ CVDisplayLinkRef display_link; | |
+ GArray *frame_handlers; | |
}; | |
struct _GdkQuartzDisplayClass | |
@@ -40,6 +47,12 @@ struct _GdkQuartzDisplayClass | |
GdkDisplayClass display_class; | |
}; | |
+typedef struct | |
+{ | |
+ GdkQuartzFrameCallback callback; | |
+ GdkWindow *window; | |
+} FrameHandler; | |
+ | |
static GdkWindow * | |
gdk_quartz_display_get_default_group (GdkDisplay *display) | |
{ | |
@@ -108,6 +121,193 @@ gdk_quartz_display_init_input (GdkDisplay *display) | |
g_list_free (list); | |
} | |
+static gint64 | |
+host_to_frame_clock_time (gint64 host_time) | |
+{ | |
+ static mach_timebase_info_data_t timebase_info; | |
+ | |
+ /* | |
+ * NOTE: | |
+ * | |
+ * This code is taken from GLib to match g_get_monotonic_time(). | |
+ */ | |
+ if (timebase_info.denom == 0) | |
+ { | |
+ /* This is a fraction that we must use to scale | |
+ * mach_absolute_time() by in order to reach nanoseconds. | |
+ * | |
+ * We've only ever observed this to be 1/1, but maybe it could be | |
+ * 1000/1 if mach time is microseconds already, or 1/1000 if | |
+ * picoseconds. Try to deal nicely with that. | |
+ */ | |
+ mach_timebase_info (&timebase_info); | |
+ | |
+ /* We actually want microseconds... */ | |
+ if (timebase_info.numer % 1000 == 0) | |
+ timebase_info.numer /= 1000; | |
+ else | |
+ timebase_info.denom *= 1000; | |
+ | |
+ /* We want to make the numer 1 to avoid having to multiply... */ | |
+ if (timebase_info.denom % timebase_info.numer == 0) | |
+ { | |
+ timebase_info.denom /= timebase_info.numer; | |
+ timebase_info.numer = 1; | |
+ } | |
+ else | |
+ { | |
+ /* We could just multiply by timebase_info.numer below, but why | |
+ * bother for a case that may never actually exist... | |
+ * | |
+ * Plus -- performing the multiplication would risk integer | |
+ * overflow. If we ever actually end up in this situation, we | |
+ * should more carefully evaluate the correct course of action. | |
+ */ | |
+ mach_timebase_info (&timebase_info); /* Get a fresh copy for a better message */ | |
+ g_error ("Got weird mach timebase info of %d/%d. Please file a bug against GLib.", | |
+ timebase_info.numer, timebase_info.denom); | |
+ } | |
+ } | |
+ | |
+ return host_time / timebase_info.denom; | |
+} | |
+ | |
+void | |
+_gdk_quartz_display_add_frame_callback (GdkDisplay *display, | |
+ GdkQuartzFrameCallback callback, | |
+ GdkWindow *window) | |
+{ | |
+ GdkQuartzDisplay *display_quartz; | |
+ FrameHandler handler; | |
+ | |
+ display_quartz = GDK_QUARTZ_DISPLAY (display); | |
+ | |
+ handler.callback = callback; | |
+ handler.window = window; | |
+ | |
+ if (display_quartz->frame_handlers == NULL) | |
+ display_quartz->frame_handlers = g_array_new (FALSE, FALSE, sizeof (FrameHandler)); | |
+ | |
+ g_array_append_val (display_quartz->frame_handlers, handler); | |
+ | |
+ if (display_quartz->frame_handlers->len == 1) | |
+ CVDisplayLinkStart (display_quartz->display_link); | |
+} | |
+ | |
+void | |
+_gdk_quartz_display_remove_frame_callback (GdkDisplay *display, | |
+ GdkQuartzFrameCallback callback, | |
+ GdkWindow *window) | |
+{ | |
+ GdkQuartzDisplay *display_quartz; | |
+ gint i; | |
+ | |
+ display_quartz = GDK_QUARTZ_DISPLAY (display); | |
+ | |
+ for (i = 0; i < display_quartz->frame_handlers->len; i++) | |
+ { | |
+ FrameHandler *handler = &g_array_index (display_quartz->frame_handlers, FrameHandler, i); | |
+ | |
+ if ((handler->window == window) && (handler->callback == callback)) | |
+ { | |
+ g_array_remove_index_fast (display_quartz->frame_handlers, i); | |
+ break; | |
+ } | |
+ } | |
+ | |
+ if (display_quartz->frame_handlers->len == 0) | |
+ CVDisplayLinkStart (display_quartz->display_link); | |
+} | |
+ | |
+struct frame_data { | |
+ GdkDisplay *display; | |
+ gint64 now; | |
+ gint64 output_time; | |
+ gint64 refresh_interval; | |
+}; | |
+ | |
+static gboolean | |
+process_frame_data (gpointer data) | |
+{ | |
+ struct frame_data *info = data; | |
+ GdkQuartzDisplay *display_quartz; | |
+ GArray *frame_handlers; | |
+ guint i; | |
+ | |
+ g_assert (info != NULL); | |
+ | |
+ display_quartz = GDK_QUARTZ_DISPLAY (info->display); | |
+ | |
+ frame_handlers = display_quartz->frame_handlers; | |
+ display_quartz->frame_handlers = NULL; | |
+ | |
+ if (frame_handlers != NULL) | |
+ { | |
+ for (i = 0; i < frame_handlers->len; i++) | |
+ { | |
+ FrameHandler *handler = &g_array_index (frame_handlers, FrameHandler, i); | |
+ | |
+ handler->callback (info->display, | |
+ handler->window, | |
+ info->refresh_interval, | |
+ info->now, | |
+ info->output_time); | |
+ } | |
+ | |
+ g_array_unref (frame_handlers); | |
+ } | |
+ | |
+ return G_SOURCE_REMOVE; | |
+} | |
+ | |
+static CVReturn | |
+gdk_quartz_display_frame_cb (CVDisplayLinkRef display_link, | |
+ const CVTimeStamp *inNow, | |
+ const CVTimeStamp *inOutputTime, | |
+ CVOptionFlags flagsIn, | |
+ CVOptionFlags *flagsOut, | |
+ void *user_data) | |
+{ | |
+ struct frame_data *info; | |
+ double period; | |
+ | |
+ period = CVDisplayLinkGetActualOutputVideoRefreshPeriod (display_link); | |
+ if (period == 0.0) | |
+ period = 1.0 / 60.0; | |
+ | |
+ info = g_slice_new0 (struct frame_data); | |
+ info->now = host_to_frame_clock_time (inNow->hostTime); | |
+ info->output_time = host_to_frame_clock_time (inOutputTime->hostTime); | |
+ info->display = user_data; | |
+ info->refresh_interval = period * 1000000L; | |
+ | |
+ /* | |
+ * FIXME: This is *wacko!* | |
+ * | |
+ * We probably want a source that is easy and cheap for us to trigger the | |
+ * gtk main loop. This callback is always executed from a high-priority | |
+ * thread. | |
+ */ | |
+ gdk_threads_add_idle (process_frame_data, info); | |
+ | |
+ return kCVReturnSuccess; | |
+} | |
+ | |
+static void | |
+gdk_quartz_display_init_display_link (GdkDisplay *display) | |
+{ | |
+ GdkQuartzDisplay *display_quartz = GDK_QUARTZ_DISPLAY (display); | |
+ CVReturn ret; | |
+ | |
+ ret = CVDisplayLinkCreateWithActiveCGDisplays (&display_quartz->display_link); | |
+ if (ret != kCVReturnSuccess) | |
+ return; | |
+ | |
+ CVDisplayLinkSetOutputCallback (display_quartz->display_link, | |
+ gdk_quartz_display_frame_cb, | |
+ display_quartz); | |
+} | |
+ | |
GdkDisplay * | |
_gdk_quartz_display_open (const gchar *display_name) | |
{ | |
@@ -129,6 +329,8 @@ _gdk_quartz_display_open (const gchar *display_name) | |
gdk_quartz_display_init_input (_gdk_display); | |
+ gdk_quartz_display_init_display_link (_gdk_display); | |
+ | |
#if 0 | |
/* FIXME: Remove the #if 0 when we have these functions */ | |
_gdk_quartz_dnd_init (); | |
@@ -284,6 +486,9 @@ gdk_quartz_display_finalize (GObject *object) | |
g_list_free_full (display_quartz->input_devices, g_object_unref); | |
+ CVDisplayLinkRelease (display_quartz->display_link); | |
+ g_clear_pointer (&display_quartz->frame_handlers, g_array_unref); | |
+ | |
G_OBJECT_CLASS (gdk_quartz_display_parent_class)->finalize (object); | |
} | |
diff --git a/gdk/quartz/gdkprivate-quartz.h b/gdk/quartz/gdkprivate-quartz.h | |
index 5d2fcc1..67a392a 100644 | |
--- a/gdk/quartz/gdkprivate-quartz.h | |
+++ b/gdk/quartz/gdkprivate-quartz.h | |
@@ -125,6 +125,19 @@ void _gdk_quartz_display_create_window_impl (GdkDisplay *display, | |
GdkWindowAttr *attributes, | |
gint attributes_mask); | |
+/* Display methods - frame clock */ | |
+typedef void (*GdkQuartzFrameCallback) (GdkDisplay *display, | |
+ GdkWindow *window, | |
+ gint64 refresh_interval, | |
+ gint64 now, | |
+ gint64 presentation_time); | |
+void _gdk_quartz_display_add_frame_callback (GdkDisplay *display, | |
+ GdkQuartzFrameCallback callback, | |
+ GdkWindow *window); | |
+void _gdk_quartz_display_remove_frame_callback (GdkDisplay *display, | |
+ GdkQuartzFrameCallback callback, | |
+ GdkWindow *window); | |
+ | |
/* Display methods - keymap */ | |
GdkKeymap * _gdk_quartz_display_get_keymap (GdkDisplay *display); | |
diff --git a/gdk/quartz/gdkwindow-quartz.c b/gdk/quartz/gdkwindow-quartz.c | |
index 62710a2..4b97f9b 100644 | |
--- a/gdk/quartz/gdkwindow-quartz.c | |
+++ b/gdk/quartz/gdkwindow-quartz.c | |
@@ -22,6 +22,7 @@ | |
#include <gdk/gdk.h> | |
#include <gdk/gdkdeviceprivate.h> | |
#include <gdk/gdkdisplayprivate.h> | |
+#include <gdk/gdkframeclockprivate.h> | |
#include "gdkwindowimpl.h" | |
#include "gdkprivate-quartz.h" | |
@@ -776,6 +777,52 @@ get_nsscreen_for_point (gint x, gint y) | |
return screen; | |
} | |
+static void | |
+frame_callback (GdkDisplay *display, | |
+ GdkWindow *window, | |
+ gint64 refresh_interval, | |
+ gint64 now, | |
+ gint64 presentation_time) | |
+{ | |
+ GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (window->impl); | |
+ GdkFrameClock *frame_clock; | |
+ GdkFrameTimings *timings; | |
+ | |
+ frame_clock = gdk_window_get_frame_clock (window); | |
+ if (frame_clock == NULL) | |
+ return; | |
+ | |
+ _gdk_frame_clock_thaw (frame_clock); | |
+ | |
+ timings = gdk_frame_clock_get_timings (frame_clock, impl->pending_frame_counter); | |
+ impl->pending_frame_counter = 0; | |
+ | |
+ if (timings == NULL) | |
+ return; | |
+ | |
+ timings->refresh_interval = refresh_interval; | |
+ timings->presentation_time = presentation_time - refresh_interval; | |
+} | |
+ | |
+static void | |
+on_frame_clock_before_paint (GdkFrameClock *frame_clock, | |
+ GdkWindow *window) | |
+{ | |
+} | |
+ | |
+static void | |
+on_frame_clock_after_paint (GdkFrameClock *frame_clock, | |
+ GdkWindow *window) | |
+{ | |
+ GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (window->impl); | |
+ GdkDisplay *display = gdk_window_get_display (window); | |
+ | |
+ impl->pending_frame_counter = gdk_frame_clock_get_frame_counter (frame_clock); | |
+ | |
+ _gdk_quartz_display_add_frame_callback (display, frame_callback, window); | |
+ _gdk_frame_clock_freeze (frame_clock); | |
+} | |
+ | |
void | |
_gdk_quartz_display_create_window_impl (GdkDisplay *display, | |
GdkWindow *window, | |
@@ -787,6 +834,7 @@ _gdk_quartz_display_create_window_impl (GdkDisplay *display, | |
{ | |
GdkWindowImplQuartz *impl; | |
GdkWindowImplQuartz *parent_impl; | |
+ GdkFrameClock *frame_clock; | |
GDK_QUARTZ_ALLOC_POOL; | |
@@ -921,6 +969,13 @@ _gdk_quartz_display_create_window_impl (GdkDisplay *display, | |
if (attributes_mask & GDK_WA_TYPE_HINT) | |
gdk_window_set_type_hint (window, attributes->type_hint); | |
+ | |
+ 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); | |
} | |
void | |
diff --git a/gdk/quartz/gdkwindow-quartz.h b/gdk/quartz/gdkwindow-quartz.h | |
index 4c8347c..4d603bd 100644 | |
--- a/gdk/quartz/gdkwindow-quartz.h | |
+++ b/gdk/quartz/gdkwindow-quartz.h | |
@@ -64,6 +64,8 @@ struct _GdkWindowImplQuartz | |
gint shadow_top; | |
gint shadow_max; | |
+ | |
+ gint pending_frame_counter; | |
}; | |
struct _GdkWindowImplQuartzClass |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment