m2gtkshim.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  1. /*
  2. * m2gtkshim.c - small C support library for the m2-GTK4 Modula-2 bindings.
  3. *
  4. * GNU Modula-2 cannot register GObject subclasses (no class_init /
  5. * vfunc overrides), which blocks widgets and paintables that need a
  6. * custom snapshot. This shim provides two such types whose snapshot
  7. * vfuncs hand a Cairo context to a Modula-2 callback, so the drawing
  8. * itself reuses the existing Cairo binding:
  9. *
  10. * M2GtkWidget - a GtkWidget subclass; the M2 callback is
  11. * void draw(GtkWidget *w, cairo_t *cr,
  12. * int width, int height, gpointer user);
  13. * M2Paintable - a GdkPaintable implementation; the M2 callback is
  14. * void draw(cairo_t *cr, int width, int height,
  15. * gpointer user);
  16. *
  17. * The M2 callback addresses and user data are supplied at construction;
  18. * an optional destroy notify frees the user data.
  19. */
  20. #include <gtk/gtk.h>
  21. /* ------------------------------------------------------------------ */
  22. /* M2GtkWidget */
  23. /* ------------------------------------------------------------------ */
  24. typedef void (*M2WidgetDrawFunc) (GtkWidget *widget, cairo_t *cr,
  25. int width, int height, gpointer user_data);
  26. typedef struct _M2GtkWidget M2GtkWidget;
  27. typedef struct _M2GtkWidgetClass M2GtkWidgetClass;
  28. struct _M2GtkWidget
  29. {
  30. GtkWidget parent_instance;
  31. M2WidgetDrawFunc draw;
  32. gpointer user_data;
  33. GDestroyNotify destroy;
  34. };
  35. struct _M2GtkWidgetClass
  36. {
  37. GtkWidgetClass parent_class;
  38. };
  39. G_DEFINE_TYPE (M2GtkWidget, m2_gtk_widget, GTK_TYPE_WIDGET)
  40. static void
  41. m2_gtk_widget_snapshot (GtkWidget *widget, GtkSnapshot *snapshot)
  42. {
  43. M2GtkWidget *self = (M2GtkWidget *) widget;
  44. int width = gtk_widget_get_width (widget);
  45. int height = gtk_widget_get_height (widget);
  46. if (self->draw == NULL || width <= 0 || height <= 0)
  47. return;
  48. graphene_rect_t bounds = GRAPHENE_RECT_INIT (0.f, 0.f, width, height);
  49. cairo_t *cr = gtk_snapshot_append_cairo (snapshot, &bounds);
  50. self->draw (widget, cr, width, height, self->user_data);
  51. cairo_destroy (cr);
  52. }
  53. static void
  54. m2_gtk_widget_finalize (GObject *object)
  55. {
  56. M2GtkWidget *self = (M2GtkWidget *) object;
  57. if (self->destroy != NULL)
  58. self->destroy (self->user_data);
  59. G_OBJECT_CLASS (m2_gtk_widget_parent_class)->finalize (object);
  60. }
  61. static void
  62. m2_gtk_widget_class_init (M2GtkWidgetClass *klass)
  63. {
  64. GObjectClass *object_class = G_OBJECT_CLASS (klass);
  65. GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
  66. object_class->finalize = m2_gtk_widget_finalize;
  67. widget_class->snapshot = m2_gtk_widget_snapshot;
  68. }
  69. static void
  70. m2_gtk_widget_init (M2GtkWidget *self)
  71. {
  72. self->draw = NULL;
  73. self->user_data = NULL;
  74. self->destroy = NULL;
  75. }
  76. GtkWidget *
  77. m2_gtk_widget_new (M2WidgetDrawFunc draw, gpointer user_data,
  78. GDestroyNotify destroy)
  79. {
  80. M2GtkWidget *self = g_object_new (m2_gtk_widget_get_type (), NULL);
  81. self->draw = draw;
  82. self->user_data = user_data;
  83. self->destroy = destroy;
  84. return GTK_WIDGET (self);
  85. }
  86. /* ------------------------------------------------------------------ */
  87. /* M2Paintable (implements GdkPaintable) */
  88. /* ------------------------------------------------------------------ */
  89. typedef void (*M2PaintableDrawFunc) (cairo_t *cr, int width, int height,
  90. gpointer user_data);
  91. typedef struct _M2Paintable M2Paintable;
  92. typedef struct _M2PaintableClass M2PaintableClass;
  93. struct _M2Paintable
  94. {
  95. GObject parent_instance;
  96. M2PaintableDrawFunc draw;
  97. gpointer user_data;
  98. GDestroyNotify destroy;
  99. int width;
  100. int height;
  101. };
  102. struct _M2PaintableClass
  103. {
  104. GObjectClass parent_class;
  105. };
  106. static void
  107. m2_paintable_snapshot (GdkPaintable *paintable, GtkSnapshot *snapshot,
  108. double width, double height)
  109. {
  110. M2Paintable *self = (M2Paintable *) paintable;
  111. graphene_rect_t bounds = GRAPHENE_RECT_INIT (0.f, 0.f, (float) width,
  112. (float) height);
  113. cairo_t *cr = gtk_snapshot_append_cairo (snapshot, &bounds);
  114. if (self->draw != NULL)
  115. self->draw (cr, (int) width, (int) height, self->user_data);
  116. cairo_destroy (cr);
  117. }
  118. static int
  119. m2_paintable_get_intrinsic_width (GdkPaintable *paintable)
  120. {
  121. return ((M2Paintable *) paintable)->width;
  122. }
  123. static int
  124. m2_paintable_get_intrinsic_height (GdkPaintable *paintable)
  125. {
  126. return ((M2Paintable *) paintable)->height;
  127. }
  128. static double
  129. m2_paintable_get_intrinsic_aspect_ratio (GdkPaintable *paintable)
  130. {
  131. M2Paintable *self = (M2Paintable *) paintable;
  132. return self->height > 0 ? (double) self->width / (double) self->height : 0.0;
  133. }
  134. static void
  135. m2_paintable_paintable_init (GdkPaintableInterface *iface)
  136. {
  137. iface->snapshot = m2_paintable_snapshot;
  138. iface->get_intrinsic_width = m2_paintable_get_intrinsic_width;
  139. iface->get_intrinsic_height = m2_paintable_get_intrinsic_height;
  140. iface->get_intrinsic_aspect_ratio = m2_paintable_get_intrinsic_aspect_ratio;
  141. }
  142. G_DEFINE_TYPE_WITH_CODE (M2Paintable, m2_paintable, G_TYPE_OBJECT,
  143. G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
  144. m2_paintable_paintable_init))
  145. static void
  146. m2_paintable_finalize (GObject *object)
  147. {
  148. M2Paintable *self = (M2Paintable *) object;
  149. if (self->destroy != NULL)
  150. self->destroy (self->user_data);
  151. G_OBJECT_CLASS (m2_paintable_parent_class)->finalize (object);
  152. }
  153. static void
  154. m2_paintable_class_init (M2PaintableClass *klass)
  155. {
  156. G_OBJECT_CLASS (klass)->finalize = m2_paintable_finalize;
  157. }
  158. static void
  159. m2_paintable_init (M2Paintable *self)
  160. {
  161. self->draw = NULL;
  162. self->user_data = NULL;
  163. self->destroy = NULL;
  164. self->width = 0;
  165. self->height = 0;
  166. }
  167. GdkPaintable *
  168. m2_paintable_new (M2PaintableDrawFunc draw, int width, int height,
  169. gpointer user_data, GDestroyNotify destroy)
  170. {
  171. M2Paintable *self = g_object_new (m2_paintable_get_type (), NULL);
  172. self->draw = draw;
  173. self->width = width;
  174. self->height = height;
  175. self->user_data = user_data;
  176. self->destroy = destroy;
  177. return GDK_PAINTABLE (self);
  178. }
  179. /* ------------------------------------------------------------------ */
  180. /* M2ListModel (implements GListModel) */
  181. /* ------------------------------------------------------------------ */
  182. typedef guint (*M2ListModelGetNItemsFunc) (gpointer user_data);
  183. typedef gpointer (*M2ListModelGetItemFunc) (guint position,
  184. gpointer user_data);
  185. typedef struct _M2ListModel M2ListModel;
  186. typedef struct _M2ListModelClass M2ListModelClass;
  187. struct _M2ListModel
  188. {
  189. GObject parent_instance;
  190. M2ListModelGetNItemsFunc get_n_items;
  191. M2ListModelGetItemFunc get_item;
  192. gpointer user_data;
  193. GDestroyNotify destroy;
  194. GType item_type;
  195. };
  196. struct _M2ListModelClass
  197. {
  198. GObjectClass parent_class;
  199. };
  200. static guint
  201. m2_list_model_get_n_items (GListModel *list)
  202. {
  203. M2ListModel *self = (M2ListModel *) list;
  204. return self->get_n_items != NULL ? self->get_n_items (self->user_data) : 0;
  205. }
  206. static GType
  207. m2_list_model_get_item_type (GListModel *list)
  208. {
  209. return ((M2ListModel *) list)->item_type;
  210. }
  211. static gpointer
  212. m2_list_model_get_item (GListModel *list, guint position)
  213. {
  214. M2ListModel *self = (M2ListModel *) list;
  215. return self->get_item != NULL ? self->get_item (position, self->user_data)
  216. : NULL;
  217. }
  218. static void
  219. m2_list_model_list_init (GListModelInterface *iface)
  220. {
  221. iface->get_n_items = m2_list_model_get_n_items;
  222. iface->get_item_type = m2_list_model_get_item_type;
  223. iface->get_item = m2_list_model_get_item;
  224. }
  225. G_DEFINE_TYPE_WITH_CODE (M2ListModel, m2_list_model, G_TYPE_OBJECT,
  226. G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL,
  227. m2_list_model_list_init))
  228. static void
  229. m2_list_model_finalize (GObject *object)
  230. {
  231. M2ListModel *self = (M2ListModel *) object;
  232. if (self->destroy != NULL)
  233. self->destroy (self->user_data);
  234. G_OBJECT_CLASS (m2_list_model_parent_class)->finalize (object);
  235. }
  236. static void
  237. m2_list_model_class_init (M2ListModelClass *klass)
  238. {
  239. G_OBJECT_CLASS (klass)->finalize = m2_list_model_finalize;
  240. }
  241. static void
  242. m2_list_model_init (M2ListModel *self)
  243. {
  244. self->get_n_items = NULL;
  245. self->get_item = NULL;
  246. self->user_data = NULL;
  247. self->destroy = NULL;
  248. self->item_type = G_TYPE_OBJECT;
  249. }
  250. GListModel *
  251. m2_list_model_new (M2ListModelGetNItemsFunc get_n_items,
  252. M2ListModelGetItemFunc get_item, GType item_type,
  253. gpointer user_data, GDestroyNotify destroy)
  254. {
  255. M2ListModel *self = g_object_new (m2_list_model_get_type (), NULL);
  256. self->get_n_items = get_n_items;
  257. self->get_item = get_item;
  258. self->item_type = item_type;
  259. self->user_data = user_data;
  260. self->destroy = destroy;
  261. return G_LIST_MODEL (self);
  262. }
  263. void
  264. m2_list_model_items_changed (GListModel *model, guint position,
  265. guint removed, guint added)
  266. {
  267. g_list_model_items_changed (model, position, removed, added);
  268. }
  269. /* ------------------------------------------------------------------ */
  270. /* M2LayoutManager (subclass of GtkLayoutManager) */
  271. /* ------------------------------------------------------------------ */
  272. typedef void (*M2LayoutMeasureFunc) (GtkWidget *widget, int orientation,
  273. int for_size, int *minimum, int *natural,
  274. gpointer user_data);
  275. typedef void (*M2LayoutAllocateFunc) (GtkWidget *widget, int width, int height,
  276. int baseline, gpointer user_data);
  277. typedef struct _M2LayoutManager M2LayoutManager;
  278. typedef struct _M2LayoutManagerClass M2LayoutManagerClass;
  279. struct _M2LayoutManager
  280. {
  281. GtkLayoutManager parent_instance;
  282. M2LayoutMeasureFunc measure;
  283. M2LayoutAllocateFunc allocate;
  284. gpointer user_data;
  285. GDestroyNotify destroy;
  286. };
  287. struct _M2LayoutManagerClass
  288. {
  289. GtkLayoutManagerClass parent_class;
  290. };
  291. static void
  292. m2_layout_manager_measure (GtkLayoutManager *manager, GtkWidget *widget,
  293. GtkOrientation orientation, int for_size,
  294. int *minimum, int *natural, int *minimum_baseline,
  295. int *natural_baseline)
  296. {
  297. M2LayoutManager *self = (M2LayoutManager *) manager;
  298. *minimum = 0;
  299. *natural = 0;
  300. if (self->measure != NULL)
  301. self->measure (widget, (int) orientation, for_size, minimum, natural,
  302. self->user_data);
  303. *minimum_baseline = -1;
  304. *natural_baseline = -1;
  305. }
  306. static void
  307. m2_layout_manager_allocate (GtkLayoutManager *manager, GtkWidget *widget,
  308. int width, int height, int baseline)
  309. {
  310. M2LayoutManager *self = (M2LayoutManager *) manager;
  311. if (self->allocate != NULL)
  312. self->allocate (widget, width, height, baseline, self->user_data);
  313. }
  314. G_DEFINE_TYPE (M2LayoutManager, m2_layout_manager, GTK_TYPE_LAYOUT_MANAGER)
  315. static void
  316. m2_layout_manager_finalize (GObject *object)
  317. {
  318. M2LayoutManager *self = (M2LayoutManager *) object;
  319. if (self->destroy != NULL)
  320. self->destroy (self->user_data);
  321. G_OBJECT_CLASS (m2_layout_manager_parent_class)->finalize (object);
  322. }
  323. static void
  324. m2_layout_manager_class_init (M2LayoutManagerClass *klass)
  325. {
  326. GObjectClass *object_class = G_OBJECT_CLASS (klass);
  327. GtkLayoutManagerClass *manager_class = GTK_LAYOUT_MANAGER_CLASS (klass);
  328. object_class->finalize = m2_layout_manager_finalize;
  329. manager_class->measure = m2_layout_manager_measure;
  330. manager_class->allocate = m2_layout_manager_allocate;
  331. }
  332. static void
  333. m2_layout_manager_init (M2LayoutManager *self)
  334. {
  335. self->measure = NULL;
  336. self->allocate = NULL;
  337. self->user_data = NULL;
  338. self->destroy = NULL;
  339. }
  340. GtkLayoutManager *
  341. m2_layout_manager_new (M2LayoutMeasureFunc measure, M2LayoutAllocateFunc allocate,
  342. gpointer user_data, GDestroyNotify destroy)
  343. {
  344. M2LayoutManager *self = g_object_new (m2_layout_manager_get_type (), NULL);
  345. self->measure = measure;
  346. self->allocate = allocate;
  347. self->user_data = user_data;
  348. self->destroy = destroy;
  349. return GTK_LAYOUT_MANAGER (self);
  350. }
  351. /* Position a child at (x, y) with the given size (helper for allocate). */
  352. void
  353. m2_layout_allocate_child (GtkWidget *child, int x, int y, int width, int height)
  354. {
  355. graphene_point_t point = GRAPHENE_POINT_INIT (x, y);
  356. GskTransform *transform = gsk_transform_translate (NULL, &point);
  357. gtk_widget_allocate (child, width, height, -1, transform);
  358. gsk_transform_unref (transform);
  359. }
  360. /* ------------------------------------------------------------------ */
  361. /* M2SnapshotPaintable (GdkPaintable using GtkSnapshot) */
  362. /* ------------------------------------------------------------------ */
  363. typedef void (*M2SnapshotDrawFunc) (GtkSnapshot *snapshot, int width,
  364. int height, gpointer user_data);
  365. typedef struct _M2SnapshotPaintable M2SnapshotPaintable;
  366. typedef struct _M2SnapshotPaintableClass M2SnapshotPaintableClass;
  367. struct _M2SnapshotPaintable
  368. {
  369. GObject parent_instance;
  370. M2SnapshotDrawFunc draw;
  371. gpointer user_data;
  372. GDestroyNotify destroy;
  373. int width;
  374. int height;
  375. };
  376. struct _M2SnapshotPaintableClass
  377. {
  378. GObjectClass parent_class;
  379. };
  380. static void
  381. m2_snapshot_paintable_snapshot (GdkPaintable *paintable, GtkSnapshot *snapshot,
  382. double width, double height)
  383. {
  384. M2SnapshotPaintable *self = (M2SnapshotPaintable *) paintable;
  385. if (self->draw != NULL)
  386. self->draw (snapshot, (int) width, (int) height, self->user_data);
  387. }
  388. static int
  389. m2_snapshot_paintable_get_intrinsic_width (GdkPaintable *paintable)
  390. {
  391. return ((M2SnapshotPaintable *) paintable)->width;
  392. }
  393. static int
  394. m2_snapshot_paintable_get_intrinsic_height (GdkPaintable *paintable)
  395. {
  396. return ((M2SnapshotPaintable *) paintable)->height;
  397. }
  398. static double
  399. m2_snapshot_paintable_get_intrinsic_aspect_ratio (GdkPaintable *paintable)
  400. {
  401. M2SnapshotPaintable *self = (M2SnapshotPaintable *) paintable;
  402. return self->height > 0 ? (double) self->width / (double) self->height : 0.0;
  403. }
  404. static void
  405. m2_snapshot_paintable_paintable_init (GdkPaintableInterface *iface)
  406. {
  407. iface->snapshot = m2_snapshot_paintable_snapshot;
  408. iface->get_intrinsic_width = m2_snapshot_paintable_get_intrinsic_width;
  409. iface->get_intrinsic_height = m2_snapshot_paintable_get_intrinsic_height;
  410. iface->get_intrinsic_aspect_ratio = m2_snapshot_paintable_get_intrinsic_aspect_ratio;
  411. }
  412. G_DEFINE_TYPE_WITH_CODE (M2SnapshotPaintable, m2_snapshot_paintable,
  413. G_TYPE_OBJECT,
  414. G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
  415. m2_snapshot_paintable_paintable_init))
  416. static void
  417. m2_snapshot_paintable_finalize (GObject *object)
  418. {
  419. M2SnapshotPaintable *self = (M2SnapshotPaintable *) object;
  420. if (self->destroy != NULL)
  421. self->destroy (self->user_data);
  422. G_OBJECT_CLASS (m2_snapshot_paintable_parent_class)->finalize (object);
  423. }
  424. static void
  425. m2_snapshot_paintable_class_init (M2SnapshotPaintableClass *klass)
  426. {
  427. G_OBJECT_CLASS (klass)->finalize = m2_snapshot_paintable_finalize;
  428. }
  429. static void
  430. m2_snapshot_paintable_init (M2SnapshotPaintable *self)
  431. {
  432. self->draw = NULL;
  433. self->user_data = NULL;
  434. self->destroy = NULL;
  435. self->width = 0;
  436. self->height = 0;
  437. }
  438. GdkPaintable *
  439. m2_snapshot_paintable_new (M2SnapshotDrawFunc draw, int width, int height,
  440. gpointer user_data, GDestroyNotify destroy)
  441. {
  442. M2SnapshotPaintable *self = g_object_new (m2_snapshot_paintable_get_type (),
  443. NULL);
  444. self->draw = draw;
  445. self->width = width;
  446. self->height = height;
  447. self->user_data = user_data;
  448. self->destroy = destroy;
  449. return GDK_PAINTABLE (self);
  450. }