aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2019-07-29 00:24:09 +0200
committerDavid Robillard <d@drobilla.net>2019-07-29 22:44:21 +0200
commit0f3a2c93e1d756921bc59d2faf3a1486db1e2b70 (patch)
tree246f6ceb953612b9d2e403814c4dbae4a8aa6f78
parentf97516d6cf2da7537fe18a02552b1e7583850c49 (diff)
X11: Implement double buffering for Cairo
Also save and restore cairo context state around callbacks, so applications don't need to worry about smashing cairo state across exposures.
-rw-r--r--pugl/detail/x11_cairo.c97
1 files changed, 66 insertions, 31 deletions
diff --git a/pugl/detail/x11_cairo.c b/pugl/detail/x11_cairo.c
index 787c111..1867007 100644
--- a/pugl/detail/x11_cairo.c
+++ b/pugl/detail/x11_cairo.c
@@ -32,8 +32,10 @@
#include <stdlib.h>
typedef struct {
- cairo_surface_t* surface;
- cairo_t* cr;
+ cairo_surface_t* back;
+ cairo_t* backCr;
+ cairo_surface_t* front;
+ cairo_t* frontCr;
} PuglX11CairoSurface;
static int
@@ -52,31 +54,36 @@ puglX11CairoConfigure(PuglView* view)
static int
puglX11CairoCreate(PuglView* view)
{
- PuglInternals* const impl = view->impl;
- PuglX11CairoSurface* const surface =
- (PuglX11CairoSurface*)calloc(1, sizeof(PuglX11CairoSurface));
-
- impl->surface = surface;
-
- surface->surface = cairo_xlib_surface_create(
- impl->display, impl->win, impl->vi->visual, view->width, view->height);
-
- if (!surface->surface) {
- return 1;
+ PuglInternals* const impl = view->impl;
+ const double width = view->width;
+ const double height = view->height;
+ PuglX11CairoSurface surface = { 0 };
+
+ surface.back = cairo_xlib_surface_create(
+ impl->display, impl->win, impl->vi->visual, width, height);
+ surface.front = cairo_surface_create_similar(
+ surface.back, CAIRO_CONTENT_COLOR, width, height);
+ surface.backCr = cairo_create(surface.back);
+ surface.frontCr = cairo_create(surface.front);
+
+ cairo_status_t st = CAIRO_STATUS_SUCCESS;
+ if (!surface.back || !surface.backCr ||
+ !surface.front || !surface.frontCr ||
+ (st = cairo_surface_status(surface.back)) ||
+ (st = cairo_surface_status(surface.front)) ||
+ (st = cairo_status(surface.backCr)) ||
+ (st = cairo_status(surface.frontCr))) {
+ cairo_destroy(surface.frontCr);
+ cairo_destroy(surface.backCr);
+ cairo_surface_destroy(surface.front);
+ cairo_surface_destroy(surface.back);
+ return PUGL_ERR_CREATE_CONTEXT;
}
- cairo_status_t st = cairo_surface_status(surface->surface);
- if (st) {
- fprintf(stderr, "error: failed to create cairo surface (%s)\n",
- cairo_status_to_string(st));
- } else if (!(surface->cr = cairo_create(surface->surface))) {
- fprintf(stderr, "error: failed to create cairo context\n");
- } else if ((st = cairo_status(surface->cr))) {
- cairo_surface_destroy(surface->surface);
- fprintf(stderr, "error: cairo context is invalid (%s)\n",
- cairo_status_to_string(st));
- }
- return (int)st;
+ impl->surface = calloc(1, sizeof(PuglX11CairoSurface));
+ *(PuglX11CairoSurface*)impl->surface = surface;
+
+ return 0;
}
static int
@@ -85,22 +92,40 @@ puglX11CairoDestroy(PuglView* view)
PuglInternals* const impl = view->impl;
PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface;
- cairo_destroy(surface->cr);
- cairo_surface_destroy(surface->surface);
+ cairo_destroy(surface->frontCr);
+ cairo_destroy(surface->backCr);
+ cairo_surface_destroy(surface->front);
+ cairo_surface_destroy(surface->back);
free(surface);
impl->surface = NULL;
return 0;
}
static int
-puglX11CairoEnter(PuglView* PUGL_UNUSED(view), bool PUGL_UNUSED(drawing))
+puglX11CairoEnter(PuglView* view, bool drawing)
{
+ PuglInternals* const impl = view->impl;
+ PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface;
+
+ if (drawing) {
+ cairo_save(surface->frontCr);
+ }
+
return 0;
}
static int
-puglX11CairoLeave(PuglView* PUGL_UNUSED(view), bool PUGL_UNUSED(drawing))
+puglX11CairoLeave(PuglView* view, bool drawing)
{
+ PuglInternals* const impl = view->impl;
+ PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface;
+
+ if (drawing) {
+ cairo_set_source_surface(surface->backCr, surface->front, 0, 0);
+ cairo_paint(surface->backCr);
+ cairo_restore(surface->frontCr);
+ }
+
return 0;
}
@@ -110,7 +135,17 @@ puglX11CairoResize(PuglView* view, int width, int height)
PuglInternals* const impl = view->impl;
PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface;
- cairo_xlib_surface_set_size(surface->surface, width, height);
+ cairo_xlib_surface_set_size(surface->back, width, height);
+
+ cairo_destroy(surface->frontCr);
+ cairo_surface_destroy(surface->front);
+ if (!(surface->front = cairo_surface_create_similar(
+ surface->back, CAIRO_CONTENT_COLOR, width, height))) {
+ return PUGL_ERR_CREATE_CONTEXT;
+ }
+
+ surface->frontCr = cairo_create(surface->front);
+ cairo_save(surface->frontCr);
return 0;
}
@@ -121,7 +156,7 @@ puglX11CairoGetContext(PuglView* view)
PuglInternals* const impl = view->impl;
PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface;
- return surface->cr;
+ return surface->frontCr;
}
const PuglBackend*