wl-app

example app to learn the basic of wayland
Log | Files | Refs | README

commit 0ba1ae3cadba020a3cf061b79dd16609c1db7e95
parent dc2df0e654de5e3e0fb16766af1295ce5918f131
Author: mrgrouse <mrgrouse@mrgrouse.com>
Date:   Fri, 23 Oct 2026 21:19:01 -0400

Abandon project

Diffstat:
MMakefile | 27+++++++++++++++------------
AREADME.md | 6++++++
Abuf.c | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mmain.c | 61+++++++++++++++++++++++++++++++++++++++++++++++++------------
Mtypes.h | 71++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Awl.c | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 290 insertions(+), 27 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,29 +1,32 @@ CC = clang -.DEFAULT_GOAL := wl-app CFLAGS = -O2 -Werror -Wall -Wextra -DEBUG_CFLAGS = -O0 -Werror -Wall -Wextra -g +#DEBUG_CFLAGS = -O0 -Werror -Wall -Wextra -g +DEBUG_CFLAGS = -O0 -g -LDFLAGS = -lc +CFLAGS = $(DEBUG_CFLAGS) # uncomment for debugging + +LDFLAGS = -lrt -lc #LDFLAGS=-fPIE -static # for static linking -SRCS = main.c +SRCS = buf.c main.c wl.c OBJS = $(SRCS:.c=.o) -BIN = wl-app +TARGET = wl-app + +.DEFAULT_GOAL := $(TARGET) PREFIX=/usr/local -wl-app: o - $(CC) $(CFLAGS) $(OBJS) -o $(BIN) $(LDFLAGS) +all: $(TARGET) -o: - $(CC) $(CFLAGS) -c $(SRCS) +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ $(LDFLAGS) -debug: - $(CC) $(DEBUG_CFLAGS) $(SRCS) -o $(BIN) $(LDFLAGS) +$(TARGET): $(OBJS) + $(CC) -o $(TARGET) $(OBJS) $(LDFLAGS) clean: - rm -f $(BIN) $(OBJS) + rm -f $(BIN) $(OBJS) $(TARGET) install: wl-app cp -f $(BIN) $(PREFIX)/bin/$(BIN) diff --git a/README.md b/README.md @@ -0,0 +1,6 @@ +# wl-app +my failed/abandoned attempt at creating a wayland client in pure c according to the guide [here](https://gaultier.github.io/blog/wayland_from_scratch.html) +its a great guide but for some reason it will not work on my compositor/libc + +however this did help me figure out makefiles, multi-file c projects and lots more + diff --git a/buf.c b/buf.c @@ -0,0 +1,73 @@ +#include <assert.h> +#include <errno.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/un.h> + +#include "types.h" + +#define roundup_4(n) (((n) + 3) & -4) + +// buf_write_u32/u16 are literally the same thing just different types +void buf_write_u32(char *buf, uint64_t *buf_size, uint64_t buf_cap, + uint32_t x) { + assert(*buf_size + sizeof(x) <= buf_cap); // prevent buffer overflow + + assert(((size_t)buf + *buf_size) % sizeof(x) == 0); // fail if not divisible + + *(uint32_t *)(buf + *buf_size) = x; + *buf_size += sizeof(x); +} + +void buf_write_u16(char *buf, uint64_t *buf_size, uint64_t buf_cap, + uint16_t x) { + assert(*buf_size + sizeof(x) <= buf_cap); // prevent buffer overflow + assert(((size_t)buf + *buf_size) % sizeof(x) == + 0); // ensure each element is ui16 + + *(uint16_t *)(buf + *buf_size) = x; + *buf_size += sizeof(x); +} + +void buf_write_string(char *buf, uint64_t *buf_size, uint64_t buf_cap, + char *src, uint32_t src_len) { + assert(*buf_size + src_len <= buf_cap); // prevent buffer overflow + + buf_write_u32(buf, buf_size, buf_cap, src_len); + memcpy(buf + *buf_size, src, roundup_4(src_len)); + *buf_size += roundup_4(src_len); +} + +uint32_t buf_read_u32(char **buf, uint64_t *buf_size) { + assert(*buf_size >= sizeof(uint32_t)); // prevent buffer overflow + assert((size_t)*buf % sizeof(uint32_t) == 0); // ensure each element is ui32 + + uint32_t res = *(uint32_t *)(*buf); + *buf += sizeof(res); + *buf_size -= sizeof(res); + + return res; +} + +uint16_t buf_read_u16(char **buf, uint64_t *buf_size) { + assert(*buf_size >= sizeof(uint16_t)); // prevent bufer overflow + assert((size_t)*buf % sizeof(uint16_t) == 0); + + uint16_t res = *(uint16_t *)(*buf); + *buf += sizeof(res); + *buf_size -= sizeof(res); + + return res; +} + +void buf_read_n(char **buf, uint64_t *buf_size, char *dst, uint64_t n) { + assert(*buf_size >= n); + + memcpy(dst, *buf, n); + + *buf += n; + *buf_size -= n; +} diff --git a/main.c b/main.c @@ -1,29 +1,39 @@ +#include <assert.h> #include <errno.h> +#include <fcntl.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/mman.h> #include <sys/socket.h> +#include <sys/stat.h> #include <sys/un.h> +#include <unistd.h> #include "types.h" -static int wayland_display_connect(string *wsp) { +static void create_shared_memory_file(uint64_t size, state_t *state) { + string name = {.data = "/", .len = 255}; - struct sockaddr_un addr = {.sun_family = AF_UNIX}; - memcpy(addr.sun_path, wsp->data, - wsp->len); // only way to assign our path to the addr .sun_path[], - // compiler expects length at runtime but it is variable - - int fd = socket(AF_UNIX, SOCK_STREAM, 0); + for (uint64_t i = 1; i < name.len; i++) { + name.data[i] = ((double)rand()) / (double)RAND_MAX * 26 + 'a'; + } + int fd = shm_open(name.data, O_RDWR | O_EXCL | O_CREAT, 0600); if (fd == -1) exit(errno); - // int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) - if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) + + assert(shm_unlink(name.data) != -1); + + if (ftruncate(fd, size) == -1) exit(errno); - return fd; + state->shm_pool_data = + mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + assert((void *)-1 != state->shm_pool_data); + assert(state->shm_pool_data != NULL); + state->shm_fd = fd; } int main(void) { @@ -50,6 +60,33 @@ int main(void) { return 1; } - wayland_display_connect(&wl_sock_path); - return 0; + int fd = wayland_display_connect(&wl_sock_path); + + state_t state = { + .wl_registry = wayland_wl_display_get_registry(fd), + .w = 117, + .h = 150, + .stride = 117 * color_channels, + }; + + // Single buffering. + state.shm_pool_size = state.h * state.stride; + + while (1) { + string read_buf = { + .data = "", + .len = 4096, + }; + int64_t read_bytes = recv(fd, read_buf.data, sizeof(read_buf.data), 0); + if (read_bytes == -1) + exit(errno); + + char *msg = read_buf.data; + uint64_t msg_len = (uint64_t)read_bytes; + + while (msg_len > 0) + wayland_handle_message(fd, &state, &msg, &msg_len); + } + +return 0; } diff --git a/types.h b/types.h @@ -1,7 +1,73 @@ -// types.h: function prototypes and structs +// types.h: function prototypes, structs and data types typedef struct { char *data; uint8_t len; } string; -static int wayland_display_connect(string *wsp); -\ No newline at end of file +static uint32_t wayland_current_id = 1; + +static const uint32_t wayland_display_object_id = 1; +static const uint16_t wayland_wl_registry_event_global = 0; +static const uint16_t wayland_shm_pool_event_format = 0; +static const uint16_t wayland_wl_buffer_event_release = 0; +static const uint16_t wayland_xdg_wm_base_event_ping = 0; +static const uint16_t wayland_xdg_toplevel_event_configure = 0; +static const uint16_t wayland_xdg_toplevel_event_close = 1; +static const uint16_t wayland_xdg_surface_event_configure = 0; +static const uint16_t wayland_wl_display_get_registry_opcode = 1; +static const uint16_t wayland_wl_registry_bind_opcode = 0; +static const uint16_t wayland_wl_compositor_create_surface_opcode = 0; +static const uint16_t wayland_xdg_wm_base_pong_opcode = 3; +static const uint16_t wayland_xdg_surface_ack_configure_opcode = 4; +static const uint16_t wayland_wl_shm_create_pool_opcode = 0; +static const uint16_t wayland_xdg_wm_base_get_xdg_surface_opcode = 2; +static const uint16_t wayland_wl_shm_pool_create_buffer_opcode = 0; +static const uint16_t wayland_wl_surface_attach_opcode = 1; +static const uint16_t wayland_xdg_surface_get_toplevel_opcode = 1; +static const uint16_t wayland_wl_surface_commit_opcode = 6; +static const uint16_t wayland_wl_display_error_event = 0; +static const uint32_t wayland_format_xrgb8888 = 1; +static const uint32_t wayland_header_size = 8; +static const uint32_t color_channels = 4; +typedef enum state_state_t state_state_t; + +enum state_state_t { + STATE_NONE, + STATE_SURFACE_ACKED_CONFIGURE, + STATE_SURFACE_ATTACHED, +}; + +typedef struct state_t state_t; +struct state_t { + uint32_t wl_registry; + uint32_t wl_shm; + uint32_t wl_shm_pool; + uint32_t wl_buffer; + uint32_t xdg_wm_base; + uint32_t xdg_surface; + uint32_t wl_compositor; + uint32_t wl_surface; + uint32_t xdg_toplevel; + uint32_t stride; + uint32_t w; + uint32_t h; + uint32_t shm_pool_size; + int shm_fd; + uint8_t *shm_pool_data; + state_state_t state; +}; + +// wl.c +int wayland_display_connect(string *wsp); +void wayland_handle_message(int fd, state_t *state, char **msg, + uint64_t *msg_len); +uint32_t wayland_wl_display_get_registry(int fd); + +// buf.c +void buf_write_u32(char *buf, uint64_t *buf_size, uint64_t buf_cap, uint32_t x); +void buf_write_u16(char *buf, uint64_t *buf_size, uint64_t buf_cap, uint16_t x); +void buf_write_string(char *buf, uint64_t *buf_size, uint64_t buf_cap, + char *src, uint32_t src_len); +uint32_t buf_read_u32(char **buf, uint64_t *buf_size); +uint16_t buf_read_u16(char **buf, uint64_t *buf_size); +void buf_read_n(char **buf, uint64_t *buf_size, char *dst, uint64_t n); diff --git a/wl.c b/wl.c @@ -0,0 +1,79 @@ +#include <assert.h> +#include <errno.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/un.h> + +#include "types.h" + +#define roundup_4(n) (((n) + 3) & -4) + +int wayland_display_connect(string *wsp) { + + struct sockaddr_un addr = {.sun_family = AF_UNIX}; + memcpy(addr.sun_path, wsp->data, + wsp->len); // only way to assign our path to the addr .sun_path[], + // compiler expects length at runtime but it is variable + + int fd = socket(AF_UNIX, SOCK_STREAM, 0); + + if (fd == -1) + exit(errno); + // int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) + if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) + exit(errno); + + return fd; +} + +void wayland_handle_message(int fd, state_t *state, char **msg, + uint64_t *msg_len) { + assert(*msg_len >= 8); + + uint32_t object_id = buf_read_u32(msg, msg_len); + assert(object_id <= wayland_current_id); + + uint16_t opcode = buf_read_u16(msg, msg_len); + + uint16_t announced_size = buf_read_u16(msg, msg_len); + assert(roundup_4(announced_size) <= announced_size); + + uint32_t header_size = + sizeof(object_id) + sizeof(opcode) + sizeof(announced_size); + assert(announced_size <= header_size + *msg_len); + + if (object_id == state->wl_registry && + opcode == wayland_wl_registry_event_global) { + // TODO + } + // Following: Lots of `if (opcode == ...) {... } else if (opcode = ...) { ... + // } [...]` +} + +uint32_t wayland_wl_display_get_registry(int fd) { + uint64_t msg_size = 0; + char msg[128] = ""; + buf_write_u32(msg, &msg_size, sizeof(msg), wayland_display_object_id); + + buf_write_u16(msg, &msg_size, sizeof(msg), + wayland_wl_display_get_registry_opcode); + + uint16_t msg_announced_size = + wayland_header_size + sizeof(wayland_current_id); + assert(roundup_4(msg_announced_size) == msg_announced_size); + buf_write_u16(msg, &msg_size, sizeof(msg), msg_announced_size); + + wayland_current_id++; + buf_write_u32(msg, &msg_size, sizeof(msg), wayland_current_id); + + if ((int64_t)msg_size != send(fd, msg, msg_size, MSG_DONTWAIT)) + exit(errno); + + printf("-> wl_display@%u.get_registry: wl_registry=%u\n", + wayland_display_object_id, wayland_current_id); + + return wayland_current_id; +}