From c41bd320be0862648188bf4cb3700203c41fb70b Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 6 May 2020 17:30:53 +0200 Subject: [PATCH] examples/scene-graph: new example --- examples/meson.build | 4 + examples/scene-graph.c | 199 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 203 insertions(+) create mode 100644 examples/scene-graph.c diff --git a/examples/meson.build b/examples/meson.build index 2c69fffd..1e428e81 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -51,6 +51,10 @@ compositors = { 'quads': { 'src': 'quads.c', }, + 'scene-graph': { + 'src': 'scene-graph.c', + 'proto': ['xdg-shell'], + }, } clients = { diff --git a/examples/scene-graph.c b/examples/scene-graph.c new file mode 100644 index 00000000..724e52cb --- /dev/null +++ b/examples/scene-graph.c @@ -0,0 +1,199 @@ +#define _POSIX_C_SOURCE 200112L +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Simple compositor making use of the scene-graph API. Input is unimplemented. + * + * New surfaces are stacked on top of the existing ones as they appear. */ + +struct server { + struct wl_display *display; + struct wlr_backend *backend; + struct wlr_scene *scene; + + struct wl_list outputs; + struct wl_list surfaces; + + struct wl_listener new_output; + struct wl_listener new_surface; +}; + +struct surface { + struct wlr_surface *wlr; + struct wlr_scene_surface *scene_surface; + struct wl_list link; + + struct wl_listener destroy; +}; + +struct output { + struct wl_list link; + struct server *server; + struct wlr_output *wlr; + + struct wl_listener frame; +}; + +static void output_handle_frame(struct wl_listener *listener, void *data) { + struct output *output = wl_container_of(listener, output, frame); + + if (!wlr_output_attach_render(output->wlr, NULL)) { + return; + } + + struct wlr_renderer *renderer = wlr_backend_get_renderer(output->wlr->backend); + assert(renderer != NULL); + + int width, height; + wlr_output_effective_resolution(output->wlr, &width, &height); + wlr_renderer_begin(renderer, width, height); + wlr_renderer_clear(renderer, (float[4]){ 0.3, 0.3, 0.3, 1.0 }); + + wlr_scene_render_output(output->server->scene, output->wlr, 0, 0, NULL); + wlr_output_render_software_cursors(output->wlr, NULL); + + wlr_renderer_end(renderer); + + if (!wlr_output_commit(output->wlr)) { + return; + } + + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + struct surface *surface; + wl_list_for_each(surface, &output->server->surfaces, link) { + wlr_surface_send_frame_done(surface->wlr, &now); + } +} + +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; + + 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); + wl_list_insert(&server->outputs, &output->link); + + if (!wl_list_empty(&wlr_output->modes)) { + struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); + wlr_output_set_mode(wlr_output, mode); + wlr_output_commit(wlr_output); + } + + wlr_output_create_global(wlr_output); +} + +static void surface_handle_destroy(struct wl_listener *listener, void *data) { + struct surface *surface = wl_container_of(listener, surface, destroy); + wlr_scene_node_destroy(&surface->scene_surface->node); + wl_list_remove(&surface->destroy.link); + wl_list_remove(&surface->link); + free(surface); +} + +static void server_handle_new_surface(struct wl_listener *listener, + void *data) { + struct server *server = wl_container_of(listener, server, new_surface); + struct wlr_surface *wlr_surface = data; + + int pos = 50 * wl_list_length(&server->surfaces); + + struct surface *surface = calloc(1, sizeof(struct surface)); + surface->wlr = wlr_surface; + surface->destroy.notify = surface_handle_destroy; + wl_signal_add(&wlr_surface->events.destroy, &surface->destroy); + surface->scene_surface = + wlr_scene_surface_create(&server->scene->node, wlr_surface); + wl_list_insert(server->surfaces.prev, &surface->link); + + wlr_scene_node_set_position(&surface->scene_surface->node, pos, pos); +} + +int main(int argc, char *argv[]) { + wlr_log_init(WLR_DEBUG, NULL); + + char *startup_cmd = NULL; + + int c; + while ((c = getopt(argc, argv, "s:")) != -1) { + switch (c) { + case 's': + startup_cmd = optarg; + break; + default: + printf("usage: %s [-s startup-command]\n", argv[0]); + return EXIT_FAILURE; + } + } + if (optind < argc) { + printf("usage: %s [-s startup-command]\n", argv[0]); + return EXIT_FAILURE; + } + + struct server server = {0}; + server.display = wl_display_create(); + server.backend = wlr_backend_autocreate(server.display); + server.scene = wlr_scene_create(); + + struct wlr_renderer *renderer = wlr_backend_get_renderer(server.backend); + wlr_renderer_init_wl_display(renderer, server.display); + + struct wlr_compositor *compositor = + wlr_compositor_create(server.display, renderer); + + wlr_xdg_shell_create(server.display); + + wl_list_init(&server.outputs); + wl_list_init(&server.surfaces); + + server.new_output.notify = server_handle_new_output; + wl_signal_add(&server.backend->events.new_output, &server.new_output); + + server.new_surface.notify = server_handle_new_surface; + wl_signal_add(&compositor->events.new_surface, &server.new_surface); + + const char *socket = wl_display_add_socket_auto(server.display); + if (!socket) { + wl_display_destroy(server.display); + return EXIT_FAILURE; + } + + if (!wlr_backend_start(server.backend)) { + wl_display_destroy(server.display); + return EXIT_FAILURE; + } + + setenv("WAYLAND_DISPLAY", socket, true); + if (startup_cmd != NULL) { + if (fork() == 0) { + execl("/bin/sh", "/bin/sh", "-c", startup_cmd, (void *)NULL); + } + } + + wlr_log(WLR_INFO, "Running Wayland compositor on WAYLAND_DISPLAY=%s", + socket); + wl_display_run(server.display); + + wl_display_destroy_clients(server.display); + wl_display_destroy(server.display); + return EXIT_SUCCESS; +}