From 9a2fe3e091849518f3d2f79114ba67fa4fc63adf Mon Sep 17 00:00:00 2001 From: nasr Date: Fri, 9 Oct 2026 12:37:56 +0200 Subject: feature: window with sqaures etc etc --- build.sh | 19 +- source/base/base_arena.cpp | 2 +- source/base/base_core.h | 73 +++-- source/base/base_include.h | 69 ++-- source/base/base_platform.cpp | 5 - source/base/base_platform.h | 136 ++++---- source/base/base_platform_linux.cpp | 7 +- source/base/base_platform_linux_wayland.cpp | 365 ++++++++++++++-------- source/base/base_platform_linux_wayland_input.cpp | 159 ++++++++++ source/base/base_render_vulkan.cpp | 2 +- source/base/base_stack.cpp | 2 +- source/loo/loo.cpp | 26 +- 12 files changed, 581 insertions(+), 284 deletions(-) delete mode 100644 source/base/base_platform.cpp create mode 100644 source/base/base_platform_linux_wayland_input.cpp diff --git a/build.sh b/build.sh index 73373e2..4465cec 100755 --- a/build.sh +++ b/build.sh @@ -8,18 +8,27 @@ echo BUILD_DIR=build SOURCE_DIR=source INCLUDES="-I${SOURCE_DIR}/loo -I${SOURCE_DIR} -I${SOURCE_DIR}/base" -CFLAGS="-fno-rtti -fno-exceptions -Wall -Werror -fanalyzer -no-pie -Wno-unused-function -Wno-unused-variable -O0" +CFLAGS="-fno-rtti -fno-exceptions -Wall -Wundef -Werror -fanalyzer -no-pie -Wno-unused-function -Wno-unused-variable -O0 " LIBS="" DEBUG=1 -VULKAN=1 +VULKAN=0 WAYLAND=1 X11=0 if [ $WAYLAND -eq 1 ]; then - LIBS+=" $(pkg-config --libs wayland-client)" - CFLAGS+=" $(pkg-config --cflags wayland-client) -DWAYLAND=1 -DX11=0" + CFLAGS+=" -DWAYLAND=1 -DX11=0" + LIBS+="-lwayland-client -lxkbcommon" + +# TODO: how often do we need the xdg protocol? + GENERATED_DIR="${BUILD_DIR}/generated" + XDG_SHELL_XML=/usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml + mkdir -p "$GENERATED_DIR" + wayland-scanner client-header "$XDG_SHELL_XML" "${GENERATED_DIR}/xdg-shell-client-protocol.h" + wayland-scanner private-code "$XDG_SHELL_XML" "${GENERATED_DIR}/xdg-shell-protocol.c" + INCLUDES+=" -I${GENERATED_DIR}" + elif [ $X11 -eq 1 ]; then LIBS+=" -lX11" CFLAGS+=" -DX11=1 -DWAYLAND=0" @@ -37,4 +46,4 @@ fi mkdir -p "$BUILD_DIR" echo "Building ${BUILD_DIR}/loo..." -g++ $INCLUDES $CFLAGS "${SOURCE_DIR}/loo/loo.cpp" -o "${BUILD_DIR}/loo" $LIBS \ No newline at end of file +g++ $INCLUDES $CFLAGS "${SOURCE_DIR}/loo/loo.cpp" -o "${BUILD_DIR}/loo" $LIBS diff --git a/source/base/base_arena.cpp b/source/base/base_arena.cpp index b4235e6..759080c 100644 --- a/source/base/base_arena.cpp +++ b/source/base/base_arena.cpp @@ -125,7 +125,7 @@ arena_resize_block_align(MemArena *arena, void *old_memory, u64 new_size, u64 ol if((arena->base_position + arena->previous_position) == old_memory) { arena->current_position = arena->previous_position + new_size; if(new_size > old_size) { - MemSet(&arena->current_position, new_size - old_size); + MemSetZero(old_mem + old_size, new_size - old_size); } return (MemArena *)old_memory; diff --git a/source/base/base_core.h b/source/base/base_core.h index 9d0f5e7..d708ce1 100644 --- a/source/base/base_core.h +++ b/source/base/base_core.h @@ -14,10 +14,31 @@ //- architecture definitions -#if defined(__arm__) || defined(__aarch64__) +#if defined(__arm__) || defined(__aarch64__) || defined(_M_ARM64) #define ARCH_ARM 1 -#elif defined(__i386__) || defined(__x86_64__) +#elif defined(__x86_64__) || defined(_M_X64) #define ARCH_x86_64 1 +#elif defined(__i386__) || defined(_M_IX86) +#define ARCH_X86 1 +#endif + +#ifndef COMPILER_MSVC +#define COMPILER_MSVC 0 +#endif +#ifndef COMPILER_CLANG +#define COMPILER_CLANG 0 +#endif +#ifndef COMPILER_GCC +#define COMPILER_GCC 0 +#endif +#ifndef ARCH_ARM +#define ARCH_ARM 0 +#endif +#ifndef ARCH_x86_64 +#define ARCH_x86_64 0 +#endif +#ifndef ARCH_X86 +#define ARCH_X86 0 #endif //- compiler definitions @@ -39,26 +60,27 @@ #define Unused(x) (void)(x) #define Nil 0 -#define Deprecated __attribute__((__deprecated__)) +#define Deprecated [[deprecated]] //- breakpoints -#if ARCH_ARM -#define breakpoint() __asm__ volatile("brk #0") -#elif ARCH_x86_64 - -#if COMPILER_GCC -#define breakpoint() __asm__ volatile("int3") -#endif -#endif - #if COMPILER_MSVC #include #define breakpoint() __debugbreak() +#define Trap() __fastfail(0) +#elif COMPILER_CLANG +#define breakpoint() __builtin_debugtrap() +#define Trap() __builtin_trap() +#elif ARCH_ARM +#define breakpoint() __asm__ volatile("brk #0") +#define Trap() __builtin_trap() +#else +#define breakpoint() __asm__ volatile("int3") +#define Trap() __builtin_trap() #endif //- #define MemCpy(dest, src, len) memcpy((dest), (src), (len)) -#define MemSet(dest, len) memset((dest), (1), (len)) +#define MemSet(dest, val, len) memset((dest), (val), (len)) #define MemSetZero(dest, len) memset((dest), (0), (len)) //- @@ -107,11 +129,17 @@ typedef intptr_t smm; //- -#define Assert(expression) _assert(expression) +#define Assert(expression) do { \ + if (!(expression)) { \ + breakpoint(); \ + Trap(); \ + } \ + } \ + while(0) // NOTE: apparantly in macros this strinifys whataever you put after the # #define AssertLog(expression, ...) do { \ - if (!expression) { \ + if (!(expression)) { \ Log("%s:%d: %s\n", __FILE__, __LINE__, #expression); \ Log(__VA_ARGS__);\ Assert(false);\ @@ -119,21 +147,6 @@ typedef intptr_t smm; } \ while(0) -internal void _assert(bool expression); - - -internal void _assert(bool expression) -{ - - if (!expression) { - breakpoint(); -#if COMPILER_MSVC - __fastfail(0); -#else - __builtin_trap(); // NOTE: always stop, breakpoint() is empty on arm without DEBUG -#endif - } -} diff --git a/source/base/base_include.h b/source/base/base_include.h index 401f682..22727f5 100644 --- a/source/base/base_include.h +++ b/source/base/base_include.h @@ -1,47 +1,70 @@ #ifndef BASE_INCLUDE_H #define BASE_INCLUDE_H -#if defined(__linux__) || defined(__APPLE__) -// #message "PLATFORM LINUX APPLE" +#if defined(__APPLE__) +#define PLATFORM_APPLE 1 #define PLATFORM_UNIX 1 +#elif defined(__linux__) #define PLATFORM_LINUX 1 -#elif defined(__linux__) && !defined(__APPLE__) -#message "PLATFORM LINUX" -#define PLATFORM_LINUX 1 -#elif defined(_WIN32) || defined(_WIN64) -#message "PLATFORM WINDOWS" +#define PLATFORM_UNIX 1 +#elif defined(_WIN32) #define PLATFORM_WINDOWS 1 -#elif defined(__APPLE__) -#define PLATFORM_APPLE 1 -#message "PLATFORM APPLE" #else -#message platform unknown +#error Unsupported platform #endif // platform definitions +#ifndef PLATFORM_APPLE +#define PLATFORM_APPLE 0 +#endif +#ifndef PLATFORM_LINUX +#define PLATFORM_LINUX 0 +#endif +#ifndef PLATFORM_UNIX +#define PLATFORM_UNIX 0 +#endif +#ifndef PLATFORM_WINDOWS +#define PLATFORM_WINDOWS 0 +#endif +#ifndef DEBUG +#define DEBUG 0 +#endif +#ifndef X11 +#define X11 0 +#endif +#ifndef WAYLAND +#define WAYLAND 0 +#endif +#ifndef VULKAN +#define VULKAN 0 +#endif +#ifndef OPENGL +#define OPENGL 0 +#endif + #if PLATFORM_UNIX #include #include -#include #include #include // needed for making a connection to displays (wayland) #include -#include #include #include #include -#if DEBUG -#include -#endif - #if PLATFORM_LINUX +#include + #if X11 #include #include #include #elif WAYLAND -#include + + +#include // wl_display, wl_surface, wl_seat, wl_keyboard, ... (core protocol) +#include "xdg-shell-client-protocol.h" // xdg_wm_base, xdg_surface, xdg_toplevel (generated by build.sh) +#include // the only types of inputs the server will probably send on wayland #endif #endif // PLATFORM_LINUX @@ -59,12 +82,11 @@ //- #include -#include #include #include -#include #include #include +#include //- #include "base_core.h" @@ -80,7 +102,12 @@ #include "base_arena.cpp" #include "base_stack.cpp" #include "base_string.cpp" -#include "base_platform.cpp" + +#if PLATFORM_LINUX +#include "base_platform_linux.cpp" +#elif PLATFORM_WINDOWS +#include "base_platform_win32.cpp" +#endif #include "base_rand.cpp" #include "base_simd.cpp" diff --git a/source/base/base_platform.cpp b/source/base/base_platform.cpp deleted file mode 100644 index ae5b2e9..0000000 --- a/source/base/base_platform.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#if PLATFORM_LINUX -#include "base_platform_linux.cpp" -#elif PLATFORM_WINDOWS -#include "base_platform_win32.cpp" -#endif \ No newline at end of file diff --git a/source/base/base_platform.h b/source/base/base_platform.h index 10f90b0..875986d 100644 --- a/source/base/base_platform.h +++ b/source/base/base_platform.h @@ -16,14 +16,30 @@ struct Directory { String8List *file_names; }; -#if 0 // TODO: @WAYLAND and @X11 implementation -typedef struct LinuxWindowHandle LinuxWindowHandle; -struct LinuxWindowHandle { +#if 1 // TODO: @WAYLAND and @X11 implementation + +typedef struct Display Display; +struct Display +{ + int width; + int height; +}; + +typedef struct Window Window; +struct Window +{ + int width; + int height; +}; + +typedef struct LinuxWindowParams LinuxWindowParams; +struct LinuxWindowParams { + cstring name; Display *display; Window window; }; -#define LinuxLoadSharedLibrary(library, name) unix_ +#define LinuxLoadSharedLibrary(arena, library) unix_load_shared_library(arena, library) #endif //- Shared libraries @@ -51,14 +67,16 @@ internal void unix_log_with_args(const char *format, ...); #define FileExists(path) unix_file_exists(path) #define DirLoad(arena, path) unix_open_dir(arena, path) -#define OSExit() unix_os_exit(); -#if COMPILER_GCC || COMPILER_CLANG -// TODO: __VA_ARGS__ CLANG +#if DEBUG +#define OSExit(code) _exit(code) #define Log(...) unix_log_with_args(__VA_ARGS__) -#define LogStr8(message) \ - Log(Str8Fmt, Str8Arg(message)) // NOTE: for this to wwork we need the /analyze argument -#endif // COMPILER_GCC || COMPILER_CLANG +#define LogStr8(message) Log(Str8Fmt, Str8Arg(message)) +#else +#define OSExit(code) Unused(code) +#define Log(...) ((void)0) +#define LogStr8(message) ((void)0) +#endif // disable comments when debug mode is disabled //- Entrypoint and platform window layer #define ENTRYPOINT() int main(int count, char **args) @@ -95,59 +113,55 @@ internal s32 x11_read_input(); //- Wayland #elif WAYLAND -//- protocol values defined in the wayland library. it's a library load less i guess -global_variable const u32 wayland_display_object_id = 1; -global_variable const u16 wayland_wl_registry_event_global = 0; -global_variable const u16 wayland_shm_pool_event_format = 0; -global_variable const u16 wayland_wl_buffer_event_release = 0; -global_variable const u16 wayland_xdg_wm_base_event_ping = 0; -global_variable const u16 wayland_xdg_toplevel_event_configure = 0; -global_variable const u16 wayland_xdg_toplevel_event_close = 1; -global_variable const u16 wayland_xdg_surface_event_configure = 0; -global_variable const u16 wayland_wl_display_get_registry_opcode = 1; -global_variable const u16 wayland_wl_registry_bind_opcode = 0; -global_variable const u16 wayland_wl_compositor_create_surface_opcode = 0; -global_variable const u16 wayland_xdg_wm_base_pong_opcode = 3; -global_variable const u16 wayland_xdg_surface_ack_configure_opcode = 4; -global_variable const u16 wayland_wl_shm_create_pool_opcode = 0; -global_variable const u16 wayland_xdg_wm_base_get_xdg_surface_opcode = 2; -global_variable const u16 wayland_wl_shm_pool_create_buffer_opcode = 0; -global_variable const u16 wayland_wl_surface_attach_opcode = 1; -global_variable const u16 wayland_xdg_surface_get_toplevel_opcode = 1; -global_variable const u16 wayland_wl_surface_commit_opcode = 6; -global_variable const u16 wayland_wl_display_error_event = 0; -global_variable const u32 wayland_format_xrgb8888 = 1; -global_variable const u32 wayland_header_size = 8; -global_variable const u32 color_channels = 4; - -enum state_state_t: u16 { - STATE_NONE, - STATE_SURFACE_ACKED_CONFIGURE, - STATE_SURFACE_ATTACHED, -}; +typedef struct WaylandState WaylandState; +struct WaylandState { + //- connection + struct wl_display *display; + struct wl_registry *registry; + + struct wl_compositor *compositor; + struct wl_shm *shm; + struct xdg_wm_base *xdg_wm_base; + struct wl_seat *seat; + + //- window objects -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; + struct wl_surface *surface; + struct xdg_surface *xdg_surface; + struct xdg_toplevel *xdg_toplevel; + + //- keyboard input + struct wl_keyboard *keyboard; + b32 keys_down[256]; // indexed by linux scancode + +//- mouse (pointer) input + +struct wl_pointer *pointer; // wl_pointer is a predefined struct +f32 mouse_x, mouse_y; +b32 mouse_buttons[3]; +f32 x_offset, y_offset; +b32 mouse_in_window; + + + + //- software framebuffer + struct wl_buffer *buffer; + u8 *pixels; + u32 width; + u32 height; + u32 stride; + u32 size; + + b32 should_close; }; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wattributes" +#include "xdg-shell-protocol.c" +#pragma GCC diagnostic pop + +#include "base_platform_linux_wayland_input.cpp" #include "base_platform_linux_wayland.cpp" #endif // X11 and Wayland -- set by build params @@ -190,7 +204,7 @@ internal void win32_log(String8List messages); #define FileExists(path) win32_file_exists(path) #define Win32FileExists(path) win32_file_exists(path) #define DirLoad(arena, path) win32_open_dir(arena, original_path); -#define OSExit() win32_os_exit(); +#define OSExit(code) win32_os_exit() #if COMPILER_GCC || COMPILER_CLANG @@ -200,7 +214,7 @@ internal void win32_logf(const char *fmt, ...) PRINTF_FORMAT; #elif COMPILER_MSVC internal void win32_logf(_Printf_format_string_ const char *fmt, ...); -#define Log(fmt, ...) win32_logf(fmt, __VA_ARGS__) +#define Log(fmt, ...) win32_logf(fmt, ##__VA_ARGS__) #endif // COMPILER_GCC || COMPILER_CLANG diff --git a/source/base/base_platform_linux.cpp b/source/base/base_platform_linux.cpp index c0bf879..61a4bea 100644 --- a/source/base/base_platform_linux.cpp +++ b/source/base/base_platform_linux.cpp @@ -17,9 +17,9 @@ internal String8 unix_get_env(const char *var) { buffer = str8_chop_left(buffer, equal_sign_pos + 1); } - return buffer; -} - ++index; + return buffer; + } + ++index; } AssertLog(buffer.size != 0, "env value not found\n"); @@ -149,7 +149,6 @@ internal void unix_log_with_args(const char *format, ...) va_end(arguments); } -internal void unix_os_exit() { _exit(0); } // TODO: rewrite this internal String8List *unix_open_dir(MemArena *arena, String8 *path) diff --git a/source/base/base_platform_linux_wayland.cpp b/source/base/base_platform_linux_wayland.cpp index 772080b..5823a69 100644 --- a/source/base/base_platform_linux_wayland.cpp +++ b/source/base/base_platform_linux_wayland.cpp @@ -1,193 +1,294 @@ -// https://gaultier.github.io/blog/wayland_from_scratch.html#creating-a-registry -// TODO: replace unix_get_env with a generic macro +//- software framebuffer +// TODO: delete this section once vulkan is presenting. vkCreateWaylandSurfaceKHR only needs +// state->display and state->surface, the driver then creates and attaches buffers itself. +// creates a wl_buffer backed by shared memory that both we and the compositor can read. +internal struct wl_buffer *wayland_create_shm_buffer(WaylandState *state) { + state->stride = state->width * 4; // 4 bytes per pixel (XRGB8888) + state->size = state->stride * state->height; -//- wayland -internal int wayland_display_connect(MemArena *arena) { + int fd = memfd_create("loo-shared-memory", MFD_CLOEXEC); + AssertLog((fd != -1), "Failed to open buffer for shared memory."); + AssertLog((ftruncate(fd, state->size) != -1), "Failed to truncate buffer."); - String8 str8_xdg_runtime_dir = unix_get_env("XDG_RUNTIME_DIR"); - char *xdg_runtime_dir = null_terminate(arena, &str8_xdg_runtime_dir); - sockaddr_un addr = {}; - u64 socket_path_len = 0; + state->pixels = (u8 *)mmap(NULL, state->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + AssertLog((state->pixels != MAP_FAILED), "Failed to mmap shared memory."); -// copy the xdg_runtime_dir value to the beginning of the bfufer -{ - addr.sun_family = AF_UNIX; - MemCpy(addr.sun_path, xdg_runtime_dir, str8_xdg_runtime_dir.size); - socket_path_len += str8_xdg_runtime_dir.size; - addr.sun_path[socket_path_len++] = '/'; // add an ending slash -} + // a pool is the compositor-side view of our shared memory. libwayland sends the fd + // over the socket for us (the SCM_RIGHTS dance, in the other direction). + struct wl_shm_pool *pool = wl_shm_create_pool(state->shm, fd, state->size); + + // a buffer is a typed slice of the pool: offset, size, stride and pixel format + struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, 0, state->width, state->height, + state->stride, WL_SHM_FORMAT_XRGB8888); - String8 str8_wayland_display_value = unix_get_env("WAYLAND_DISPLAY"); -Assert(str8_wayland_display_value.size != 0); - char *wayland_display = null_terminate(arena, &str8_wayland_display_value); + wl_shm_pool_destroy(pool); + close(fd); + + return buffer; +} - if(wayland_display == NULL) - { - const char *wayland_display_default = "wayland-0"; - u64 wayland_display_default_len = cstring8_length((u8 *)wayland_display_default); - MemCpy(addr.sun_path, wayland_display_default, wayland_display_default_len); +internal void wayland_draw_gradient(WaylandState *state) { + u32 *pixels = (u32 *)state->pixels; + for (u32 y = 0; y < state->height; y++) { + for (u32 x = 0; x < state->width; x++) { + u8 r = (u8)(x * 255 / state->width); + u8 g = (u8)(y * 255 / state->height); + u8 b = 0x80; + pixels[y * state->width + x] = (r << 16) | (g << 8) | b; + } } - else - { - MemCpy(addr.sun_path + str8_xdg_runtime_dir.size + 1, wayland_display, str8_wayland_display_value.size); } - int fd = socket(AF_UNIX, SOCK_STREAM, 0); +internal void draw_square(u32 *pixels, WaylandState *state, u32 x_position, u32 y_position) { -#if DEBUG // helps to read those errors? i guess i can just read stderr? +for(u32 y = y_position; y < y_position + 10; ++y) { +for(u32 x = x_position; x < x_position + 10; ++x) { +u8 r = 0xff; + u8 g = 0xff; +u8 b = 0xff; + pixels[y * state->width + x] = (r << 16) | (g << 8) | b; -if(fd == -1) -{ -perror("failed to open socket"); +} } +} -if(connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) -{ -perror("failed to connect to socket"); +internal void wayland_draw_weird_gradient(WaylandState *state) { -} +local_persist u32 frame = 0; +frame += 1; -#endif + local_persist u32 start_x = state->width / 2; + local_persist u32 start_y = state->width / 2; +start_x += frame; - AssertLog((fd != -1), "failed to open socket\n"); - AssertLog((connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1), "failed to connect to socket\n"); +draw_square((u32 *)state->pixels, state, start_x , start_y); -return fd; } -//- helper functions +//- xdg_wm_base events -internal void buf_write_u32(char *buf, u64 *buf_size, u64 buf_cap, - u32 x) { - Assert(*buf_size + sizeof(x) <= buf_cap); - Assert(((size_t)buf + *buf_size) % sizeof(x) == 0); - - *(u32 *)(buf + *buf_size) = x; - *buf_size += sizeof(x); +// ping: the compositor checks we're still alive. if we don't pong, it may show the +internal void wayland_xdg_wm_base_ping(void *data, struct xdg_wm_base *xdg_wm_base, u32 serial) { + xdg_wm_base_pong(xdg_wm_base, serial); } -internal void buf_write_u16(char *buf, u64 *buf_size, u64 buf_cap, - u16 x) { - Assert(*buf_size + sizeof(x) <= buf_cap); - Assert(((size_t)buf + *buf_size) % sizeof(x) == 0); +global_variable const struct xdg_wm_base_listener wayland_xdg_wm_base_listener = { + wayland_xdg_wm_base_ping, +}; + + +//- frame + + +internal void wayland_frame_done(void *data, struct wl_callback *callback, u32 time_ms); + +global_variable const struct wl_callback_listener wayland_frame_listener = { +wayland_frame_done, + +}; + + +internal void wayland_frame_done(void *data, struct wl_callback *callback, u32 time_ms) { + +WaylandState *state = (WaylandState *)data; +wl_callback_destroy(callback); + +wayland_draw_weird_gradient(state); + struct wl_callback *next = wl_surface_frame(state->surface); + wl_callback_add_listener(next, &wayland_frame_listener, state); + + wl_surface_attach(state->surface, state->buffer, 0, 0); + wl_surface_damage(state->surface, 0, 0, state->width, state->height); + wl_surface_commit(state->surface); - *(u16 *)(buf + *buf_size) = x; - *buf_size += sizeof(x); } -internal void buf_write_string(char *buf, u64 *buf_size, u64 buf_cap, - char *src, u32 src_len) { - Assert(*buf_size + src_len <= buf_cap); - 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); + +//- xdg_surface events + +internal void wayland_xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, u32 serial) { + WaylandState *state = (WaylandState *)data; + + xdg_surface_ack_configure(xdg_surface, serial); + + if (state->buffer == NULL) { + state->buffer = wayland_create_shm_buffer(state); +wayland_draw_weird_gradient(state); + + struct wl_callback *cb = wl_surface_frame(state->surface); + wl_callback_add_listener(cb, &wayland_frame_listener, state); + wl_surface_attach(state->surface, state->buffer, 0, 0); + wl_surface_damage(state->surface, 0, 0, state->width, state->height); + wl_surface_commit(state->surface); + } + + // attach = "this buffer is the surface's content", damage = "this region changed", + // commit = "apply everything I sent since the last commit, atomically". + // a wl_surface has pending state and current state, nothing is visible until commit. + wl_surface_attach(state->surface, state->buffer, 0, 0); + wl_surface_damage(state->surface, 0, 0, state->width, state->height); + wl_surface_commit(state->surface); } -internal u32 buf_read_u32(char **buf, u64 *buf_size) { - Assert(*buf_size >= sizeof(u32)); - Assert((size_t)*buf % sizeof(u32) == 0); +global_variable const struct xdg_surface_listener wayland_xdg_surface_listener = { + wayland_xdg_surface_configure, +}; - u32 res = *(u32 *)(*buf); - *buf += sizeof(res); - *buf_size -= sizeof(res); - return res; +//- xdg_toplevel events + +// TODO: handle resizing. with vulkan that means recreating the swapchain. +internal void wayland_xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, + s32 width, s32 height, struct wl_array *states) { + +WaylandState *state = (WaylandState *)data; + Log("<- xdg_toplevel.configure: width=%d height=%d\n", width, height); +state->height = height; +state->width = width; + } -internal u16 buf_read_u16(char **buf, u64 *buf_size) { - Assert(*buf_size >= sizeof(u16)); - Assert((size_t)*buf % sizeof(u16) == 0); +internal void wayland_xdg_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel) { + WaylandState *state = (WaylandState *)data; + Log("<- xdg_toplevel.close\n"); + state->should_close = true; +} - u16 res = *(u16 *)(*buf); - *buf += sizeof(res); - *buf_size -= sizeof(res); +internal void wayland_xdg_toplevel_resize_edge(void *data, struct xdg_toplevel *xdg_toplevel, + s32 width, s32 height, struct wl_array *states) { - return res; + +} +internal void wayland_xdg_toplevel_configure_bounds(void *data, struct xdg_toplevel *xdg_toplevel, + s32 width, s32 height) { } -internal void buf_read_n(char **buf, u64 *buf_size, char *dst, u64 n) { - Assert(*buf_size >= n); +internal void wayland_xdg_toplevel_wm_capabilities(void *data, struct xdg_toplevel *xdg_toplevel, + struct wl_array *capabilities) { +} - memcpy(dst, *buf, n); +global_variable const struct xdg_toplevel_listener wayland_xdg_toplevel_listener = { + wayland_xdg_toplevel_configure, + wayland_xdg_toplevel_close, + wayland_xdg_toplevel_configure_bounds, + wayland_xdg_toplevel_wm_capabilities, +}; + + +//- wl_registry events + +internal void wayland_registry_global(void *data, struct wl_registry *registry, u32 name, + const char *interface, u32 version) { + WaylandState *state = (WaylandState *)data; + + Log("<- wl_registry.global: name=%u interface=%s version=%u\n", name, interface, version); + + if (CstringIsMatch(interface, wl_compositor_interface.name)) { + state->compositor = (struct wl_compositor *) + wl_registry_bind(registry, name, &wl_compositor_interface, Min(version, 4)); + } else if (CstringIsMatch(interface, wl_shm_interface.name)) { + state->shm = (struct wl_shm *) + wl_registry_bind(registry, name, &wl_shm_interface, 1); + } else if(CstringIsMatch(interface, xdg_wm_base_interface.name)) { + state->xdg_wm_base = (struct xdg_wm_base *) + wl_registry_bind(registry, name, &xdg_wm_base_interface, 1); + xdg_wm_base_add_listener(state->xdg_wm_base, &wayland_xdg_wm_base_listener, state); + } else if (CstringIsMatch(interface, wl_seat_interface.name)) { + state->seat = (struct wl_seat *) + wl_registry_bind(registry, name, &wl_seat_interface, Min(version, 5)); + wl_seat_add_listener(state->seat, &wayland_seat_listener, state); // has the keyboard handler + } +} - *buf += n; - *buf_size -= n; +internal void wayland_registry_global_remove(void *data, struct wl_registry *registry, u32 name) { } -global_variable u64 wayland_current_id = 1; +global_variable const struct wl_registry_listener wayland_registry_listener = { + wayland_registry_global, + wayland_registry_global_remove, +}; -internal u32 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); +//- entrypoint - buf_write_u16(msg, &msg_size, sizeof(msg), - wayland_wl_display_get_registry_opcode); +internal void wayland_entrypoint(MemArena *arena, LinuxWindowParams *params) { - u16 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); + WaylandState state = {}; - wayland_current_id++; - buf_write_u32(msg, &msg_size, sizeof(msg), wayland_current_id); + state.display = wl_display_connect(NULL); + AssertLog((state.display != NULL), "failed to connect to wayland display\n"); - if ((s64)msg_size != send(fd, msg, msg_size, MSG_DONTWAIT)) - exit(1); + // ask for the registry and listen to its global events + state.registry = wl_display_get_registry(state.display); + wl_registry_add_listener(state.registry, &wayland_registry_listener, &state); - Log("-> wl_display@%u.get_registry: wl_registry=%u\n", - wayland_display_object_id, wayland_current_id); + wl_display_roundtrip(state.display); - return wayland_current_id; -} + AssertLog((state.compositor != NULL), "compositor has no wl_compositor\n"); + AssertLog((state.shm != NULL), "compositor has no wl_shm\n"); + AssertLog((state.xdg_wm_base != NULL), "compositor has no xdg_wm_base\n"); + if (state.seat == NULL) { + Log("no wl_seat: running without keyboard input\n"); + } + // build the window: surface -> give it the xdg role -> make it a toplevel + state.surface = wl_compositor_create_surface(state.compositor); + state.xdg_surface = xdg_wm_base_get_xdg_surface(state.xdg_wm_base, state.surface); + xdg_surface_add_listener(state.xdg_surface, &wayland_xdg_surface_listener, &state); - internal void create_shared_memory_file(u64 size, state_t *state) { - char name[255] = "/"; - for (u64 index = 1; index < cstring8_length((u8 *)name); ++index) { - name[index] = ((double)rand()) / (double)RAND_MAX * 26 + 'a'; - } - - int fd = shm_open(name, O_RDWR | O_EXCL | O_CREAT, 0600); - AssertLog((fd != -1), "Failed to open buffer for shared memory."); - - Assert(shm_unlink(name) != -1); - - AssertLog((ftruncate(fd, size) != -1), "Failed to truncate buffer."); - - state->shm_pool_data = (u8 *)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; + state.xdg_toplevel = xdg_surface_get_toplevel(state.xdg_surface); + xdg_toplevel_add_listener(state.xdg_toplevel, &wayland_xdg_toplevel_listener, &state); + xdg_toplevel_set_title(state.xdg_toplevel, params->name); + xdg_toplevel_set_app_id(state.xdg_toplevel, params->name); + + wl_surface_commit(state.surface); + + // + // TODO: blocking is fine while the picture is static. for a game loop that renders + // every frame we'll instead use wl_display_dispatch_pending + a non blocking read + // (wl_display_prepare_read / wl_display_read_events with poll on wl_display_get_fd). + while (!state.should_close) { + + if (wl_display_dispatch(state.display) == -1) { + Log("wayland connection lost\n"); +goto regular_exit; + } + +if(state.keys_down[KEY_W]) { Log("Move Forward"); } +if(state.keys_down[KEY_A]) { Log("Move Left"); } + if(state.keys_down[KEY_D]) { Log("Move Right"); } + if(state.keys_down[KEY_S]) { Log("Move Back"); } +if(state.keys_down[KEY_Q]) { +goto cleanup_exit; } - internal void wayland_handle_message(int fd, state_t *state, char **msg, - uint64_t *msg_len) { - Assert(*msg_len >= 8); - - u32 object_id = buf_read_u32(msg, msg_len); - Assert(object_id <= wayland_current_id); - - u16 opcode = buf_read_u16(msg, msg_len); - - u16 announced_size = buf_read_u16(msg, msg_len); - Assert(roundup_4(announced_size) <= announced_size); - - u32 header_size = sizeof(object_id) + sizeof(opcode) + sizeof(announced_size); -Assert(announced_size <= header_size + *msg_len); +} + + + cleanup_exit: - if (object_id == state->wl_registry && - opcode == wayland_wl_registry_event_global) { - // TODO - } - // Following: Lots of `if (opcode == ...) {... } else if (opcode = ...) { ... } [...]` + if (state.keyboard) wl_keyboard_destroy(state.keyboard); + if (state.buffer) wl_buffer_destroy(state.buffer); + if (state.pixels) munmap(state.pixels, state.size); + xdg_toplevel_destroy(state.xdg_toplevel); + xdg_surface_destroy(state.xdg_surface); + wl_surface_destroy(state.surface); + if (state.seat) wl_seat_destroy(state.seat); + xdg_wm_base_destroy(state.xdg_wm_base); + wl_shm_destroy(state.shm); + wl_compositor_destroy(state.compositor); + wl_registry_destroy(state.registry); +return; + +regular_exit: +return; } diff --git a/source/base/base_platform_linux_wayland_input.cpp b/source/base/base_platform_linux_wayland_input.cpp new file mode 100644 index 0000000..0d66fb4 --- /dev/null +++ b/source/base/base_platform_linux_wayland_input.cpp @@ -0,0 +1,159 @@ +internal void wayland_keyboard_keymap(void *data, struct wl_keyboard *keyboard, u32 format, s32 fd, u32 size) { + Log("<- wl_keyboard.keymap: format=%u fd=%d size=%u\n", format, fd, size); + close(fd); +} + +internal void wayland_keyboard_enter(void *data, struct wl_keyboard *keyboard, u32 serial, + struct wl_surface *surface, struct wl_array *keys) { + WaylandState *state = (WaylandState *)data; + Log("<- wl_keyboard.enter\n"); + + // wl_array is just { size_t size; size_t alloc; void *data; }. here data is a list of u32. + u32 *key = (u32 *)keys->data; + u32 key_count = keys->size / sizeof(u32); + for (u32 i = 0; i < key_count; ++i) { + if (key[i] < sizeof(state->keys_down) / sizeof(state->keys_down[0])) { + state->keys_down[key[i]] = true; + } + } +} + +// leave: we lost focus. after this we will NOT receive release events for keys that +// are still held, so we release everything ourselves. otherwise keys get "stuck". +internal void wayland_keyboard_leave(void *data, struct wl_keyboard *keyboard, u32 serial, + struct wl_surface *surface) { + WaylandState *state = (WaylandState *)data; + Log("<- wl_keyboard.leave\n"); + + MemSetZero(state->keys_down, sizeof(state->keys_down)); +} + +internal void wayland_keyboard_key(void *data, struct wl_keyboard *keyboard, u32 serial, + u32 time, u32 key, u32 key_state) { + WaylandState *state = (WaylandState *)data; + b32 pressed = (key_state == WL_KEYBOARD_KEY_STATE_PRESSED); + + Log("<- wl_keyboard.key: key=%u %s\n", key, pressed ? "pressed" : "released"); + + if (key < sizeof(state->keys_down) / sizeof(state->keys_down[0])) { + state->keys_down[key] = pressed; + } + + if (key == KEY_ESC && pressed) { + state->should_close = true; + } +} + +internal void wayland_keyboard_modifiers(void *data, struct wl_keyboard *keyboard, u32 serial, u32 mods_depressed, u32 mods_latched, + u32 mods_locked, u32 group) { +} + +internal void wayland_keyboard_repeat_info(void *data, struct wl_keyboard *keyboard, + s32 rate, s32 delay) { + Log("<- wl_keyboard.repeat_info: rate=%d delay=%d\n", rate, delay); +} + +// the listener: a struct of function pointers, one per event, in the order the events +// are declared in wayland.xml (which is also their opcode). libwayland indexes into this +// struct with the opcode it reads from the message header. +global_variable const struct wl_keyboard_listener wayland_keyboard_listener = { + wayland_keyboard_keymap, // opcode 0 + wayland_keyboard_enter, // opcode 1 + wayland_keyboard_leave, // opcode 2 + wayland_keyboard_key, // opcode 3 + wayland_keyboard_modifiers, // opcode 4 + wayland_keyboard_repeat_info, // opcode 5 +}; + +//- mouse (pointer) input + +enum wayland_mouse_button_pressed: b32 { + +PRESSED, +RELEASED, + +}; + +internal void wayland_button_event(void *data, struct wl_pointer *pointer, + u32 serial, u32 time, u32 button, u32 state) { + + +} + + +internal void wayland_pointer_enter(void *data, struct wl_pointer *pointer, + u32 serial, struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { +WaylandState *state = (WaylandState *)data; +Log("<- pointer enteirng window"); +state->mouse_in_window = true; +state->x_offset = surface_x; +state->y_offset = surface_y; + + +} +internal void wayland_pointer_motion(void *data, struct wl_pointer *pointer, u32 time, + wl_fixed_t surface_x, wl_fixed_t surface_y) { + WaylandState *state = (WaylandState *)data; + state->x_offset = (f32)wl_fixed_to_double(surface_x); + state->y_offset = (f32)wl_fixed_to_double(surface_y); +} + +internal void wayland_pointer_leave(void *data, struct wl_pointer *pointer, u32 serial, wl_surface *surface) { +WaylandState *state = (WaylandState *)data; +state->mouse_in_window = false; +Log("<- pointer leaving window"); +} + + + +global_variable const struct wl_pointer_listener wayland_pointer_listener = { +wayland_pointer_enter, +wayland_pointer_leave, +wayland_pointer_motion, + wayland_button_event, +}; + + + + + +//- wl_seat events + +// capabilities: tells us which devices this seat has, as a bitmask: +// WL_SEAT_CAPABILITY_POINTER = 1, WL_SEAT_CAPABILITY_KEYBOARD = 2, WL_SEAT_CAPABILITY_TOUCH = 4 +// this can be sent again later (keyboard unplugged / plugged in), so we handle both directions. +internal void wayland_seat_capabilities(void *data, struct wl_seat *seat, u32 capabilities) { + + WaylandState *state = (WaylandState *)data; + b32 has_keyboard = (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) != 0; + + + Log("<- wl_seat.capabilities: %u\n", capabilities); + + if (has_keyboard && state->keyboard == NULL) { + // get_keyboard is a request that creates a new object (a "new_id" argument). + // libwayland allocates the id, sends the message, and hands us the proxy. + state->keyboard = wl_seat_get_keyboard(seat); + wl_keyboard_add_listener(state->keyboard, &wayland_keyboard_listener, state); + } else if (!has_keyboard && state->keyboard != NULL) { + // keyboard went away: tell the compositor we're done with the object, free the proxy. + // the `release` request only exists since wl_seat version 3; before that we can + // only free our side of it with destroy. + if (wl_keyboard_get_version(state->keyboard) >= WL_KEYBOARD_RELEASE_SINCE_VERSION) { + wl_keyboard_release(state->keyboard); + } else { + wl_keyboard_destroy(state->keyboard); + } + state->keyboard = NULL; + } +} + +// name: a human readable seat name like "seat0". only useful for multi-seat setups. +internal void wayland_seat_name(void *data, struct wl_seat *seat, const char *name) { + Log("<- wl_seat.name: %s\n", name); +} + +global_variable const struct wl_seat_listener wayland_seat_listener = { + wayland_seat_capabilities, // opcode 0 + wayland_seat_name, // opcode 1 (since version 2) +}; diff --git a/source/base/base_render_vulkan.cpp b/source/base/base_render_vulkan.cpp index 9295d00..6b51fa1 100644 --- a/source/base/base_render_vulkan.cpp +++ b/source/base/base_render_vulkan.cpp @@ -38,7 +38,7 @@ vk_render_entrypoint(MemArena *arena, WinParams *window, Params *params) #endif } -#ifdef BASE_DEBUG +#if DEBUG bool enable_validation_layer = true; #else bool enable_validation_layer = false; diff --git a/source/base/base_stack.cpp b/source/base/base_stack.cpp index bf3f045..4516ae4 100644 --- a/source/base/base_stack.cpp +++ b/source/base/base_stack.cpp @@ -125,7 +125,7 @@ internal MemStack *stack_push_align(MemStack *stack, u64 size, u8 alignment) stack->current_offset += size; - return (MemStack *)MemSet((MemStack *)next_address, size); + return (MemStack *)MemSetZero((MemStack *)next_address, size); } internal void *stack_push(MemStack *stack, u64 size) diff --git a/source/loo/loo.cpp b/source/loo/loo.cpp index e2de702..3a2cfef 100644 --- a/source/loo/loo.cpp +++ b/source/loo/loo.cpp @@ -5,30 +5,10 @@ ENTRYPOINT() MemArena *arena = ArenaCreate(MiB(100)); Assert(arena); -int fd = wayland_display_connect(arena); + LinuxWindowParams *params = PushStruct(arena, LinuxWindowParams); +params->name = (cstring)"Loo"; - state_t state = {0}; - state.wl_registry = wayland_wl_display_get_registry(fd), - state.w = 117, - state.h = 150, - state.stride = 117 * color_channels, - state.shm_pool_size = state.h * state.stride; - - - while (1) { - char read_buf[4096] = ""; - int64_t read_bytes = recv(fd, read_buf, sizeof(read_buf), 0); - if (read_bytes == -1) - exit(errno); - - char *msg = read_buf; - uint64_t msg_len = (uint64_t)read_bytes; - - while (msg_len > 0) - wayland_handle_message(fd, &state, &msg, &msg_len); - } + wayland_entrypoint(arena, params); return 0; - - } -- cgit v1.3.1