+static void snaffle_colours(frontend *fe)
+{
+ fe->colours = midend_colours(fe->me, &fe->ncolours);
+}
+
+static void set_colour(frontend *fe, int colour)
+{
+ cairo_set_source_rgb(fe->cr,
+ fe->colours[3*colour + 0],
+ fe->colours[3*colour + 1],
+ fe->colours[3*colour + 2]);
+}
+
+static void set_window_background(frontend *fe, int colour)
+{
+#if GTK_CHECK_VERSION(3,20,0)
+ char css_buf[512];
+ sprintf(css_buf, ".background { "
+ "background-color: #%02x%02x%02x; }",
+ (unsigned)(fe->colours[3*colour + 0] * 255),
+ (unsigned)(fe->colours[3*colour + 1] * 255),
+ (unsigned)(fe->colours[3*colour + 2] * 255));
+ if (!fe->css_provider)
+ fe->css_provider = gtk_css_provider_new();
+ if (!gtk_css_provider_load_from_data(
+ GTK_CSS_PROVIDER(fe->css_provider), css_buf, -1, NULL))
+ assert(0 && "Couldn't load CSS");
+ gtk_style_context_add_provider(
+ gtk_widget_get_style_context(fe->window),
+ GTK_STYLE_PROVIDER(fe->css_provider),
+ GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+ gtk_style_context_add_provider(
+ gtk_widget_get_style_context(fe->area),
+ GTK_STYLE_PROVIDER(fe->css_provider),
+ GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+#elif GTK_CHECK_VERSION(3,0,0)
+ GdkRGBA rgba;
+ rgba.red = fe->colours[3*colour + 0];
+ rgba.green = fe->colours[3*colour + 1];
+ rgba.blue = fe->colours[3*colour + 2];
+ rgba.alpha = 1.0;
+ gdk_window_set_background_rgba(gtk_widget_get_window(fe->area), &rgba);
+ gdk_window_set_background_rgba(gtk_widget_get_window(fe->window), &rgba);
+#else
+ GdkColormap *colmap;
+
+ colmap = gdk_colormap_get_system();
+ fe->background.red = fe->colours[3*colour + 0] * 65535;
+ fe->background.green = fe->colours[3*colour + 1] * 65535;
+ fe->background.blue = fe->colours[3*colour + 2] * 65535;
+ if (!gdk_colormap_alloc_color(colmap, &fe->background, FALSE, FALSE)) {
+ g_error("couldn't allocate background (#%02x%02x%02x)\n",
+ fe->background.red >> 8, fe->background.green >> 8,
+ fe->background.blue >> 8);
+ }
+ gdk_window_set_background(gtk_widget_get_window(fe->area),
+ &fe->background);
+ gdk_window_set_background(gtk_widget_get_window(fe->window),
+ &fe->background);
+#endif
+}
+
+static PangoLayout *make_pango_layout(frontend *fe)
+{
+ return (pango_cairo_create_layout(fe->cr));
+}
+
+static void draw_pango_layout(frontend *fe, PangoLayout *layout,
+ int x, int y)
+{
+ cairo_move_to(fe->cr, x, y);
+ pango_cairo_show_layout(fe->cr, layout);
+}
+
+static void save_screenshot_png(frontend *fe, const char *screenshot_file)
+{
+ cairo_surface_write_to_png(fe->image, screenshot_file);
+}
+
+static void do_clip(frontend *fe, int x, int y, int w, int h)
+{
+ cairo_new_path(fe->cr);
+ cairo_rectangle(fe->cr, x, y, w, h);
+ cairo_clip(fe->cr);
+}
+
+static void do_unclip(frontend *fe)
+{
+ cairo_reset_clip(fe->cr);
+}
+
+static void do_draw_rect(frontend *fe, int x, int y, int w, int h)
+{
+ cairo_save(fe->cr);
+ cairo_new_path(fe->cr);
+ cairo_set_antialias(fe->cr, CAIRO_ANTIALIAS_NONE);
+ cairo_rectangle(fe->cr, x, y, w, h);
+ cairo_fill(fe->cr);
+ cairo_restore(fe->cr);
+}
+
+static void do_draw_line(frontend *fe, int x1, int y1, int x2, int y2)
+{
+ cairo_new_path(fe->cr);
+ cairo_move_to(fe->cr, x1 + 0.5, y1 + 0.5);
+ cairo_line_to(fe->cr, x2 + 0.5, y2 + 0.5);
+ cairo_stroke(fe->cr);
+}
+
+static void do_draw_thick_line(frontend *fe, float thickness,
+ float x1, float y1, float x2, float y2)
+{
+ cairo_save(fe->cr);
+ cairo_set_line_width(fe->cr, thickness);
+ cairo_new_path(fe->cr);
+ cairo_move_to(fe->cr, x1, y1);
+ cairo_line_to(fe->cr, x2, y2);
+ cairo_stroke(fe->cr);
+ cairo_restore(fe->cr);
+}
+
+static void do_draw_poly(frontend *fe, int *coords, int npoints,
+ int fillcolour, int outlinecolour)
+{
+ int i;
+
+ cairo_new_path(fe->cr);
+ for (i = 0; i < npoints; i++)
+ cairo_line_to(fe->cr, coords[i*2] + 0.5, coords[i*2 + 1] + 0.5);
+ cairo_close_path(fe->cr);
+ if (fillcolour >= 0) {
+ set_colour(fe, fillcolour);
+ cairo_fill_preserve(fe->cr);
+ }
+ assert(outlinecolour >= 0);
+ set_colour(fe, outlinecolour);
+ cairo_stroke(fe->cr);
+}
+
+static void do_draw_circle(frontend *fe, int cx, int cy, int radius,
+ int fillcolour, int outlinecolour)
+{
+ cairo_new_path(fe->cr);
+ cairo_arc(fe->cr, cx + 0.5, cy + 0.5, radius, 0, 2*PI);
+ cairo_close_path(fe->cr); /* Just in case... */
+ if (fillcolour >= 0) {
+ set_colour(fe, fillcolour);
+ cairo_fill_preserve(fe->cr);
+ }
+ assert(outlinecolour >= 0);
+ set_colour(fe, outlinecolour);
+ cairo_stroke(fe->cr);
+}
+
+static void setup_blitter(blitter *bl, int w, int h)
+{
+ bl->image = cairo_image_surface_create(CAIRO_FORMAT_RGB24, w, h);
+}
+
+static void teardown_blitter(blitter *bl)
+{
+ cairo_surface_destroy(bl->image);
+}
+
+static void do_blitter_save(frontend *fe, blitter *bl, int x, int y)
+{
+ cairo_t *cr = cairo_create(bl->image);
+
+ cairo_set_source_surface(cr, fe->image, -x, -y);
+ cairo_paint(cr);
+ cairo_destroy(cr);
+}
+
+static void do_blitter_load(frontend *fe, blitter *bl, int x, int y)
+{
+ cairo_set_source_surface(fe->cr, bl->image, x, y);
+ cairo_paint(fe->cr);
+}
+
+static void clear_backing_store(frontend *fe)
+{
+ fe->image = NULL;
+}
+
+static void wipe_and_maybe_destroy_cairo(frontend *fe, cairo_t *cr,
+ int destroy)
+{
+ cairo_set_source_rgb(cr, fe->colours[0], fe->colours[1], fe->colours[2]);
+ cairo_paint(cr);
+ if (destroy)
+ cairo_destroy(cr);
+}
+
+static void setup_backing_store(frontend *fe)
+{
+#ifndef USE_CAIRO_WITHOUT_PIXMAP
+ fe->pixmap = gdk_pixmap_new(gtk_widget_get_window(fe->area),
+ fe->pw, fe->ph, -1);
+#endif
+ fe->image = cairo_image_surface_create(CAIRO_FORMAT_RGB24,
+ fe->pw, fe->ph);
+
+ wipe_and_maybe_destroy_cairo(fe, cairo_create(fe->image), TRUE);
+#ifndef USE_CAIRO_WITHOUT_PIXMAP
+ wipe_and_maybe_destroy_cairo(fe, gdk_cairo_create(fe->pixmap), TRUE);
+#endif
+#if GTK_CHECK_VERSION(3,22,0)
+ {
+ GdkWindow *gdkwin;
+ cairo_region_t *region;
+ GdkDrawingContext *drawctx;
+ cairo_t *cr;
+
+ gdkwin = gtk_widget_get_window(fe->area);
+ region = gdk_window_get_clip_region(gdkwin);
+ drawctx = gdk_window_begin_draw_frame(gdkwin, region);
+ cr = gdk_drawing_context_get_cairo_context(drawctx);
+ wipe_and_maybe_destroy_cairo(fe, cr, FALSE);
+ gdk_window_end_draw_frame(gdkwin, drawctx);
+ cairo_region_destroy(region);
+ }
+#else
+ wipe_and_maybe_destroy_cairo(
+ fe, gdk_cairo_create(gtk_widget_get_window(fe->area)), TRUE);
+#endif
+}
+
+static int backing_store_ok(frontend *fe)
+{
+ return (!!fe->image);
+}
+
+static void teardown_backing_store(frontend *fe)
+{
+ cairo_surface_destroy(fe->image);
+#ifndef USE_CAIRO_WITHOUT_PIXMAP
+ gdk_pixmap_unref(fe->pixmap);
+#endif
+ fe->image = NULL;
+}
+
+#endif
+
+/* ----------------------------------------------------------------------
+ * GDK drawing functions.
+ */
+
+#ifndef USE_CAIRO
+
+static void setup_drawing(frontend *fe)