tInitial Compositor skeleton - spkp - Stacking wayland compositor
(HTM) git clone git://git.z3bra.org/spkp.git
(DIR) Log
(DIR) Files
(DIR) Refs
---
(DIR) commit 44185c5c8a0c3d205c90a8ca42a5ab899b71a7a8
(HTM) Author: Willy Goiffon <dev@z3bra.org>
Date: Sat, 7 Nov 2020 11:50:20 +0100
Initial Compositor skeleton
Diffstat:
A arg.h | 65 +++++++++++++++++++++++++++++++
A compositor.c | 213 +++++++++++++++++++++++++++++++
A config.def.h | 1 +
A config.mk | 12 ++++++++++++
A mkfile | 24 ++++++++++++++++++++++++
5 files changed, 315 insertions(+), 0 deletions(-)
---
(DIR) diff --git a/arg.h b/arg.h
t@@ -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
(DIR) diff --git a/compositor.c b/compositor.c
t@@ -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;
+}
(DIR) diff --git a/config.def.h b/config.def.h
t@@ -0,0 +1 @@
+float background[4] = { 1.0, 0.6, 0.6, 0.3 };
(DIR) diff --git a/config.mk b/config.mk
t@@ -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
(DIR) diff --git a/mkfile b/mkfile
t@@ -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