commit d773b21fba02fd65f8e9c0ab480b1aba0c84c04b
Author: Luke Willis <lukejw@loquat.dev>
Date: Thu, 26 Mar 2026 20:37:29 -0400
Got a basic bar working
Diffstat:
7 files changed, 301 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1 @@
+*~
diff --git a/README.md b/README.md
@@ -0,0 +1,3 @@
+# wig - Widgets in Guile
+
+Wig is a widget engine for Wayland configured via Guile Scheme.
diff --git a/guix.scm b/guix.scm
@@ -0,0 +1,26 @@
+(use-modules (guix packages)
+ (guix licenses)
+ (guix git)
+ (guix build-system meson)
+ (guix gexp)
+ (gnu packages)
+ (gnu packages freedesktop)
+ (gnu packages guile)
+ (gnu packages pkg-config))
+
+(define wig
+ (package
+ (name "wig")
+ (version "0.1.0")
+ (source (git-checkout (url (dirname (current-filename)))))
+ (build-system meson-build-system)
+ (native-inputs (list pkg-config
+ wayland-protocols
+ wlr-protocols))
+ (inputs (list wayland))
+ (synopsis "Widgets in Guile")
+ (description #f)
+ (home-page "https://git.monastech.xyz/wig")
+ (license gpl3+)))
+
+wig
diff --git a/meson.build b/meson.build
@@ -0,0 +1,33 @@
+project('wig', 'c',
+ meson_version: '>=1.8.0')
+
+# Fix math.h errors with glibc
+cc = meson.get_compiler('c')
+m_dep = cc.find_library('m', required : false)
+
+deps = [
+ dependency('wayland-client'),
+ m_dep
+]
+
+# Set up wayland protocols
+wlr_protocols = dependency('wlr-protocols')
+wlr_protocols_data = wlr_protocols.get_variable('pkgdatadir')
+
+wl_mod = import('wayland')
+wayland_protocols = [
+ wl_mod.scan_xml(wl_mod.find_protocol('xdg-shell')),
+ wl_mod.scan_xml(join_paths(wlr_protocols_data, 'unstable/wlr-layer-shell-unstable-v1.xml'))
+]
+
+common_sources = files(
+ 'src/wl.c'
+)
+
+executable(
+ 'wig',
+ wayland_protocols,
+ common_sources + ['src/main.c'],
+ install : true,
+ dependencies : deps
+)
diff --git a/src/main.c b/src/main.c
@@ -0,0 +1,184 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include <wayland-client.h>
+
+#include "wlr-layer-shell-unstable-v1-client-protocol.h"
+
+#include "wl.h"
+
+struct wig_state {
+ struct wl_display *display;
+ struct wl_registry *registry;
+
+ struct wl_compositor *compositor;
+ struct wl_shm *shm;
+ struct zwlr_layer_shell_v1 *layer_shell;
+
+ struct wl_surface *surface;
+ struct zwlr_layer_surface_v1 *layer_surface;
+};
+
+/*
+ * Buffer
+ */
+
+static void wl_buffer_release(void *data, struct wl_buffer *wl_buffer)
+{
+ wl_buffer_destroy(wl_buffer);
+}
+
+static const struct wl_buffer_listener wl_buffer_listener = {
+ .release = wl_buffer_release,
+};
+
+static struct wl_buffer *draw_frame(struct wig_state *state, int width,
+ int height)
+{
+ int stride = width * 4;
+ int size = stride * height;
+
+ int fd = allocate_shm_file(size);
+ if (fd == -1) {
+ return NULL;
+ }
+
+ uint32_t *data = mmap(NULL, size,
+ PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (data == MAP_FAILED) {
+ close(fd);
+ return NULL;
+ }
+
+ struct wl_shm_pool *pool = wl_shm_create_pool(state->shm, fd, size);
+ struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, 0,
+ width, height,
+ stride,
+ WL_SHM_FORMAT_ARGB8888);
+ wl_shm_pool_destroy(pool);
+ close(fd);
+
+ /* Draw checkerboxed background */
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ if ((x + y / 8 * 8) % 16 < 8)
+ data[y * width + x] = 0xFF666666;
+ else
+ data[y * width + x] = 0xFFEEEEEE;
+ }
+ }
+
+ munmap(data, size);
+ wl_buffer_add_listener(buffer, &wl_buffer_listener, NULL);
+ return buffer;
+}
+
+/*
+ * Surface
+ */
+
+static void zwlr_layer_surface_configure(void *data, struct zwlr_layer_surface_v1
+ *zwlr_layer_surface_v1,
+ uint32_t serial, uint32_t width,
+ uint32_t height)
+{
+ struct wig_state *state = data;
+ zwlr_layer_surface_v1_ack_configure(zwlr_layer_surface_v1, serial);
+
+ struct wl_buffer *buffer = draw_frame(state, width, height);
+ wl_surface_attach(state->surface, buffer, 0, 0);
+ wl_surface_commit(state->surface);
+}
+
+static void zwlr_layer_surface_closed(void *data, struct zwlr_layer_surface_v1
+ *zwlr_layer_surface_v1)
+{
+ /* TODO */
+}
+
+static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
+ .configure = zwlr_layer_surface_configure,
+ .closed = zwlr_layer_surface_closed,
+};
+
+/*
+ * Registry
+ */
+
+static void registry_handle_global(void *data,
+ struct wl_registry *registry,
+ uint32_t name, const char *interface,
+ uint32_t version)
+{
+ struct wig_state *state = data;
+
+ if (strcmp(interface, wl_compositor_interface.name) == 0) {
+ state->compositor =
+ wl_registry_bind(registry, name, &wl_compositor_interface, 4);
+ } else if (strcmp(interface, wl_shm_interface.name) == 0) {
+ state->shm =
+ wl_registry_bind(registry, name, &wl_shm_interface, 1);
+ } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
+ state->layer_shell =
+ wl_registry_bind(registry, name,
+ &zwlr_layer_shell_v1_interface, 2);
+ }
+}
+
+static void
+registry_handle_global_remove(void *data, struct wl_registry *registry,
+ uint32_t name)
+{
+ /* TODO */
+}
+
+static const struct wl_registry_listener registry_listener = {
+.global = registry_handle_global,.global_remove =
+ registry_handle_global_remove,};
+
+/* Main */
+
+int main(int argc, char *argv[])
+{
+ struct wig_state state = { NULL };
+
+ state.display = wl_display_connect(NULL);
+ if (!state.display) {
+ fprintf(stderr, "Failed to connect to Wayland display.\n");
+ return 1;
+ }
+
+ state.registry = wl_display_get_registry(state.display);
+ wl_registry_add_listener(state.registry, ®istry_listener, &state);
+ wl_display_roundtrip(state.display);
+
+ /* Create surface */
+ state.surface = wl_compositor_create_surface(state.compositor);
+ state.layer_surface =
+ zwlr_layer_shell_v1_get_layer_surface(state.layer_shell,
+ state.surface,
+ NULL,
+ ZWLR_LAYER_SHELL_V1_LAYER_TOP,
+ "wig_shell");
+ zwlr_layer_surface_v1_add_listener(state.layer_surface,
+ &layer_surface_listener, &state);
+ zwlr_layer_surface_v1_set_size(state.layer_surface, 0, 64);
+ zwlr_layer_surface_v1_set_anchor(state.layer_surface,
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM);
+ zwlr_layer_surface_v1_set_exclusive_zone(state.layer_surface, 64);
+ zwlr_layer_surface_v1_set_margin(state.layer_surface, 8, 8, 8, 8);
+ wl_surface_commit(state.surface);
+
+ /* Loop */
+ while (wl_display_dispatch(state.display) != -1) {
+ /* TODO */
+ }
+
+ wl_display_disconnect(state.display);
+ return 0;
+}
diff --git a/src/wl.c b/src/wl.c
@@ -0,0 +1,48 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <time.h>
+#include <unistd.h>
+
+static void randname(char *buf)
+{
+ struct timespec ts;
+ clock_gettime(CLOCK_REALTIME, &ts);
+ long r = ts.tv_nsec;
+ for (int i = 0; i < 6; ++i) {
+ buf[i] = 'A' + (r & 15) + (r & 16) * 2;
+ r >>= 5;
+ }
+}
+
+static int create_shm_file(void)
+{
+ int retries = 100;
+ do {
+ char name[] = "/wl_shm-XXXXXX";
+ randname(name + sizeof(name) - 7);
+ --retries;
+ int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
+ if (fd >= 0) {
+ shm_unlink(name);
+ return fd;
+ }
+ } while (retries > 0 && errno == EEXIST);
+ return -1;
+}
+
+int allocate_shm_file(size_t size)
+{
+ int fd = create_shm_file();
+ if (fd < 0)
+ return -1;
+ int ret;
+ do {
+ ret = ftruncate(fd, size);
+ } while (ret < 0 && errno == EINTR);
+ if (ret < 0) {
+ close(fd);
+ return -1;
+ }
+ return fd;
+}
diff --git a/src/wl.h b/src/wl.h
@@ -0,0 +1,6 @@
+#ifndef WIG_WL
+#define WIG_WL
+
+int allocate_shm_file(size_t size);
+
+#endif