From a7bcf5e9cd6884d010b5cec0146293a0515242fc Mon Sep 17 00:00:00 2001 From: skal Date: Wed, 28 Jan 2026 00:41:07 +0100 Subject: feat: Implement fullscreen, keyboard controls, and pulsating heptagon This commit fulfills tasks 1 and 2, and adds a synchronized visual effect. - **Fullscreen Mode**: Added '--fullscreen' command-line argument and dynamic toggling via 'F' key. - **Keyboard Controls**: Implemented 'Esc' to exit and 'F' to toggle fullscreen in 'src/platform.cc'. - **Synchronized Visuals**: Added a pulsating heptagon effect in 'src/gpu/gpu.cc' and 'src/gpu/shader.wgsl' that scales and changes color based on the real-time audio peak from the synth. - **Refactor**: Abstracted platform-specific WebGPU surface creation into 'src/platform.cc' to keep 'src/gpu/gpu.cc' cross-platform. - **Build System**: Corrected 'CMakeLists.txt' to properly link 'wgpu-native' and platform frameworks, and updated 'project_init.sh' to build the submodule. - **Documentation**: Updated 'HOWTO.md' and 'PROJECT_CONTEXT.md' with new features and decisions. --- src/platform.cc | 137 +++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 126 insertions(+), 11 deletions(-) (limited to 'src/platform.cc') diff --git a/src/platform.cc b/src/platform.cc index bfb566c..09cc7ac 100644 --- a/src/platform.cc +++ b/src/platform.cc @@ -1,11 +1,55 @@ #include "platform.h" + +#ifdef _WIN32 +#define GLFW_EXPOSE_NATIVE_WIN32 +#elif defined(__APPLE__) +#define GLFW_EXPOSE_NATIVE_COCOA +#else +#define GLFW_EXPOSE_NATIVE_X11 +#define GLFW_EXPOSE_NATIVE_WAYLAND +#endif + #include +#include + +#ifdef __APPLE__ +#import +#import +#import +#endif static GLFWwindow *window = nullptr; +static int windowed_x, windowed_y, windowed_w, windowed_h; +static bool g_is_fullscreen = false; -void platform_init() { +static void glfw_key_callback(GLFWwindow *cb_window, int key, int scancode, + int action, int mods) { + if (action == GLFW_PRESS) { + if (key == GLFW_KEY_ESCAPE) { + glfwSetWindowShouldClose(cb_window, GLFW_TRUE); + } else if (key == GLFW_KEY_F) { + platform_toggle_fullscreen(); + } + } +} + +void platform_init_window(bool fullscreen) { glfwInit(); + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); window = glfwCreateWindow(1280, 720, "demo64k", nullptr, nullptr); + glfwSetKeyCallback(window, glfw_key_callback); + + g_is_fullscreen = fullscreen; + if (fullscreen) { + // Save current windowed mode dimensions before going fullscreen + glfwGetWindowPos(window, &windowed_x, &windowed_y); + glfwGetWindowSize(window, &windowed_w, &windowed_h); + + GLFWmonitor *monitor = glfwGetPrimaryMonitor(); + const GLFWvidmode *mode = glfwGetVideoMode(monitor); + glfwSetWindowMonitor(window, monitor, 0, 0, mode->width, mode->height, + mode->refreshRate); + } } void platform_shutdown() { @@ -13,18 +57,89 @@ void platform_shutdown() { glfwTerminate(); } -void platform_poll() { - glfwPollEvents(); -} +void platform_poll() { glfwPollEvents(); } -bool platform_should_close() { - return glfwWindowShouldClose(window); -} +bool platform_should_close() { return glfwWindowShouldClose(window); } + +void platform_toggle_fullscreen() { + g_is_fullscreen = !g_is_fullscreen; + if (g_is_fullscreen) { + glfwGetWindowPos(window, &windowed_x, &windowed_y); + glfwGetWindowSize(window, &windowed_w, &windowed_h); -GLFWwindow *platform_get_window() { - return window; + GLFWmonitor *monitor = glfwGetPrimaryMonitor(); + const GLFWvidmode *mode = glfwGetVideoMode(monitor); + glfwSetWindowMonitor(window, monitor, 0, 0, mode->width, mode->height, + mode->refreshRate); + } else { + glfwSetWindowMonitor(window, nullptr, windowed_x, windowed_y, windowed_w, + windowed_h, 0); + } } -double platform_get_time() { - return glfwGetTime(); +GLFWwindow *platform_get_window() { return window; } + +double platform_get_time() { return glfwGetTime(); } + +WGPUSurface platform_create_wgpu_surface(WGPUInstance instance) { +#if defined(GLFW_EXPOSE_NATIVE_COCOA) + id metal_layer = NULL; + NSWindow *ns_window = glfwGetCocoaWindow(window); + [ns_window.contentView setWantsLayer:YES]; + metal_layer = [CAMetalLayer layer]; + [ns_window.contentView setLayer:metal_layer]; + + WGPUSurfaceSourceMetalLayer metal_src = {}; + metal_src.chain.sType = WGPUSType_SurfaceSourceMetalLayer; + metal_src.layer = metal_layer; + + WGPUSurfaceDescriptor surface_desc = {}; + surface_desc.nextInChain = (const WGPUChainedStruct *)&metal_src; + + return wgpuInstanceCreateSurface(instance, &surface_desc); + +#elif defined(GLFW_EXPOSE_NATIVE_WIN32) + HWND hwnd = glfwGetWin32Window(window); + HINSTANCE hinstance = GetModuleHandle(NULL); + + WGPUSurfaceSourceWindowsHWND win_src = {}; + win_src.chain.sType = WGPUSType_SurfaceSourceWindowsHWND; + win_src.hinstance = hinstance; + win_src.hwnd = hwnd; + + WGPUSurfaceDescriptor surface_desc = {}; + surface_desc.nextInChain = (const WGPUChainedStruct *)&win_src; + + return wgpuInstanceCreateSurface(instance, &surface_desc); + +#elif defined(GLFW_EXPOSE_NATIVE_X11) || defined(GLFW_EXPOSE_NATIVE_WAYLAND) + if (glfwGetPlatform() == GLFW_PLATFORM_X11) { + Display *x11_display = glfwGetX11Display(); + Window x11_window = glfwGetX11Window(window); + + WGPUSurfaceSourceXlibWindow x11_src = {}; + x11_src.chain.sType = WGPUSType_SurfaceSourceXlibWindow; + x11_src.display = x11_display; + x11_src.window = x11_window; + + WGPUSurfaceDescriptor surface_desc = {}; + surface_desc.nextInChain = (const WGPUChainedStruct *)&x11_src; + + return wgpuInstanceCreateSurface(instance, &surface_desc); + } else if (glfwGetPlatform() == GLFW_PLATFORM_WAYLAND) { + struct wl_display *wayland_display = glfwGetWaylandDisplay(); + struct wl_surface *wayland_surface = glfwGetWaylandWindow(window); + + WGPUSurfaceSourceWaylandSurface wl_src = {}; + wl_src.chain.sType = WGPUSType_SurfaceSourceWaylandSurface; + wl_src.display = wayland_display; + wl_src.surface = wayland_surface; + + WGPUSurfaceDescriptor surface_desc = {}; + surface_desc.nextInChain = (const WGPUChainedStruct *)&wl_src; + + return wgpuInstanceCreateSurface(instance, &surface_desc); + } +#endif + return nullptr; } -- cgit v1.2.3