summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorA404M <ahmadmahmoudiprogrammer@gmail.com>2025-01-12 01:35:28 +0330
committerA404M <ahmadmahmoudiprogrammer@gmail.com>2025-01-12 01:35:28 +0330
commite31791a06b4e92b783412fa17e43fc769e1da8bb (patch)
tree3d345edefda11fdc601b5ac0205bd9b0da63c10c
-rw-r--r--.gitignore4
-rw-r--r--Makefile62
-rw-r--r--src/main.c437
3 files changed, 503 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3c50c0b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+build/
+generated/
+.cache
+compile_commands.json
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..3136504
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,62 @@
+CC := gcc
+# CC := tcc
+
+PROJECT_NAME := cube
+
+BUILD_DIR := ./build
+SRC_DIR := ./src
+GEN_DIR := ./generated
+
+SRCS := $(shell find $(SRC_DIR) -name "*.c")
+OBJS := $(BUILD_DIR)/xdg-shell.c.o $(SRCS:%=$(BUILD_DIR)/%.o)
+
+RED := \033[0;31m
+GREEN := \033[0;32m
+NC := \033[0m
+
+# INC_DIRS := $(shell find $(SRC_DIRS) -type d)
+INC_DIRS := $(SRC_DIR) $(GEN_DIR)
+INC_FLAGS := $(addprefix -I,$(INC_DIRS))
+
+CFLAGS := $(INC_FLAGS) -Wall -Wextra -lwayland-client -std=gnu23 -Ofast
+# CFLAGS := $(INC_FLAGS) -Wall -Wextra -lwayland-client -std=gnu23 -Oz
+# CFLAGS := $(INC_FLAGS) -Wall -Wextra -lwayland-client -std=gnu23 -g
+
+EXEC_FILE := $(BUILD_DIR)/$(PROJECT_NAME)
+
+all: $(EXEC_FILE)
+
+$(EXEC_FILE): $(shell find $(SRC_DIR) -name "*.c") $(OBJS)
+ $(CC) $(CFLAGS) $(OBJS) -o $@ $(LDFLAGS)
+
+$(BUILD_DIR)/%.c.o: %.c
+ mkdir -p $(dir $@)
+ $(CC) $(CFLAGS) -c $< -o $@
+
+$(BUILD_DIR)/xdg-shell.c.o:
+ mkdir -p $(GEN_DIR)
+ mkdir -p $(BUILD_DIR)
+ wayland-scanner client-header /usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml $(GEN_DIR)/xdg-shell.h
+ wayland-scanner public-code /usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml $(GEN_DIR)/xdg-shell.c
+ gcc -c $(GEN_DIR)/xdg-shell.c -o $(BUILD_DIR)/xdg-shell.c.o $(CFLAGS)
+
+lsp-files: clean
+ bear -- make all
+
+.PHONY: clean
+clean:
+ rm -rf $(BUILD_DIR)
+ rm -rf $(GEN_DIR)
+
+run: $(EXEC_FILE)
+ $(EXEC_FILE) $(args)
+
+val-run: $(EXEC_FILE)
+ valgrind --log-file="val.log" --leak-check=full --track-origins=yes --show-leak-kinds=all -s $(EXEC_FILE) $(args)
+
+gdb-run: $(EXEC_FILE)
+ gdb $(EXEC_FILE) $(args)
+
+# $@ = left hand of :
+# $< = right hand of : first one of them
+# $^ = right hand of : all
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..00a310f
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,437 @@
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include <wayland-client-protocol.h>
+#include <wayland-client.h>
+
+#include "xdg-shell.h"
+
+struct wl_display *display = NULL;
+struct wl_registry *registry = NULL;
+struct wl_compositor *compositor = NULL;
+struct wl_surface *surface = NULL;
+
+struct wl_shm *shm = NULL;
+struct wl_buffer *buffer = NULL;
+void *shm_data;
+
+struct xdg_wm_base *xdg_wm_base = NULL;
+struct xdg_surface *xdg_surface = NULL;
+struct xdg_toplevel *xdg_toplevel = NULL;
+
+//=================
+// Shared Memory
+//=================
+static int set_cloexec_or_close(int fd) {
+ if (fd == -1)
+ return -1;
+
+ long flags = fcntl(fd, F_GETFD);
+ if (flags == -1)
+ goto err;
+
+ if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
+ goto err;
+
+ return fd;
+
+err:
+ close(fd);
+ return -1;
+}
+
+static int create_tmpfile_cloexec(char *tmpname) {
+ int fd;
+
+#ifdef HAVE_MKOSTEMP
+ fd = mkostemp(tmpname, O_CLOEXEC);
+ if (fd >= 0)
+ unlink(tmpname);
+#else
+ fd = mkstemp(tmpname);
+ if (fd >= 0) {
+ fd = set_cloexec_or_close(fd);
+ unlink(tmpname);
+ }
+#endif
+
+ return fd;
+}
+
+/*
+ * Create a new, unique, anonymous file of the given size, and
+ * return the file descriptor for it. The file descriptor is set
+ * CLOEXEC. The file is immediately suitable for mmap()'ing
+ * the given size at offset zero.
+ *
+ * The file should not have a permanent backing store like a disk,
+ * but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
+ *
+ * The file name is deleted from the file system.
+ *
+ * The file is suitable for buffer sharing between processes by
+ * transmitting the file descriptor over Unix sockets using the
+ * SCM_RIGHTS methods.
+ */
+int os_create_anonymous_file(off_t size) {
+ static const char template[] = "/a404m-shared-XXXXXX";
+
+ const char *path = getenv("XDG_RUNTIME_DIR");
+ if (!path) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ char *name = malloc(strlen(path) + sizeof(template));
+ if (!name)
+ return -1;
+ strcpy(name, path);
+ strcat(name, template);
+
+ int fd = create_tmpfile_cloexec(name);
+
+ free(name);
+
+ if (fd < 0)
+ return -1;
+
+ if (ftruncate(fd, size) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+//=============
+// Shm
+//=============
+static void shm_format_handler(void *data, struct wl_shm *shm,
+ uint32_t format) {
+ (void)data;
+ (void)shm;
+
+ fprintf(stderr, "Format %d\n", format);
+}
+
+static const struct wl_shm_listener shm_listener = {
+ .format = shm_format_handler,
+};
+
+//==============
+// XDG
+//==============
+static void xdg_wm_base_ping_handler(void *data, struct xdg_wm_base *wm_base,
+ uint32_t serial) {
+ (void)data;
+
+ xdg_wm_base_pong(wm_base, serial);
+}
+
+static const struct xdg_wm_base_listener xdg_wm_base_listener = {
+ .ping = xdg_wm_base_ping_handler,
+};
+
+static void xdg_surface_configure_handler(void *data,
+ struct xdg_surface *xdg_surface,
+ uint32_t serial) {
+ (void)data;
+
+ xdg_surface_ack_configure(xdg_surface, serial);
+}
+
+static const struct xdg_surface_listener xdg_surface_listener = {
+ .configure = xdg_surface_configure_handler,
+};
+
+static void xdg_toplevel_configure_handler(void *data,
+ struct xdg_toplevel *toplevel,
+ int32_t width, int32_t height,
+ struct wl_array *states) {
+ (void)data;
+ (void)toplevel;
+ (void)states;
+
+ fprintf(stderr, "XDG toplevel configure: %dx%d\n", width, height);
+}
+
+static void xdg_toplevel_close_handler(void *data,
+ struct xdg_toplevel *toplevel) {
+ (void)data;
+ (void)toplevel;
+}
+
+static const struct xdg_toplevel_listener xdg_toplevel_listener = {
+ .configure = xdg_toplevel_configure_handler,
+ .close = xdg_toplevel_close_handler,
+};
+
+//==============
+// Global
+//==============
+static void global_registry_handler(void *data, struct wl_registry *registry,
+ uint32_t id, const char *interface,
+ uint32_t version) {
+ printf("Got a registry event for <%s>, id: %d, version: %d.\n", interface, id,
+ version);
+ if (strcmp(interface, "wl_compositor") == 0) {
+ compositor = wl_registry_bind(registry, id, &wl_compositor_interface, 4);
+ } else if (strcmp(interface, "xdg_wm_base") == 0) {
+ xdg_wm_base = wl_registry_bind(registry, id, &xdg_wm_base_interface, 1);
+ } else if (strcmp(interface, "wl_shm") == 0) {
+ shm = wl_registry_bind(registry, id, &wl_shm_interface, 1);
+ wl_shm_add_listener(shm, &shm_listener, NULL);
+ }
+}
+
+static void global_registry_remove_handler(void *data,
+ struct wl_registry *registry,
+ uint32_t id) {
+ printf("Got a registry losing event for <%d>\n", id);
+}
+
+static const struct wl_registry_listener registry_listener = {
+ .global = global_registry_handler,
+ .global_remove = global_registry_remove_handler,
+};
+
+//==========
+// Buffer
+//==========
+static struct wl_buffer *create_buffer(int width, int height) {
+ struct wl_shm_pool *pool;
+ int stride = width * 4; // 4 bytes per pixel in our ARGB8888 format.
+ int size = stride * height;
+ int fd;
+ struct wl_buffer *buff;
+
+ fd = os_create_anonymous_file(size);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to create a buffer. size: %d\n", size);
+ exit(1);
+ }
+
+ shm_data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (shm_data == MAP_FAILED) {
+ fprintf(stderr, "mmap failed!\n");
+ close(fd);
+ exit(1);
+ }
+
+ pool = wl_shm_create_pool(shm, fd, size);
+ buff = wl_shm_pool_create_buffer(pool, 0, width, height, stride,
+ WL_SHM_FORMAT_ARGB8888);
+ wl_shm_pool_destroy(pool);
+
+ return buff;
+}
+
+//=========
+// A404M
+//=========
+
+static const int width = 480, height = 360;
+static int last_frame = 0;
+
+static int cube_pos_x = 0;
+static int cube_pos_y = 0;
+static bool cube_flag_x = true;
+static bool cube_flag_y = true;
+static constexpr int cube_size = 50;
+static uint32_t cube_color = 0xff22aaff;
+static constexpr uint32_t back_color = 0xaa222222;
+
+static void collide() {
+ cube_color = (0xff000000 | (0xffffff & rand()));
+}
+
+static void draw() {
+ uint32_t *pixel = shm_data;
+
+ const int cube_end_pos_x = cube_pos_x + cube_size;
+ const int cube_end_pos_y = cube_pos_y + cube_size;
+
+ int cube_begin_loop_x = cube_pos_x - 1;
+ int cube_begin_loop_y = cube_pos_y - 1;
+
+ if (cube_begin_loop_x < 0) {
+ cube_begin_loop_x = 0;
+ }
+ if (cube_begin_loop_y < 0) {
+ cube_begin_loop_y = 0;
+ }
+
+ int cube_end_loop_x = cube_pos_x + cube_size + 1;
+ int cube_end_loop_y = cube_pos_y + cube_size + 1;
+
+ if (cube_end_loop_x > width) {
+ cube_end_loop_x = width;
+ }
+ if (cube_end_loop_y > height) {
+ cube_end_loop_y = height;
+ }
+
+ for (int x = cube_begin_loop_x; x < cube_end_loop_x; ++x) {
+ for (int y = cube_begin_loop_y; y < cube_end_loop_y; ++y) {
+ if (cube_pos_x < x && x < cube_end_pos_x && cube_pos_y < y &&
+ y < cube_end_pos_y) {
+ pixel[x + y * width] = cube_color;
+ } else {
+ // *pixel = (rand() << 3 * 8) | (0xffffff & rand());
+ pixel[x + y * width] = back_color;
+ }
+ }
+ }
+
+ if (cube_flag_x) {
+ ++cube_pos_x;
+ } else {
+ --cube_pos_x;
+ }
+ if (cube_flag_y) {
+ ++cube_pos_y;
+ } else {
+ --cube_pos_y;
+ }
+
+ if (cube_pos_x < 0) {
+ cube_flag_x = true;
+ collide();
+ } else if (cube_pos_x + cube_size >= width) {
+ cube_flag_x = false;
+ collide();
+ }
+
+ if (cube_pos_y < 0) {
+ cube_flag_y = true;
+ collide();
+ } else if (cube_pos_y + cube_size >= height) {
+ cube_flag_y = false;
+ collide();
+ }
+
+ wl_surface_attach(surface, buffer, 0, 0);
+ wl_surface_damage_buffer(surface, cube_begin_loop_x, cube_begin_loop_y, cube_end_loop_x, cube_end_loop_y);
+ wl_surface_commit(surface);
+}
+
+static const struct wl_callback_listener surface_frame_listener;
+
+static void surface_frame_done(void *data, struct wl_callback *callback,
+ uint32_t time) {
+ // printf("frame %d\n", time - last_frame);
+ last_frame = time;
+ wl_callback_destroy(callback);
+
+ wl_callback_add_listener(wl_surface_frame(surface), &surface_frame_listener,
+ data);
+
+ draw();
+}
+
+static const struct wl_callback_listener surface_frame_listener = {
+ .done = surface_frame_done,
+};
+//==========
+// Main
+//==========
+
+int main(int argc, char *argv[]) {
+ (void)argc;
+ (void)argv;
+
+ srand(time(NULL));
+
+ cube_pos_x = rand() % width;
+ cube_pos_y = rand() % height;
+
+ display = wl_display_connect(NULL);
+ if (display == NULL) {
+ fprintf(stderr, "Failed to connect to display.\n");
+ exit(1);
+ }
+ printf("Connected to display.\n");
+
+ registry = wl_display_get_registry(display);
+ wl_registry_add_listener(registry, &registry_listener, NULL);
+
+ wl_display_dispatch(display);
+ wl_display_roundtrip(display);
+
+ // Check compositor.
+ fprintf(stderr, " - Checking compositor...\n");
+ if (compositor == NULL) {
+ fprintf(stderr, "Can't find compositor.\n");
+ exit(1);
+ } else {
+ printf("Found compositor!\n");
+ }
+
+ // Surface
+ surface = wl_compositor_create_surface(compositor);
+ if (surface == NULL) {
+ exit(1);
+ }
+
+ // XDG.
+ xdg_wm_base_add_listener(xdg_wm_base, &xdg_wm_base_listener, NULL);
+
+ xdg_surface = xdg_wm_base_get_xdg_surface(xdg_wm_base, surface);
+ xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, NULL);
+
+ xdg_toplevel = xdg_surface_get_toplevel(xdg_surface);
+ xdg_toplevel_add_listener(xdg_toplevel, &xdg_toplevel_listener, NULL);
+
+ // This step is important on Weston.
+ wl_surface_commit(surface);
+ wl_display_roundtrip(display);
+
+ struct wl_callback *fcb = wl_surface_frame(surface);
+
+ wl_callback_add_listener(fcb, &surface_frame_listener, NULL);
+
+ // Create a buffer.
+ buffer = create_buffer(width, height);
+
+ wl_surface_attach(surface, buffer, 0, 0);
+ wl_surface_commit(surface);
+
+ // Drawing pixels.
+ draw();
+
+ /*
+ wl_surface_attach(surface, buffer, 0, 0);
+ wl_surface_damage_buffer(surface, 0, 0, width, height);
+ wl_surface_commit(surface);
+ */
+
+ // Display loop.
+ while (true) {
+ int cond = wl_display_dispatch(display);
+ // printf("%d\n", cond);
+
+ if (cond == -1) {
+ break;
+ }
+ }
+
+ // Free resources.
+ xdg_toplevel_destroy(xdg_toplevel);
+
+ xdg_surface_destroy(xdg_surface);
+
+ wl_surface_destroy(surface);
+
+ wl_display_disconnect(display);
+ printf("Disconnected from display.\n");
+
+ return 0;
+}