commit d773b21fba02fd65f8e9c0ab480b1aba0c84c04b
Author: Luke Willis <lukejw@loquat.dev>
Date:   Thu, 26 Mar 2026 20:37:29 -0400

Got a basic bar working

Diffstat:
A.gitignore | 1+
AREADME.md | 3+++
Aguix.scm | 26++++++++++++++++++++++++++
Ameson.build | 33+++++++++++++++++++++++++++++++++
Asrc/main.c | 184+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/wl.c | 48++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/wl.h | 6++++++
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, &registry_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