spkp

Stacking wayland compositor
git clone git://git.z3bra.org/sp:kp.git
Log | Files | Refs

commit 44185c5c8a0c3d205c90a8ca42a5ab899b71a7a8
Author: Willy Goiffon <dev@z3bra.org>
Date:   Sat,  7 Nov 2020 11:50:20 +0100

Initial Compositor skeleton

Diffstat:
Aarg.h | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acompositor.c | 213+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aconfig.def.h | 1+
Aconfig.mk | 12++++++++++++
Amkfile | 24++++++++++++++++++++++++
5 files changed, 315 insertions(+), 0 deletions(-)

diff --git a/arg.h b/arg.h @@ -0,0 +1,65 @@ +/* + * Copy me if you can. + * by 20h + */ + +#ifndef ARG_H__ +#define ARG_H__ + +extern char *argv0; + +/* use main(int argc, char *argv[]) */ +#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\ + argv[0] && argv[0][1]\ + && argv[0][0] == '-';\ + argc--, argv++) {\ + char argc_;\ + char **argv_;\ + int brk_;\ + if (argv[0][1] == '-' && argv[0][2] == '\0') {\ + argv++;\ + argc--;\ + break;\ + }\ + for (brk_ = 0, argv[0]++, argv_ = argv;\ + argv[0][0] && !brk_;\ + argv[0]++) {\ + if (argv_ != argv)\ + break;\ + argc_ = argv[0][0];\ + switch (argc_) + +/* Handles obsolete -NUM syntax */ +#define ARGNUM case '0':\ + case '1':\ + case '2':\ + case '3':\ + case '4':\ + case '5':\ + case '6':\ + case '7':\ + case '8':\ + case '9' + +#define ARGEND }\ + } + +#define ARGC() argc_ + +#define ARGNUMF() (brk_ = 1, estrtonum(argv[0], 0, INT_MAX)) + +#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\ + ((x), abort(), (char *)0) :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\ + (char *)0 :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#define LNGARG() &argv[0][0] + +#endif diff --git a/compositor.c b/compositor.c @@ -0,0 +1,213 @@ +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <wayland-server.h> +#include <wlr/backend.h> +#include <wlr/render/egl.h> +#include <wlr/render/wlr_renderer.h> +#include <wlr/types/wlr_output.h> +#include <wlr/types/wlr_output_damage.h> + +#include <wlr/types/wlr_idle.h> +#include <wlr/types/wlr_data_control_v1.h> +#include <wlr/types/wlr_gamma_control_v1.h> +#include <wlr/types/wlr_screencopy_v1.h> +#include <wlr/types/wlr_primary_selection_v1.h> + +#include "arg.h" +#include "config.h" + +/* keep track of internal compositor state */ +struct state { + struct wl_display *dpy; + struct wl_event_loop *evloop; + struct wlr_backend *backend; + struct wl_listener new_output; + struct wl_list outputs; +}; + +/* compositor output */ +struct output { + struct state *server; /* pointer to the containing struct */ + + struct wlr_output *wlr_output; + struct wlr_output_damage *damage; + + struct wl_listener destroy; + struct wl_listener damage_frame; + + /* pointer to the next output */ + struct wl_list link; +}; + +static void usage(char *); + +/* callback functions triggered when a new event occur */ +static void cb_new_output(struct wl_listener *, void *); +static void cb_destroy_output(struct wl_listener *, void *); +static void cb_damage_frame(struct wl_listener *, void *); + +void +usage(char *pgm) +{ + fprintf(stderr, "usage: %s [-h]\n", pgm); +} + +/* + * Triggered whenever a new output (eg, monitor) is connected to the + * compositor. This output is configured (set "mode" if needed) and + * added to the outputs list. + * Listeners are setup on this output for when stuff needs to get + * rendered, or when output is disconnected. + */ +void +cb_new_output(struct wl_listener *listener, void *data) +{ + struct state *server; + struct output *output; + struct wlr_output *wlr_output; + struct wlr_output_mode *mode; + + server = wl_container_of(listener, server, new_output); + wlr_output = data; + + /* + * Configure output mode when advertized. This is only needed + * for DRM backend, as other backends don't have such thing as an + * "output" or "mode". They simply run within a window, and thus + * have nothing to configure on their own. + */ + if (!wl_list_empty(&wlr_output->modes)) { + mode = wl_container_of(wlr_output->modes.prev, mode, link); + wlr_output_set_mode(wlr_output, mode); + } + + output = calloc(1, sizeof(*output)); + output->server = server; + output->wlr_output = wlr_output; + output->damage = wlr_output_damage_create(wlr_output); + + wl_list_insert(&server->outputs, &output->link); + + /* setup callbacks for our output */ + output->destroy.notify = cb_destroy_output; + output->damage_frame.notify = cb_damage_frame; + + /* setup signals for above notifies */ + wl_signal_add(&wlr_output->events.destroy, &output->destroy); + wl_signal_add(&output->damage->events.frame, &output->damage_frame); + + wlr_output_create_global(wlr_output); +} + +/* + * Output gets disconnected. Remove it from the list and release memory + */ +void +cb_destroy_output(struct wl_listener *listener, void *data) +{ + struct output *output; + + output = wl_container_of(listener, output, destroy); + + wl_list_remove(&output->link); + wl_list_remove(&output->destroy.link); + wl_list_remove(&output->damage_frame.link); + + free(output); +} + +/* + * Output frame buffer was modified (damaged) since last render. Compute + * the new buffer for rendering. + */ +void +cb_damage_frame(struct wl_listener *listener, void *data) +{ + bool needs_frame; + struct output *output; + struct wlr_output *wlr_output; + struct wlr_renderer *renderer; + pixman_region32_t damage; + + output = wl_container_of(listener, output, damage_frame); + wlr_output = output->wlr_output; + + if (!wlr_output->enabled) + return; + + renderer = wlr_backend_get_renderer(wlr_output->backend); + + /* create a damage buffer to render our frame */ + pixman_region32_init(&damage); + wlr_output_damage_attach_render(output->damage, &needs_frame, &damage); + + if (!needs_frame) { + wlr_output_rollback(wlr_output); + pixman_region32_fini(&damage); + return; + } + + /* paint surface with the background color */ + wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height); + wlr_renderer_clear(renderer, background); + wlr_renderer_end(renderer); + + /* apply damaged region on-screen */ + wlr_output_set_damage(wlr_output, &damage); + pixman_region32_fini(&damage); + + /* commit output, if needed */ + wlr_output_commit(wlr_output); +} + +int +main(int argc, char *argv[]) +{ + char *argv0; + const char *socket; + struct state server; + + ARGBEGIN { + case 'h': + default: + usage(argv0); + return -1; + break; /* NOTREACHED */ + } ARGEND; + + server.dpy = wl_display_create(); + assert(server.dpy); + server.evloop = wl_display_get_event_loop(server.dpy); + assert(server.evloop); + + server.backend = wlr_backend_autocreate(server.dpy, NULL); + assert(server.backend); + + wl_list_init(&server.outputs); + + server.new_output.notify = cb_new_output; + wl_signal_add(&server.backend->events.new_output, &server.new_output); + + socket = wl_display_add_socket_auto(server.dpy); + assert(socket); + + if (!wlr_backend_start(server.backend)) { + fprintf(stderr, "Failed to start backend\n"); + wl_display_destroy(server.dpy); + return -1; + } + + /* all these interfaces are managed internally by wlroots */ + wl_display_init_shm(server.dpy); + wlr_gamma_control_manager_v1_create(server.dpy); + wlr_screencopy_manager_v1_create(server.dpy); + wlr_primary_selection_v1_device_manager_create(server.dpy); + wlr_idle_create(server.dpy); + + /* trigger internal event loop */ + wl_display_run(server.dpy); + wl_display_destroy(server.dpy); + + return 0; +} diff --git a/config.def.h b/config.def.h @@ -0,0 +1 @@ +float background[4] = { 1.0, 0.6, 0.6, 0.3 }; diff --git a/config.mk b/config.mk @@ -0,0 +1,12 @@ +CC = cc +LD = ${CC} + +PREFIX = /usr/local +MANDIR = ${PREFIX}/share/man + +CPPFLAGS = -DWLR_USE_UNSTABLE +CFLAGS = -Wall -Wextra -pedantic -g +LDFLAGS = +LIBS = +WL_CFLAGS = -I/usr/include/libdrm -I/usr/include/pixman-1 +WL_LIBS = -lwlroots -lwayland-server -lpixman-1 diff --git a/mkfile b/mkfile @@ -0,0 +1,24 @@ +<config.mk + +all:V: compositor + +%: %.o + ${LD} ${LDFLAGS} $stem.o ${WL_LIBS} -o $stem + +compositor.o: config.h +%.o: %.c + ${CC} ${CPPFLAGS} ${CFLAGS} ${WL_CFLAGS} -c $stem.c -o $stem.o + +config.h: config.def.h + cp $prereq $target + +clean:V: + rm -f compositor *.o + +install:V: compositor + mkdir -p ${DESTDIR}${PREFIX}/bin + cp compositor ${DESTDIR}${PREFIX}/bin/compositor + chmod 755 ${DESTDIR}${PREFIX}/bin/compositor + +uninstall:V: + rm ${DESTDIR}${PREFIX}/bin/compositor