diff options
author | A404M <ahmadmahmoudiprogrammer@gmail.com> | 2025-01-11 17:40:46 +0330 |
---|---|---|
committer | A404M <ahmadmahmoudiprogrammer@gmail.com> | 2025-01-11 17:40:46 +0330 |
commit | fb38f415bfce68d21152fa24d95d86665fa0c748 (patch) | |
tree | b71bab2c512a6aaeaeeba2fde32aa5ac85bf4d44 |
-rw-r--r-- | .gitignore | 4 | ||||
-rw-r--r-- | Makefile | 62 | ||||
-rw-r--r-- | src/main.c | 363 |
3 files changed, 429 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..995ab1e --- /dev/null +++ b/Makefile @@ -0,0 +1,62 @@ +CC := gcc +# CC := tcc + +PROJECT_NAME := noise + +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..a4e049c --- /dev/null +++ b/src/main.c @@ -0,0 +1,363 @@ +#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; + +void draw() { + uint32_t *pixel = shm_data; + for (uint i = 0; i < width * height; ++i) { + *pixel = (rand() << 3 * 8) | (0xffffff & rand()); + ++pixel; + } +} + +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(); + wl_surface_attach(surface, buffer, 0, 0); + wl_surface_damage_buffer(surface, 0, 0, width, height); + wl_surface_commit(surface); +} + +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)); + + 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, ®istry_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; +} |