aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2020-02-18 23:24:11 +0100
committerDavid Robillard <d@drobilla.net>2020-02-18 23:24:11 +0100
commit2495d2deb77c8474d7bbe467a0d033909ee3263c (patch)
tree98bc630e7450137085707ca8ffc99c689bfd9915
parent0059c630d7406f672c113d6dc6da78810160ddbb (diff)
downloadpugl-2495d2deb77c8474d7bbe467a0d033909ee3263c.tar.gz
pugl-2495d2deb77c8474d7bbe467a0d033909ee3263c.tar.bz2
pugl-2495d2deb77c8474d7bbe467a0d033909ee3263c.zip
X11: Use ephemeral Cairo surfaces for drawing
This uses a similar approach to Gtk, where surfaces are created every draw call. It is a bit slower for a single application in most cases, but uses less memory when drawing isn't happening and plays more nicely with other views or applications.
-rw-r--r--pugl/detail/x11_cairo.c117
1 files changed, 66 insertions, 51 deletions
diff --git a/pugl/detail/x11_cairo.c b/pugl/detail/x11_cairo.c
index 7a02dcd..03a3f8e 100644
--- a/pugl/detail/x11_cairo.c
+++ b/pugl/detail/x11_cairo.c
@@ -22,6 +22,7 @@
#include "pugl/detail/x11.h"
#include "pugl/pugl.h"
#include "pugl/pugl_cairo.h"
+#include "pugl/pugl_stub.h"
#include <X11/Xutil.h>
#include <cairo-xlib.h>
@@ -31,36 +32,56 @@
#include <stdio.h>
#include <stdlib.h>
-typedef struct {
+typedef struct {
cairo_surface_t* back;
cairo_surface_t* front;
cairo_t* cr;
} PuglX11CairoSurface;
+static void
+puglX11CairoClose(PuglView* view)
+{
+ PuglInternals* const impl = view->impl;
+ PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface;
+
+ cairo_surface_destroy(surface->front);
+ cairo_surface_destroy(surface->back);
+ surface->front = surface->back = NULL;
+}
+
static PuglStatus
-puglX11CairoCreate(PuglView* view)
+puglX11CairoOpen(PuglView* view)
{
- PuglInternals* const impl = view->impl;
- const int width = (int)view->frame.width;
- const int height = (int)view->frame.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);
-
- cairo_status_t st = CAIRO_STATUS_SUCCESS;
- if (!surface.back || !surface.front ||
- (st = cairo_surface_status(surface.back)) ||
- (st = cairo_surface_status(surface.front))) {
- cairo_surface_destroy(surface.front);
- cairo_surface_destroy(surface.back);
+ PuglInternals* const impl = view->impl;
+ PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface;
+
+ surface->back = cairo_xlib_surface_create(impl->display,
+ impl->win,
+ impl->vi->visual,
+ (int)view->frame.width,
+ (int)view->frame.height);
+
+ surface->front = cairo_surface_create_similar(
+ surface->back,
+ cairo_surface_get_content(surface->back),
+ (int)view->frame.width,
+ (int)view->frame.height);
+
+ if (cairo_surface_status(surface->back) ||
+ cairo_surface_status(surface->front)) {
+ puglX11CairoClose(view);
return PUGL_CREATE_CONTEXT_FAILED;
}
- impl->surface = calloc(1, sizeof(PuglX11CairoSurface));
- *(PuglX11CairoSurface*)impl->surface = surface;
+ return PUGL_SUCCESS;
+}
+
+static PuglStatus
+puglX11CairoCreate(PuglView* view)
+{
+ PuglInternals* const impl = view->impl;
+
+ impl->surface = (cairo_surface_t*)calloc(1, sizeof(PuglX11CairoSurface));
return PUGL_SUCCESS;
}
@@ -71,10 +92,9 @@ puglX11CairoDestroy(PuglView* view)
PuglInternals* const impl = view->impl;
PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface;
- cairo_surface_destroy(surface->front);
- cairo_surface_destroy(surface->back);
+ puglX11CairoClose(view);
free(surface);
- impl->surface = NULL;
+
return PUGL_SUCCESS;
}
@@ -83,15 +103,17 @@ puglX11CairoEnter(PuglView* view, const PuglEventExpose* expose)
{
PuglInternals* const impl = view->impl;
PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface;
+ PuglStatus st = PUGL_SUCCESS;
- if (expose) {
+ if (expose && !(st = puglX11CairoOpen(view))) {
surface->cr = cairo_create(surface->front);
- if (!surface->cr || cairo_status(surface->cr)) {
- return PUGL_CREATE_CONTEXT_FAILED;
+
+ if (cairo_status(surface->cr)) {
+ st = PUGL_CREATE_CONTEXT_FAILED;
}
}
- return PUGL_SUCCESS;
+ return st;
}
static PuglStatus
@@ -101,35 +123,28 @@ puglX11CairoLeave(PuglView* view, const PuglEventExpose* expose)
PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface;
if (expose) {
- // Destroy front context that was used by the user for drawing
+ // Destroy front context and create a new one for drawing to the back
cairo_destroy(surface->cr);
-
- // Create a new context for the back and paint the front onto it
surface->cr = cairo_create(surface->back);
+
+ // Clip to expose region
+ cairo_rectangle(surface->cr,
+ expose->x,
+ expose->y,
+ expose->width,
+ expose->height);
+ cairo_clip(surface->cr);
+
+ // Paint front onto back
cairo_set_source_surface(surface->cr, surface->front, 0, 0);
+ cairo_set_operator(surface->cr, CAIRO_OPERATOR_SOURCE);
cairo_paint(surface->cr);
- cairo_destroy(surface->cr);
- surface->cr = NULL;
- // Flush back to X
+ // Flush to X and close everything
+ cairo_destroy(surface->cr);
cairo_surface_flush(surface->back);
- }
-
- return PUGL_SUCCESS;
-}
-
-static PuglStatus
-puglX11CairoResize(PuglView* view, int width, int height)
-{
- PuglInternals* const impl = view->impl;
- PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface;
-
- cairo_xlib_surface_set_size(surface->back, width, height);
-
- cairo_surface_destroy(surface->front);
- if (!(surface->front = cairo_surface_create_similar(
- surface->back, CAIRO_CONTENT_COLOR, width, height))) {
- return PUGL_CREATE_CONTEXT_FAILED;
+ puglX11CairoClose(view);
+ surface->cr = NULL;
}
return PUGL_SUCCESS;
@@ -153,7 +168,7 @@ puglCairoBackend(void)
puglX11CairoDestroy,
puglX11CairoEnter,
puglX11CairoLeave,
- puglX11CairoResize,
+ puglStubResize,
puglX11CairoGetContext
};