[gtk+/wip/ebassi/gsk: 2/11] gsk: GskLayer
- From: Emmanuele Bassi <ebassi src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/wip/ebassi/gsk: 2/11] gsk: GskLayer
- Date: Sun, 9 Aug 2015 15:50:45 +0000 (UTC)
commit 46a6718647ba6d390dc475b92dceb435d1135725
Author: Emmanuele Bassi <ebassi gnome org>
Date: Fri Feb 20 17:20:06 2015 +0000
gsk: GskLayer
The GskLayer type is the base element of the scene graph that the user
interacts with. Each layer has a frame, which describes the position and
size with regards to its parent.
gsk/Makefile.am | 22 +-
gsk/gsk.h | 32 +
gsk/gskdebug.c | 56 ++
gsk/gskdebug.h | 53 ++
gsk/gsklayer.c | 1802 +++++++++++++++++++++++++++++++++++++++++++++++++
gsk/gsklayer.h | 223 ++++++
gsk/gsklayeriter.c | 204 ++++++
gsk/gsklayeriter.h | 74 ++
gsk/gsklayerprivate.h | 166 +++++
gsk/gsklayerstate.c | 220 ++++++
gsk/gskmacros.h | 31 +
gsk/gsktypes.h | 37 +
12 files changed, 2917 insertions(+), 3 deletions(-)
---
diff --git a/gsk/Makefile.am b/gsk/Makefile.am
index d3ed498..4842d59 100644
--- a/gsk/Makefile.am
+++ b/gsk/Makefile.am
@@ -37,14 +37,30 @@ CLEANFILES =
lib_LTLIBRARIES =
-gsk_public_source_h =
-gsk_private_source_h =
+gsk_public_source_h = \
+ gskdebug.h \
+ gsklayer.h \
+ gsklayeriter.h \
+ gskmacros.h \
+ gsktypes.h
+
+gsk_private_source_h = \
+ gsklayerprivate.h
+
gsk_private_source_c =
-gsk_source_c =
+
+gsk_source_c = \
+ gskdebug.c \
+ gsklayer.c \
+ gsklayeriter.c \
+ gsklayerstate.c
+
+all_sources = $(gsk_private_source_c) $(gsk_source_c) $(gsk_public_source_h) $(gsk_private_source_h)
$(BUILT_SOURCES)
libgsk_3_la_SOURCES = $(all_sources)
libgsk_3_la_CFLAGS = $(AM_CFLAGS) $(GDK_HIDDEN_VISIBILITY_CFLAGS)
libgsk_3_la_LIBADD = $(GSK_DEP_LIBS) $(top_builddir)/gdk/libgdk-3.la
+libgsk_3_la_DEPENDENCIES = $(top_builddir)/gdk/libgdk-3.la
libgsk_3_la_LDFLAGS = $(LDADD)
lib_LTLIBRARIES += libgsk-3.la
diff --git a/gsk/gsk.h b/gsk/gsk.h
new file mode 100644
index 0000000..850355a
--- /dev/null
+++ b/gsk/gsk.h
@@ -0,0 +1,32 @@
+/* GSK - The GTK scene graph toolkit
+ * Copyright 2015 Emmanuele Bassi
+ *
+ * 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.1 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/>.
+ */
+
+#ifndef __GSK_H__
+#define __GSK_H__
+
+#define __GSK_H_INSIDE__
+
+#include <gsk/gsktypes.h>
+#include <gsk/gskmacros.h>
+
+#include <gsk/gskdebug.h>
+#include <gsk/gsklayer.h>
+#include <gsk/gsklayeriter.h>
+
+#undef __GSK_H_INSIDE__
+
+#endif /* __GSK_H__ */
diff --git a/gsk/gskdebug.c b/gsk/gskdebug.c
new file mode 100644
index 0000000..6b7110b
--- /dev/null
+++ b/gsk/gskdebug.c
@@ -0,0 +1,56 @@
+/* GSK - The GTK scene graph toolkit
+ * Copyright 2015 Emmanuele Bassi
+ *
+ * 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.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gskdebug.h"
+
+static guint gsk_debug_flags;
+
+#ifdef G_ENABLE_DEBUG
+static const GDebugKey gsk_debug_keys[] = {
+ { "geometry", GSK_DEBUG_GEOMETRY },
+ { "render", GSK_DEBUG_RENDER },
+ { "layout", GSK_DEBUG_LAYOUT },
+};
+#endif /* G_ENABLE_DEBUG */
+
+guint
+gsk_get_debug_flags (void)
+{
+#ifdef G_ENABLE_DEBUG
+ static gboolean env_check;
+
+ if (G_UNLIKELY (!env_check))
+ {
+ const char *tmp = g_getenv ("GSK_DEBUG");
+
+ env_check = TRUE;
+ if (tmp != NULL)
+ gsk_debug_flags = g_parse_debug_string (tmp, gsk_debug_keys,
+ G_N_ELEMENTS (gsk_debug_keys));
+ }
+#endif
+
+ return gsk_debug_flags;
+}
+
+void
+gsk_set_debug_flags (guint flags)
+{
+ gsk_debug_flags = flags;
+}
diff --git a/gsk/gskdebug.h b/gsk/gskdebug.h
new file mode 100644
index 0000000..bd3f5e7
--- /dev/null
+++ b/gsk/gskdebug.h
@@ -0,0 +1,53 @@
+/* GSK - The GTK scene graph toolkit
+ * Copyright 2015 Emmanuele Bassi
+ *
+ * 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.1 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/>.
+ */
+
+#ifndef __GSK_DEBUG_H__
+#define __GSK_DEBUG_H__
+
+#if !defined (__GSK_H_INSIDE__) && !defined (GSK_COMPILATION)
+#error "Only <gsk/gsk.h> can be included directly."
+#endif
+
+#include <gsk/gsktypes.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+ GSK_DEBUG_GEOMETRY = 1 << 0,
+ GSK_DEBUG_RENDER = 1 << 1,
+ GSK_DEBUG_LAYOUT = 1 << 2
+} GskDebugFlags;
+
+#ifdef G_ENABLE_DEBUG
+# define GSK_NOTE(type,action) G_STMT_START { \
+ if (gsk_get_debug_flags () & GSK_DEBUG_ ## type) { \
+ action; \
+ } } G_STMT_END
+
+#else
+# define GSK_NOTE(type,action)
+#endif
+
+GDK_AVAILABLE_IN_3_18
+guint gsk_get_debug_flags (void);
+
+GDK_AVAILABLE_IN_3_18
+void gsk_set_debug_flags (guint flags);
+
+G_END_DECLS
+
+#endif /* __GSK_DEBUG_H__ */
diff --git a/gsk/gsklayer.c b/gsk/gsklayer.c
new file mode 100644
index 0000000..570bba1
--- /dev/null
+++ b/gsk/gsklayer.c
@@ -0,0 +1,1802 @@
+/* GSK - The GTK scene graph toolkit
+ * Copyright 2015 Emmanuele Bassi
+ *
+ * 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.1 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/>.
+ */
+
+/**
+ * SECTION:gsklayer
+ * @Short_description: Base element in the scene graph
+ * @Title: GskLayer
+ *
+ * #GskLayer is the base element inside a GSK scene graph.
+ */
+
+#include "config.h"
+
+#include "gsklayerprivate.h"
+#include "gskdebug.h"
+
+/**
+ * GskLayer:
+ *
+ * The `GskLayer` structure contains only private data.
+ *
+ * Since: 3.18
+ */
+
+#define GSK_LAYER_PRIV(obj) ((GskLayerPrivate *) gsk_layer_get_instance_private ((GskLayer *) (obj)))
+
+typedef struct {
+ /* The scene graph */
+ GskLayer *parent;
+ GskLayer *next_sibling;
+ GskLayer *prev_sibling;
+ GskLayer *first_child;
+ GskLayer *last_child;
+
+ guint n_children;
+
+ char *debug_name;
+
+ /* The age of the layer; used by LayerIter */
+ gint64 age;
+
+ /* The target state of the layer */
+ GskLayerState model;
+
+ GdkFrameClock *frame_clock;
+
+ /* Bitfields: keep at the end */
+ guint hidden : 1;
+ guint needs_redraw : 1;
+ guint needs_relayout : 1;
+ guint in_layout : 1;
+} GskLayerPrivate;
+
+enum {
+ PROP_PARENT = 1,
+ PROP_NEXT_SIBLING,
+ PROP_PREV_SIBLING,
+ PROP_FIRST_CHILD,
+ PROP_LAST_CHILD,
+ PROP_N_CHILDREN,
+
+ PROP_HIDDEN,
+
+ PROP_BACKGROUND_COLOR,
+ PROP_OPACITY,
+
+ PROP_FRAME,
+ PROP_PIVOT_POINT,
+ PROP_BOUNDS,
+ PROP_POSITION,
+
+ PROP_TRANSFORM,
+ PROP_CHILD_TRANSFORM,
+
+ N_PROPS
+};
+
+enum {
+ CHILD_ADDED,
+ CHILD_REMOVED,
+ LAYOUT_CHILDREN,
+ QUEUE_RELAYOUT,
+ QUEUE_REDRAW,
+
+ LAST_SIGNAL
+};
+
+static void gsk_layer_queue_redraw_internal (GskLayer *self,
+ GskLayer *origin);
+static void gsk_layer_queue_relayout_internal (GskLayer *self,
+ GskLayer *origin);
+static void gsk_layer_revalidate_layout (GskLayer *self);
+
+G_DEFINE_TYPE_WITH_PRIVATE (GskLayer, gsk_layer, G_TYPE_INITIALLY_UNOWNED)
+
+static guint layer_signals[LAST_SIGNAL] = { 0, };
+static GParamSpec *layer_props[N_PROPS] = { NULL, };
+
+static inline void
+gsk_layer_set_parent (GskLayer *self,
+ GskLayer *parent)
+{
+ g_assert (self != parent);
+ GSK_LAYER_PRIV (self)->parent = parent;
+}
+
+static inline void
+gsk_layer_set_next_sibling (GskLayer *self,
+ GskLayer *sibling)
+{
+ if (sibling != NULL)
+ g_assert (GSK_LAYER_PRIV (sibling)->parent == GSK_LAYER_PRIV (self)->parent);
+ GSK_LAYER_PRIV (self)->next_sibling = sibling;
+}
+
+static inline void
+gsk_layer_set_prev_sibling (GskLayer *self,
+ GskLayer *sibling)
+{
+ if (sibling != NULL)
+ g_assert (GSK_LAYER_PRIV (sibling)->parent == GSK_LAYER_PRIV (self)->parent);
+ GSK_LAYER_PRIV (self)->prev_sibling = sibling;
+}
+
+static inline void
+gsk_layer_set_first_child (GskLayer *self,
+ GskLayer *child)
+{
+ if (child != NULL)
+ g_assert (GSK_LAYER_PRIV (child)->parent == self);
+ GSK_LAYER_PRIV (self)->first_child = child;
+}
+
+static inline void
+gsk_layer_set_last_child (GskLayer *self,
+ GskLayer *child)
+{
+ if (child != NULL)
+ g_assert (GSK_LAYER_PRIV (child)->parent == self);
+ GSK_LAYER_PRIV (self)->last_child = child;
+}
+
+static inline gboolean
+gsk_layer_in_relayout (GskLayer *self)
+{
+ return GSK_LAYER_PRIV (self)->in_layout;
+}
+
+GskLayerState *
+gsk_layer_get_state (GskLayer *self)
+{
+ GskLayerPrivate *priv = gsk_layer_get_instance_private (self);
+
+ return &priv->model;
+}
+
+static gboolean
+gsk_layer_real_queue_redraw (GskLayer *self,
+ GskLayer *origin)
+{
+ return TRUE;
+}
+
+static gboolean
+gsk_layer_real_queue_relayout (GskLayer *self,
+ GskLayer *origin)
+{
+ return TRUE;
+}
+
+static void
+gsk_layer_real_get_preferred_size (GskLayer *self,
+ graphene_size_t *size)
+{
+ const GeometryInfo *info;
+
+ info = gsk_layer_state_peek_geometry_info (gsk_layer_get_state (self));
+
+ graphene_size_init_from_size (size, &info->bounds.size);
+}
+
+static gboolean
+gsk_layer_real_draw (GskLayer *self,
+ cairo_t *cr)
+{
+ GskLayerState *state = gsk_layer_get_state (self);
+ const RenderInfo *rinfo = gsk_layer_state_peek_render_info (state);
+ const GeometryInfo *ginfo = gsk_layer_state_peek_geometry_info (state);
+
+ cairo_save (cr);
+
+ gdk_cairo_set_source_rgba (cr, &rinfo->background_color);
+
+ cairo_rectangle (cr,
+ ginfo->bounds.origin.x,
+ ginfo->bounds.origin.y,
+ ginfo->bounds.size.width,
+ ginfo->bounds.size.height);
+ cairo_fill (cr);
+
+ cairo_restore (cr);
+
+ GSK_LAYER_PRIV (self)->needs_redraw = FALSE;
+
+ return FALSE;
+}
+
+static void
+gsk_layer_real_layout_children (GskLayer *layer)
+{
+}
+
+static void
+gsk_layer_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+}
+
+static void
+gsk_layer_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+}
+
+static void
+gsk_layer_finalize (GObject *gobject)
+{
+ GskLayerPrivate *priv = gsk_layer_get_instance_private ((GskLayer *) gobject);
+
+ g_clear_pointer (&priv->debug_name, g_free);
+
+ gsk_layer_state_clear (&priv->model);
+
+ G_OBJECT_CLASS (gsk_layer_parent_class)->finalize (gobject);
+}
+
+static void
+gsk_layer_dispose (GObject *gobject)
+{
+ GskLayerPrivate *priv = gsk_layer_get_instance_private ((GskLayer *) gobject);
+
+ if (priv->frame_clock != NULL)
+ g_signal_handlers_disconnect_by_func (priv->frame_clock,
+ gsk_layer_revalidate_layout,
+ gobject);
+
+ g_clear_object (&priv->frame_clock);
+
+ G_OBJECT_CLASS (gsk_layer_parent_class)->dispose (gobject);
+}
+
+static void
+gsk_layer_class_init (GskLayerClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->set_property = gsk_layer_set_property;
+ gobject_class->get_property = gsk_layer_get_property;
+ gobject_class->dispose = gsk_layer_dispose;
+ gobject_class->finalize = gsk_layer_finalize;
+
+ klass->queue_redraw = gsk_layer_real_queue_redraw;
+ klass->queue_relayout = gsk_layer_real_queue_relayout;
+ klass->get_preferred_size = gsk_layer_real_get_preferred_size;
+ klass->draw = gsk_layer_real_draw;
+ klass->layout_children = gsk_layer_real_layout_children;
+
+ layer_signals[CHILD_ADDED] =
+ g_signal_new (g_intern_static_string ("child-added"),
+ GSK_TYPE_LAYER,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GskLayerClass, child_added),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 1, GSK_TYPE_LAYER);
+
+ layer_signals[CHILD_REMOVED] =
+ g_signal_new (g_intern_static_string ("child-removed"),
+ GSK_TYPE_LAYER,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GskLayerClass, child_removed),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 1, GSK_TYPE_LAYER);
+
+ layer_signals[LAYOUT_CHILDREN] =
+ g_signal_new (g_intern_static_string ("layout-children"),
+ GSK_TYPE_LAYER,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GskLayerClass, layout_children),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 0);
+
+ layer_signals[QUEUE_RELAYOUT] =
+ g_signal_new (g_intern_static_string ("queue-relayout"),
+ GSK_TYPE_LAYER,
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 0);
+
+ layer_signals[QUEUE_REDRAW] =
+ g_signal_new (g_intern_static_string ("queue-redraw"),
+ GSK_TYPE_LAYER,
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 0);
+}
+
+static void
+gsk_layer_init (GskLayer *self)
+{
+}
+
+/**
+ * gsk_layer_new:
+ *
+ * Creates a new #GskLayer instance.
+ *
+ * Returns: (transfer full): the newly created #GskLayer instance
+ *
+ * Since: 3.18
+ */
+GskLayer *
+gsk_layer_new (void)
+{
+ return g_object_new (GSK_TYPE_LAYER, NULL);
+}
+
+#define GSK_LAYER_DEFINE_GETTER(TypeName, type_name, FieldType, field_name) \
+FieldType \
+type_name ## _get_ ## field_name (TypeName *self) \
+{ \
+ TypeName ## Private *priv = type_name ## _get_instance_private (self); \
+\
+ g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (self, type_name ## _get_type ()), (FieldType) 0); \
+\
+ return priv->field_name; \
+}
+
+/**
+ * gsk_layer_get_parent:
+ * @self: a #GskLayer
+ *
+ * Retrieves the parent layer of a #GskLayer.
+ *
+ * Returns: (transfer none) (nullable): a #GskLayer
+ *
+ * Since: 3.18
+ */
+GSK_LAYER_DEFINE_GETTER (GskLayer, gsk_layer, GskLayer *, parent)
+
+/**
+ * gsk_layer_get_next_sibling:
+ * @self: a #GskLayer
+ *
+ * Retrieves the next sibling of a #GskLayer.
+ *
+ * Returns: (transfer none) (nullable): a #GskLayer
+ *
+ * Since: 3.18
+ */
+GSK_LAYER_DEFINE_GETTER (GskLayer, gsk_layer, GskLayer *, next_sibling)
+
+/**
+ * gsk_layer_get_prev_sibling:
+ * @self: a #GskLayer
+ *
+ * Retrieves the previous sibling of a #GskLayer.
+ *
+ * Returns: (transfer none) (nullable): a #GskLayer
+ *
+ * Since: 3.18
+ */
+GSK_LAYER_DEFINE_GETTER (GskLayer, gsk_layer, GskLayer *, prev_sibling)
+
+/**
+ * gsk_layer_get_first_child:
+ * @self: a #GskLayer
+ *
+ * Retrieves the first child of a #GskLayer.
+ *
+ * Returns: (transfer none) (nullable): a #GskLayer
+ *
+ * Since: 3.18
+ */
+GSK_LAYER_DEFINE_GETTER (GskLayer, gsk_layer, GskLayer *, first_child)
+
+/**
+ * gsk_layer_get_last_child:
+ * @self: a #GskLayer
+ *
+ * Retrieves the last child of a #GskLayer.
+ *
+ * Returns: (transfer none) (nullable): a #GskLayer
+ *
+ * Since: 3.18
+ */
+GSK_LAYER_DEFINE_GETTER (GskLayer, gsk_layer, GskLayer *, last_child)
+
+/**
+ * gsk_layer_get_n_children:
+ * @self: a #GskLayer
+ *
+ * Retrieves the number of children of a #GskLayer
+ *
+ * Returns: the number of children of the layer
+ *
+ * Since: 3.18
+ */
+GSK_LAYER_DEFINE_GETTER (GskLayer, gsk_layer, guint, n_children)
+
+#undef GSK_LAYER_DEFINE_GETTER
+
+/*< private >
+ * gsk_layer_get_age:
+ * @self: a #GskLayer
+ *
+ * Retrieves the age of the layer. The age changes when the
+ * list of children of the layer changes.
+ *
+ * Returns: the age of the layer
+ */
+gint64
+gsk_layer_get_age (GskLayer *self)
+{
+ return GSK_LAYER_PRIV (self)->age;
+}
+
+/**
+ * gsk_layer_set_hidden:
+ * @self: a #GskLayer
+ * @hidden: whether the layer should be hidden
+ *
+ * Sets whether a #GskLayer should be hidden.
+ *
+ * Hidden layers are not drawn, which means that any descendant of
+ * a hidden layer is also not drawn.
+ *
+ * Since: 3.18
+ */
+void
+gsk_layer_set_hidden (GskLayer *self,
+ gboolean hidden)
+{
+ GskLayerPrivate *priv = gsk_layer_get_instance_private (self);
+
+ g_return_if_fail (GSK_IS_LAYER (self));
+
+ hidden = !!hidden;
+
+ if (priv->hidden == hidden)
+ return;
+
+ priv->hidden = hidden;
+
+ /* Queue a redraw of the parent, if any is set; we don't want
+ * to blow away the cached state of the layer, so we don't
+ * queue a redraw on it directly.
+ */
+ if (priv->parent != NULL)
+ gsk_layer_queue_redraw_internal (priv->parent, self);
+}
+
+/**
+ * gsk_layer_get_hidden:
+ * @self: a #GskLayer
+ *
+ * Checks whether a #GskLayer has been hidden.
+ *
+ * Returns: %TRUE if the layer was hidden
+ *
+ * Since: 3.18
+ */
+gboolean
+gsk_layer_get_hidden (GskLayer *self)
+{
+ GskLayerPrivate *priv = gsk_layer_get_instance_private (self);
+
+ g_return_val_if_fail (GSK_IS_LAYER (self), FALSE);
+
+ return priv->hidden;
+}
+
+gboolean
+gsk_layer_draw (GskLayer *self,
+ cairo_t *cr)
+{
+ return GSK_LAYER_GET_CLASS (self)->draw (self, cr);
+}
+
+/*< private >
+ * gsk_layer_get_debug_name:
+ * @self: a #GskLayer
+ *
+ * Retrieves a printable string representing the layer, to
+ * be used in debug messages.
+ *
+ * Returns: (transfer none): the printable debug name of the layer
+ */
+const char *
+gsk_layer_get_debug_name (GskLayer *self)
+{
+ GskLayerPrivate *priv = gsk_layer_get_instance_private (self);
+
+ if (G_UNLIKELY (priv->debug_name == NULL))
+ priv->debug_name = g_strdup_printf ("<%s>[%p]", G_OBJECT_TYPE_NAME (self), self);
+
+ return priv->debug_name;
+}
+
+static inline void
+gsk_layer_remove_child_internal (GskLayer *self,
+ GskLayer *child)
+{
+ GskLayer *prev_sibling, *next_sibling;
+ GskLayer *old_first, *old_last;
+
+ if (GSK_LAYER_PRIV (child)->parent != self)
+ {
+ g_critical ("The layer '%s' is not a child of layer '%s', "
+ "so it cannot be removed from its children.",
+ gsk_layer_get_debug_name (child),
+ gsk_layer_get_debug_name (self));
+ return;
+ }
+
+ old_first = GSK_LAYER_PRIV (self)->first_child;
+ old_last = GSK_LAYER_PRIV (self)->last_child;
+
+ prev_sibling = GSK_LAYER_PRIV (child)->prev_sibling;
+ next_sibling = GSK_LAYER_PRIV (child)->next_sibling;
+
+ if (prev_sibling != NULL)
+ gsk_layer_set_next_sibling (prev_sibling, next_sibling);
+ if (next_sibling != NULL)
+ gsk_layer_set_prev_sibling (next_sibling, prev_sibling);
+
+ if (GSK_LAYER_PRIV (self)->first_child == child)
+ gsk_layer_set_first_child (self, next_sibling);
+ if (GSK_LAYER_PRIV (self)->last_child == child)
+ gsk_layer_set_last_child (self, prev_sibling);
+
+ GSK_LAYER_PRIV (self)->n_children -= 1;
+ GSK_LAYER_PRIV (self)->age += 1;
+
+ /* Unset the graph fields on the child */
+ gsk_layer_set_parent (child, NULL);
+ gsk_layer_set_prev_sibling (child, NULL);
+ gsk_layer_set_next_sibling (child, NULL);
+
+ g_signal_emit (self, layer_signals[CHILD_REMOVED], 0, child);
+
+ /* Release the reference acquired in gsk_layer_add_child_internal() */
+ g_object_unref (child);
+
+ if (old_first != GSK_LAYER_PRIV (self)->first_child)
+ ;
+
+ if (old_last != GSK_LAYER_PRIV (self)->last_child)
+ ;
+}
+
+typedef void (* GskLayerAddChildFunc) (GskLayer *self,
+ GskLayer *child,
+ gpointer data);
+
+static inline void
+gsk_layer_add_child_internal (GskLayer *self,
+ GskLayer *child,
+ GskLayerAddChildFunc add_func,
+ gpointer add_func_data)
+{
+ GskLayer *old_first, *old_last;
+
+ if (GSK_LAYER_PRIV (child)->parent != NULL)
+ {
+ g_critical ("The layer '%s' cannot be a child of layer '%s', "
+ "because it already has a parent layer '%s'.",
+ gsk_layer_get_debug_name (child),
+ gsk_layer_get_debug_name (self),
+ gsk_layer_get_debug_name (GSK_LAYER_PRIV (child)->parent));
+ return;
+ }
+
+ old_first = GSK_LAYER_PRIV (self)->first_child;
+ old_last = GSK_LAYER_PRIV (self)->last_child;
+
+ /* Acquire a reference, to be released when the child is removed */
+ g_object_ref_sink (child);
+
+ /* Reset the state of the child */
+ gsk_layer_set_parent (child, self);
+ gsk_layer_set_prev_sibling (child, NULL);
+ gsk_layer_set_next_sibling (child, NULL);
+
+ /* Call the delegate function for setting up the graph */
+ add_func (self, child, add_func_data);
+
+ GSK_LAYER_PRIV (self)->n_children += 1;
+ GSK_LAYER_PRIV (self)->age += 1;
+
+ g_signal_emit (self, layer_signals[CHILD_ADDED], 0, child);
+
+ /* Relayout the graph */
+ gsk_layer_queue_relayout (child);
+
+ if (old_first != GSK_LAYER_PRIV (self)->first_child)
+ ;
+
+ if (old_last != GSK_LAYER_PRIV (self)->last_child)
+ ;
+}
+
+static void
+insert_child_at_index (GskLayer *self,
+ GskLayer *child,
+ gpointer data)
+{
+ int index_ = GPOINTER_TO_INT (data);
+
+ gsk_layer_set_parent (child, self);
+
+ if (index_ == 0)
+ {
+ GskLayer *next = GSK_LAYER_PRIV (self)->first_child;
+
+ /* prepend */
+ gsk_layer_set_prev_sibling (child, NULL);
+ gsk_layer_set_next_sibling (child, next);
+ if (next != NULL)
+ gsk_layer_set_prev_sibling (next, child);
+ }
+ else if (index_ < 0 || index_ >= GSK_LAYER_PRIV (self)->n_children)
+ {
+ GskLayer *prev = GSK_LAYER_PRIV (self)->last_child;
+
+ /* append */
+ gsk_layer_set_next_sibling (child, NULL);
+ gsk_layer_set_prev_sibling (child, prev);
+ if (prev != NULL)
+ gsk_layer_set_next_sibling (prev, child);
+ }
+ else
+ {
+ GskLayer *iter;
+ guint i;
+
+ /* insert */
+ for (iter = GSK_LAYER_PRIV (self)->first_child, i = 0;
+ iter != NULL;
+ iter = GSK_LAYER_PRIV (iter)->next_sibling, i++)
+ {
+ if (i == index_)
+ {
+ GskLayer *prev = GSK_LAYER_PRIV (iter)->prev_sibling;
+ GskLayer *next = GSK_LAYER_PRIV (iter)->next_sibling;
+
+ gsk_layer_set_prev_sibling (child, prev);
+ gsk_layer_set_next_sibling (child, next);
+
+ gsk_layer_set_next_sibling (prev, child);
+ gsk_layer_set_prev_sibling (next, child);
+
+ break;
+ }
+ }
+ }
+
+ if (GSK_LAYER_PRIV (child)->prev_sibling == NULL)
+ gsk_layer_set_first_child (self, child);
+ if (GSK_LAYER_PRIV (child)->next_sibling == NULL)
+ gsk_layer_set_last_child (self, child);
+}
+
+static void
+insert_child_after (GskLayer *self,
+ GskLayer *child,
+ gpointer data)
+{
+ GskLayer *sibling = data;
+
+ if (sibling == NULL)
+ sibling = GSK_LAYER_PRIV (self)->last_child;
+
+ gsk_layer_set_prev_sibling (child, sibling);
+
+ if (sibling != NULL)
+ {
+ GskLayer *tmp = GSK_LAYER_PRIV (sibling)->next_sibling;
+
+ gsk_layer_set_next_sibling (child, tmp);
+
+ if (tmp != NULL)
+ gsk_layer_set_prev_sibling (tmp, child);
+
+ gsk_layer_set_next_sibling (sibling, child);
+ }
+ else
+ gsk_layer_set_next_sibling (child, NULL);
+
+ if (GSK_LAYER_PRIV (child)->prev_sibling == NULL)
+ gsk_layer_set_first_child (self, child);
+ if (GSK_LAYER_PRIV (child)->next_sibling == NULL)
+ gsk_layer_set_last_child (self, child);
+}
+
+static void
+insert_child_before (GskLayer *self,
+ GskLayer *child,
+ gpointer data)
+{
+ GskLayer *sibling = data;
+
+ if (sibling == NULL)
+ sibling = GSK_LAYER_PRIV (self)->first_child;
+
+ gsk_layer_set_next_sibling (child, sibling);
+
+ if (sibling != NULL)
+ {
+ GskLayer *tmp = GSK_LAYER_PRIV (sibling)->prev_sibling;
+
+ gsk_layer_set_prev_sibling (child, tmp);
+
+ if (tmp != NULL)
+ gsk_layer_set_next_sibling (tmp, child);
+
+ gsk_layer_set_prev_sibling (sibling, child);
+ }
+ else
+ gsk_layer_set_prev_sibling (child, NULL);
+
+ if (GSK_LAYER_PRIV (child)->prev_sibling == NULL)
+ gsk_layer_set_first_child (self, child);
+ if (GSK_LAYER_PRIV (child)->next_sibling == NULL)
+ gsk_layer_set_last_child (self, child);
+}
+
+typedef struct {
+ GskLayer *prev;
+ GskLayer *next;
+} InsertBetweenData;
+
+static void
+insert_child_between (GskLayer *self,
+ GskLayer *child,
+ gpointer data_)
+{
+ InsertBetweenData *data = data_;
+
+ gsk_layer_set_prev_sibling (child, data->prev);
+ gsk_layer_set_next_sibling (child, data->next);
+
+ if (data->prev != NULL)
+ gsk_layer_set_next_sibling (data->prev, child);
+ if (data->next != NULL)
+ gsk_layer_set_prev_sibling (data->next, child);
+
+ if (GSK_LAYER_PRIV (child)->prev_sibling == NULL)
+ gsk_layer_set_first_child (self, child);
+ if (GSK_LAYER_PRIV (child)->next_sibling == NULL)
+ gsk_layer_set_last_child (self, child);
+}
+
+/**
+ * gsk_layer_insert_child_at_index:
+ * @self: a #GskLayer
+ * @child: the child #GskLayer to add
+ * @index_: the index of the child layer, starting from zero
+ *
+ * Inserts a new @child #GskLayer in the list of children of @self,
+ * at the given @index_.
+ *
+ * If @index_ is negative, or greater or equal to the number of children
+ * of @self, the @child is appended to the list of children.
+ *
+ * If @index_ is zero, the @child is prepended to the list of children.
+ *
+ * Returns: (transfer none): the layer with a new child
+ *
+ * Since: 3.18
+ */
+GskLayer *
+gsk_layer_insert_child_at_index (GskLayer *self,
+ GskLayer *child,
+ int index_)
+{
+ g_return_val_if_fail (GSK_IS_LAYER (self), NULL);
+ g_return_val_if_fail (GSK_IS_LAYER (child), self);
+
+ gsk_layer_add_child_internal (self, child,
+ insert_child_at_index,
+ GINT_TO_POINTER (index_));
+
+ return self;
+}
+
+/**
+ * gsk_layer_insert_child_before:
+ * @self: a #GskLayer
+ * @child: the child layer to add
+ * @sibling: (nullable): an optional #GskLayer, child of @self
+ *
+ * Inserts a new @child #GskLayer in the list of children of @self.
+ *
+ * If @sibling is set, the @child will be added before @sibling in
+ * the list; if it is unset, the @child will be prepended to the
+ * list of children.
+ *
+ * Returns: (transfer none): the layer with a new child
+ *
+ * Since: 3.18
+ */
+GskLayer *
+gsk_layer_insert_child_before (GskLayer *self,
+ GskLayer *child,
+ GskLayer *sibling)
+{
+ g_return_val_if_fail (GSK_IS_LAYER (self), NULL);
+ g_return_val_if_fail (GSK_IS_LAYER (child), self);
+ g_return_val_if_fail (sibling == NULL || GSK_IS_LAYER (sibling), self);
+
+ gsk_layer_add_child_internal (self, child, insert_child_before, sibling);
+
+ return self;
+}
+
+/**
+ * gsk_layer_insert_child_after:
+ * @self: a #GskLayer
+ * @child: the child layer to add
+ * @sibling: (nullable): an optional #GskLayer, child of @self
+ *
+ * Inserts a new @child #GskLayer in the list of children of @self.
+ *
+ * If @sibling is set, the @child will be added after @sibling in
+ * the list; if it is unset, the @child will be appended to the
+ * list of children.
+ *
+ * Returns: (transfer none): the layer with a new child
+ *
+ * Since: 3.18
+ */
+GskLayer *
+gsk_layer_insert_child_after (GskLayer *self,
+ GskLayer *child,
+ GskLayer *sibling)
+{
+ g_return_val_if_fail (GSK_IS_LAYER (self), NULL);
+ g_return_val_if_fail (GSK_IS_LAYER (child), self);
+ g_return_val_if_fail (sibling == NULL || GSK_IS_LAYER (sibling), self);
+
+ gsk_layer_add_child_internal (self, child, insert_child_after, sibling);
+
+ return self;
+}
+
+/**
+ * gsk_layer_replace_child:
+ * @self: a #GskLayer
+ * @old_child: the child layer to be replaced
+ * @new_child: the child layer to replace @old_child
+ *
+ * Replaces @old_child with @new_child in the list of children of @self.
+ *
+ * Returns: (transfer none): the layer with the new child
+ *
+ * Since: 3.18
+ */
+GskLayer *
+gsk_layer_replace_child (GskLayer *self,
+ GskLayer *old_child,
+ GskLayer *new_child)
+{
+ InsertBetweenData data;
+
+ g_return_val_if_fail (GSK_IS_LAYER (self), NULL);
+ g_return_val_if_fail (GSK_IS_LAYER (old_child), self);
+ g_return_val_if_fail (GSK_IS_LAYER (new_child), self);
+
+ if (GSK_LAYER_PRIV (old_child)->parent != self)
+ {
+ g_critical ("Unable to replace layer '%s' in layer '%s': "
+ "it is a child of layer '%s'",
+ gsk_layer_get_debug_name (old_child),
+ gsk_layer_get_debug_name (self),
+ gsk_layer_get_debug_name (GSK_LAYER_PRIV (old_child)->parent));
+ return self;
+ }
+
+ data.prev = GSK_LAYER_PRIV (old_child)->prev_sibling;
+ data.next = GSK_LAYER_PRIV (old_child)->next_sibling;
+
+ gsk_layer_remove_child_internal (self, old_child);
+ gsk_layer_add_child_internal (self, new_child, insert_child_between, &data);
+
+ return self;
+}
+
+/**
+ * gsk_layer_add_child:
+ * @self: a #GskLayer
+ * @child: the child #GskLayer to add
+ *
+ * Adds a @child #GskLayer to the list of children of the given #GskLayer.
+ *
+ * The child is appended to the list of children of @self.
+ *
+ * The #GskLayer will acquire a reference on the newly added @child.
+ *
+ * Returns: (transfer none): the layer with a new child
+ *
+ * Since: 3.18
+ */
+GskLayer *
+gsk_layer_add_child (GskLayer *self,
+ GskLayer *child)
+{
+ g_return_val_if_fail (GSK_IS_LAYER (self), NULL);
+ g_return_val_if_fail (GSK_IS_LAYER (child), self);
+
+ gsk_layer_add_child_internal (self, child, insert_child_after, NULL);
+
+ return self;
+}
+
+/**
+ * gsk_layer_remove_child:
+ * @self: a #GskLayer
+ * @child: the child #GskLayer to remove
+ *
+ * Removes a @child #GskLayer from the list of children of the given #GskLayer.
+ *
+ * The #GskLayer will release the reference acquired on the @child.
+ *
+ * Returns: (transfer none): the layer without the child
+ *
+ * Since: 3.18
+ */
+GskLayer *
+gsk_layer_remove_child (GskLayer *self,
+ GskLayer *child)
+{
+ g_return_val_if_fail (GSK_IS_LAYER (self), NULL);
+ g_return_val_if_fail (GSK_IS_LAYER (child), self);
+
+ gsk_layer_remove_child_internal (self, child);
+
+ return self;
+}
+
+/**
+ * gsk_layer_get_child_at_index:
+ * @self: a #GskLayer
+ * @index_: the index of the child to retrieve
+ *
+ * Retrieves the #GskLayer in the list of children of @self, at the
+ * given index.
+ *
+ * Returns: (transfer none): the layer at the given index
+ *
+ * Since: 3.18
+ */
+GskLayer *
+gsk_layer_get_child_at_index (GskLayer *self,
+ int index_)
+{
+ GskLayer *iter;
+ int i;
+
+ g_return_val_if_fail (GSK_IS_LAYER (self), NULL);
+ g_return_val_if_fail (index_ <= GSK_LAYER_PRIV (self)->n_children, NULL);
+
+ for (iter = GSK_LAYER_PRIV (self)->first_child, i = 0;
+ iter != NULL;
+ iter = GSK_LAYER_PRIV (iter)->next_sibling, i++)
+ {
+ if (i == index_)
+ return iter;
+ }
+
+ return NULL;
+}
+
+/**
+ * gsk_layer_contains:
+ * @self: a #GskLayer
+ * @descendant: the descendant to check
+ *
+ * Checks whether @self contains @descendant.
+ *
+ * Returns: %TRUE if @self contains the given #GskLayer
+ *
+ * Since: 3.18
+ */
+gboolean
+gsk_layer_contains (GskLayer *self,
+ GskLayer *descendant)
+{
+ GskLayer *iter;
+
+ g_return_val_if_fail (GSK_IS_LAYER (self), FALSE);
+ g_return_val_if_fail (GSK_IS_LAYER (descendant), FALSE);
+
+ for (iter = descendant; iter != NULL; iter = GSK_LAYER_PRIV (iter)->parent)
+ {
+ if (iter == self)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static GskLayer *
+gsk_layer_get_top_level (GskLayer *self)
+{
+ GskLayer *iter;
+
+ iter = self;
+ while (iter != NULL)
+ {
+ if (GSK_LAYER_PRIV (iter)->parent == NULL)
+ return iter;
+
+ iter = GSK_LAYER_PRIV (iter)->parent;
+ }
+
+ return NULL;
+}
+
+static void
+gsk_layer_queue_redraw_internal (GskLayer *self,
+ GskLayer *origin)
+{
+ GskLayer *iter;
+
+ iter = self;
+ while (iter != NULL)
+ {
+ GskLayerPrivate *priv = gsk_layer_get_instance_private (iter);
+
+ /* We reached a point in the graph where we already queued a
+ * redraw, so we don't need to recurse any further
+ */
+ if (priv->needs_redraw)
+ break;
+
+ /* We ask the layer's implementation whether or not the layer content
+ * should be invalidated; this allows layers to bail out of the invalidation
+ * process.
+ */
+ priv->needs_redraw = GSK_LAYER_GET_CLASS (iter)->queue_redraw (iter, origin);
+ if (!priv->needs_redraw)
+ break;
+
+ iter = priv->parent;
+ }
+
+ iter = gsk_layer_get_top_level (self);
+ if (GSK_LAYER_PRIV (iter)->frame_clock != NULL)
+ g_signal_emit (iter, layer_signals[QUEUE_REDRAW], 0);
+}
+
+static void
+gsk_layer_queue_relayout_internal (GskLayer *self,
+ GskLayer *origin)
+{
+ GskLayer *iter;
+
+ iter = self;
+ while (iter != NULL)
+ {
+ GskLayerPrivate *priv = gsk_layer_get_instance_private (iter);
+
+ /* Hidden branches do not need to propagate further */
+ if (priv->hidden)
+ break;
+
+ /* We reached a point in the graph where we already queued a
+ * relayout, so we don't need to recurse any further
+ */
+ if (priv->needs_relayout)
+ break;
+
+ /* We ask the layer's implementation whether or not the layer geometry
+ * should be invalidated; this allows layers to bail out of the invalidation
+ * process.
+ */
+ priv->needs_relayout = GSK_LAYER_GET_CLASS (iter)->queue_relayout (iter, origin);
+ if (!priv->needs_relayout)
+ break;
+
+ iter = priv->parent;
+ }
+
+ if (iter == NULL)
+ {
+ iter = gsk_layer_get_top_level (self);
+ if (GSK_LAYER_PRIV (iter)->frame_clock != NULL)
+ g_signal_emit (iter, layer_signals[QUEUE_RELAYOUT], 0);
+ }
+}
+
+static inline void
+gsk_layer_maybe_queue_relayout (GskLayer *self)
+{
+ GskLayerPrivate *priv = gsk_layer_get_instance_private (self);
+
+ if (priv->parent == NULL && !priv->needs_relayout)
+ {
+ priv->needs_relayout = TRUE;
+
+ if (priv->frame_clock != NULL)
+ {
+ GSK_NOTE (LAYOUT, g_print (G_STRLOC ": called on root, requesting layout."));
+ gdk_frame_clock_request_phase (priv->frame_clock, GDK_FRAME_CLOCK_PHASE_LAYOUT);
+ return;
+ }
+ }
+
+ if (priv->parent != NULL)
+ {
+ if (priv->needs_relayout && gsk_layer_in_relayout (priv->parent))
+ {
+ GSK_NOTE (LAYOUT, g_print (G_STRLOC ": called during parent's layout."));
+ priv->needs_relayout = FALSE;
+ return;
+ }
+ }
+
+ gsk_layer_queue_relayout (self);
+}
+
+/**
+ * gsk_layer_queue_redraw:
+ * @self: a #GskLayer
+ *
+ * Queues a redraw on a #GskLayer.
+ *
+ * This function should be called when the contents of the
+ * layer change.
+ *
+ * See also: gsk_layer_queue_relayout()
+ *
+ * Since: 3.18
+ */
+void
+gsk_layer_queue_redraw (GskLayer *self)
+{
+ GskLayerPrivate *priv = gsk_layer_get_instance_private (self);
+
+ g_return_if_fail (GSK_IS_LAYER (self));
+
+ /* Hidden layers do no contribute to the content */
+ if (priv->hidden)
+ return;
+
+ gsk_layer_queue_redraw_internal (self, self);
+}
+
+/**
+ * gsk_layer_needs_redraw:
+ * @self: a #GskLayer
+ *
+ * Checks whether the content of a #GskLayer has been invalidated,
+ * and the layer should be redrawn.
+ *
+ * See also: gsk_layer_needs_relayout()
+ *
+ * Returns: %TRUE if the #GskLayer needs to be redrawn
+ *
+ * Since: 3.18
+ */
+gboolean
+gsk_layer_needs_redraw (GskLayer *self)
+{
+ GskLayerPrivate *priv = gsk_layer_get_instance_private (self);
+
+ g_return_val_if_fail (GSK_IS_LAYER (self), FALSE);
+
+ return priv->needs_redraw;
+}
+
+/**
+ * gsk_layer_queue_relayout:
+ * @self: a #GskLayer
+ *
+ * Queues a relayout on a #GskLayer.
+ *
+ * This function should be called when the geometry of the
+ * layer change.
+ *
+ * See also: gsk_layer_queue_redraw()
+ *
+ * Since: 3.18
+ */
+void
+gsk_layer_queue_relayout (GskLayer *self)
+{
+ GskLayerPrivate *priv = gsk_layer_get_instance_private (self);
+
+ g_return_if_fail (GSK_IS_LAYER (self));
+
+ /* Hidden layers do not contribute to the layout */
+ if (priv->hidden)
+ return;
+
+ gsk_layer_queue_relayout_internal (self, self);
+}
+
+/**
+ * gsk_layer_needs_relayout:
+ * @self: a #GskLayer
+ *
+ * Checks whether the geometry of a #GskLayer has been invalidated,
+ * and the layer should be resized.
+ *
+ * Returns: %TRUE if the #GskLayer needs to be resized
+ *
+ * Since: 3.18
+ */
+gboolean
+gsk_layer_needs_relayout (GskLayer *self)
+{
+ GskLayerPrivate *priv = gsk_layer_get_instance_private (self);
+
+ g_return_val_if_fail (GSK_IS_LAYER (self), FALSE);
+
+ return priv->needs_relayout;
+}
+
+/**
+ * gsk_layer_set_pivot_point:
+ * @self: a #GskLayer
+ * @point: the coordinates of the pivot point
+ *
+ * Sets the coordinates of the pivot point of a #GskLayer.
+ *
+ * The pivot point is used to determine the #GskLayer:frame of the layer,
+ * as well as the center of all the transformations.
+ *
+ * The coordinates of the point are in layer-relative, normalized space,
+ * which means that the (0, 0) coordinates are the top-left corner of the
+ * layer; the (0.5, 0.5) coordinates are the center of the layer; and the
+ * (1, 1) coordinates are the bottom-right corner of the layer.
+ *
+ * Since: 3.18
+ */
+void
+gsk_layer_set_pivot_point (GskLayer *self,
+ const graphene_point_t *point)
+{
+ GeometryInfo *info;
+
+ g_return_if_fail (GSK_IS_LAYER (self));
+ g_return_if_fail (point != NULL);
+
+ info = gsk_layer_state_get_geometry_info (gsk_layer_get_state (self));
+ graphene_point_init_from_point (&info->pivot_point, point);
+ gsk_layer_maybe_queue_relayout (self);
+}
+
+/**
+ * gsk_layer_get_pivot_point:
+ * @self: a #GskLayer
+ * @point: (out caller-allocates): return location for the pivot point
+ *
+ * Retrieves the coordinates of the pivot point of a #GskLayer.
+ *
+ * Since: 3.18
+ */
+void
+gsk_layer_get_pivot_point (GskLayer *self,
+ graphene_point_t *point)
+{
+ const GeometryInfo *info;
+
+ g_return_if_fail (GSK_IS_LAYER (self));
+ g_return_if_fail (point != NULL);
+
+ info = gsk_layer_state_peek_geometry_info (gsk_layer_get_state (self));
+ graphene_point_init_from_point (point, &info->pivot_point);
+}
+
+/**
+ * gsk_layer_set_position:
+ * @self: a #GskLayer
+ * @point: the coordinates of the position
+ *
+ * Sets the coordinates of the position of a #GskLayer.
+ *
+ * The coordinates of the position are in parent-relative space,
+ * and specify the origin of the #GskLayer:frame of the layer.
+ *
+ * Since: 3.18
+ */
+void
+gsk_layer_set_position (GskLayer *self,
+ const graphene_point_t *point)
+{
+ GeometryInfo *info;
+
+ g_return_if_fail (GSK_IS_LAYER (self));
+ g_return_if_fail (point != NULL);
+
+ info = gsk_layer_state_get_geometry_info (gsk_layer_get_state (self));
+ graphene_point_init_from_point (&info->position, point);
+
+ gsk_layer_maybe_queue_relayout (self);
+}
+
+/**
+ * gsk_layer_get_position:
+ * @self: a #GskLayer
+ * @position: (out caller-allocates): return location for the position
+ *
+ * Retrieves the coordinates of the position of a #GskLayer.
+ *
+ * Since: 3.18
+ */
+void
+gsk_layer_get_position (GskLayer *self,
+ graphene_point_t *position)
+{
+ const GeometryInfo *info;
+
+ g_return_if_fail (GSK_IS_LAYER (self));
+ g_return_if_fail (position != NULL);
+
+ info = gsk_layer_state_peek_geometry_info (gsk_layer_get_state (self));
+ graphene_point_init_from_point (position, &info->position);
+}
+
+/**
+ * gsk_layer_set_bounds:
+ * @self: a #GskLayer
+ * @bounds: the bounds rectangle
+ *
+ * Sets the bounds rectangle of a #GskLayer.
+ *
+ * The bounds rectangle has an origin in the top-left corner of
+ * the layer, and it's expressed in layer-relative coordinate space.
+ *
+ * Since: 3.18
+ */
+void
+gsk_layer_set_bounds (GskLayer *self,
+ const graphene_rect_t *bounds)
+{
+ GeometryInfo *info;
+
+ g_return_if_fail (GSK_IS_LAYER (self));
+ g_return_if_fail (bounds != NULL);
+
+ info = gsk_layer_state_get_geometry_info (gsk_layer_get_state (self));
+ graphene_rect_init_from_rect (&info->bounds, bounds);
+ gsk_layer_maybe_queue_relayout (self);
+}
+
+/**
+ * gsk_layer_get_bounds:
+ * @self: a #GskLayer
+ * @bounds: (out caller-allocates): return location for the bounds
+ * rectangle
+ *
+ * Retrieves the bounds rectangle of a #GskLayer.
+ *
+ * The bounds rectangle has an origin expressed in layer-relative coordinate
+ * space; if you need the parent-relative coordinates, use gsk_layer_get_frame()
+ * instead.
+ *
+ * Since: 3.18
+ */
+void
+gsk_layer_get_bounds (GskLayer *self,
+ graphene_rect_t *bounds)
+{
+ const GeometryInfo *info;
+
+ g_return_if_fail (GSK_IS_LAYER (self));
+ g_return_if_fail (bounds != NULL);
+
+ info = gsk_layer_state_peek_geometry_info (gsk_layer_get_state (self));
+ graphene_rect_init_from_rect (bounds, &info->bounds);
+}
+
+/**
+ * gsk_layer_set_frame:
+ * @self: a #GskLayer
+ * @frame: the frame rectangle
+ *
+ * Sets the frame rectangle of a #GskLayer.
+ *
+ * This function is a convenience for setting the #GskLayer:position
+ * and #GskLayer:bounds of a layer.
+ *
+ * Since: 3.18
+ */
+void
+gsk_layer_set_frame (GskLayer *self,
+ const graphene_rect_t *frame)
+{
+ GeometryInfo *info;
+
+ g_return_if_fail (GSK_IS_LAYER (self));
+ g_return_if_fail (frame != NULL);
+
+ info = gsk_layer_state_get_geometry_info (gsk_layer_get_state (self));
+
+ info->bounds.origin.x = 0.f;
+ info->bounds.origin.y = 0.f;
+ info->bounds.size.width = frame->size.width;
+ info->bounds.size.height = frame->size.height;
+
+ info->position.x = frame->origin.x
+ + (info->bounds.origin.x + info->bounds.size.width)
+ * info->pivot_point.x;
+ info->position.y = frame->origin.y
+ + (info->bounds.origin.y + info->bounds.size.height)
+ * info->pivot_point.y;
+
+ gsk_layer_maybe_queue_relayout (self);
+}
+
+/**
+ * gsk_layer_get_frame:
+ * @self: a #GskLayer
+ * @frame: (out caller-allocates): the frame rectangle
+ *
+ * Retrieves the frame rectangle of a #GskLayer.
+ *
+ * The rectangle is determined by the #GskLayer:bounds of the layer,
+ * as well as its #GskLayer:position and #GskLayer:pivot-point.
+ *
+ * The frame is unaffected by transformations.
+ *
+ * Since: 3.18
+ */
+void
+gsk_layer_get_frame (GskLayer *self,
+ graphene_rect_t *frame)
+{
+ g_return_if_fail (GSK_IS_LAYER (self));
+ g_return_if_fail (frame != NULL);
+
+ gsk_layer_state_get_frame (gsk_layer_get_state (self), frame);
+
+ /* If the layer has no parent, the origin is set to (0, 0) */
+ if (GSK_LAYER_PRIV (self)->parent == NULL)
+ graphene_point_init (&frame->origin, 0, 0);
+
+ graphene_rect_round_to_pixel (frame);
+}
+
+/**
+ * gsk_layer_get_preferred_size:
+ * @self: a #GskLayer
+ * @size: (out caller-allocates): the preferred size of the layer
+ *
+ * Retrieves the preferred size of a #GskLayer.
+ *
+ * If the layer does not override the #GskLayerClass.get_preferred_size
+ * virtual function, this function returns the size of the #GskLayer:bounds
+ * property.
+ *
+ * Since: 3.18
+ */
+void
+gsk_layer_get_preferred_size (GskLayer *self,
+ graphene_size_t *size)
+{
+ g_return_if_fail (GSK_IS_LAYER (self));
+ g_return_if_fail (size != NULL);
+
+ GSK_LAYER_GET_CLASS (self)->get_preferred_size (self, size);
+}
+
+void
+gsk_layer_set_rotation (GskLayer *self,
+ const graphene_euler_t *rotation)
+{
+ TransformInfo *info;
+
+ g_return_if_fail (GSK_IS_LAYER (self));
+ g_return_if_fail (rotation != NULL);
+
+ info = gsk_layer_state_get_transform_info (gsk_layer_get_state (self));
+ graphene_euler_init_from_euler (&info->rotation, rotation);
+ info->needs_modelview_update = TRUE;
+ info->needs_inverse_update = TRUE;
+
+ gsk_layer_queue_redraw (self);
+}
+
+void
+gsk_layer_get_rotation (GskLayer *self,
+ graphene_euler_t *rotation)
+{
+ const TransformInfo *info;
+
+ g_return_if_fail (GSK_IS_LAYER (self));
+ g_return_if_fail (rotation != NULL);
+
+ info = gsk_layer_state_peek_transform_info (gsk_layer_get_state (self));
+ graphene_euler_init_from_euler (rotation, &info->rotation);
+}
+
+void
+gsk_layer_set_scale (GskLayer *self,
+ float scale_x,
+ float scale_y)
+{
+ TransformInfo *info;
+
+ g_return_if_fail (GSK_IS_LAYER (self));
+
+ info = gsk_layer_state_get_transform_info (gsk_layer_get_state (self));
+ graphene_vec3_init (&info->scale, scale_x, scale_y, graphene_vec3_get_z (&info->scale));
+
+ info->needs_modelview_update = TRUE;
+ info->needs_inverse_update = TRUE;
+
+ gsk_layer_queue_redraw (self);
+}
+
+void
+gsk_layer_get_scale (GskLayer *self,
+ float *scale_x,
+ float *scale_y)
+{
+ const TransformInfo *info;
+
+ g_return_if_fail (GSK_IS_LAYER (self));
+
+ info = gsk_layer_state_peek_transform_info (gsk_layer_get_state (self));
+ if (scale_x != NULL)
+ *scale_x = graphene_vec3_get_x (&info->scale);
+ if (scale_y != NULL)
+ *scale_y = graphene_vec3_get_y (&info->scale);
+}
+
+static inline void
+gsk_layer_maybe_update_modelview (GskLayer *self)
+{
+ TransformInfo *tinfo;
+#if 0
+ const GeometryInfo *ginfo;
+ graphene_point3d_t pivot, unpivot;
+
+ ginfo = gsk_layer_state_peek_geometry_info (gsk_layer_get_state (self));
+#endif
+
+ tinfo = gsk_layer_state_get_transform_info (gsk_layer_get_state (self));
+
+ if (!tinfo->needs_modelview_update)
+ return;
+
+ g_print (G_STRLOC ": Updating modelview...\n");
+
+ /* Set the initial state of the modelview */
+ if (GSK_LAYER_PRIV (self)->parent != NULL)
+ {
+ GskLayerState *parent_state;
+ const TransformInfo *parent_info;
+
+ parent_state = gsk_layer_get_state (GSK_LAYER_PRIV (self)->parent);
+ parent_info = gsk_layer_state_get_transform_info (parent_state);
+
+ graphene_matrix_init_from_matrix (&tinfo->modelview, &parent_info->child_transform);
+ }
+ else
+ graphene_matrix_init_identity (&tinfo->modelview);
+
+#if 0
+ pivot.x = ginfo->pivot_point.x * (ginfo->bounds.origin.x + ginfo->bounds.size.width);
+ pivot.y = ginfo->pivot_point.y * (ginfo->bounds.origin.y + ginfo->bounds.size.height);
+ pivot.z = tinfo->pivot_z;
+
+ unpivot.x = -pivot.x;
+ unpivot.y = -pivot.y;
+ unpivot.z = -pivot.z;
+
+ graphene_matrix_translate (&tinfo->modelview, &pivot);
+#endif
+
+ /* If the transform matrix is unset, we apply the decomposed
+ * transformations, otherwise we only apply the transform
+ * matrix
+ */
+ if (graphene_matrix_is_identity (&tinfo->transform))
+ {
+ graphene_point3d_t translation =
+ GRAPHENE_POINT3D_INIT (tinfo->translate.x,
+ tinfo->translate.y,
+ tinfo->translate.z);
+
+ /* Apply the pivot translation (and the addition translation) */
+ graphene_matrix_translate (&tinfo->modelview, &translation);
+
+ /* Then the scale */
+ graphene_matrix_scale (&tinfo->modelview,
+ graphene_vec3_get_x (&tinfo->scale),
+ graphene_vec3_get_y (&tinfo->scale),
+ graphene_vec3_get_z (&tinfo->scale));
+
+ /* Then the rotation */
+ {
+ graphene_quaternion_t q;
+
+ graphene_quaternion_init_from_euler (&q, &tinfo->rotation);
+ graphene_matrix_rotate_quaternion (&tinfo->modelview, &q);
+ }
+
+ GSK_NOTE (GEOMETRY, g_print ("Applying decomposed translations to %s\n",
+ gsk_layer_get_debug_name (self)));
+ }
+ else
+ {
+ /* Apply the transform matrix as is */
+ GSK_NOTE (GEOMETRY, g_print ("Applying transform to %s\n",
+ gsk_layer_get_debug_name (self)));
+ graphene_matrix_multiply (&tinfo->modelview,
+ &tinfo->transform,
+ &tinfo->modelview);
+ }
+
+#if 0
+ /* Undo the pivot translation */
+ graphene_matrix_translate (&tinfo->modelview, &unpivot);
+#endif
+
+ tinfo->needs_modelview_update = FALSE;
+ tinfo->needs_inverse_update = TRUE;
+}
+
+const graphene_matrix_t *
+gsk_layer_get_modelview (GskLayer *self)
+{
+ TransformInfo *info;
+
+ gsk_layer_maybe_update_modelview (self);
+ info = gsk_layer_state_get_transform_info (gsk_layer_get_state (self));
+ g_assert (!info->needs_modelview_update);
+
+ return &info->modelview;
+}
+
+const graphene_matrix_t *
+gsk_layer_get_inverse_modelview (GskLayer *self)
+{
+ TransformInfo *info;
+
+ gsk_layer_maybe_update_modelview (self);
+ info = gsk_layer_state_get_transform_info (gsk_layer_get_state (self));
+ if (!info->needs_inverse_update)
+ return &info->inverse;
+
+ graphene_matrix_inverse (&info->modelview, &info->inverse);
+ info->needs_inverse_update = FALSE;
+
+ return &info->inverse;
+}
+
+void
+gsk_layer_set_background_color (GskLayer *self,
+ const GdkRGBA *background_color)
+{
+ RenderInfo *info;
+
+ g_return_if_fail (GSK_IS_LAYER (self));
+ g_return_if_fail (background_color != NULL);
+
+ info = gsk_layer_state_get_render_info (gsk_layer_get_state (self));
+ info->background_color = *background_color;
+ gsk_layer_queue_redraw (self);
+}
+
+void
+gsk_layer_set_opacity (GskLayer *self,
+ double opacity)
+{
+ RenderInfo *info;
+
+ g_return_if_fail (GSK_IS_LAYER (self));
+
+ info = gsk_layer_state_get_render_info (gsk_layer_get_state (self));
+ info->opacity = CLAMP (opacity, 0, 1);
+ gsk_layer_queue_redraw (self);
+}
+
+void
+gsk_layer_set_clip (GskLayer *self,
+ const graphene_rect_t *clip)
+{
+ RenderInfo *info;
+
+ g_return_if_fail (GSK_IS_LAYER (self));
+
+ info = gsk_layer_state_get_render_info (gsk_layer_get_state (self));
+ if (clip == NULL)
+ {
+ info->use_clip = FALSE;
+ return;
+ }
+
+ graphene_rect_init_from_rect (&info->clip, clip);
+ info->use_clip = TRUE;
+
+ gsk_layer_queue_redraw (self);
+}
+
+void
+gsk_layer_get_clip (GskLayer *self,
+ graphene_rect_t *clip)
+{
+ const RenderInfo *info;
+
+ g_return_if_fail (GSK_IS_LAYER (self));
+ g_return_if_fail (clip != NULL);
+
+ info = gsk_layer_state_peek_render_info (gsk_layer_get_state (self));
+ if (info->use_clip)
+ {
+ graphene_rect_init_from_rect (clip, &info->clip);
+ return;
+ }
+
+ gsk_layer_get_bounds (self, clip);
+}
+
+static void
+gsk_layer_revalidate_layout (GskLayer *root)
+{
+ if (!GSK_LAYER_PRIV (root)->needs_relayout)
+ return;
+
+ if (GSK_LAYER_PRIV (root)->in_layout)
+ return;
+
+ GSK_LAYER_PRIV (root)->in_layout = TRUE;
+
+ GSK_LAYER_PRIV (root)->needs_relayout = FALSE;
+
+ if (GSK_LAYER_PRIV (root)->n_children != 0)
+ g_signal_emit (root, layer_signals[LAYOUT_CHILDREN], 0);
+
+ GSK_LAYER_PRIV (root)->in_layout = FALSE;
+}
+
+void
+gsk_layer_set_frame_clock (GskLayer *self,
+ GdkFrameClock *frame_clock)
+{
+ GskLayerPrivate *priv = gsk_layer_get_instance_private (self);
+ GdkFrameClock *old_frame_clock;
+
+ g_return_if_fail (GSK_IS_LAYER (self));
+ g_return_if_fail (priv->parent == NULL);
+ g_return_if_fail (frame_clock == NULL || GDK_IS_FRAME_CLOCK (frame_clock));
+
+ old_frame_clock = priv->frame_clock;
+
+ if (g_set_object (&priv->frame_clock, frame_clock))
+ {
+ if (old_frame_clock != NULL)
+ g_signal_handlers_disconnect_by_func (old_frame_clock,
+ gsk_layer_revalidate_layout,
+ self);
+
+ if (priv->frame_clock != NULL)
+ {
+ g_signal_connect_object (priv->frame_clock, "layout",
+ G_CALLBACK (gsk_layer_revalidate_layout),
+ self,
+ G_CONNECT_SWAPPED);
+ gsk_layer_queue_relayout (self);
+ }
+ }
+}
diff --git a/gsk/gsklayer.h b/gsk/gsklayer.h
new file mode 100644
index 0000000..ff582fb
--- /dev/null
+++ b/gsk/gsklayer.h
@@ -0,0 +1,223 @@
+/* GSK - The GTK scene graph toolkit
+ * Copyright 2015 Emmanuele Bassi
+ *
+ * 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.1 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/>.
+ */
+
+#ifndef __GSK_LAYER_H__
+#define __GSK_LAYER_H__
+
+#if !defined (__GSK_H_INSIDE__) && !defined (GSK_COMPILATION)
+#error "Only <gsk/gsk.h> can be included directly."
+#endif
+
+#include <gsk/gsktypes.h>
+#include <gsk/gsklayeriter.h>
+
+G_BEGIN_DECLS
+
+#define GSK_TYPE_LAYER (gsk_layer_get_type ())
+
+GDK_AVAILABLE_IN_3_18
+G_DECLARE_DERIVABLE_TYPE (GskLayer, gsk_layer, GSK, LAYER, GInitiallyUnowned)
+
+/**
+ * GskLayerClass:
+ *
+ * The `GskLayerClass` structure contains only private data.
+ *
+ * Since: 3.16
+ */
+struct _GskLayerClass
+{
+ /*< private >*/
+ GInitiallyUnownedClass parent_class;
+
+ /*< public >*/
+ gboolean (* queue_redraw) (GskLayer *self,
+ GskLayer *origin);
+ gboolean (* queue_relayout) (GskLayer *self,
+ GskLayer *origin);
+
+ void (* get_preferred_size) (GskLayer *self,
+ graphene_size_t *size);
+
+ void (* child_added) (GskLayer *self,
+ GskLayer *child);
+ void (* child_removed) (GskLayer *self,
+ GskLayer *child);
+
+ void (* layout_children) (GskLayer *self);
+
+ gboolean (* draw) (GskLayer *self,
+ cairo_t *cr);
+
+ /*< private >*/
+ gpointer _padding[16];
+};
+
+GDK_AVAILABLE_IN_3_18
+GskLayer * gsk_layer_new (void);
+
+/* Scene graph */
+
+GDK_AVAILABLE_IN_3_18
+GskLayer * gsk_layer_get_parent (GskLayer *self);
+GDK_AVAILABLE_IN_3_18
+GskLayer * gsk_layer_get_next_sibling (GskLayer *self);
+GDK_AVAILABLE_IN_3_18
+GskLayer * gsk_layer_get_prev_sibling (GskLayer *self);
+GDK_AVAILABLE_IN_3_18
+GskLayer * gsk_layer_get_first_child (GskLayer *self);
+GDK_AVAILABLE_IN_3_18
+GskLayer * gsk_layer_get_last_child (GskLayer *self);
+GDK_AVAILABLE_IN_3_18
+guint gsk_layer_get_n_children (GskLayer *self);
+GDK_AVAILABLE_IN_3_18
+GList * gsk_layer_get_children (GskLayer *self);
+
+GDK_AVAILABLE_IN_3_18
+GskLayer * gsk_layer_add_child (GskLayer *self,
+ GskLayer *child);
+GDK_AVAILABLE_IN_3_18
+GskLayer * gsk_layer_remove_child (GskLayer *self,
+ GskLayer *child);
+GDK_AVAILABLE_IN_3_18
+GskLayer * gsk_layer_insert_child_at_index (GskLayer *self,
+ GskLayer *child,
+ int index_);
+GDK_AVAILABLE_IN_3_18
+GskLayer * gsk_layer_insert_child_after (GskLayer *self,
+ GskLayer *child,
+ GskLayer *sibling);
+GDK_AVAILABLE_IN_3_18
+GskLayer * gsk_layer_insert_child_before (GskLayer *self,
+ GskLayer *child,
+ GskLayer *sibling);
+GDK_AVAILABLE_IN_3_18
+GskLayer * gsk_layer_replace_child (GskLayer *self,
+ GskLayer *old_child,
+ GskLayer *new_child);
+GDK_AVAILABLE_IN_3_18
+GskLayer * gsk_layer_get_child_at_index (GskLayer *self,
+ int index_);
+GDK_AVAILABLE_IN_3_18
+gboolean gsk_layer_contains (GskLayer *self,
+ GskLayer *descendant);
+
+GDK_AVAILABLE_IN_3_18
+void gsk_layer_queue_redraw (GskLayer *self);
+GDK_AVAILABLE_IN_3_18
+gboolean gsk_layer_needs_redraw (GskLayer *self);
+GDK_AVAILABLE_IN_3_18
+void gsk_layer_queue_relayout (GskLayer *self);
+GDK_AVAILABLE_IN_3_18
+gboolean gsk_layer_needs_relayout (GskLayer *self);
+
+GDK_AVAILABLE_IN_3_18
+void gsk_layer_set_hidden (GskLayer *self,
+ gboolean hidden);
+GDK_AVAILABLE_IN_3_18
+gboolean gsk_layer_get_hidden (GskLayer *self);
+GDK_AVAILABLE_IN_3_18
+void gsk_layer_set_opacity (GskLayer *self,
+ double opacity);
+GDK_AVAILABLE_IN_3_18
+double gsk_layer_get_opacity (GskLayer *self);
+GDK_AVAILABLE_IN_3_18
+void gsk_layer_set_background_color (GskLayer *self,
+ const GdkRGBA *bg_color);
+GDK_AVAILABLE_IN_3_18
+void gsk_layer_get_background_color (GskLayer *self,
+ GdkRGBA *bg_color);
+GDK_AVAILABLE_IN_3_18
+void gsk_layer_set_clip (GskLayer *self,
+ const graphene_rect_t *clip);
+GDK_AVAILABLE_IN_3_18
+void gsk_layer_get_clip (GskLayer *self,
+ graphene_rect_t *clip);
+
+/* Geometry */
+
+GDK_AVAILABLE_IN_3_18
+void gsk_layer_set_frame (GskLayer *self,
+ const graphene_rect_t *frame);
+GDK_AVAILABLE_IN_3_18
+void gsk_layer_get_frame (GskLayer *self,
+ graphene_rect_t *frame);
+GDK_AVAILABLE_IN_3_18
+void gsk_layer_set_pivot_point (GskLayer *self,
+ const graphene_point_t *point);
+GDK_AVAILABLE_IN_3_18
+void gsk_layer_get_pivot_point (GskLayer *self,
+ graphene_point_t *point);
+GDK_AVAILABLE_IN_3_18
+void gsk_layer_set_bounds (GskLayer *self,
+ const graphene_rect_t *bounds);
+GDK_AVAILABLE_IN_3_18
+void gsk_layer_get_bounds (GskLayer *self,
+ graphene_rect_t *bounds);
+GDK_AVAILABLE_IN_3_18
+void gsk_layer_set_position (GskLayer *self,
+ const graphene_point_t *position);
+GDK_AVAILABLE_IN_3_18
+void gsk_layer_get_position (GskLayer *self,
+ graphene_point_t *position);
+
+GDK_AVAILABLE_IN_3_18
+void gsk_layer_get_preferred_size (GskLayer *self,
+ graphene_size_t *size);
+
+/* Transformations */
+
+GDK_AVAILABLE_IN_3_18
+void gsk_layer_set_rotation (GskLayer *self,
+ const graphene_euler_t *rotation);
+GDK_AVAILABLE_IN_3_18
+void gsk_layer_get_rotation (GskLayer *self,
+ graphene_euler_t *rotation);
+GDK_AVAILABLE_IN_3_18
+void gsk_layer_set_scale (GskLayer *self,
+ float scale_x,
+ float scale_y);
+GDK_AVAILABLE_IN_3_18
+void gsk_layer_get_scale (GskLayer *self,
+ float *scale_x,
+ float *scale_y);
+GDK_AVAILABLE_IN_3_18
+void gsk_layer_set_translation (GskLayer *self,
+ const graphene_point3d_t *translation);
+GDK_AVAILABLE_IN_3_18
+void gsk_layer_get_translation (GskLayer *self,
+ graphene_point3d_t *translation);
+GDK_AVAILABLE_IN_3_18
+void gsk_layer_set_transform (GskLayer *self,
+ const graphene_matrix_t *transform);
+GDK_AVAILABLE_IN_3_18
+void gsk_layer_get_transform (GskLayer *self,
+ graphene_matrix_t *transform);
+GDK_AVAILABLE_IN_3_18
+void gsk_layer_set_child_transform (GskLayer *self,
+ const graphene_matrix_t *transform);
+GDK_AVAILABLE_IN_3_18
+void gsk_layer_get_child_transform (GskLayer *self,
+ graphene_matrix_t *transform);
+
+GDK_AVAILABLE_IN_3_18
+void gsk_layer_set_frame_clock (GskLayer *self,
+ GdkFrameClock *frame_clock);
+
+G_END_DECLS
+
+#endif /* __GSK_LAYER_H__ */
diff --git a/gsk/gsklayeriter.c b/gsk/gsklayeriter.c
new file mode 100644
index 0000000..5a51f76
--- /dev/null
+++ b/gsk/gsklayeriter.c
@@ -0,0 +1,204 @@
+/* GSK - The GTK scene graph toolkit
+ * Copyright 2015 Emmanuele Bassi
+ *
+ * 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.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gsklayerprivate.h"
+
+static GskLayerIter * gsk_layer_iter_copy (GskLayerIter *iter);
+static void gsk_layer_iter_free (GskLayerIter *iter);
+
+G_DEFINE_BOXED_TYPE (GskLayerIter, gsk_layer_iter, gsk_layer_iter_copy, gsk_layer_iter_free)
+
+/**
+ * gsk_layer_iter_init:
+ * @iter: the #GskLayerIter to initialize
+ * @root: a #GskLayer
+ *
+ * Initializes a #GskLayerIter with a #GskLayer as its @root.
+ *
+ * The initialized iterator can be used to traverse the children of
+ * the @root layer. You typically use a #GskLayerIter like this:
+ *
+ * |[<!-- language="C" -->
+ * GskLayerIter iter;
+ * GskLayer *child;
+ *
+ * gsk_layer_iter_init (&iter, layer);
+ * while (gsk_layer_iter_next (&iter, &child))
+ * {
+ * // ...
+ * }
+ * ]|
+ *
+ * Modifying the list of children of @root while iterating will
+ * invalidate the iterator. Use gsk_layer_iter_remove() to safely
+ * remove children while iterating.
+ *
+ * Since: 3.18
+ */
+void
+gsk_layer_iter_init (GskLayerIter *iter,
+ GskLayer *root)
+{
+ g_return_if_fail (iter != NULL);
+ g_return_if_fail (GSK_IS_LAYER (root));
+
+ iter->root = root;
+ iter->root_age = gsk_layer_get_age (iter->root);
+ iter->current = NULL;
+}
+
+/**
+ * gsk_layer_iter_is_valid:
+ * @iter: a #GskLayerIter
+ *
+ * Checks whether @iter is valid or not.
+ *
+ * A #GskLayerIter is valid if:
+ *
+ * - it has been initialized with a #GskLayer
+ * - the list of children of #GskLayer hasn't changed
+ *
+ * Returns: %TRUE if the iterator is valid
+ *
+ * Since: 3.18
+ */
+gboolean
+gsk_layer_iter_is_valid (GskLayerIter *iter)
+{
+ if (iter == NULL)
+ return FALSE;
+
+ if (iter->root == NULL)
+ return FALSE;
+
+ return gsk_layer_get_age (iter->root) == iter->root_age;
+}
+
+/**
+ * gsk_layer_iter_next:
+ * @iter: a #GskLayerIter
+ * @child: (out) (transfer none): the next layer in the list of children
+ *
+ * Advances the iterator to the next child of the #GskLayer used to
+ * initialize the #GskLayerIter.
+ *
+ * If the iterator successfully advanced, this function returns %TRUE and
+ * the child #GskLayer.
+ *
+ * If the iterator could not advance, this function returns %FALSE.
+ *
+ * Returns: %TRUE if the iterator advanced
+ *
+ * Since: 3.18
+ */
+gboolean
+gsk_layer_iter_next (GskLayerIter *iter,
+ GskLayer **child)
+{
+ g_return_val_if_fail (gsk_layer_iter_is_valid (iter), FALSE);
+
+ if (iter->current == NULL)
+ iter->current = gsk_layer_get_first_child (iter->root);
+ else
+ iter->current = gsk_layer_get_next_sibling (iter->current);
+
+ if (child != NULL)
+ *child = iter->current;
+
+ return iter->current != NULL;
+}
+
+/**
+ * gsk_layer_iter_prev:
+ * @iter: a #GskLayerIte
+ * @child: (out) (transfer none): the previous layer in the list of children
+ *
+ * Advances the iterator to the previous child of the #GskLayer used to
+ * initialize the #GskLayerIter.
+ *
+ * If the iterator successfully advanced, this function returns %TRUE and
+ * the child #GskLayer.
+ *
+ * If the iterator could not advance, this function returns %FALSE.
+ *
+ * Returns: %TRUE if the iterator advanced
+ *
+ * Since: 3.18
+ */
+gboolean
+gsk_layer_iter_prev (GskLayerIter *iter,
+ GskLayer **child)
+{
+ g_return_val_if_fail (gsk_layer_iter_is_valid (iter), FALSE);
+
+ if (iter->current == NULL)
+ iter->current = gsk_layer_get_last_child (iter->root);
+ else
+ iter->current = gsk_layer_get_prev_sibling (iter->current);
+
+ if (child != NULL)
+ *child = iter->current;
+
+ return iter->current != NULL;
+}
+
+/**
+ * gsk_layer_iter_remove:
+ * @iter: a #GskLayerIter
+ *
+ * Safely removes the #GskLayer currently pointed by a #GskLayerIter
+ * from the list of children of the layer used to initialize the @iter.
+ *
+ * This function can only be called after gsk_layer_iter_next() or
+ * gsk_layer_iter_prev() returned %TRUE.
+ *
+ * This function will call gsk_layer_remove_child() internally.
+ *
+ * Since: 3.18
+ */
+void
+gsk_layer_iter_remove (GskLayerIter *iter)
+{
+ GskLayer *cur;
+
+ g_return_if_fail (gsk_layer_iter_is_valid (iter));
+
+ cur = iter->current;
+ if (cur != NULL)
+ {
+ iter->current = gsk_layer_get_prev_sibling (cur);
+
+ gsk_layer_remove_child (iter->root, cur);
+
+ /* Increase the age to match remove_child() */
+ iter->root_age += 1;
+ }
+}
+
+static GskLayerIter *
+gsk_layer_iter_copy (GskLayerIter *src)
+{
+ return g_slice_dup (GskLayerIter, src);
+}
+
+static void
+gsk_layer_iter_free (GskLayerIter *iter)
+{
+ g_slice_free (GskLayerIter, iter);
+}
diff --git a/gsk/gsklayeriter.h b/gsk/gsklayeriter.h
new file mode 100644
index 0000000..f967821
--- /dev/null
+++ b/gsk/gsklayeriter.h
@@ -0,0 +1,74 @@
+/* GSK - The GTK scene graph toolkit
+ * Copyright 2015 Emmanuele Bassi
+ *
+ * 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.1 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/>.
+ */
+
+#ifndef __GSK_LAYER_ITER_H__
+#define __GSK_LAYER_ITER_H__
+
+#if !defined (__GSK_H_INSIDE__) && !defined (GSK_COMPILATION)
+#error "Only <gsk/gsk.h> can be included directly."
+#endif
+
+#include <gsk/gsktypes.h>
+
+G_BEGIN_DECLS
+
+#define GSK_TYPE_LAYER_ITER (gsk_layer_iter_get_type ())
+
+/**
+ * GskLayerIter:
+ *
+ * A structure that allows to efficiently iterate over a branch
+ * of the scene graph.
+ *
+ * You should typicall place a `GskLayerIter` structure on the
+ * stack.
+ *
+ * The contents of the `GskLayerIter` structure are private and
+ * should only be accessed using the provided API.
+ *
+ * Since: 3.18
+ */
+struct _GskLayerIter
+{
+ /*< private >*/
+ GSK_PRIVATE_FIELD (GskLayer *, root);
+ GSK_PRIVATE_FIELD (GskLayer *, current);
+ GSK_PRIVATE_FIELD (gint64, root_age);
+ GSK_PRIVATE_FIELD (gpointer, padding1);
+ GSK_PRIVATE_FIELD (gpointer, padding2);
+};
+
+GDK_AVAILABLE_IN_3_18
+GType gsk_layer_iter_get_type (void) G_GNUC_CONST;
+
+GDK_AVAILABLE_IN_3_18
+void gsk_layer_iter_init (GskLayerIter *iter,
+ GskLayer *root);
+GDK_AVAILABLE_IN_3_18
+gboolean gsk_layer_iter_is_valid (GskLayerIter *iter);
+GDK_AVAILABLE_IN_3_18
+gboolean gsk_layer_iter_next (GskLayerIter *iter,
+ GskLayer **child);
+GDK_AVAILABLE_IN_3_18
+gboolean gsk_layer_iter_prev (GskLayerIter *iter,
+ GskLayer **child);
+GDK_AVAILABLE_IN_3_18
+void gsk_layer_iter_remove (GskLayerIter *iter);
+
+G_END_DECLS
+
+#endif /* __GSK_LAYER_ITER_H__ */
diff --git a/gsk/gsklayerprivate.h b/gsk/gsklayerprivate.h
new file mode 100644
index 0000000..b18550f
--- /dev/null
+++ b/gsk/gsklayerprivate.h
@@ -0,0 +1,166 @@
+#ifndef __GSK_LAYER_PRIVATE_H__
+#define __GSK_LAYER_PRIVATE_H__
+
+#include "gsklayer.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GskLayerState GskLayerState;
+typedef struct _GeometryInfo GeometryInfo;
+typedef struct _TransformInfo TransformInfo;
+typedef struct _RenderInfo RenderInfo;
+
+/*< private >
+ * GeometryInfo:
+ * @bounds: The bounds of the layer, expressed as a rectangle
+ * with the origin in the top left corner of the layer
+ * @pivot_point: The normalized point, in layer coordinate
+ * space, used as the center for layout and transformations
+ * @position: The coordinates, in parent-relative coordinate
+ * space, of the pivot point
+ *
+ * The geometry of the layer.
+ *
+ * 20 80
+ * +======|=============|======+
+ * | |
+ * 10 - +-------------+ |
+ * | | | | h |
+ * | | | | e |
+ * | |------P------| i |
+ * | | | | g |
+ * | | | | h |
+ * 50 - +-------------+ t |
+ * | ^^^^ width ^^^^ |
+ * +===========================+
+ *
+ * The composite of all the geometry information is called "frame"
+ * and it's re-computed on request; the frame is the bounds rectangle
+ * with an origin in parent-relative coordinates. Each parent uses
+ * the frame of its children layers when drawing them.
+ *
+ * Given:
+ * - a pivot point set at (0.5, 0.5)
+ * - a bounds rectangle is: (0, 0, 60, 40)
+ * - a position P set at (100, 60)
+ *
+ * Then:
+ * - the frame rectangle is: (20, 10, 60, 40)
+ *
+ * Where:
+ * - frame.origin.x = position.x - (bounds.origin.x + bounds.size.width) * pivot.x
+ * - frame.origin.y = position.y - (bounds.origin.y + bounds.size.height) * pivot.y
+ * - frame.size.width = bounds.origin.x + bounds.size.width
+ * - frame.size.height = bounds.origin.y + bounds.size.height
+ *
+ * If we set the pivot point to (0, 0), i.e. the top-left corner of
+ * the layer, and we don't want to change the frame rectangle, we
+ * need to also set the position at (20, 10); similarly, if we set
+ * the pivot point to be (1, 1), i.e. the bottom-right corner of the
+ * layer, we need to set the position at (80, 50).
+ */
+struct _GeometryInfo
+{
+ graphene_rect_t bounds;
+
+ graphene_point_t pivot_point;
+ graphene_point_t position;
+};
+
+/*< private >
+ * TransformInfo:
+ * @rotation: the decomposed rotations, in Euler angles
+ * @scale: the decomposed scaling factors
+ * @translate: the decomposed translation factors
+ * @pivot_z: the Z component of the pivot point
+ * @transform: the additional transformation matrix for the layer
+ * @child_transform: the transformation matrix applied before
+ * rendering each child
+ * @modelview: the transformation matrix applied to a layer
+ * @inverse: the inverse of @modelview, used for hit testing
+ * @needs_modelview_update: whether the @transform needs to be updated
+ * @needs_inverse_update: whether the @inverse needs to be updated
+ *
+ * The transformations applied to a layer.
+ *
+ * The transformations are stored in two ways: decomposed, for
+ * writing simple accessors; and composed, for use when rendering
+ * and hit testing.
+ */
+struct _TransformInfo
+{
+ graphene_euler_t rotation;
+ graphene_vec3_t scale;
+ graphene_point3d_t translate;
+
+ float pivot_z;
+
+ graphene_matrix_t transform;
+ graphene_matrix_t child_transform;
+
+ graphene_matrix_t modelview;
+ graphene_matrix_t inverse;
+
+ guint needs_modelview_update : 1;
+ guint needs_inverse_update : 1;
+};
+
+/*< private >
+ * RenderInfo:
+ * @background_color: the color used to render the background
+ * @opacity: the opacity to be applied to the layer
+ *
+ * The rendering state applied to a layer when drawing.
+ */
+struct _RenderInfo
+{
+ GdkRGBA background_color;
+
+ double opacity;
+
+ graphene_rect_t clip;
+
+ guint use_clip : 1;
+};
+
+/*< private >
+ * GskLayerState:
+ * @geometry_info: the geometry of the layer
+ * @transform_info: the transformations of the layer
+ * @render_info: the content of the layer
+ *
+ * A structure representing the state of a #GskLayer.
+ */
+struct _GskLayerState
+{
+ GeometryInfo *geometry_info;
+ TransformInfo *transform_info;
+ RenderInfo *render_info;
+};
+
+gint64 gsk_layer_get_age (GskLayer *layer);
+GskLayerState * gsk_layer_get_state (GskLayer *layer);
+const char * gsk_layer_get_debug_name (GskLayer *layer);
+gboolean gsk_layer_draw (GskLayer *layer,
+ cairo_t *cr);
+
+const graphene_matrix_t *gsk_layer_get_modelview (GskLayer *layer);
+const graphene_matrix_t *gsk_layer_get_inverse_modelview (GskLayer *layer);
+
+GskLayerState * gsk_layer_state_copy (GskLayerState *state);
+void gsk_layer_state_free (GskLayerState *state);
+void gsk_layer_state_clear (GskLayerState *state);
+
+const GeometryInfo * gsk_layer_state_peek_geometry_info (GskLayerState *state);
+GeometryInfo * gsk_layer_state_get_geometry_info (GskLayerState *state);
+const TransformInfo * gsk_layer_state_peek_transform_info (GskLayerState *state);
+TransformInfo * gsk_layer_state_get_transform_info (GskLayerState *state);
+const RenderInfo * gsk_layer_state_peek_render_info (GskLayerState *state);
+RenderInfo * gsk_layer_state_get_render_info (GskLayerState *state);
+
+void gsk_layer_state_get_frame (GskLayerState *state,
+ graphene_rect_t *frame);
+
+G_END_DECLS
+
+#endif /* __GSK_LAYER_PRIVATE_H__ */
diff --git a/gsk/gsklayerstate.c b/gsk/gsklayerstate.c
new file mode 100644
index 0000000..766ea52
--- /dev/null
+++ b/gsk/gsklayerstate.c
@@ -0,0 +1,220 @@
+#include "config.h"
+
+#include "gsklayerprivate.h"
+
+GskLayerState *
+gsk_layer_state_copy (GskLayerState *src)
+{
+ GskLayerState *dest;
+
+ if (src == NULL)
+ return NULL;
+
+ dest = g_slice_new (GskLayerState);
+ dest->geometry_info = g_slice_dup (GeometryInfo, src->geometry_info);
+ dest->transform_info = g_slice_dup (TransformInfo, src->transform_info);
+ dest->render_info = g_slice_dup (RenderInfo, src->render_info);
+
+ return dest;
+}
+
+void
+gsk_layer_state_clear (GskLayerState *state)
+{
+ g_slice_free (GeometryInfo, state->geometry_info);
+ g_slice_free (TransformInfo, state->transform_info);
+ g_slice_free (RenderInfo, state->render_info);
+}
+
+void
+gsk_layer_state_free (GskLayerState *state)
+{
+ if (state != NULL)
+ {
+ gsk_layer_state_clear (state);
+
+ g_slice_free (GskLayerState, state);
+ }
+}
+
+static const GeometryInfo default_geometry_info = {
+ /* .bounds = */
+ GRAPHENE_RECT_INIT (0, 0, 0, 0),
+
+ /* .pivot_point = */
+ GRAPHENE_POINT_INIT (0.5, 0.5),
+
+ /* .position = */
+ GRAPHENE_POINT_INIT (0, 0),
+};
+
+static const RenderInfo default_render_info = {
+ /* .background_color = */
+ { 0., 0., 0., 1. },
+
+ /* .opacity = */
+ 1.,
+
+ /* .clip = */
+ GRAPHENE_RECT_INIT (0, 0, 0, 0),
+
+ /* .use_clip = */
+ FALSE,
+};
+
+/*< private >
+ * gsk_layer_state_peek_geometry_info:
+ * @state: a #GskLayerState
+ *
+ * Retrieves a read-only pointer to the layer's geometry info.
+ *
+ * Use this function when implementing getter functions.
+ *
+ * Returns: (transfer none): the geometry info of a particular layer state
+ */
+const GeometryInfo *
+gsk_layer_state_peek_geometry_info (GskLayerState *state)
+{
+ if (state->geometry_info == NULL)
+ return &default_geometry_info;
+
+ return state->geometry_info;
+}
+
+/*< private >
+ * gsk_layer_state_get_geometry_info:
+ * @state: a #GskLayerState
+ *
+ * Retrieves a pointer to the layer's geometry info.
+ *
+ * Use this function when implementing setter functions.
+ *
+ * Returns: (transfer none): the geometry info of a particular layer state
+ */
+GeometryInfo *
+gsk_layer_state_get_geometry_info (GskLayerState *state)
+{
+ if (state->geometry_info == NULL)
+ state->geometry_info = g_slice_dup (GeometryInfo, &default_geometry_info);
+
+ return state->geometry_info;
+}
+
+static void
+transform_info_init (TransformInfo *info)
+{
+ graphene_euler_init (&info->rotation, 0.f, 0.f, 0.f);
+ graphene_vec3_init (&info->scale, 1.f, 1.f, 1.f);
+ graphene_point3d_init (&info->translate, 0.f, 0.f, 0.f);
+
+ info->pivot_z = 0.f;
+
+ graphene_matrix_init_identity (&info->transform);
+ graphene_matrix_init_identity (&info->child_transform);
+
+ graphene_matrix_init_identity (&info->modelview);
+ graphene_matrix_init_identity (&info->inverse);
+
+ info->needs_modelview_update = FALSE;
+ info->needs_inverse_update = FALSE;
+}
+
+/*< private >
+ * gsk_layer_state_peek_transform_info:
+ * @state: a #GskLayerState
+ *
+ * Retrieves a read-only pointer to the layer's transformation info.
+ *
+ * Use this function when implementing getters.
+ *
+ * Returns: (transfer none): the transformation info of a particular layer state
+ */
+const TransformInfo *
+gsk_layer_state_peek_transform_info (GskLayerState *state)
+{
+ static TransformInfo default_transform_info;
+ static gboolean default_transform_info_set;
+
+ if (G_UNLIKELY (!default_transform_info_set))
+ transform_info_init (&default_transform_info);
+
+ if (state->transform_info == NULL)
+ return &default_transform_info;
+
+ return state->transform_info;
+}
+
+/*< private >
+ * gsk_layer_state_get_transform_info:
+ * @state: a #GskLayerState
+ *
+ * Retrieves a pointer to the layer's transformation info.
+ *
+ * Returns: (transfer none): the transformation info of a particular layer state
+ */
+TransformInfo *
+gsk_layer_state_get_transform_info (GskLayerState *state)
+{
+ if (state->transform_info == NULL)
+ {
+ state->transform_info = g_slice_new (TransformInfo);
+ transform_info_init (state->transform_info);
+ }
+
+ return state->transform_info;
+}
+
+/*< private >
+ * gsk_layer_state_peek_render_info:
+ * @state: a #GskLayerState
+ *
+ * Retrieves a read-only pointer to the layer's rendering info.
+ *
+ * Use this function when implementing getters.
+ *
+ * Returns: (transfer none): the rendering info of a particular layer state
+ */
+const RenderInfo *
+gsk_layer_state_peek_render_info (GskLayerState *state)
+{
+ if (state->render_info == NULL)
+ return &default_render_info;
+
+ return state->render_info;
+}
+
+/*< private >
+ * gsk_layer_state_get_render_info:
+ * @state: a #GskLayerState
+ *
+ * Retrieves a pointer to the layer's rendering info.
+ *
+ * Use this function when implementing setters.
+ *
+ * Returns: (transfer none): the rendering info of a particular layer state
+ */
+RenderInfo *
+gsk_layer_state_get_render_info (GskLayerState *state)
+{
+ if (state->render_info == NULL)
+ state->render_info = g_slice_dup (RenderInfo, &default_render_info);
+
+ return state->render_info;
+}
+
+void
+gsk_layer_state_get_frame (GskLayerState *state,
+ graphene_rect_t *frame)
+{
+ const GeometryInfo *info = gsk_layer_state_peek_geometry_info (state);
+
+ frame->origin.x = info->position.x
+ - (info->bounds.origin.x + info->bounds.size.width)
+ * info->pivot_point.x;
+ frame->origin.y = info->position.y
+ - (info->bounds.origin.y + info->bounds.size.height)
+ * info->pivot_point.y;
+
+ frame->size.width = info->bounds.size.width;
+ frame->size.height = info->bounds.size.height;
+}
diff --git a/gsk/gskmacros.h b/gsk/gskmacros.h
new file mode 100644
index 0000000..dca266b
--- /dev/null
+++ b/gsk/gskmacros.h
@@ -0,0 +1,31 @@
+/* GSK - The GTK scene graph toolkit
+ * Copyright 2015 Emmanuele Bassi
+ *
+ * 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.1 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/>.
+ */
+
+#ifndef __GSK_MACROS_H__
+#define __GSK_MACROS_H__
+
+#if !defined (__GSK_H_INSIDE__) && !defined (GSK_COMPILATION)
+#error "Only <gsk/gsk.h> can be included directly."
+#endif
+
+#ifdef GSK_COMPILATION
+#define GSK_PRIVATE_FIELD(TypeName,field_name) TypeName field_name
+#else
+#define GSK_PRIVATE_FIELD(TypeName,field_name) TypeName __gsk_private_ ## field_name
+#endif
+
+#endif /* __GSK_MACROS_H__ */
diff --git a/gsk/gsktypes.h b/gsk/gsktypes.h
new file mode 100644
index 0000000..00eb701
--- /dev/null
+++ b/gsk/gsktypes.h
@@ -0,0 +1,37 @@
+/* GSK - The GTK scene graph toolkit
+ * Copyright 2015 Emmanuele Bassi
+ *
+ * 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/>.
+ */
+
+#ifndef __GSK_TYPES_H__
+#define __GSK_TYPES_H__
+
+#if !defined (__GSK_H_INSIDE__) && !defined (GSK_COMPILATION)
+#error "Only <gsk/gsk.h> can be included directly."
+#endif
+
+#include <glib.h>
+#include <graphene.h>
+#include <gdk/gdk.h>
+#include <gsk/gskmacros.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GskLayer GskLayer;
+typedef struct _GskLayerIter GskLayerIter;
+
+G_END_DECLS
+
+#endif /* __GSK_TYPES_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]