[gtk+/wip/chergert/quartz-frame-clock] WIP on various quartz performance
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/wip/chergert/quartz-frame-clock] WIP on various quartz performance
- Date: Thu, 1 Oct 2015 00:17:28 +0000 (UTC)
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]