Skip to content

Instantly share code, notes, and snippets.

@chergert
Last active September 3, 2015 09:53
Show Gist options
  • Save chergert/9a374f1265ac45d00a44 to your computer and use it in GitHub Desktop.
Save chergert/9a374f1265ac45d00a44 to your computer and use it in GitHub Desktop.
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