diff options
| -rwxr-xr-x | build.sh | 19 | ||||
| -rw-r--r-- | source/base/base_arena.cpp | 2 | ||||
| -rw-r--r-- | source/base/base_core.h | 73 | ||||
| -rw-r--r-- | source/base/base_include.h | 69 | ||||
| -rw-r--r-- | source/base/base_platform.cpp | 5 | ||||
| -rw-r--r-- | source/base/base_platform.h | 134 | ||||
| -rw-r--r-- | source/base/base_platform_linux.cpp | 7 | ||||
| -rw-r--r-- | source/base/base_platform_linux_wayland.cpp | 365 | ||||
| -rw-r--r-- | source/base/base_platform_linux_wayland_input.cpp | 159 | ||||
| -rw-r--r-- | source/base/base_render_vulkan.cpp | 2 | ||||
| -rw-r--r-- | source/base/base_stack.cpp | 2 | ||||
| -rw-r--r-- | source/loo/loo.cpp | 26 |
12 files changed, 580 insertions, 283 deletions
| @@ -8,18 +8,27 @@ echo | |||
| 8 | BUILD_DIR=build | 8 | BUILD_DIR=build |
| 9 | SOURCE_DIR=source | 9 | SOURCE_DIR=source |
| 10 | INCLUDES="-I${SOURCE_DIR}/loo -I${SOURCE_DIR} -I${SOURCE_DIR}/base" | 10 | INCLUDES="-I${SOURCE_DIR}/loo -I${SOURCE_DIR} -I${SOURCE_DIR}/base" |
| 11 | CFLAGS="-fno-rtti -fno-exceptions -Wall -Werror -fanalyzer -no-pie -Wno-unused-function -Wno-unused-variable -O0" | 11 | CFLAGS="-fno-rtti -fno-exceptions -Wall -Wundef -Werror -fanalyzer -no-pie -Wno-unused-function -Wno-unused-variable -O0 " |
| 12 | LIBS="" | 12 | LIBS="" |
| 13 | 13 | ||
| 14 | DEBUG=1 | 14 | DEBUG=1 |
| 15 | 15 | ||
| 16 | VULKAN=1 | 16 | VULKAN=0 |
| 17 | WAYLAND=1 | 17 | WAYLAND=1 |
| 18 | X11=0 | 18 | X11=0 |
| 19 | 19 | ||
| 20 | if [ $WAYLAND -eq 1 ]; then | 20 | if [ $WAYLAND -eq 1 ]; then |
| 21 | LIBS+=" $(pkg-config --libs wayland-client)" | 21 | CFLAGS+=" -DWAYLAND=1 -DX11=0" |
| 22 | CFLAGS+=" $(pkg-config --cflags wayland-client) -DWAYLAND=1 -DX11=0" | 22 | LIBS+="-lwayland-client -lxkbcommon" |
| 23 | |||
| 24 | # TODO: how often do we need the xdg protocol? | ||
| 25 | GENERATED_DIR="${BUILD_DIR}/generated" | ||
| 26 | XDG_SHELL_XML=/usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml | ||
| 27 | mkdir -p "$GENERATED_DIR" | ||
| 28 | wayland-scanner client-header "$XDG_SHELL_XML" "${GENERATED_DIR}/xdg-shell-client-protocol.h" | ||
| 29 | wayland-scanner private-code "$XDG_SHELL_XML" "${GENERATED_DIR}/xdg-shell-protocol.c" | ||
| 30 | INCLUDES+=" -I${GENERATED_DIR}" | ||
| 31 | |||
| 23 | elif [ $X11 -eq 1 ]; then | 32 | elif [ $X11 -eq 1 ]; then |
| 24 | LIBS+=" -lX11" | 33 | LIBS+=" -lX11" |
| 25 | CFLAGS+=" -DX11=1 -DWAYLAND=0" | 34 | CFLAGS+=" -DX11=1 -DWAYLAND=0" |
| @@ -37,4 +46,4 @@ fi | |||
| 37 | mkdir -p "$BUILD_DIR" | 46 | mkdir -p "$BUILD_DIR" |
| 38 | 47 | ||
| 39 | echo "Building ${BUILD_DIR}/loo..." | 48 | echo "Building ${BUILD_DIR}/loo..." |
| 40 | g++ $INCLUDES $CFLAGS "${SOURCE_DIR}/loo/loo.cpp" -o "${BUILD_DIR}/loo" $LIBS \ No newline at end of file | 49 | 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 | |||
| 125 | if((arena->base_position + arena->previous_position) == old_memory) { | 125 | if((arena->base_position + arena->previous_position) == old_memory) { |
| 126 | arena->current_position = arena->previous_position + new_size; | 126 | arena->current_position = arena->previous_position + new_size; |
| 127 | if(new_size > old_size) { | 127 | if(new_size > old_size) { |
| 128 | MemSet(&arena->current_position, new_size - old_size); | 128 | MemSetZero(old_mem + old_size, new_size - old_size); |
| 129 | } | 129 | } |
| 130 | 130 | ||
| 131 | return (MemArena *)old_memory; | 131 | 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 @@ | |||
| 14 | 14 | ||
| 15 | //- architecture definitions | 15 | //- architecture definitions |
| 16 | 16 | ||
| 17 | #if defined(__arm__) || defined(__aarch64__) | 17 | #if defined(__arm__) || defined(__aarch64__) || defined(_M_ARM64) |
| 18 | #define ARCH_ARM 1 | 18 | #define ARCH_ARM 1 |
| 19 | #elif defined(__i386__) || defined(__x86_64__) | 19 | #elif defined(__x86_64__) || defined(_M_X64) |
| 20 | #define ARCH_x86_64 1 | 20 | #define ARCH_x86_64 1 |
| 21 | #elif defined(__i386__) || defined(_M_IX86) | ||
| 22 | #define ARCH_X86 1 | ||
| 23 | #endif | ||
| 24 | |||
| 25 | #ifndef COMPILER_MSVC | ||
| 26 | #define COMPILER_MSVC 0 | ||
| 27 | #endif | ||
| 28 | #ifndef COMPILER_CLANG | ||
| 29 | #define COMPILER_CLANG 0 | ||
| 30 | #endif | ||
| 31 | #ifndef COMPILER_GCC | ||
| 32 | #define COMPILER_GCC 0 | ||
| 33 | #endif | ||
| 34 | #ifndef ARCH_ARM | ||
| 35 | #define ARCH_ARM 0 | ||
| 36 | #endif | ||
| 37 | #ifndef ARCH_x86_64 | ||
| 38 | #define ARCH_x86_64 0 | ||
| 39 | #endif | ||
| 40 | #ifndef ARCH_X86 | ||
| 41 | #define ARCH_X86 0 | ||
| 21 | #endif | 42 | #endif |
| 22 | 43 | ||
| 23 | //- compiler definitions | 44 | //- compiler definitions |
| @@ -39,26 +60,27 @@ | |||
| 39 | #define Unused(x) (void)(x) | 60 | #define Unused(x) (void)(x) |
| 40 | #define Nil 0 | 61 | #define Nil 0 |
| 41 | 62 | ||
| 42 | #define Deprecated __attribute__((__deprecated__)) | 63 | #define Deprecated [[deprecated]] |
| 43 | 64 | ||
| 44 | //- breakpoints | 65 | //- breakpoints |
| 45 | #if ARCH_ARM | ||
| 46 | #define breakpoint() __asm__ volatile("brk #0") | ||
| 47 | #elif ARCH_x86_64 | ||
| 48 | |||
| 49 | #if COMPILER_GCC | ||
| 50 | #define breakpoint() __asm__ volatile("int3") | ||
| 51 | #endif | ||
| 52 | #endif | ||
| 53 | |||
| 54 | #if COMPILER_MSVC | 66 | #if COMPILER_MSVC |
| 55 | #include <intrin.h> | 67 | #include <intrin.h> |
| 56 | #define breakpoint() __debugbreak() | 68 | #define breakpoint() __debugbreak() |
| 69 | #define Trap() __fastfail(0) | ||
| 70 | #elif COMPILER_CLANG | ||
| 71 | #define breakpoint() __builtin_debugtrap() | ||
| 72 | #define Trap() __builtin_trap() | ||
| 73 | #elif ARCH_ARM | ||
| 74 | #define breakpoint() __asm__ volatile("brk #0") | ||
| 75 | #define Trap() __builtin_trap() | ||
| 76 | #else | ||
| 77 | #define breakpoint() __asm__ volatile("int3") | ||
| 78 | #define Trap() __builtin_trap() | ||
| 57 | #endif | 79 | #endif |
| 58 | 80 | ||
| 59 | //- | 81 | //- |
| 60 | #define MemCpy(dest, src, len) memcpy((dest), (src), (len)) | 82 | #define MemCpy(dest, src, len) memcpy((dest), (src), (len)) |
| 61 | #define MemSet(dest, len) memset((dest), (1), (len)) | 83 | #define MemSet(dest, val, len) memset((dest), (val), (len)) |
| 62 | #define MemSetZero(dest, len) memset((dest), (0), (len)) | 84 | #define MemSetZero(dest, len) memset((dest), (0), (len)) |
| 63 | 85 | ||
| 64 | //- | 86 | //- |
| @@ -107,11 +129,17 @@ typedef intptr_t smm; | |||
| 107 | 129 | ||
| 108 | //- | 130 | //- |
| 109 | 131 | ||
| 110 | #define Assert(expression) _assert(expression) | 132 | #define Assert(expression) do { \ |
| 133 | if (!(expression)) { \ | ||
| 134 | breakpoint(); \ | ||
| 135 | Trap(); \ | ||
| 136 | } \ | ||
| 137 | } \ | ||
| 138 | while(0) | ||
| 111 | 139 | ||
| 112 | // NOTE: apparantly in macros this strinifys whataever you put after the # | 140 | // NOTE: apparantly in macros this strinifys whataever you put after the # |
| 113 | #define AssertLog(expression, ...) do { \ | 141 | #define AssertLog(expression, ...) do { \ |
| 114 | if (!expression) { \ | 142 | if (!(expression)) { \ |
| 115 | Log("%s:%d: %s\n", __FILE__, __LINE__, #expression); \ | 143 | Log("%s:%d: %s\n", __FILE__, __LINE__, #expression); \ |
| 116 | Log(__VA_ARGS__);\ | 144 | Log(__VA_ARGS__);\ |
| 117 | Assert(false);\ | 145 | Assert(false);\ |
| @@ -119,21 +147,6 @@ typedef intptr_t smm; | |||
| 119 | } \ | 147 | } \ |
| 120 | while(0) | 148 | while(0) |
| 121 | 149 | ||
| 122 | internal void _assert(bool expression); | ||
| 123 | |||
| 124 | |||
| 125 | internal void _assert(bool expression) | ||
| 126 | { | ||
| 127 | |||
| 128 | if (!expression) { | ||
| 129 | breakpoint(); | ||
| 130 | #if COMPILER_MSVC | ||
| 131 | __fastfail(0); | ||
| 132 | #else | ||
| 133 | __builtin_trap(); // NOTE: always stop, breakpoint() is empty on arm without DEBUG | ||
| 134 | #endif | ||
| 135 | } | ||
| 136 | } | ||
| 137 | 150 | ||
| 138 | 151 | ||
| 139 | 152 | ||
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 @@ | |||
| 1 | #ifndef BASE_INCLUDE_H | 1 | #ifndef BASE_INCLUDE_H |
| 2 | #define BASE_INCLUDE_H | 2 | #define BASE_INCLUDE_H |
| 3 | 3 | ||
| 4 | #if defined(__linux__) || defined(__APPLE__) | 4 | #if defined(__APPLE__) |
| 5 | // #message "PLATFORM LINUX APPLE" | 5 | #define PLATFORM_APPLE 1 |
| 6 | #define PLATFORM_UNIX 1 | 6 | #define PLATFORM_UNIX 1 |
| 7 | #elif defined(__linux__) | ||
| 7 | #define PLATFORM_LINUX 1 | 8 | #define PLATFORM_LINUX 1 |
| 8 | #elif defined(__linux__) && !defined(__APPLE__) | 9 | #define PLATFORM_UNIX 1 |
| 9 | #message "PLATFORM LINUX" | 10 | #elif defined(_WIN32) |
| 10 | #define PLATFORM_LINUX 1 | ||
| 11 | #elif defined(_WIN32) || defined(_WIN64) | ||
| 12 | #message "PLATFORM WINDOWS" | ||
| 13 | #define PLATFORM_WINDOWS 1 | 11 | #define PLATFORM_WINDOWS 1 |
| 14 | #elif defined(__APPLE__) | ||
| 15 | #define PLATFORM_APPLE 1 | ||
| 16 | #message "PLATFORM APPLE" | ||
| 17 | #else | 12 | #else |
| 18 | #message platform unknown | 13 | #error Unsupported platform |
| 19 | #endif // platform definitions | 14 | #endif // platform definitions |
| 20 | 15 | ||
| 16 | #ifndef PLATFORM_APPLE | ||
| 17 | #define PLATFORM_APPLE 0 | ||
| 18 | #endif | ||
| 19 | #ifndef PLATFORM_LINUX | ||
| 20 | #define PLATFORM_LINUX 0 | ||
| 21 | #endif | ||
| 22 | #ifndef PLATFORM_UNIX | ||
| 23 | #define PLATFORM_UNIX 0 | ||
| 24 | #endif | ||
| 25 | #ifndef PLATFORM_WINDOWS | ||
| 26 | #define PLATFORM_WINDOWS 0 | ||
| 27 | #endif | ||
| 28 | #ifndef DEBUG | ||
| 29 | #define DEBUG 0 | ||
| 30 | #endif | ||
| 31 | #ifndef X11 | ||
| 32 | #define X11 0 | ||
| 33 | #endif | ||
| 34 | #ifndef WAYLAND | ||
| 35 | #define WAYLAND 0 | ||
| 36 | #endif | ||
| 37 | #ifndef VULKAN | ||
| 38 | #define VULKAN 0 | ||
| 39 | #endif | ||
| 40 | #ifndef OPENGL | ||
| 41 | #define OPENGL 0 | ||
| 42 | #endif | ||
| 43 | |||
| 21 | #if PLATFORM_UNIX | 44 | #if PLATFORM_UNIX |
| 22 | #include <unistd.h> | 45 | #include <unistd.h> |
| 23 | #include <dirent.h> | 46 | #include <dirent.h> |
| 24 | #include <sys/syscall.h> | ||
| 25 | #include <sys/mman.h> | 47 | #include <sys/mman.h> |
| 26 | #include <sys/socket.h> // needed for making a connection to displays (wayland) | 48 | #include <sys/socket.h> // needed for making a connection to displays (wayland) |
| 27 | #include <sys/stat.h> | 49 | #include <sys/stat.h> |
| 28 | #include <sys/types.h> | ||
| 29 | #include <sys/un.h> | 50 | #include <sys/un.h> |
| 30 | #include <fcntl.h> | 51 | #include <fcntl.h> |
| 31 | #include <dlfcn.h> | 52 | #include <dlfcn.h> |
| 32 | 53 | ||
| 33 | #if DEBUG | ||
| 34 | #include <errno.h> | ||
| 35 | #endif | ||
| 36 | |||
| 37 | #if PLATFORM_LINUX | 54 | #if PLATFORM_LINUX |
| 38 | 55 | ||
| 56 | #include <linux/input-event-codes.h> | ||
| 57 | |||
| 39 | #if X11 | 58 | #if X11 |
| 40 | #include <X11/X.h> | 59 | #include <X11/X.h> |
| 41 | #include <X11/Xlib.h> | 60 | #include <X11/Xlib.h> |
| 42 | #include <X11/keysym.h> | 61 | #include <X11/keysym.h> |
| 43 | #elif WAYLAND | 62 | #elif WAYLAND |
| 44 | #include <wayland-client.h> | 63 | |
| 64 | |||
| 65 | #include <wayland-client.h> // wl_display, wl_surface, wl_seat, wl_keyboard, ... (core protocol) | ||
| 66 | #include "xdg-shell-client-protocol.h" // xdg_wm_base, xdg_surface, xdg_toplevel (generated by build.sh) | ||
| 67 | #include <xkbcommon/xkbcommon.h> // the only types of inputs the server will probably send on wayland | ||
| 45 | #endif | 68 | #endif |
| 46 | 69 | ||
| 47 | #endif // PLATFORM_LINUX | 70 | #endif // PLATFORM_LINUX |
| @@ -59,12 +82,11 @@ | |||
| 59 | //- | 82 | //- |
| 60 | 83 | ||
| 61 | #include <time.h> | 84 | #include <time.h> |
| 62 | #include <math.h> | ||
| 63 | #include <string.h> | 85 | #include <string.h> |
| 64 | #include <stdint.h> | 86 | #include <stdint.h> |
| 65 | #include <stddef.h> | ||
| 66 | #include <stdio.h> | 87 | #include <stdio.h> |
| 67 | #include <stdarg.h> | 88 | #include <stdarg.h> |
| 89 | #include <errno.h> | ||
| 68 | 90 | ||
| 69 | //- | 91 | //- |
| 70 | #include "base_core.h" | 92 | #include "base_core.h" |
| @@ -80,7 +102,12 @@ | |||
| 80 | #include "base_arena.cpp" | 102 | #include "base_arena.cpp" |
| 81 | #include "base_stack.cpp" | 103 | #include "base_stack.cpp" |
| 82 | #include "base_string.cpp" | 104 | #include "base_string.cpp" |
| 83 | #include "base_platform.cpp" | 105 | |
| 106 | #if PLATFORM_LINUX | ||
| 107 | #include "base_platform_linux.cpp" | ||
| 108 | #elif PLATFORM_WINDOWS | ||
| 109 | #include "base_platform_win32.cpp" | ||
| 110 | #endif | ||
| 84 | 111 | ||
| 85 | #include "base_rand.cpp" | 112 | #include "base_rand.cpp" |
| 86 | #include "base_simd.cpp" | 113 | #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 @@ | |||
| 1 | #if PLATFORM_LINUX | ||
| 2 | #include "base_platform_linux.cpp" | ||
| 3 | #elif PLATFORM_WINDOWS | ||
| 4 | #include "base_platform_win32.cpp" | ||
| 5 | #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 { | |||
| 16 | String8List *file_names; | 16 | String8List *file_names; |
| 17 | }; | 17 | }; |
| 18 | 18 | ||
| 19 | #if 0 // TODO: @WAYLAND and @X11 implementation | 19 | #if 1 // TODO: @WAYLAND and @X11 implementation |
| 20 | typedef struct LinuxWindowHandle LinuxWindowHandle; | 20 | |
| 21 | struct LinuxWindowHandle { | 21 | typedef struct Display Display; |
| 22 | struct Display | ||
| 23 | { | ||
| 24 | int width; | ||
| 25 | int height; | ||
| 26 | }; | ||
| 27 | |||
| 28 | typedef struct Window Window; | ||
| 29 | struct Window | ||
| 30 | { | ||
| 31 | int width; | ||
| 32 | int height; | ||
| 33 | }; | ||
| 34 | |||
| 35 | typedef struct LinuxWindowParams LinuxWindowParams; | ||
| 36 | struct LinuxWindowParams { | ||
| 37 | cstring name; | ||
| 22 | Display *display; | 38 | Display *display; |
| 23 | Window window; | 39 | Window window; |
| 24 | }; | 40 | }; |
| 25 | 41 | ||
| 26 | #define LinuxLoadSharedLibrary(library, name) unix_ | 42 | #define LinuxLoadSharedLibrary(arena, library) unix_load_shared_library(arena, library) |
| 27 | #endif | 43 | #endif |
| 28 | 44 | ||
| 29 | //- Shared libraries | 45 | //- Shared libraries |
| @@ -51,14 +67,16 @@ internal void unix_log_with_args(const char *format, ...); | |||
| 51 | #define FileExists(path) unix_file_exists(path) | 67 | #define FileExists(path) unix_file_exists(path) |
| 52 | #define DirLoad(arena, path) unix_open_dir(arena, path) | 68 | #define DirLoad(arena, path) unix_open_dir(arena, path) |
| 53 | 69 | ||
| 54 | #define OSExit() unix_os_exit(); | ||
| 55 | 70 | ||
| 56 | #if COMPILER_GCC || COMPILER_CLANG | 71 | #if DEBUG |
| 57 | // TODO: __VA_ARGS__ CLANG | 72 | #define OSExit(code) _exit(code) |
| 58 | #define Log(...) unix_log_with_args(__VA_ARGS__) | 73 | #define Log(...) unix_log_with_args(__VA_ARGS__) |
| 59 | #define LogStr8(message) \ | 74 | #define LogStr8(message) Log(Str8Fmt, Str8Arg(message)) |
| 60 | Log(Str8Fmt, Str8Arg(message)) // NOTE: for this to wwork we need the /analyze argument | 75 | #else |
| 61 | #endif // COMPILER_GCC || COMPILER_CLANG | 76 | #define OSExit(code) Unused(code) |
| 77 | #define Log(...) ((void)0) | ||
| 78 | #define LogStr8(message) ((void)0) | ||
| 79 | #endif // disable comments when debug mode is disabled | ||
| 62 | 80 | ||
| 63 | //- Entrypoint and platform window layer | 81 | //- Entrypoint and platform window layer |
| 64 | #define ENTRYPOINT() int main(int count, char **args) | 82 | #define ENTRYPOINT() int main(int count, char **args) |
| @@ -95,59 +113,55 @@ internal s32 x11_read_input(); | |||
| 95 | //- Wayland | 113 | //- Wayland |
| 96 | #elif WAYLAND | 114 | #elif WAYLAND |
| 97 | 115 | ||
| 98 | //- protocol values defined in the wayland library. it's a library load less i guess | 116 | typedef struct WaylandState WaylandState; |
| 99 | global_variable const u32 wayland_display_object_id = 1; | 117 | struct WaylandState { |
| 100 | global_variable const u16 wayland_wl_registry_event_global = 0; | 118 | //- connection |
| 101 | global_variable const u16 wayland_shm_pool_event_format = 0; | 119 | struct wl_display *display; |
| 102 | global_variable const u16 wayland_wl_buffer_event_release = 0; | 120 | struct wl_registry *registry; |
| 103 | global_variable const u16 wayland_xdg_wm_base_event_ping = 0; | ||
| 104 | global_variable const u16 wayland_xdg_toplevel_event_configure = 0; | ||
| 105 | global_variable const u16 wayland_xdg_toplevel_event_close = 1; | ||
| 106 | global_variable const u16 wayland_xdg_surface_event_configure = 0; | ||
| 107 | global_variable const u16 wayland_wl_display_get_registry_opcode = 1; | ||
| 108 | global_variable const u16 wayland_wl_registry_bind_opcode = 0; | ||
| 109 | global_variable const u16 wayland_wl_compositor_create_surface_opcode = 0; | ||
| 110 | global_variable const u16 wayland_xdg_wm_base_pong_opcode = 3; | ||
| 111 | global_variable const u16 wayland_xdg_surface_ack_configure_opcode = 4; | ||
| 112 | global_variable const u16 wayland_wl_shm_create_pool_opcode = 0; | ||
| 113 | global_variable const u16 wayland_xdg_wm_base_get_xdg_surface_opcode = 2; | ||
| 114 | global_variable const u16 wayland_wl_shm_pool_create_buffer_opcode = 0; | ||
| 115 | global_variable const u16 wayland_wl_surface_attach_opcode = 1; | ||
| 116 | global_variable const u16 wayland_xdg_surface_get_toplevel_opcode = 1; | ||
| 117 | global_variable const u16 wayland_wl_surface_commit_opcode = 6; | ||
| 118 | global_variable const u16 wayland_wl_display_error_event = 0; | ||
| 119 | global_variable const u32 wayland_format_xrgb8888 = 1; | ||
| 120 | global_variable const u32 wayland_header_size = 8; | ||
| 121 | global_variable const u32 color_channels = 4; | ||
| 122 | 121 | ||
| 123 | enum state_state_t: u16 { | 122 | struct wl_compositor *compositor; |
| 124 | STATE_NONE, | 123 | struct wl_shm *shm; |
| 125 | STATE_SURFACE_ACKED_CONFIGURE, | 124 | struct xdg_wm_base *xdg_wm_base; |
| 126 | STATE_SURFACE_ATTACHED, | 125 | struct wl_seat *seat; |
| 127 | }; | 126 | |
| 127 | //- window objects | ||
| 128 | 128 | ||
| 129 | typedef struct state_t state_t; | 129 | struct wl_surface *surface; |
| 130 | struct state_t { | 130 | struct xdg_surface *xdg_surface; |
| 131 | uint32_t wl_registry; | 131 | struct xdg_toplevel *xdg_toplevel; |
| 132 | uint32_t wl_shm; | 132 | |
| 133 | uint32_t wl_shm_pool; | 133 | //- keyboard input |
| 134 | uint32_t wl_buffer; | 134 | struct wl_keyboard *keyboard; |
| 135 | uint32_t xdg_wm_base; | 135 | b32 keys_down[256]; // indexed by linux scancode |
| 136 | uint32_t xdg_surface; | 136 | |
| 137 | uint32_t wl_compositor; | 137 | //- mouse (pointer) input |
| 138 | uint32_t wl_surface; | 138 | |
| 139 | uint32_t xdg_toplevel; | 139 | struct wl_pointer *pointer; // wl_pointer is a predefined <wayland-client.h> struct |
| 140 | uint32_t stride; | 140 | f32 mouse_x, mouse_y; |
| 141 | uint32_t w; | 141 | b32 mouse_buttons[3]; |
| 142 | uint32_t h; | 142 | f32 x_offset, y_offset; |
| 143 | uint32_t shm_pool_size; | 143 | b32 mouse_in_window; |
| 144 | int shm_fd; | 144 | |
| 145 | uint8_t *shm_pool_data; | 145 | |
| 146 | 146 | ||
| 147 | state_state_t state; | 147 | //- software framebuffer |
| 148 | struct wl_buffer *buffer; | ||
| 149 | u8 *pixels; | ||
| 150 | u32 width; | ||
| 151 | u32 height; | ||
| 152 | u32 stride; | ||
| 153 | u32 size; | ||
| 154 | |||
| 155 | b32 should_close; | ||
| 148 | }; | 156 | }; |
| 149 | 157 | ||
| 158 | #pragma GCC diagnostic push | ||
| 159 | #pragma GCC diagnostic ignored "-Wattributes" | ||
| 160 | #include "xdg-shell-protocol.c" | ||
| 161 | #pragma GCC diagnostic pop | ||
| 162 | |||
| 150 | 163 | ||
| 164 | #include "base_platform_linux_wayland_input.cpp" | ||
| 151 | #include "base_platform_linux_wayland.cpp" | 165 | #include "base_platform_linux_wayland.cpp" |
| 152 | 166 | ||
| 153 | #endif // X11 and Wayland -- set by build params | 167 | #endif // X11 and Wayland -- set by build params |
| @@ -190,7 +204,7 @@ internal void win32_log(String8List messages); | |||
| 190 | #define FileExists(path) win32_file_exists(path) | 204 | #define FileExists(path) win32_file_exists(path) |
| 191 | #define Win32FileExists(path) win32_file_exists(path) | 205 | #define Win32FileExists(path) win32_file_exists(path) |
| 192 | #define DirLoad(arena, path) win32_open_dir(arena, original_path); | 206 | #define DirLoad(arena, path) win32_open_dir(arena, original_path); |
| 193 | #define OSExit() win32_os_exit(); | 207 | #define OSExit(code) win32_os_exit() |
| 194 | 208 | ||
| 195 | #if COMPILER_GCC || COMPILER_CLANG | 209 | #if COMPILER_GCC || COMPILER_CLANG |
| 196 | 210 | ||
| @@ -200,7 +214,7 @@ internal void win32_logf(const char *fmt, ...) PRINTF_FORMAT; | |||
| 200 | #elif COMPILER_MSVC | 214 | #elif COMPILER_MSVC |
| 201 | internal void win32_logf(_Printf_format_string_ const char *fmt, ...); | 215 | internal void win32_logf(_Printf_format_string_ const char *fmt, ...); |
| 202 | 216 | ||
| 203 | #define Log(fmt, ...) win32_logf(fmt, __VA_ARGS__) | 217 | #define Log(fmt, ...) win32_logf(fmt, ##__VA_ARGS__) |
| 204 | 218 | ||
| 205 | #endif // COMPILER_GCC || COMPILER_CLANG | 219 | #endif // COMPILER_GCC || COMPILER_CLANG |
| 206 | 220 | ||
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) { | |||
| 17 | buffer = str8_chop_left(buffer, equal_sign_pos + 1); | 17 | buffer = str8_chop_left(buffer, equal_sign_pos + 1); |
| 18 | } | 18 | } |
| 19 | 19 | ||
| 20 | return buffer; | 20 | return buffer; |
| 21 | } | 21 | } |
| 22 | ++index; | 22 | ++index; |
| 23 | } | 23 | } |
| 24 | 24 | ||
| 25 | AssertLog(buffer.size != 0, "env value not found\n"); | 25 | AssertLog(buffer.size != 0, "env value not found\n"); |
| @@ -149,7 +149,6 @@ internal void unix_log_with_args(const char *format, ...) | |||
| 149 | va_end(arguments); | 149 | va_end(arguments); |
| 150 | } | 150 | } |
| 151 | 151 | ||
| 152 | internal void unix_os_exit() { _exit(0); } | ||
| 153 | 152 | ||
| 154 | // TODO: rewrite this | 153 | // TODO: rewrite this |
| 155 | internal String8List *unix_open_dir(MemArena *arena, String8 *path) | 154 | 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 @@ | |||
| 1 | // https://gaultier.github.io/blog/wayland_from_scratch.html#creating-a-registry | ||
| 2 | // TODO: replace unix_get_env with a generic macro | ||
| 3 | 1 | ||
| 2 | //- software framebuffer | ||
| 3 | // TODO: delete this section once vulkan is presenting. vkCreateWaylandSurfaceKHR only needs | ||
| 4 | // state->display and state->surface, the driver then creates and attaches buffers itself. | ||
| 4 | 5 | ||
| 6 | // creates a wl_buffer backed by shared memory that both we and the compositor can read. | ||
| 7 | internal struct wl_buffer *wayland_create_shm_buffer(WaylandState *state) { | ||
| 8 | state->stride = state->width * 4; // 4 bytes per pixel (XRGB8888) | ||
| 9 | state->size = state->stride * state->height; | ||
| 5 | 10 | ||
| 6 | //- wayland | 11 | int fd = memfd_create("loo-shared-memory", MFD_CLOEXEC); |
| 7 | internal int wayland_display_connect(MemArena *arena) { | 12 | AssertLog((fd != -1), "Failed to open buffer for shared memory."); |
| 13 | AssertLog((ftruncate(fd, state->size) != -1), "Failed to truncate buffer."); | ||
| 8 | 14 | ||
| 9 | String8 str8_xdg_runtime_dir = unix_get_env("XDG_RUNTIME_DIR"); | 15 | state->pixels = (u8 *)mmap(NULL, state->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); |
| 10 | char *xdg_runtime_dir = null_terminate(arena, &str8_xdg_runtime_dir); | 16 | AssertLog((state->pixels != MAP_FAILED), "Failed to mmap shared memory."); |
| 11 | sockaddr_un addr = {}; | ||
| 12 | u64 socket_path_len = 0; | ||
| 13 | 17 | ||
| 14 | // copy the xdg_runtime_dir value to the beginning of the bfufer | 18 | // a pool is the compositor-side view of our shared memory. libwayland sends the fd |
| 15 | { | 19 | // over the socket for us (the SCM_RIGHTS dance, in the other direction). |
| 16 | addr.sun_family = AF_UNIX; | 20 | struct wl_shm_pool *pool = wl_shm_create_pool(state->shm, fd, state->size); |
| 17 | MemCpy(addr.sun_path, xdg_runtime_dir, str8_xdg_runtime_dir.size); | 21 | |
| 18 | socket_path_len += str8_xdg_runtime_dir.size; | 22 | // a buffer is a typed slice of the pool: offset, size, stride and pixel format |
| 19 | addr.sun_path[socket_path_len++] = '/'; // add an ending slash | 23 | struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, 0, state->width, state->height, |
| 20 | } | 24 | state->stride, WL_SHM_FORMAT_XRGB8888); |
| 21 | 25 | ||
| 22 | String8 str8_wayland_display_value = unix_get_env("WAYLAND_DISPLAY"); | 26 | wl_shm_pool_destroy(pool); |
| 23 | Assert(str8_wayland_display_value.size != 0); | 27 | close(fd); |
| 24 | char *wayland_display = null_terminate(arena, &str8_wayland_display_value); | 28 | |
| 29 | return buffer; | ||
| 30 | } | ||
| 25 | 31 | ||
| 26 | if(wayland_display == NULL) | 32 | internal void wayland_draw_gradient(WaylandState *state) { |
| 27 | { | 33 | u32 *pixels = (u32 *)state->pixels; |
| 28 | const char *wayland_display_default = "wayland-0"; | 34 | for (u32 y = 0; y < state->height; y++) { |
| 29 | u64 wayland_display_default_len = cstring8_length((u8 *)wayland_display_default); | 35 | for (u32 x = 0; x < state->width; x++) { |
| 30 | MemCpy(addr.sun_path, wayland_display_default, wayland_display_default_len); | 36 | u8 r = (u8)(x * 255 / state->width); |
| 37 | u8 g = (u8)(y * 255 / state->height); | ||
| 38 | u8 b = 0x80; | ||
| 39 | pixels[y * state->width + x] = (r << 16) | (g << 8) | b; | ||
| 40 | } | ||
| 31 | } | 41 | } |
| 32 | else | ||
| 33 | { | ||
| 34 | MemCpy(addr.sun_path + str8_xdg_runtime_dir.size + 1, wayland_display, str8_wayland_display_value.size); | ||
| 35 | } | 42 | } |
| 36 | 43 | ||
| 37 | int fd = socket(AF_UNIX, SOCK_STREAM, 0); | 44 | internal void draw_square(u32 *pixels, WaylandState *state, u32 x_position, u32 y_position) { |
| 38 | 45 | ||
| 39 | #if DEBUG // helps to read those errors? i guess i can just read stderr? | 46 | for(u32 y = y_position; y < y_position + 10; ++y) { |
| 40 | 47 | ||
| 48 | for(u32 x = x_position; x < x_position + 10; ++x) { | ||
| 41 | 49 | ||
| 50 | u8 r = 0xff; | ||
| 51 | u8 g = 0xff; | ||
| 52 | u8 b = 0xff; | ||
| 53 | pixels[y * state->width + x] = (r << 16) | (g << 8) | b; | ||
| 42 | 54 | ||
| 43 | if(fd == -1) | 55 | } |
| 44 | { | ||
| 45 | perror("failed to open socket"); | ||
| 46 | } | 56 | } |
| 47 | 57 | ||
| 58 | } | ||
| 48 | 59 | ||
| 49 | if(connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) | 60 | internal void wayland_draw_weird_gradient(WaylandState *state) { |
| 50 | { | ||
| 51 | perror("failed to connect to socket"); | ||
| 52 | 61 | ||
| 53 | } | 62 | local_persist u32 frame = 0; |
| 63 | frame += 1; | ||
| 54 | 64 | ||
| 55 | #endif | 65 | local_persist u32 start_x = state->width / 2; |
| 66 | local_persist u32 start_y = state->width / 2; | ||
| 56 | 67 | ||
| 68 | start_x += frame; | ||
| 57 | 69 | ||
| 58 | AssertLog((fd != -1), "failed to open socket\n"); | 70 | draw_square((u32 *)state->pixels, state, start_x , start_y); |
| 59 | AssertLog((connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1), "failed to connect to socket\n"); | ||
| 60 | 71 | ||
| 61 | return fd; | ||
| 62 | 72 | ||
| 63 | } | 73 | } |
| 64 | 74 | ||
| 65 | 75 | ||
| 66 | //- helper functions | 76 | //- xdg_wm_base events |
| 67 | |||
| 68 | internal void buf_write_u32(char *buf, u64 *buf_size, u64 buf_cap, | ||
| 69 | u32 x) { | ||
| 70 | Assert(*buf_size + sizeof(x) <= buf_cap); | ||
| 71 | Assert(((size_t)buf + *buf_size) % sizeof(x) == 0); | ||
| 72 | 77 | ||
| 73 | *(u32 *)(buf + *buf_size) = x; | 78 | // ping: the compositor checks we're still alive. if we don't pong, it may show the |
| 74 | *buf_size += sizeof(x); | 79 | internal void wayland_xdg_wm_base_ping(void *data, struct xdg_wm_base *xdg_wm_base, u32 serial) { |
| 80 | xdg_wm_base_pong(xdg_wm_base, serial); | ||
| 75 | } | 81 | } |
| 76 | 82 | ||
| 77 | internal void buf_write_u16(char *buf, u64 *buf_size, u64 buf_cap, | 83 | global_variable const struct xdg_wm_base_listener wayland_xdg_wm_base_listener = { |
| 78 | u16 x) { | 84 | wayland_xdg_wm_base_ping, |
| 79 | Assert(*buf_size + sizeof(x) <= buf_cap); | 85 | }; |
| 80 | Assert(((size_t)buf + *buf_size) % sizeof(x) == 0); | 86 | |
| 87 | |||
| 88 | //- frame | ||
| 89 | |||
| 90 | |||
| 91 | internal void wayland_frame_done(void *data, struct wl_callback *callback, u32 time_ms); | ||
| 92 | |||
| 93 | global_variable const struct wl_callback_listener wayland_frame_listener = { | ||
| 94 | wayland_frame_done, | ||
| 95 | |||
| 96 | }; | ||
| 81 | 97 | ||
| 82 | *(u16 *)(buf + *buf_size) = x; | ||
| 83 | *buf_size += sizeof(x); | ||
| 84 | } | ||
| 85 | 98 | ||
| 86 | internal void buf_write_string(char *buf, u64 *buf_size, u64 buf_cap, | 99 | internal void wayland_frame_done(void *data, struct wl_callback *callback, u32 time_ms) { |
| 87 | char *src, u32 src_len) { | 100 | |
| 88 | Assert(*buf_size + src_len <= buf_cap); | 101 | WaylandState *state = (WaylandState *)data; |
| 102 | wl_callback_destroy(callback); | ||
| 103 | |||
| 104 | wayland_draw_weird_gradient(state); | ||
| 105 | struct wl_callback *next = wl_surface_frame(state->surface); | ||
| 106 | wl_callback_add_listener(next, &wayland_frame_listener, state); | ||
| 107 | |||
| 108 | wl_surface_attach(state->surface, state->buffer, 0, 0); | ||
| 109 | wl_surface_damage(state->surface, 0, 0, state->width, state->height); | ||
| 110 | wl_surface_commit(state->surface); | ||
| 89 | 111 | ||
| 90 | buf_write_u32(buf, buf_size, buf_cap, src_len); | ||
| 91 | memcpy(buf + *buf_size, src, roundup_4(src_len)); | ||
| 92 | *buf_size += roundup_4(src_len); | ||
| 93 | } | 112 | } |
| 94 | 113 | ||
| 95 | internal u32 buf_read_u32(char **buf, u64 *buf_size) { | ||
| 96 | Assert(*buf_size >= sizeof(u32)); | ||
| 97 | Assert((size_t)*buf % sizeof(u32) == 0); | ||
| 98 | 114 | ||
| 99 | u32 res = *(u32 *)(*buf); | ||
| 100 | *buf += sizeof(res); | ||
| 101 | *buf_size -= sizeof(res); | ||
| 102 | 115 | ||
| 103 | return res; | 116 | //- xdg_surface events |
| 117 | |||
| 118 | internal void wayland_xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, u32 serial) { | ||
| 119 | WaylandState *state = (WaylandState *)data; | ||
| 120 | |||
| 121 | xdg_surface_ack_configure(xdg_surface, serial); | ||
| 122 | |||
| 123 | if (state->buffer == NULL) { | ||
| 124 | state->buffer = wayland_create_shm_buffer(state); | ||
| 125 | wayland_draw_weird_gradient(state); | ||
| 126 | |||
| 127 | struct wl_callback *cb = wl_surface_frame(state->surface); | ||
| 128 | wl_callback_add_listener(cb, &wayland_frame_listener, state); | ||
| 129 | wl_surface_attach(state->surface, state->buffer, 0, 0); | ||
| 130 | wl_surface_damage(state->surface, 0, 0, state->width, state->height); | ||
| 131 | wl_surface_commit(state->surface); | ||
| 132 | } | ||
| 133 | |||
| 134 | // attach = "this buffer is the surface's content", damage = "this region changed", | ||
| 135 | // commit = "apply everything I sent since the last commit, atomically". | ||
| 136 | // a wl_surface has pending state and current state, nothing is visible until commit. | ||
| 137 | wl_surface_attach(state->surface, state->buffer, 0, 0); | ||
| 138 | wl_surface_damage(state->surface, 0, 0, state->width, state->height); | ||
| 139 | wl_surface_commit(state->surface); | ||
| 104 | } | 140 | } |
| 105 | 141 | ||
| 106 | internal u16 buf_read_u16(char **buf, u64 *buf_size) { | 142 | global_variable const struct xdg_surface_listener wayland_xdg_surface_listener = { |
| 107 | Assert(*buf_size >= sizeof(u16)); | 143 | wayland_xdg_surface_configure, |
| 108 | Assert((size_t)*buf % sizeof(u16) == 0); | 144 | }; |
| 145 | |||
| 146 | |||
| 147 | //- xdg_toplevel events | ||
| 109 | 148 | ||
| 110 | u16 res = *(u16 *)(*buf); | 149 | // TODO: handle resizing. with vulkan that means recreating the swapchain. |
| 111 | *buf += sizeof(res); | 150 | internal void wayland_xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, |
| 112 | *buf_size -= sizeof(res); | 151 | s32 width, s32 height, struct wl_array *states) { |
| 152 | |||
| 153 | WaylandState *state = (WaylandState *)data; | ||
| 154 | Log("<- xdg_toplevel.configure: width=%d height=%d\n", width, height); | ||
| 155 | state->height = height; | ||
| 156 | state->width = width; | ||
| 157 | |||
| 158 | } | ||
| 113 | 159 | ||
| 114 | return res; | 160 | internal void wayland_xdg_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel) { |
| 161 | WaylandState *state = (WaylandState *)data; | ||
| 162 | Log("<- xdg_toplevel.close\n"); | ||
| 163 | state->should_close = true; | ||
| 115 | } | 164 | } |
| 116 | 165 | ||
| 117 | internal void buf_read_n(char **buf, u64 *buf_size, char *dst, u64 n) { | 166 | internal void wayland_xdg_toplevel_resize_edge(void *data, struct xdg_toplevel *xdg_toplevel, |
| 118 | Assert(*buf_size >= n); | 167 | s32 width, s32 height, struct wl_array *states) { |
| 119 | 168 | ||
| 120 | memcpy(dst, *buf, n); | ||
| 121 | 169 | ||
| 122 | *buf += n; | 170 | } |
| 123 | *buf_size -= n; | 171 | internal void wayland_xdg_toplevel_configure_bounds(void *data, struct xdg_toplevel *xdg_toplevel, |
| 172 | s32 width, s32 height) { | ||
| 124 | } | 173 | } |
| 125 | 174 | ||
| 126 | global_variable u64 wayland_current_id = 1; | 175 | internal void wayland_xdg_toplevel_wm_capabilities(void *data, struct xdg_toplevel *xdg_toplevel, |
| 176 | struct wl_array *capabilities) { | ||
| 177 | } | ||
| 127 | 178 | ||
| 128 | internal u32 wayland_wl_display_get_registry(int fd) { | 179 | global_variable const struct xdg_toplevel_listener wayland_xdg_toplevel_listener = { |
| 129 | uint64_t msg_size = 0; | 180 | wayland_xdg_toplevel_configure, |
| 130 | char msg[128] = ""; | 181 | wayland_xdg_toplevel_close, |
| 131 | buf_write_u32(msg, &msg_size, sizeof(msg), wayland_display_object_id); | 182 | wayland_xdg_toplevel_configure_bounds, |
| 183 | wayland_xdg_toplevel_wm_capabilities, | ||
| 184 | }; | ||
| 132 | 185 | ||
| 133 | buf_write_u16(msg, &msg_size, sizeof(msg), | ||
| 134 | wayland_wl_display_get_registry_opcode); | ||
| 135 | 186 | ||
| 136 | u16 msg_announced_size = wayland_header_size + sizeof(wayland_current_id); | 187 | //- wl_registry events |
| 137 | Assert(roundup_4(msg_announced_size) == msg_announced_size); | ||
| 138 | buf_write_u16(msg, &msg_size, sizeof(msg), msg_announced_size); | ||
| 139 | 188 | ||
| 140 | wayland_current_id++; | 189 | internal void wayland_registry_global(void *data, struct wl_registry *registry, u32 name, |
| 141 | buf_write_u32(msg, &msg_size, sizeof(msg), wayland_current_id); | 190 | const char *interface, u32 version) { |
| 191 | WaylandState *state = (WaylandState *)data; | ||
| 142 | 192 | ||
| 143 | if ((s64)msg_size != send(fd, msg, msg_size, MSG_DONTWAIT)) | 193 | Log("<- wl_registry.global: name=%u interface=%s version=%u\n", name, interface, version); |
| 144 | exit(1); | ||
| 145 | 194 | ||
| 146 | Log("-> wl_display@%u.get_registry: wl_registry=%u\n", | 195 | if (CstringIsMatch(interface, wl_compositor_interface.name)) { |
| 147 | wayland_display_object_id, wayland_current_id); | 196 | state->compositor = (struct wl_compositor *) |
| 197 | wl_registry_bind(registry, name, &wl_compositor_interface, Min(version, 4)); | ||
| 198 | } else if (CstringIsMatch(interface, wl_shm_interface.name)) { | ||
| 199 | state->shm = (struct wl_shm *) | ||
| 200 | wl_registry_bind(registry, name, &wl_shm_interface, 1); | ||
| 201 | } else if(CstringIsMatch(interface, xdg_wm_base_interface.name)) { | ||
| 202 | state->xdg_wm_base = (struct xdg_wm_base *) | ||
| 203 | wl_registry_bind(registry, name, &xdg_wm_base_interface, 1); | ||
| 204 | xdg_wm_base_add_listener(state->xdg_wm_base, &wayland_xdg_wm_base_listener, state); | ||
| 205 | } else if (CstringIsMatch(interface, wl_seat_interface.name)) { | ||
| 206 | state->seat = (struct wl_seat *) | ||
| 207 | wl_registry_bind(registry, name, &wl_seat_interface, Min(version, 5)); | ||
| 208 | wl_seat_add_listener(state->seat, &wayland_seat_listener, state); // has the keyboard handler | ||
| 209 | } | ||
| 210 | } | ||
| 148 | 211 | ||
| 149 | return wayland_current_id; | 212 | internal void wayland_registry_global_remove(void *data, struct wl_registry *registry, u32 name) { |
| 150 | } | 213 | } |
| 151 | 214 | ||
| 215 | global_variable const struct wl_registry_listener wayland_registry_listener = { | ||
| 216 | wayland_registry_global, | ||
| 217 | wayland_registry_global_remove, | ||
| 218 | }; | ||
| 152 | 219 | ||
| 220 | //- entrypoint | ||
| 153 | 221 | ||
| 154 | internal void create_shared_memory_file(u64 size, state_t *state) { | 222 | internal void wayland_entrypoint(MemArena *arena, LinuxWindowParams *params) { |
| 155 | char name[255] = "/"; | 223 | |
| 156 | for (u64 index = 1; index < cstring8_length((u8 *)name); ++index) { | 224 | WaylandState state = {}; |
| 157 | name[index] = ((double)rand()) / (double)RAND_MAX * 26 + 'a'; | 225 | |
| 158 | } | 226 | state.display = wl_display_connect(NULL); |
| 159 | 227 | AssertLog((state.display != NULL), "failed to connect to wayland display\n"); | |
| 160 | int fd = shm_open(name, O_RDWR | O_EXCL | O_CREAT, 0600); | 228 | |
| 161 | AssertLog((fd != -1), "Failed to open buffer for shared memory."); | 229 | // ask for the registry and listen to its global events |
| 162 | 230 | state.registry = wl_display_get_registry(state.display); | |
| 163 | Assert(shm_unlink(name) != -1); | 231 | wl_registry_add_listener(state.registry, &wayland_registry_listener, &state); |
| 164 | 232 | ||
| 165 | AssertLog((ftruncate(fd, size) != -1), "Failed to truncate buffer."); | 233 | wl_display_roundtrip(state.display); |
| 166 | 234 | ||
| 167 | state->shm_pool_data = (u8 *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); | 235 | AssertLog((state.compositor != NULL), "compositor has no wl_compositor\n"); |
| 168 | Assert((void*)-1 != state->shm_pool_data); | 236 | AssertLog((state.shm != NULL), "compositor has no wl_shm\n"); |
| 169 | Assert(state->shm_pool_data != NULL); | 237 | AssertLog((state.xdg_wm_base != NULL), "compositor has no xdg_wm_base\n"); |
| 170 | state->shm_fd = fd; | 238 | if (state.seat == NULL) { |
| 239 | Log("no wl_seat: running without keyboard input\n"); | ||
| 240 | } | ||
| 241 | |||
| 242 | // build the window: surface -> give it the xdg role -> make it a toplevel | ||
| 243 | state.surface = wl_compositor_create_surface(state.compositor); | ||
| 244 | |||
| 245 | state.xdg_surface = xdg_wm_base_get_xdg_surface(state.xdg_wm_base, state.surface); | ||
| 246 | xdg_surface_add_listener(state.xdg_surface, &wayland_xdg_surface_listener, &state); | ||
| 247 | |||
| 248 | state.xdg_toplevel = xdg_surface_get_toplevel(state.xdg_surface); | ||
| 249 | xdg_toplevel_add_listener(state.xdg_toplevel, &wayland_xdg_toplevel_listener, &state); | ||
| 250 | xdg_toplevel_set_title(state.xdg_toplevel, params->name); | ||
| 251 | xdg_toplevel_set_app_id(state.xdg_toplevel, params->name); | ||
| 252 | |||
| 253 | wl_surface_commit(state.surface); | ||
| 254 | |||
| 255 | // | ||
| 256 | // TODO: blocking is fine while the picture is static. for a game loop that renders | ||
| 257 | // every frame we'll instead use wl_display_dispatch_pending + a non blocking read | ||
| 258 | // (wl_display_prepare_read / wl_display_read_events with poll on wl_display_get_fd). | ||
| 259 | while (!state.should_close) { | ||
| 260 | |||
| 261 | if (wl_display_dispatch(state.display) == -1) { | ||
| 262 | Log("wayland connection lost\n"); | ||
| 263 | goto regular_exit; | ||
| 264 | } | ||
| 265 | |||
| 266 | if(state.keys_down[KEY_W]) { Log("Move Forward"); } | ||
| 267 | if(state.keys_down[KEY_A]) { Log("Move Left"); } | ||
| 268 | if(state.keys_down[KEY_D]) { Log("Move Right"); } | ||
| 269 | if(state.keys_down[KEY_S]) { Log("Move Back"); } | ||
| 270 | if(state.keys_down[KEY_Q]) { | ||
| 271 | goto cleanup_exit; | ||
| 171 | } | 272 | } |
| 172 | 273 | ||
| 173 | internal void wayland_handle_message(int fd, state_t *state, char **msg, | 274 | } |
| 174 | uint64_t *msg_len) { | 275 | |
| 175 | Assert(*msg_len >= 8); | 276 | |
| 176 | 277 | cleanup_exit: | |
| 177 | u32 object_id = buf_read_u32(msg, msg_len); | ||
| 178 | Assert(object_id <= wayland_current_id); | ||
| 179 | |||
| 180 | u16 opcode = buf_read_u16(msg, msg_len); | ||
| 181 | |||
| 182 | u16 announced_size = buf_read_u16(msg, msg_len); | ||
| 183 | Assert(roundup_4(announced_size) <= announced_size); | ||
| 184 | |||
| 185 | u32 header_size = sizeof(object_id) + sizeof(opcode) + sizeof(announced_size); | ||
| 186 | Assert(announced_size <= header_size + *msg_len); | ||
| 187 | 278 | ||
| 188 | if (object_id == state->wl_registry && | 279 | if (state.keyboard) wl_keyboard_destroy(state.keyboard); |
| 189 | opcode == wayland_wl_registry_event_global) { | 280 | if (state.buffer) wl_buffer_destroy(state.buffer); |
| 190 | // TODO | 281 | if (state.pixels) munmap(state.pixels, state.size); |
| 191 | } | 282 | xdg_toplevel_destroy(state.xdg_toplevel); |
| 192 | // Following: Lots of `if (opcode == ...) {... } else if (opcode = ...) { ... } [...]` | 283 | xdg_surface_destroy(state.xdg_surface); |
| 284 | wl_surface_destroy(state.surface); | ||
| 285 | if (state.seat) wl_seat_destroy(state.seat); | ||
| 286 | xdg_wm_base_destroy(state.xdg_wm_base); | ||
| 287 | wl_shm_destroy(state.shm); | ||
| 288 | wl_compositor_destroy(state.compositor); | ||
| 289 | wl_registry_destroy(state.registry); | ||
| 290 | return; | ||
| 291 | |||
| 292 | regular_exit: | ||
| 293 | return; | ||
| 193 | } | 294 | } |
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 @@ | |||
| 1 | internal void wayland_keyboard_keymap(void *data, struct wl_keyboard *keyboard, u32 format, s32 fd, u32 size) { | ||
| 2 | Log("<- wl_keyboard.keymap: format=%u fd=%d size=%u\n", format, fd, size); | ||
| 3 | close(fd); | ||
| 4 | } | ||
| 5 | |||
| 6 | internal void wayland_keyboard_enter(void *data, struct wl_keyboard *keyboard, u32 serial, | ||
| 7 | struct wl_surface *surface, struct wl_array *keys) { | ||
| 8 | WaylandState *state = (WaylandState *)data; | ||
| 9 | Log("<- wl_keyboard.enter\n"); | ||
| 10 | |||
| 11 | // wl_array is just { size_t size; size_t alloc; void *data; }. here data is a list of u32. | ||
| 12 | u32 *key = (u32 *)keys->data; | ||
| 13 | u32 key_count = keys->size / sizeof(u32); | ||
| 14 | for (u32 i = 0; i < key_count; ++i) { | ||
| 15 | if (key[i] < sizeof(state->keys_down) / sizeof(state->keys_down[0])) { | ||
| 16 | state->keys_down[key[i]] = true; | ||
| 17 | } | ||
| 18 | } | ||
| 19 | } | ||
| 20 | |||
| 21 | // leave: we lost focus. after this we will NOT receive release events for keys that | ||
| 22 | // are still held, so we release everything ourselves. otherwise keys get "stuck". | ||
| 23 | internal void wayland_keyboard_leave(void *data, struct wl_keyboard *keyboard, u32 serial, | ||
| 24 | struct wl_surface *surface) { | ||
| 25 | WaylandState *state = (WaylandState *)data; | ||
| 26 | Log("<- wl_keyboard.leave\n"); | ||
| 27 | |||
| 28 | MemSetZero(state->keys_down, sizeof(state->keys_down)); | ||
| 29 | } | ||
| 30 | |||
| 31 | internal void wayland_keyboard_key(void *data, struct wl_keyboard *keyboard, u32 serial, | ||
| 32 | u32 time, u32 key, u32 key_state) { | ||
| 33 | WaylandState *state = (WaylandState *)data; | ||
| 34 | b32 pressed = (key_state == WL_KEYBOARD_KEY_STATE_PRESSED); | ||
| 35 | |||
| 36 | Log("<- wl_keyboard.key: key=%u %s\n", key, pressed ? "pressed" : "released"); | ||
| 37 | |||
| 38 | if (key < sizeof(state->keys_down) / sizeof(state->keys_down[0])) { | ||
| 39 | state->keys_down[key] = pressed; | ||
| 40 | } | ||
| 41 | |||
| 42 | if (key == KEY_ESC && pressed) { | ||
| 43 | state->should_close = true; | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | internal void wayland_keyboard_modifiers(void *data, struct wl_keyboard *keyboard, u32 serial, u32 mods_depressed, u32 mods_latched, | ||
| 48 | u32 mods_locked, u32 group) { | ||
| 49 | } | ||
| 50 | |||
| 51 | internal void wayland_keyboard_repeat_info(void *data, struct wl_keyboard *keyboard, | ||
| 52 | s32 rate, s32 delay) { | ||
| 53 | Log("<- wl_keyboard.repeat_info: rate=%d delay=%d\n", rate, delay); | ||
| 54 | } | ||
| 55 | |||
| 56 | // the listener: a struct of function pointers, one per event, in the order the events | ||
| 57 | // are declared in wayland.xml (which is also their opcode). libwayland indexes into this | ||
| 58 | // struct with the opcode it reads from the message header. | ||
| 59 | global_variable const struct wl_keyboard_listener wayland_keyboard_listener = { | ||
| 60 | wayland_keyboard_keymap, // opcode 0 | ||
| 61 | wayland_keyboard_enter, // opcode 1 | ||
| 62 | wayland_keyboard_leave, // opcode 2 | ||
| 63 | wayland_keyboard_key, // opcode 3 | ||
| 64 | wayland_keyboard_modifiers, // opcode 4 | ||
| 65 | wayland_keyboard_repeat_info, // opcode 5 | ||
| 66 | }; | ||
| 67 | |||
| 68 | //- mouse (pointer) input | ||
| 69 | |||
| 70 | enum wayland_mouse_button_pressed: b32 { | ||
| 71 | |||
| 72 | PRESSED, | ||
| 73 | RELEASED, | ||
| 74 | |||
| 75 | }; | ||
| 76 | |||
| 77 | internal void wayland_button_event(void *data, struct wl_pointer *pointer, | ||
| 78 | u32 serial, u32 time, u32 button, u32 state) { | ||
| 79 | |||
| 80 | |||
| 81 | } | ||
| 82 | |||
| 83 | |||
| 84 | internal void wayland_pointer_enter(void *data, struct wl_pointer *pointer, | ||
| 85 | u32 serial, struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { | ||
| 86 | WaylandState *state = (WaylandState *)data; | ||
| 87 | Log("<- pointer enteirng window"); | ||
| 88 | state->mouse_in_window = true; | ||
| 89 | state->x_offset = surface_x; | ||
| 90 | state->y_offset = surface_y; | ||
| 91 | |||
| 92 | |||
| 93 | } | ||
| 94 | internal void wayland_pointer_motion(void *data, struct wl_pointer *pointer, u32 time, | ||
| 95 | wl_fixed_t surface_x, wl_fixed_t surface_y) { | ||
| 96 | WaylandState *state = (WaylandState *)data; | ||
| 97 | state->x_offset = (f32)wl_fixed_to_double(surface_x); | ||
| 98 | state->y_offset = (f32)wl_fixed_to_double(surface_y); | ||
| 99 | } | ||
| 100 | |||
| 101 | internal void wayland_pointer_leave(void *data, struct wl_pointer *pointer, u32 serial, wl_surface *surface) { | ||
| 102 | WaylandState *state = (WaylandState *)data; | ||
| 103 | state->mouse_in_window = false; | ||
| 104 | Log("<- pointer leaving window"); | ||
| 105 | } | ||
| 106 | |||
| 107 | |||
| 108 | |||
| 109 | global_variable const struct wl_pointer_listener wayland_pointer_listener = { | ||
| 110 | wayland_pointer_enter, | ||
| 111 | wayland_pointer_leave, | ||
| 112 | wayland_pointer_motion, | ||
| 113 | wayland_button_event, | ||
| 114 | }; | ||
| 115 | |||
| 116 | |||
| 117 | |||
| 118 | |||
| 119 | |||
| 120 | //- wl_seat events | ||
| 121 | |||
| 122 | // capabilities: tells us which devices this seat has, as a bitmask: | ||
| 123 | // WL_SEAT_CAPABILITY_POINTER = 1, WL_SEAT_CAPABILITY_KEYBOARD = 2, WL_SEAT_CAPABILITY_TOUCH = 4 | ||
| 124 | // this can be sent again later (keyboard unplugged / plugged in), so we handle both directions. | ||
| 125 | internal void wayland_seat_capabilities(void *data, struct wl_seat *seat, u32 capabilities) { | ||
| 126 | |||
| 127 | WaylandState *state = (WaylandState *)data; | ||
| 128 | b32 has_keyboard = (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) != 0; | ||
| 129 | |||
| 130 | |||
| 131 | Log("<- wl_seat.capabilities: %u\n", capabilities); | ||
| 132 | |||
| 133 | if (has_keyboard && state->keyboard == NULL) { | ||
| 134 | // get_keyboard is a request that creates a new object (a "new_id" argument). | ||
| 135 | // libwayland allocates the id, sends the message, and hands us the proxy. | ||
| 136 | state->keyboard = wl_seat_get_keyboard(seat); | ||
| 137 | wl_keyboard_add_listener(state->keyboard, &wayland_keyboard_listener, state); | ||
| 138 | } else if (!has_keyboard && state->keyboard != NULL) { | ||
| 139 | // keyboard went away: tell the compositor we're done with the object, free the proxy. | ||
| 140 | // the `release` request only exists since wl_seat version 3; before that we can | ||
| 141 | // only free our side of it with destroy. | ||
| 142 | if (wl_keyboard_get_version(state->keyboard) >= WL_KEYBOARD_RELEASE_SINCE_VERSION) { | ||
| 143 | wl_keyboard_release(state->keyboard); | ||
| 144 | } else { | ||
| 145 | wl_keyboard_destroy(state->keyboard); | ||
| 146 | } | ||
| 147 | state->keyboard = NULL; | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 151 | // name: a human readable seat name like "seat0". only useful for multi-seat setups. | ||
| 152 | internal void wayland_seat_name(void *data, struct wl_seat *seat, const char *name) { | ||
| 153 | Log("<- wl_seat.name: %s\n", name); | ||
| 154 | } | ||
| 155 | |||
| 156 | global_variable const struct wl_seat_listener wayland_seat_listener = { | ||
| 157 | wayland_seat_capabilities, // opcode 0 | ||
| 158 | wayland_seat_name, // opcode 1 (since version 2) | ||
| 159 | }; | ||
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) | |||
| 38 | #endif | 38 | #endif |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | #ifdef BASE_DEBUG | 41 | #if DEBUG |
| 42 | bool enable_validation_layer = true; | 42 | bool enable_validation_layer = true; |
| 43 | #else | 43 | #else |
| 44 | bool enable_validation_layer = false; | 44 | 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) | |||
| 125 | 125 | ||
| 126 | stack->current_offset += size; | 126 | stack->current_offset += size; |
| 127 | 127 | ||
| 128 | return (MemStack *)MemSet((MemStack *)next_address, size); | 128 | return (MemStack *)MemSetZero((MemStack *)next_address, size); |
| 129 | } | 129 | } |
| 130 | 130 | ||
| 131 | internal void *stack_push(MemStack *stack, u64 size) | 131 | 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() | |||
| 5 | MemArena *arena = ArenaCreate(MiB(100)); | 5 | MemArena *arena = ArenaCreate(MiB(100)); |
| 6 | Assert(arena); | 6 | Assert(arena); |
| 7 | 7 | ||
| 8 | int fd = wayland_display_connect(arena); | 8 | LinuxWindowParams *params = PushStruct(arena, LinuxWindowParams); |
| 9 | params->name = (cstring)"Loo"; | ||
| 9 | 10 | ||
| 10 | state_t state = {0}; | 11 | wayland_entrypoint(arena, params); |
| 11 | state.wl_registry = wayland_wl_display_get_registry(fd), | ||
| 12 | state.w = 117, | ||
| 13 | state.h = 150, | ||
| 14 | state.stride = 117 * color_channels, | ||
| 15 | state.shm_pool_size = state.h * state.stride; | ||
| 16 | |||
| 17 | |||
| 18 | while (1) { | ||
| 19 | char read_buf[4096] = ""; | ||
| 20 | int64_t read_bytes = recv(fd, read_buf, sizeof(read_buf), 0); | ||
| 21 | if (read_bytes == -1) | ||
| 22 | exit(errno); | ||
| 23 | |||
| 24 | char *msg = read_buf; | ||
| 25 | uint64_t msg_len = (uint64_t)read_bytes; | ||
| 26 | |||
| 27 | while (msg_len > 0) | ||
| 28 | wayland_handle_message(fd, &state, &msg, &msg_len); | ||
| 29 | } | ||
| 30 | 12 | ||
| 31 | return 0; | 13 | return 0; |
| 32 | |||
| 33 | |||
| 34 | } | 14 | } |
