[gtk+/wip/chergert/quartz-frame-clock] WIP on various quartz performance



commit f8ccfe1486f53441a2bce96f4cac386ceba51747
Author: Christian Hergert <christian hergert me>
Date:   Fri Sep 4 14:06:55 2015 -0700

    WIP on various quartz performance

 gdk/gdkwindow.c                       |   35 ++---
 gdk/quartz/GdkQuartzNSWindow.c        |    6 +-
 gdk/quartz/GdkQuartzView.c            |   37 +++--
 gdk/quartz/Makefile.am                |    2 +
 gdk/quartz/gdkdisplay-quartz.c        |  117 +++++++++++++++
 gdk/quartz/gdkdisplaylinksource.c     |  261 +++++++++++++++++++++++++++++++++
 gdk/quartz/gdkdisplaylinksource.h     |   48 ++++++
 gdk/quartz/gdkdisplaymanager-quartz.c |    1 -
 gdk/quartz/gdkevents-quartz.c         |    4 +-
 gdk/quartz/gdkprivate-quartz.h        |   13 ++
 gdk/quartz/gdkwindow-quartz.c         |  136 ++++++++++-------
 gdk/quartz/gdkwindow-quartz.h         |    2 +
 gtk/gtkpixelcache.c                   |   18 +++
 gtk/gtktextview.c                     |    6 +
 14 files changed, 591 insertions(+), 95 deletions(-)
---
diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c
index 89ed898..57e2c30 100644
--- a/gdk/gdkwindow.c
+++ b/gdk/gdkwindow.c
@@ -2195,9 +2195,9 @@ gdk_window_get_window_type (GdkWindow *window)
 /**
  * gdk_window_get_visual:
  * @window: a #GdkWindow
- * 
+ *
  * Gets the #GdkVisual describing the pixel format of @window.
- * 
+ *
  * Returns: (transfer none): a #GdkVisual
  *
  * Since: 2.24
@@ -2206,16 +2206,16 @@ GdkVisual*
 gdk_window_get_visual (GdkWindow *window)
 {
   g_return_val_if_fail (GDK_IS_WINDOW (window), NULL);
-  
+
   return window->visual;
 }
 
 /**
  * gdk_window_get_screen:
  * @window: a #GdkWindow
- * 
+ *
  * Gets the #GdkScreen associated with a #GdkWindow.
- * 
+ *
  * Returns: (transfer none): the #GdkScreen associated with @window
  *
  * Since: 2.24
@@ -2231,9 +2231,9 @@ gdk_window_get_screen (GdkWindow *window)
 /**
  * gdk_window_get_display:
  * @window: a #GdkWindow
- * 
+ *
  * Gets the #GdkDisplay associated with a #GdkWindow.
- * 
+ *
  * Returns: (transfer none): the #GdkDisplay associated with @window
  *
  * Since: 2.24
@@ -3198,13 +3198,13 @@ gdk_window_flush (GdkWindow *window)
 /**
  * gdk_window_get_clip_region:
  * @window: a #GdkWindow
- * 
+ *
  * Computes the region of a window that potentially can be written
  * to by drawing primitives. This region may not take into account
  * other factors such as if the window is obscured by other windows,
  * but no area outside of this region will be affected by drawing
  * primitives.
- * 
+ *
  * Returns: a #cairo_region_t. This must be freed with cairo_region_destroy()
  *          when you are done.
  **/
@@ -3226,12 +3226,12 @@ gdk_window_get_clip_region (GdkWindow *window)
 /**
  * gdk_window_get_visible_region:
  * @window: a #GdkWindow
- * 
+ *
  * Computes the region of the @window that is potentially visible.
  * This does not necessarily take into account if the window is
  * obscured by other windows, but no area outside of this region
  * is visible.
- * 
+ *
  * Returns: a #cairo_region_t. This must be freed with cairo_region_destroy()
  *          when you are done.
  **/
@@ -3326,7 +3326,7 @@ _gdk_window_ref_cairo_surface (GdkWindow *window)
 /**
  * gdk_cairo_create:
  * @window: a #GdkWindow
- * 
+ *
  * Creates a Cairo context for drawing to @window.
  *
  * Note that calling cairo_reset_clip() on the resulting #cairo_t will
@@ -3334,7 +3334,7 @@ _gdk_window_ref_cairo_surface (GdkWindow *window)
  *
  * Returns: A newly created Cairo context. Free with
  *  cairo_destroy() when you are done drawing.
- * 
+ *
  * Since: 2.8
  **/
 cairo_t *
@@ -3631,7 +3631,6 @@ _gdk_window_process_updates_recurse_helper (GdkWindow *window,
       g_object_unref (children[i]);
     }
 
-
   g_free (free_children);
 
  out:
@@ -4151,7 +4150,7 @@ gdk_window_invalidate_maybe_recurse_full (GdkWindow            *window,
   if (gdk_display_get_debug_updates (display))
     draw_ugly_color (window, visible_region, 0);
 
-  while (window != NULL && 
+  while (window != NULL &&
         !cairo_region_is_empty (visible_region))
     {
       if (window->invalidate_handler)
@@ -6471,7 +6470,7 @@ gdk_window_get_root_coords (GdkWindow *window,
       *root_y = 0;
       return;
     }
-  
+
   impl_class = GDK_WINDOW_IMPL_GET_CLASS (window->impl);
   impl_class->get_root_coords (window->impl_window,
                               x + window->abs_x,
@@ -7522,7 +7521,7 @@ gdk_window_beep (GdkWindow *window)
       if (GDK_WINDOW_IMPL_GET_CLASS (toplevel->impl)->beep (toplevel))
         return;
     }
-  
+
   /* If windows fail to beep, we beep the display. */
   gdk_display_beep (display);
 }
@@ -10876,7 +10875,7 @@ gdk_window_configure_finished (GdkWindow *window)
  *
  * Set @window to render as partially transparent,
  * with opacity 0 being fully transparent and 1 fully opaque. (Values
- * of the opacity parameter are clamped to the [0,1] range.) 
+ * of the opacity parameter are clamped to the [0,1] range.)
  *
  * For toplevel windows this depends on support from the windowing system
  * that may not always be there. For instance, On X11, this works only on
diff --git a/gdk/quartz/GdkQuartzNSWindow.c b/gdk/quartz/GdkQuartzNSWindow.c
index f757d29..3927d9d 100644
--- a/gdk/quartz/GdkQuartzNSWindow.c
+++ b/gdk/quartz/GdkQuartzNSWindow.c
@@ -178,7 +178,6 @@
   GdkWindow *window = [[self contentView] gdkWindow];
   GdkEvent *event;
 
-  GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (window->impl);
   gboolean maximized = gdk_window_get_state (window) & GDK_WINDOW_STATE_MAXIMIZED;
 
   /* In case the window is changed when maximized remove the maximized state */
@@ -209,7 +208,6 @@
   NSRect content_rect = [self contentRectForFrameRect:[self frame]];
   GdkWindow *window = [[self contentView] gdkWindow];
   GdkEvent *event;
-  GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (window->impl);
   gboolean maximized = gdk_window_get_state (window) & GDK_WINDOW_STATE_MAXIMIZED;
 
   /* see same in windowDidMove */
@@ -254,7 +252,7 @@
                              screen:screen];
 
   [self setAcceptsMouseMovedEvents:YES];
-  [self setDelegate:self];
+  [self setDelegate:(id)self];
   [self setReleasedWhenClosed:YES];
 
   return self;
@@ -781,7 +779,6 @@ update_context_from_dragging_info (id <NSDraggingInfo> sender)
 {
   NSRect screenFrame = [[self screen] visibleFrame];
   GdkWindow *window = [[self contentView] gdkWindow];
-  GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (window->impl);
   gboolean maximized = gdk_window_get_state (window) & GDK_WINDOW_STATE_MAXIMIZED;
 
   if (!maximized)
@@ -795,7 +792,6 @@ update_context_from_dragging_info (id <NSDraggingInfo> sender)
 {
 
   GdkWindow *window = [[self contentView] gdkWindow];
-  GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (window->impl);
   gboolean maximized = gdk_window_get_state (window) & GDK_WINDOW_STATE_MAXIMIZED;
 
   if (maximized)
diff --git a/gdk/quartz/GdkQuartzView.c b/gdk/quartz/GdkQuartzView.c
index cbd7fe5..534c1da 100644
--- a/gdk/quartz/GdkQuartzView.c
+++ b/gdk/quartz/GdkQuartzView.c
@@ -122,7 +122,6 @@
 -(void)unmarkText
 {
   GDK_NOTE (EVENTS, g_print ("unmarkText\n"));
-  gchar *prev_str;
   markedRange = selectedRange = NSMakeRange (NSNotFound, 0);
 
   g_object_set_data_full (G_OBJECT (gdk_window), TIC_MARKED_TEXT, NULL, g_free);
@@ -132,7 +131,6 @@
 {
   GDK_NOTE (EVENTS, g_print ("setMarkedText\n"));
   const char *str;
-  gchar *prev_str;
 
   if (replacementRange.location == NSNotFound)
     {
@@ -182,7 +180,6 @@
   GDK_NOTE (EVENTS, g_print ("insertText\n"));
   const char *str;
   NSString *string;
-  gchar *prev_str;
 
   if ([self hasMarkedText])
     [self unmarkText];
@@ -588,16 +585,16 @@
   int i;
   cairo_region_t *region;
 
-  if (GDK_WINDOW_DESTROYED (gdk_window))
+  if (G_UNLIKELY (GDK_WINDOW_DESTROYED (gdk_window)))
     return;
 
-  if (! (gdk_window->event_mask & GDK_EXPOSURE_MASK))
+  if (G_UNLIKELY (!(gdk_window->event_mask & GDK_EXPOSURE_MASK)))
     return;
 
-  if (NSEqualRects (rect, NSZeroRect))
+  if (G_UNLIKELY (NSEqualRects (rect, NSZeroRect)))
     return;
 
-  if (!GDK_WINDOW_IS_MAPPED (gdk_window))
+  if (G_UNLIKELY (!GDK_WINDOW_IS_MAPPED (gdk_window)))
     {
       /* If the window is not yet mapped, clip_region_with_children
        * will be empty causing the usual code below to draw nothing.
@@ -624,16 +621,28 @@
     }
 
   [self getRectsBeingDrawn: &drawn_rects count: &count];
-  region = cairo_region_create ();
 
-  for (i = 0; i < count; i++)
+  if (G_LIKELY (count == 1))
     {
-      gdk_rect.x = drawn_rects[i].origin.x;
-      gdk_rect.y = drawn_rects[i].origin.y;
-      gdk_rect.width = drawn_rects[i].size.width;
-      gdk_rect.height = drawn_rects[i].size.height;
+      gdk_rect.x = drawn_rects [0].origin.x;
+      gdk_rect.y = drawn_rects [0].origin.y;
+      gdk_rect.width = drawn_rects [0].size.width;
+      gdk_rect.height = drawn_rects [0].size.height;
+      region = cairo_region_create_rectangle (&gdk_rect);
+    }
+  else
+    {
+      region = cairo_region_create ();
+
+      for (i = 0; i < count; i++)
+        {
+          gdk_rect.x = drawn_rects[i].origin.x;
+          gdk_rect.y = drawn_rects[i].origin.y;
+          gdk_rect.width = drawn_rects[i].size.width;
+          gdk_rect.height = drawn_rects[i].size.height;
 
-      cairo_region_union_rectangle (region, &gdk_rect);
+          cairo_region_union_rectangle (region, &gdk_rect);
+        }
     }
 
   impl->in_paint_rect_count++;
diff --git a/gdk/quartz/Makefile.am b/gdk/quartz/Makefile.am
index 8954849..f7c30e9 100644
--- a/gdk/quartz/Makefile.am
+++ b/gdk/quartz/Makefile.am
@@ -28,6 +28,8 @@ libgdk_quartz_la_SOURCES =            \
        gdkdevicemanager-core-quartz.c  \
        gdkdevicemanager-core-quartz.h  \
        gdkdisplay-quartz.c     \
+       gdkdisplaylinksource.c  \
+       gdkdisplaylinksource.h  \
        gdkdisplaymanager-quartz.c      \
        gdkdnd-quartz.c         \
        gdkdnd-quartz.h         \
diff --git a/gdk/quartz/gdkdisplay-quartz.c b/gdk/quartz/gdkdisplay-quartz.c
index 6b4afc8..82a00cd 100644
--- a/gdk/quartz/gdkdisplay-quartz.c
+++ b/gdk/quartz/gdkdisplay-quartz.c
@@ -20,12 +20,14 @@
 
 #include <gdk/gdk.h>
 #include <gdk/gdkdisplayprivate.h>
+#include <gdk/gdkframeclockprivate.h>
 
 #include "gdkprivate-quartz.h"
 #include "gdkquartzscreen.h"
 #include "gdkquartzwindow.h"
 #include "gdkquartzdisplay.h"
 #include "gdkquartzdevicemanager-core.h"
+#include "gdkdisplaylinksource.h"
 
 
 struct _GdkQuartzDisplay
@@ -33,6 +35,9 @@ struct _GdkQuartzDisplay
   GdkDisplay display;
 
   GList *input_devices;
+
+  GSource *frame_source;
+  GArray  *frame_handlers;
 };
 
 struct _GdkQuartzDisplayClass
@@ -40,6 +45,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 +119,107 @@ gdk_quartz_display_init_input (GdkDisplay *display)
   g_list_free (list);
 }
 
+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)
+    gdk_display_link_source_unpause ((GdkDisplayLinkSource *)display_quartz->frame_source);
+}
+
+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)
+    gdk_display_link_source_pause ((GdkDisplayLinkSource *)display_quartz->frame_source);
+}
+
+static gboolean
+gdk_quartz_display_frame_cb (gpointer data)
+{
+  GdkDisplayLinkSource *source;
+  GdkQuartzDisplay *display_quartz = data;
+  GdkDisplay *display = data;
+  GArray *frame_handlers;
+  gint64 presentation_time;
+  gint64 now;
+  guint i;
+
+  source = (GdkDisplayLinkSource *)display_quartz->frame_source;
+
+  frame_handlers = display_quartz->frame_handlers;
+  display_quartz->frame_handlers = NULL;
+
+  if (frame_handlers == NULL)
+    {
+      gdk_display_link_source_pause (source);
+      return G_SOURCE_CONTINUE;
+    }
+
+  presentation_time = source->presentation_time;
+  now = g_source_get_time (display_quartz->frame_source);
+
+  for (i = 0; i < frame_handlers->len; i++)
+    {
+      FrameHandler *handler = &g_array_index (frame_handlers, FrameHandler, i);
+
+      handler->callback (display,
+                         handler->window,
+                         source->refresh_interval,
+                         now,
+                         source->presentation_time);
+    }
+
+  g_array_unref (frame_handlers);
+
+  return G_SOURCE_CONTINUE;
+}
+
+static void
+gdk_quartz_display_init_display_link (GdkDisplay *display)
+{
+  GdkQuartzDisplay *display_quartz = GDK_QUARTZ_DISPLAY (display);
+
+  display_quartz->frame_source = gdk_display_link_source_new ();
+  g_source_set_callback (display_quartz->frame_source,
+                         gdk_quartz_display_frame_cb,
+                         display,
+                         NULL);
+  g_source_attach (display_quartz->frame_source, NULL);
+}
+
 GdkDisplay *
 _gdk_quartz_display_open (const gchar *display_name)
 {
@@ -129,6 +241,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 +398,9 @@ gdk_quartz_display_finalize (GObject *object)
 
   g_list_free_full (display_quartz->input_devices, g_object_unref);
 
+  g_source_unref (display_quartz->frame_source);
+  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/gdkdisplaylinksource.c b/gdk/quartz/gdkdisplaylinksource.c
new file mode 100644
index 0000000..40e8cc1
--- /dev/null
+++ b/gdk/quartz/gdkdisplaylinksource.c
@@ -0,0 +1,261 @@
+/* gdkdisplaylinksource.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * 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/>.
+ *
+ * Authors:
+ *   Christian Hergert <christian hergert me>
+ */
+
+#include "config.h"
+
+#include <mach/mach_time.h>
+
+#include "gdkprivate-quartz.h"
+#include "gdkdisplaylinksource.h"
+
+static gint64 host_to_frame_clock_time (gint64 host_time);
+
+static gboolean
+gdk_display_link_source_prepare (GSource *source,
+                                 gint    *timeout_)
+{
+  GdkDisplayLinkSource *impl = (GdkDisplayLinkSource *)source;
+  gint64 now;
+
+  now = g_source_get_time (source);
+
+  if (now < impl->presentation_time)
+    *timeout_ = (impl->presentation_time - now) / 1000L;
+  else
+    *timeout_ = -1;
+
+  return impl->needs_dispatch;
+}
+
+static gboolean
+gdk_display_link_source_check (GSource *source)
+{
+  GdkDisplayLinkSource *impl = (GdkDisplayLinkSource *)source;
+  return impl->needs_dispatch;
+}
+
+static gboolean
+gdk_display_link_source_dispatch (GSource     *source,
+                                  GSourceFunc  callback,
+                                  gpointer     user_data)
+{
+  GdkDisplayLinkSource *impl = (GdkDisplayLinkSource *)source;
+  gboolean ret = G_SOURCE_CONTINUE;
+
+  impl->needs_dispatch = FALSE;
+
+  if (callback != NULL)
+    ret = callback (user_data);
+
+  return ret;
+}
+
+static void
+gdk_display_link_source_finalize (GSource *source)
+{
+  GdkDisplayLinkSource *impl = (GdkDisplayLinkSource *)source;
+
+  /*
+   * TODO: ask worker to destroy itself, and then unpause it
+   *       so that it can do so.
+   */
+
+  CVDisplayLinkStop (impl->display_link);
+  CVDisplayLinkRelease (impl->display_link);
+}
+
+static GSourceFuncs gdk_display_link_source_funcs = {
+  gdk_display_link_source_prepare,
+  gdk_display_link_source_check,
+  gdk_display_link_source_dispatch,
+  gdk_display_link_source_finalize
+};
+
+void
+gdk_display_link_source_pause (GdkDisplayLinkSource *source)
+{
+  CVDisplayLinkStop (source->display_link);
+}
+
+void
+gdk_display_link_source_unpause (GdkDisplayLinkSource *source)
+{
+  CVDisplayLinkStart (source->display_link);
+}
+
+static CVReturn
+gdk_display_link_source_frame_cb (CVDisplayLinkRef   display_link,
+                                  const CVTimeStamp *inNow,
+                                  const CVTimeStamp *inOutputTime,
+                                  CVOptionFlags      flagsIn,
+                                  CVOptionFlags     *flagsOut,
+                                  void              *user_data)
+{
+  GdkDisplayLinkSource *impl = user_data;
+  gint64 presentation_time;
+  gboolean needs_wakeup;
+
+  needs_wakeup = !g_atomic_int_get (&impl->needs_dispatch);
+
+  presentation_time = host_to_frame_clock_time (inOutputTime->hostTime);
+
+  impl->presentation_time = presentation_time;
+  impl->needs_dispatch = TRUE;
+
+  if (needs_wakeup)
+     {
+      NSEvent *event;
+
+      /* Post a message so we'll break out of the message loop.
+       *
+       * We don't use g_main_context_wakeup() here because that
+       * would result in sending a message to the pipe(2) fd in
+       * the select thread which would then send this message as
+       * well. Lots of extra work.
+       */
+      event = [NSEvent otherEventWithType: NSApplicationDefined
+                                 location: NSZeroPoint
+                            modifierFlags: 0
+                                timestamp: 0
+                             windowNumber: 0
+                                  context: nil
+                                  subtype: GDK_QUARTZ_EVENT_SUBTYPE_EVENTLOOP
+                                    data1: 0
+                                    data2: 0];
+
+      [NSApp postEvent:event atStart:YES];
+     }
+
+  return kCVReturnSuccess;
+}
+
+/**
+ * gdk_display_link_source_new:
+ *
+ * Creates a new #GSource that will activate the dispatch function upon
+ * notification from a CVDisplayLink that a new frame should be drawn.
+ *
+ * Effort is made to keep the transition from the high-priority
+ * CVDisplayLink thread into this GSource lightweight. However, this is
+ * somewhat non-ideal since the best case would be to do the drawing
+ * from the high-priority thread.
+ *
+ * Returns: (transfer full): A newly created #GSource.
+ */
+GSource *
+gdk_display_link_source_new (void)
+{
+  GdkDisplayLinkSource *impl;
+  GSource *source;
+  CVReturn ret;
+  double period;
+
+  source = g_source_new (&gdk_display_link_source_funcs, sizeof *impl);
+  impl = (GdkDisplayLinkSource *)source;
+
+  /*
+   * Create our link based on currently connected displays.
+   * If there are multiple displays, this will be something that tries
+   * to work for all of them. In the future, we may want to explore multiple
+   * links based on the connected displays.
+   */
+  ret = CVDisplayLinkCreateWithActiveCGDisplays (&impl->display_link);
+  if (ret != kCVReturnSuccess)
+    {
+      g_warning ("Failed to initialize CVDisplayLink!");
+      return source;
+    }
+
+  /*
+   * Determine our nominal period between frames.
+   */
+  period = CVDisplayLinkGetActualOutputVideoRefreshPeriod (impl->display_link);
+  if (period == 0.0)
+    period = 1.0 / 60.0;
+  impl->refresh_interval = period * 1000000L;
+
+
+  /*
+   * Wire up our callback to be executed within the high-priority thread.
+   */
+  CVDisplayLinkSetOutputCallback (impl->display_link,
+                                  gdk_display_link_source_frame_cb,
+                                  source);
+
+  g_source_set_name (source, "[gdk] quartz frame timer");
+
+#if 0
+  CVDisplayLinkStart (impl->display_link);
+#endif
+
+  return source;
+}
+
+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;
+}
diff --git a/gdk/quartz/gdkdisplaylinksource.h b/gdk/quartz/gdkdisplaylinksource.h
new file mode 100644
index 0000000..7493b0c
--- /dev/null
+++ b/gdk/quartz/gdkdisplaylinksource.h
@@ -0,0 +1,48 @@
+/* gdkdisplaylinksource.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * 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/>.
+ *
+ * Authors:
+ *   Christian Hergert <christian hergert me>
+ */
+
+#ifndef GDK_DISPLAY_LINK_SOURCE_H
+#define GDK_DISPLAY_LINK_SOURCE_H
+
+#include <glib.h>
+
+#include <QuartzCore/QuartzCore.h>
+
+G_BEGIN_DECLS
+
+typedef struct
+{
+  GSource          source;
+
+  CVDisplayLinkRef display_link;
+  gint64           refresh_interval;
+
+  volatile gint64  presentation_time;
+  volatile guint   needs_dispatch;
+} GdkDisplayLinkSource;
+
+GSource *gdk_display_link_source_new     (void);
+void     gdk_display_link_source_pause   (GdkDisplayLinkSource *source);
+void     gdk_display_link_source_unpause (GdkDisplayLinkSource *source);
+
+G_END_DECLS
+
+#endif /* GDK_DISPLAY_LINK_SOURCE_H */
diff --git a/gdk/quartz/gdkdisplaymanager-quartz.c b/gdk/quartz/gdkdisplaymanager-quartz.c
index 2058fcf..fdcb72f 100644
--- a/gdk/quartz/gdkdisplaymanager-quartz.c
+++ b/gdk/quartz/gdkdisplaymanager-quartz.c
@@ -55,7 +55,6 @@ static void
 gdk_quartz_display_manager_class_init (GdkQuartzDisplayManagerClass *class)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (class);
-  GdkDisplayManagerClass *manager_class = GDK_DISPLAY_MANAGER_CLASS (class);
 
   object_class->finalize = gdk_quartz_display_manager_finalize;
 }
diff --git a/gdk/quartz/gdkevents-quartz.c b/gdk/quartz/gdkevents-quartz.c
index 2dc6319..87ee3f3 100644
--- a/gdk/quartz/gdkevents-quartz.c
+++ b/gdk/quartz/gdkevents-quartz.c
@@ -404,7 +404,7 @@ get_window_point_from_screen_point (GdkWindow *window,
 static gboolean
 is_mouse_button_press_event (NSEventType type)
 {
-  switch (type)
+  switch ((int)type)
     {
       case NSLeftMouseDown:
       case NSRightMouseDown:
@@ -895,7 +895,7 @@ fill_button_event (GdkWindow *window,
   state = get_keyboard_modifiers_from_ns_event (nsevent) |
          _gdk_quartz_events_get_current_mouse_modifiers ();
 
-  switch ([nsevent type])
+  switch ((int)[nsevent type])
     {
     case NSLeftMouseDown:
     case NSRightMouseDown:
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..6f427f9 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"
@@ -43,8 +44,6 @@ static gboolean  in_process_all_updates = FALSE;
 
 static GSList *main_window_stack;
 
-void _gdk_quartz_window_flush (GdkWindowImplQuartz *window_impl);
-
 typedef struct
 {
   gint            x, y;
@@ -154,7 +153,7 @@ gdk_window_impl_quartz_release_context (GdkWindowImplQuartz *window_impl,
   /* See comment in gdk_quartz_window_get_context(). */
   if (window_impl->in_paint_rect_count == 0)
     {
-      _gdk_quartz_window_flush (window_impl);
+      [window_impl->toplevel flushWindow];
       [window_impl->view unlockFocus];
     }
 }
@@ -218,47 +217,6 @@ gdk_window_impl_quartz_finalize (GObject *object)
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
-/* Help preventing "beam sync penalty" where CG makes all graphics code
- * block until the next vsync if we try to flush (including call display on
- * a view) too often. We do this by limiting the manual flushing done
- * outside of expose calls to less than some frequency when measured over
- * the last 4 flushes. This is a bit arbitray, but seems to make it possible
- * for some quick manual flushes (such as gtkruler or gimp’s marching ants)
- * without hitting the max flush frequency.
- *
- * If drawable NULL, no flushing is done, only registering that a flush was
- * done externally.
- */
-void
-_gdk_quartz_window_flush (GdkWindowImplQuartz *window_impl)
-{
-  static struct timeval prev_tv;
-  static gint intervals[4];
-  static gint index;
-  struct timeval tv;
-  gint ms;
-
-  gettimeofday (&tv, NULL);
-  ms = (tv.tv_sec - prev_tv.tv_sec) * 1000 + (tv.tv_usec - prev_tv.tv_usec) / 1000;
-  intervals[index++ % 4] = ms;
-
-  if (window_impl)
-    {
-      ms = intervals[0] + intervals[1] + intervals[2] + intervals[3];
-
-      /* ~25Hz on average. */
-      if (ms > 4*40)
-        {
-          if (window_impl)
-            [window_impl->toplevel flushWindow];
-
-          prev_tv = tv;
-        }
-    }
-  else
-    prev_tv = tv;
-}
-
 static cairo_user_data_key_t gdk_quartz_cairo_key;
 
 typedef struct {
@@ -418,7 +376,6 @@ _gdk_quartz_display_before_process_all_updates (GdkDisplay *display)
 void
 _gdk_quartz_display_after_process_all_updates (GdkDisplay *display)
 {
-  GSList *old_update_nswindows = update_nswindows;
   GSList *tmp_list = update_nswindows;
 
   update_nswindows = NULL;
@@ -429,17 +386,13 @@ _gdk_quartz_display_after_process_all_updates (GdkDisplay *display)
 
       [[nswindow contentView] displayIfNeeded];
 
-      _gdk_quartz_window_flush (NULL);
-
       [nswindow enableFlushWindow];
       [nswindow flushWindow];
       [nswindow release];
 
-      tmp_list = tmp_list->next;
+      tmp_list = g_slist_remove_link (tmp_list, tmp_list);
     }
 
-  g_slist_free (old_update_nswindows);
-
   in_process_all_updates = FALSE;
 
   NSEnableScreenUpdates ();
@@ -776,6 +729,62 @@ 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 = gdk_window_get_frame_clock (window);
+  GdkFrameTimings *timings;
+
+  if (frame_clock == NULL)
+    return;
+
+  _gdk_frame_clock_thaw (frame_clock);
+
+  if (impl->pending_frame_counter)
+    {
+      timings = gdk_frame_clock_get_timings (frame_clock, impl->pending_frame_counter);
+      if (timings)
+        timings->presentation_time = presentation_time - refresh_interval;
+      impl->pending_frame_counter = 0;
+    }
+
+  timings = gdk_frame_clock_get_current_timings (frame_clock);
+
+  if (timings != NULL)
+    {
+      timings->refresh_interval = refresh_interval;
+      timings->predicted_presentation_time = presentation_time;
+    }
+}
+
+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);
+  GdkFrameTimings *timings;
+
+  timings = gdk_frame_clock_get_current_timings (frame_clock);
+  if (timings != NULL)
+    impl->pending_frame_counter = timings->frame_counter;
+
+  _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 +796,7 @@ _gdk_quartz_display_create_window_impl (GdkDisplay    *display,
 {
   GdkWindowImplQuartz *impl;
   GdkWindowImplQuartz *parent_impl;
+  GdkFrameClock *frame_clock;
 
   GDK_QUARTZ_ALLOC_POOL;
 
@@ -921,6 +931,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
@@ -1118,6 +1135,7 @@ void
 gdk_window_quartz_hide (GdkWindow *window)
 {
   GdkWindowImplQuartz *impl;
+  GdkFrameClock *frame_clock;
 
   /* Make sure we're not stuck in fullscreen mode. */
 #ifndef AVAILABLE_MAC_OS_X_VERSION_10_7_AND_LATER
@@ -1147,6 +1165,10 @@ gdk_window_quartz_hide (GdkWindow *window)
     {
       [impl->view setHidden:YES];
     }
+
+  frame_clock = gdk_window_get_frame_clock (window);
+  if (frame_clock != NULL)
+    _gdk_frame_clock_thaw (frame_clock);
 }
 
 void
@@ -3025,21 +3047,25 @@ static CGContextRef
 gdk_root_window_impl_quartz_get_context (GdkWindowImplQuartz *window,
                                          gboolean             antialias)
 {
-  CGColorSpaceRef colorspace;
+  CGColorSpaceRef colorSpace;
   CGContextRef cg_context;
   GdkWindowImplQuartz *window_impl = GDK_WINDOW_IMPL_QUARTZ (window);
 
   if (GDK_WINDOW_DESTROYED (window_impl->wrapper))
     return NULL;
 
+  colorSpace = CGDisplayCopyColorSpace (CGMainDisplayID ());
+  if (!colorSpace)
+    colorSpace = CGColorSpaceCreateDeviceRGB ();
+
   /* We do not have the notion of a root window on OS X.  We fake this
    * by creating a 1x1 bitmap and return a context to that.
    */
-  colorspace = CGColorSpaceCreateWithName (kCGColorSpaceGenericRGB);
   cg_context = CGBitmapContextCreate (NULL,
-                                      1, 1, 8, 4, colorspace,
-                                      kCGImageAlphaPremultipliedLast);
-  CGColorSpaceRelease (colorspace);
+                                      1, 1, 8, 4, colorSpace,
+                                      kCGBitmapByteOrder32Host|kCGImageAlphaPremultipliedLast);
+
+  CGColorSpaceRelease (colorSpace);
 
   return cg_context;
 }
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 
diff --git a/gtk/gtkpixelcache.c b/gtk/gtkpixelcache.c
index 6661f3d..e56fb03 100644
--- a/gtk/gtkpixelcache.c
+++ b/gtk/gtkpixelcache.c
@@ -18,9 +18,15 @@
 #include "config.h"
 
 #include "gtkdebug.h"
+#include "gtkstylecontextprivate.h"
 #include "gtkpixelcacheprivate.h"
 #include "gtkstylecontextprivate.h"
 
+#ifdef GDK_WINDOWING_QUARTZ
+# include <cairo/cairo-quartz.h>
+# include <gdk/quartz/gdkquartz.h>
+#endif
+
 #define BLOW_CACHE_TIMEOUT_SEC 20
 
 /* The extra size of the offscreen surface we allocate
@@ -243,6 +249,18 @@ _gtk_pixel_cache_create_surface_if_needed (GtkPixelCache         *cache,
       cache->surface =
         gdk_window_create_similar_surface (window, content,
                                            surface_w, surface_h);
+
+#ifdef GDK_WINDOWING_QUARTZ
+      if (GDK_IS_QUARTZ_WINDOW (window))
+        {
+          cairo_surface_t *base;
+
+          base = cache->surface;
+          cache->surface = cairo_quartz_surface_create_cg_layer (base, content, surface_w, surface_h);
+          cairo_surface_destroy (base);
+        }
+#endif
+
       rect.x = 0;
       rect.y = 0;
       rect.width = surface_w;
diff --git a/gtk/gtktextview.c b/gtk/gtktextview.c
index e9860d7..7d5acf5 100644
--- a/gtk/gtktextview.c
+++ b/gtk/gtktextview.c
@@ -1652,6 +1652,10 @@ gtk_text_view_init (GtkTextView *text_view)
   gtk_widget_set_can_focus (widget, TRUE);
 
   priv->pixel_cache = _gtk_pixel_cache_new ();
+  _gtk_pixel_cache_set_content (priv->pixel_cache, CAIRO_CONTENT_COLOR);
+
+  style_context = gtk_widget_get_style_context (GTK_WIDGET (text_view));
+  _gtk_pixel_cache_set_style_context (priv->pixel_cache, style_context);
 
   style_context = gtk_widget_get_style_context (GTK_WIDGET (text_view));
   _gtk_pixel_cache_set_style_context (priv->pixel_cache, style_context);
@@ -2597,6 +2601,7 @@ gtk_text_view_update_layout_width (GtkTextView *text_view)
 static void
 gtk_text_view_update_im_spot_location (GtkTextView *text_view)
 {
+#if 0
   GdkRectangle area;
 
   if (text_view->priv->layout == NULL)
@@ -2613,6 +2618,7 @@ gtk_text_view_update_im_spot_location (GtkTextView *text_view)
   area.width = 0;
 
   gtk_im_context_set_cursor_location (text_view->priv->im_context, &area);
+#endif
 }
 
 static gboolean


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]