commit 0ba1ae3cadba020a3cf061b79dd16609c1db7e95
parent dc2df0e654de5e3e0fb16766af1295ce5918f131
Author: mrgrouse <mrgrouse@mrgrouse.com>
Date: Fri, 23 Oct 2026 21:19:01 -0400
Abandon project
Diffstat:
| M | Makefile | | | 27 | +++++++++++++++------------ |
| A | README.md | | | 6 | ++++++ |
| A | buf.c | | | 73 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | main.c | | | 61 | +++++++++++++++++++++++++++++++++++++++++++++++++------------ |
| M | types.h | | | 71 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- |
| A | wl.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;
+}