From cb5d9abf100aad1ca4e3a358a1f02fcefc3b4029 Mon Sep 17 00:00:00 2001 From: Brett Ernst Date: Mon, 10 Jul 2023 15:21:33 -0700 Subject: [PATCH] add minimalist cairo example --- examples/cairo-buffer.c | 194 ++++++++++++++++++++++++++++++++++++++++ examples/meson.build | 7 +- 2 files changed, 200 insertions(+), 1 deletion(-) create mode 100644 examples/cairo-buffer.c diff --git a/examples/cairo-buffer.c b/examples/cairo-buffer.c new file mode 100644 index 00000000..688b9910 --- /dev/null +++ b/examples/cairo-buffer.c @@ -0,0 +1,194 @@ +#define _POSIX_C_SOURCE 200112L + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Simple scene-graph example with a custom buffer drawn by Cairo. + * + * Input is unimplemented. Surfaces are unimplemented. */ + +struct cairo_buffer { + struct wlr_buffer base; + cairo_surface_t *surface; +}; + +static void cairo_buffer_destroy(struct wlr_buffer *wlr_buffer) { + struct cairo_buffer *buffer = wl_container_of(wlr_buffer, buffer, base); + cairo_surface_destroy(buffer->surface); + free(buffer); +} + +static bool cairo_buffer_begin_data_ptr_access(struct wlr_buffer *wlr_buffer, + uint32_t flags, void **data, uint32_t *format, size_t *stride) { + struct cairo_buffer *buffer = wl_container_of(wlr_buffer, buffer, base); + + if (flags & WLR_BUFFER_DATA_PTR_ACCESS_WRITE) { + return false; + } + + *format = DRM_FORMAT_ARGB8888; + *data = cairo_image_surface_get_data(buffer->surface); + *stride = cairo_image_surface_get_stride(buffer->surface); + return true; +} + +static void cairo_buffer_end_data_ptr_access(struct wlr_buffer *wlr_buffer) { +} + +static const struct wlr_buffer_impl cairo_buffer_impl = { + .destroy = cairo_buffer_destroy, + .begin_data_ptr_access = cairo_buffer_begin_data_ptr_access, + .end_data_ptr_access = cairo_buffer_end_data_ptr_access +}; + +static struct cairo_buffer *create_cairo_buffer(int width, int height) { + struct cairo_buffer *buffer = calloc(1, sizeof(*buffer)); + if (!buffer) { + return NULL; + } + + wlr_buffer_init(&buffer->base, &cairo_buffer_impl, width, height); + + buffer->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, + width, height); + if (cairo_surface_status(buffer->surface) != CAIRO_STATUS_SUCCESS) { + free(buffer); + return NULL; + } + + return buffer; +} + +struct server { + struct wl_display *display; + struct wlr_backend *backend; + struct wlr_renderer *renderer; + struct wlr_allocator *allocator; + struct wlr_scene *scene; + + struct wl_listener new_output; +}; + +struct output { + struct wl_list link; + struct server *server; + struct wlr_output *wlr; + struct wlr_scene_output *scene_output; + + struct wl_listener frame; +}; + +static void output_handle_frame(struct wl_listener *listener, void *data) { + struct output *output = wl_container_of(listener, output, frame); + + wlr_scene_output_commit(output->scene_output, NULL); +} + +static void server_handle_new_output(struct wl_listener *listener, void *data) { + struct server *server = wl_container_of(listener, server, new_output); + struct wlr_output *wlr_output = data; + + wlr_output_init_render(wlr_output, server->allocator, server->renderer); + + struct output *output = calloc(1, sizeof(struct output)); + output->wlr = wlr_output; + output->server = server; + output->frame.notify = output_handle_frame; + wl_signal_add(&wlr_output->events.frame, &output->frame); + + output->scene_output = wlr_scene_output_create(server->scene, wlr_output); + + struct wlr_output_state state; + wlr_output_state_init(&state); + wlr_output_state_set_enabled(&state, true); + struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); + if (mode != NULL) { + wlr_output_state_set_mode(&state, mode); + } + wlr_output_commit_state(wlr_output, &state); + wlr_output_state_finish(&state); +} + +int main(void) { + wlr_log_init(WLR_DEBUG, NULL); + + struct server server = {0}; + server.display = wl_display_create(); + server.backend = wlr_backend_autocreate(server.display, NULL); + server.scene = wlr_scene_create(); + + server.renderer = wlr_renderer_autocreate(server.backend); + wlr_renderer_init_wl_display(server.renderer, server.display); + + server.allocator = wlr_allocator_autocreate(server.backend, + server.renderer); + + server.new_output.notify = server_handle_new_output; + wl_signal_add(&server.backend->events.new_output, &server.new_output); + + if (!wlr_backend_start(server.backend)) { + wl_display_destroy(server.display); + return EXIT_FAILURE; + } + + struct cairo_buffer *buffer = create_cairo_buffer(256, 256); + if (!buffer) { + wl_display_destroy(server.display); + return EXIT_FAILURE; + } + + /* Begin drawing + * From cairo samples at https://www.cairographics.org/samples/ */ + cairo_t *cr = cairo_create(buffer->surface); + cairo_set_source_rgb(cr, 1, 1, 1); + cairo_paint(cr); + cairo_set_source_rgb(cr, 0, 0, 0); + + double x = 25.6, y = 128.0; + double x1 = 102.4, y1 = 230.4, + x2 = 153.6, y2 = 25.6, + x3 = 230.4, y3 = 128.0; + + cairo_move_to(cr, x, y); + cairo_curve_to(cr, x1, y1, x2, y2, x3, y3); + + cairo_set_line_width(cr, 10.0); + cairo_stroke(cr); + + cairo_set_source_rgba(cr, 1, 0.2, 0.2, 0.6); + cairo_set_line_width(cr, 6.0); + cairo_move_to(cr, x, y); + cairo_line_to(cr, x1, y1); + cairo_move_to(cr, x2, y2); + cairo_line_to(cr, x3, y3); + cairo_stroke(cr); + + cairo_destroy(cr); + /* End drawing */ + + struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_create( + &server.scene->tree, &buffer->base); + if (!scene_buffer) { + wl_display_destroy(server.display); + return EXIT_FAILURE; + } + + wlr_scene_node_set_position(&scene_buffer->node, 50, 50); + wlr_buffer_drop(&buffer->base); + + wl_display_run(server.display); + + wl_display_destroy(server.display); + return EXIT_SUCCESS; +} diff --git a/examples/meson.build b/examples/meson.build index 1a9efc59..f0ebad64 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -6,6 +6,7 @@ libpng = dependency('libpng', required: false, disabler: true) egl = dependency('egl', required: false, disabler: true) glesv2 = dependency('glesv2', required: false, disabler: true) gbm = dependency('gbm', required: false, disabler: true) +cairo = dependency('cairo', required: false, disabler: true) # These versions correspond to ffmpeg 4.0 libavutil = dependency('libavutil', version: '>=56.14.100', required: false, disabler: true) libavcodec = dependency('libavcodec', version: '>=58.18.100', required: false, disabler: true) @@ -57,6 +58,10 @@ compositors = { 'xdg-shell', ], }, + 'cairo-buffer': { + 'src': 'cairo-buffer.c', + 'dep': cairo, + }, } clients = { @@ -199,7 +204,7 @@ foreach name, info : compositors executable( name, [info.get('src'), extra_src], - dependencies: [wlroots, libdrm], + dependencies: [wlroots, libdrm, info.get('dep', [])], build_by_default: get_option('examples'), ) endforeach