|
|
@@ -0,0 +1,217 @@
|
|
|
+/*
|
|
|
+ * m2gtkshim.c - small C support library for the m2-GTK4 Modula-2 bindings.
|
|
|
+ *
|
|
|
+ * GNU Modula-2 cannot register GObject subclasses (no class_init /
|
|
|
+ * vfunc overrides), which blocks widgets and paintables that need a
|
|
|
+ * custom snapshot. This shim provides two such types whose snapshot
|
|
|
+ * vfuncs hand a Cairo context to a Modula-2 callback, so the drawing
|
|
|
+ * itself reuses the existing Cairo binding:
|
|
|
+ *
|
|
|
+ * M2GtkWidget - a GtkWidget subclass; the M2 callback is
|
|
|
+ * void draw(GtkWidget *w, cairo_t *cr,
|
|
|
+ * int width, int height, gpointer user);
|
|
|
+ * M2Paintable - a GdkPaintable implementation; the M2 callback is
|
|
|
+ * void draw(cairo_t *cr, int width, int height,
|
|
|
+ * gpointer user);
|
|
|
+ *
|
|
|
+ * The M2 callback addresses and user data are supplied at construction;
|
|
|
+ * an optional destroy notify frees the user data.
|
|
|
+ */
|
|
|
+
|
|
|
+#include <gtk/gtk.h>
|
|
|
+
|
|
|
+/* ------------------------------------------------------------------ */
|
|
|
+/* M2GtkWidget */
|
|
|
+/* ------------------------------------------------------------------ */
|
|
|
+
|
|
|
+typedef void (*M2WidgetDrawFunc) (GtkWidget *widget, cairo_t *cr,
|
|
|
+ int width, int height, gpointer user_data);
|
|
|
+
|
|
|
+typedef struct _M2GtkWidget M2GtkWidget;
|
|
|
+typedef struct _M2GtkWidgetClass M2GtkWidgetClass;
|
|
|
+
|
|
|
+struct _M2GtkWidget
|
|
|
+{
|
|
|
+ GtkWidget parent_instance;
|
|
|
+ M2WidgetDrawFunc draw;
|
|
|
+ gpointer user_data;
|
|
|
+ GDestroyNotify destroy;
|
|
|
+};
|
|
|
+
|
|
|
+struct _M2GtkWidgetClass
|
|
|
+{
|
|
|
+ GtkWidgetClass parent_class;
|
|
|
+};
|
|
|
+
|
|
|
+G_DEFINE_TYPE (M2GtkWidget, m2_gtk_widget, GTK_TYPE_WIDGET)
|
|
|
+
|
|
|
+static void
|
|
|
+m2_gtk_widget_snapshot (GtkWidget *widget, GtkSnapshot *snapshot)
|
|
|
+{
|
|
|
+ M2GtkWidget *self = (M2GtkWidget *) widget;
|
|
|
+ int width = gtk_widget_get_width (widget);
|
|
|
+ int height = gtk_widget_get_height (widget);
|
|
|
+
|
|
|
+ if (self->draw == NULL || width <= 0 || height <= 0)
|
|
|
+ return;
|
|
|
+
|
|
|
+ graphene_rect_t bounds = GRAPHENE_RECT_INIT (0.f, 0.f, width, height);
|
|
|
+ cairo_t *cr = gtk_snapshot_append_cairo (snapshot, &bounds);
|
|
|
+ self->draw (widget, cr, width, height, self->user_data);
|
|
|
+ cairo_destroy (cr);
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+m2_gtk_widget_finalize (GObject *object)
|
|
|
+{
|
|
|
+ M2GtkWidget *self = (M2GtkWidget *) object;
|
|
|
+
|
|
|
+ if (self->destroy != NULL)
|
|
|
+ self->destroy (self->user_data);
|
|
|
+
|
|
|
+ G_OBJECT_CLASS (m2_gtk_widget_parent_class)->finalize (object);
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+m2_gtk_widget_class_init (M2GtkWidgetClass *klass)
|
|
|
+{
|
|
|
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
|
|
+
|
|
|
+ object_class->finalize = m2_gtk_widget_finalize;
|
|
|
+ widget_class->snapshot = m2_gtk_widget_snapshot;
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+m2_gtk_widget_init (M2GtkWidget *self)
|
|
|
+{
|
|
|
+ self->draw = NULL;
|
|
|
+ self->user_data = NULL;
|
|
|
+ self->destroy = NULL;
|
|
|
+}
|
|
|
+
|
|
|
+GtkWidget *
|
|
|
+m2_gtk_widget_new (M2WidgetDrawFunc draw, gpointer user_data,
|
|
|
+ GDestroyNotify destroy)
|
|
|
+{
|
|
|
+ M2GtkWidget *self = g_object_new (m2_gtk_widget_get_type (), NULL);
|
|
|
+
|
|
|
+ self->draw = draw;
|
|
|
+ self->user_data = user_data;
|
|
|
+ self->destroy = destroy;
|
|
|
+ return GTK_WIDGET (self);
|
|
|
+}
|
|
|
+
|
|
|
+/* ------------------------------------------------------------------ */
|
|
|
+/* M2Paintable (implements GdkPaintable) */
|
|
|
+/* ------------------------------------------------------------------ */
|
|
|
+
|
|
|
+typedef void (*M2PaintableDrawFunc) (cairo_t *cr, int width, int height,
|
|
|
+ gpointer user_data);
|
|
|
+
|
|
|
+typedef struct _M2Paintable M2Paintable;
|
|
|
+typedef struct _M2PaintableClass M2PaintableClass;
|
|
|
+
|
|
|
+struct _M2Paintable
|
|
|
+{
|
|
|
+ GObject parent_instance;
|
|
|
+ M2PaintableDrawFunc draw;
|
|
|
+ gpointer user_data;
|
|
|
+ GDestroyNotify destroy;
|
|
|
+ int width;
|
|
|
+ int height;
|
|
|
+};
|
|
|
+
|
|
|
+struct _M2PaintableClass
|
|
|
+{
|
|
|
+ GObjectClass parent_class;
|
|
|
+};
|
|
|
+
|
|
|
+static void
|
|
|
+m2_paintable_snapshot (GdkPaintable *paintable, GtkSnapshot *snapshot,
|
|
|
+ double width, double height)
|
|
|
+{
|
|
|
+ M2Paintable *self = (M2Paintable *) paintable;
|
|
|
+ graphene_rect_t bounds = GRAPHENE_RECT_INIT (0.f, 0.f, (float) width,
|
|
|
+ (float) height);
|
|
|
+ cairo_t *cr = gtk_snapshot_append_cairo (snapshot, &bounds);
|
|
|
+
|
|
|
+ if (self->draw != NULL)
|
|
|
+ self->draw (cr, (int) width, (int) height, self->user_data);
|
|
|
+
|
|
|
+ cairo_destroy (cr);
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+m2_paintable_get_intrinsic_width (GdkPaintable *paintable)
|
|
|
+{
|
|
|
+ return ((M2Paintable *) paintable)->width;
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+m2_paintable_get_intrinsic_height (GdkPaintable *paintable)
|
|
|
+{
|
|
|
+ return ((M2Paintable *) paintable)->height;
|
|
|
+}
|
|
|
+
|
|
|
+static double
|
|
|
+m2_paintable_get_intrinsic_aspect_ratio (GdkPaintable *paintable)
|
|
|
+{
|
|
|
+ M2Paintable *self = (M2Paintable *) paintable;
|
|
|
+
|
|
|
+ return self->height > 0 ? (double) self->width / (double) self->height : 0.0;
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+m2_paintable_paintable_init (GdkPaintableInterface *iface)
|
|
|
+{
|
|
|
+ iface->snapshot = m2_paintable_snapshot;
|
|
|
+ iface->get_intrinsic_width = m2_paintable_get_intrinsic_width;
|
|
|
+ iface->get_intrinsic_height = m2_paintable_get_intrinsic_height;
|
|
|
+ iface->get_intrinsic_aspect_ratio = m2_paintable_get_intrinsic_aspect_ratio;
|
|
|
+}
|
|
|
+
|
|
|
+G_DEFINE_TYPE_WITH_CODE (M2Paintable, m2_paintable, G_TYPE_OBJECT,
|
|
|
+ G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
|
|
|
+ m2_paintable_paintable_init))
|
|
|
+
|
|
|
+static void
|
|
|
+m2_paintable_finalize (GObject *object)
|
|
|
+{
|
|
|
+ M2Paintable *self = (M2Paintable *) object;
|
|
|
+
|
|
|
+ if (self->destroy != NULL)
|
|
|
+ self->destroy (self->user_data);
|
|
|
+
|
|
|
+ G_OBJECT_CLASS (m2_paintable_parent_class)->finalize (object);
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+m2_paintable_class_init (M2PaintableClass *klass)
|
|
|
+{
|
|
|
+ G_OBJECT_CLASS (klass)->finalize = m2_paintable_finalize;
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+m2_paintable_init (M2Paintable *self)
|
|
|
+{
|
|
|
+ self->draw = NULL;
|
|
|
+ self->user_data = NULL;
|
|
|
+ self->destroy = NULL;
|
|
|
+ self->width = 0;
|
|
|
+ self->height = 0;
|
|
|
+}
|
|
|
+
|
|
|
+GdkPaintable *
|
|
|
+m2_paintable_new (M2PaintableDrawFunc draw, int width, int height,
|
|
|
+ gpointer user_data, GDestroyNotify destroy)
|
|
|
+{
|
|
|
+ M2Paintable *self = g_object_new (m2_paintable_get_type (), NULL);
|
|
|
+
|
|
|
+ self->draw = draw;
|
|
|
+ self->width = width;
|
|
|
+ self->height = height;
|
|
|
+ self->user_data = user_data;
|
|
|
+ self->destroy = destroy;
|
|
|
+ return GDK_PAINTABLE (self);
|
|
|
+}
|