diff options
author | David Robillard <d@drobilla.net> | 2019-07-29 00:24:09 +0200 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2019-07-29 22:44:21 +0200 |
commit | 0f3a2c93e1d756921bc59d2faf3a1486db1e2b70 (patch) | |
tree | 246f6ceb953612b9d2e403814c4dbae4a8aa6f78 | |
parent | f97516d6cf2da7537fe18a02552b1e7583850c49 (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.c | 97 |
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* |