// This file is part of the 64k demo project. // It implements platform-specific windowing and input using GLFW. // Handles fullscreen toggling and native surface creation for WebGPU. #include "platform/platform.h" #include "glfw3webgpu.h" #include #include // --- Callbacks --- static void framebuffer_size_callback(GLFWwindow* window, int width, int height) { PlatformState* state = (PlatformState*)glfwGetWindowUserPointer(window); if (state) { state->width = width; state->height = height; if (height > 0) { state->aspect_ratio = (float)width / (float)height; } } } static void glfw_key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) { PlatformState* state = (PlatformState*)glfwGetWindowUserPointer(window); if (action == GLFW_PRESS) { if (key == GLFW_KEY_ESCAPE || key == GLFW_KEY_Q) { glfwSetWindowShouldClose(window, GLFW_TRUE); } else if (key == GLFW_KEY_F) { if (state) platform_toggle_fullscreen(state); } } } // --- Public API Implementation --- PlatformState platform_init(bool fullscreen, int width, int height) { PlatformState state = {}; state.width = width; state.height = height; state.is_fullscreen = fullscreen; glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); state.window = glfwCreateWindow(state.width, state.height, "demo64k", nullptr, nullptr); // Immediately query the actual framebuffer size for high-DPI displays glfwGetFramebufferSize(state.window, &state.width, &state.height); if (state.height > 0) { state.aspect_ratio = (float)state.width / (float)state.height; } // Store our state in a static pointer or use the window pointer? // We'll use a pointer to a persistent state. // For this demo, we can assume the PlatformState persists in main(). // But we return by value. This is a bit tricky with callbacks. // We'll fix the callbacks in platform_poll if needed, or just let main pass // the pointer back. glfwSetWindowUserPointer(state.window, nullptr); // Will be set in main's state glfwSetKeyCallback(state.window, glfw_key_callback); glfwSetFramebufferSizeCallback(state.window, framebuffer_size_callback); if (fullscreen) { glfwGetWindowPos(state.window, &state.windowed_x, &state.windowed_y); glfwGetWindowSize(state.window, &state.windowed_w, &state.windowed_h); GLFWmonitor* monitor = glfwGetPrimaryMonitor(); const GLFWvidmode* mode = glfwGetVideoMode(monitor); glfwSetWindowMonitor(state.window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate); } state.time = glfwGetTime(); return state; } void platform_shutdown(PlatformState* state) { if (state->window) { glfwDestroyWindow(state->window); } glfwTerminate(); } void platform_poll(PlatformState* state) { // Ensure the window has the current state pointer for callbacks if (glfwGetWindowUserPointer(state->window) != state) { glfwSetWindowUserPointer(state->window, state); } glfwPollEvents(); state->time = glfwGetTime(); if (state->height > 0) { state->aspect_ratio = (float)state->width / (float)state->height; } } bool platform_should_close(PlatformState* state) { return glfwWindowShouldClose(state->window); } void platform_toggle_fullscreen(PlatformState* state) { state->is_fullscreen = !state->is_fullscreen; if (state->is_fullscreen) { glfwGetWindowPos(state->window, &state->windowed_x, &state->windowed_y); glfwGetWindowSize(state->window, &state->windowed_w, &state->windowed_h); GLFWmonitor* monitor = glfwGetPrimaryMonitor(); const GLFWvidmode* mode = glfwGetVideoMode(monitor); glfwSetWindowMonitor(state->window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate); } else { glfwSetWindowMonitor(state->window, nullptr, state->windowed_x, state->windowed_y, state->windowed_w, state->windowed_h, 0); } } WGPUSurface platform_create_wgpu_surface(WGPUInstance instance, PlatformState* state) { return glfwCreateWindowWGPUSurface(instance, state->window); } double platform_get_time() { return glfwGetTime(); } // --- WebGPU Request Helpers --- #if defined(DEMO_CROSS_COMPILE_WIN32) static void _platform_adapter_cb_win32(WGPURequestAdapterStatus status, WGPUAdapter adapter, const char* message, void* userdata) { if (status == WGPURequestAdapterStatus_Success) { *((WGPUAdapter*)userdata) = adapter; } else { printf("Request adapter failed: %s\n", message ? message : "Unknown"); } } void platform_wgpu_request_adapter(WGPUInstance instance, const WGPURequestAdapterOptions* options, WGPUAdapter* out_adapter) { wgpuInstanceRequestAdapter(instance, options, _platform_adapter_cb_win32, out_adapter); } static void _platform_device_cb_win32(WGPURequestDeviceStatus status, WGPUDevice device, const char* message, void* userdata) { if (status == WGPURequestDeviceStatus_Success) { *((WGPUDevice*)userdata) = device; } else { printf("Request device failed: %s\n", message ? message : "Unknown"); } } void platform_wgpu_request_device(WGPUAdapter adapter, const WGPUDeviceDescriptor* descriptor, WGPUDevice* out_device) { wgpuAdapterRequestDevice(adapter, descriptor, _platform_device_cb_win32, out_device); } #else static void _platform_adapter_cb_native(WGPURequestAdapterStatus status, WGPUAdapter adapter, WGPUStringView message, void* userdata, void* userdata2) { (void)userdata2; if (status == WGPURequestAdapterStatus_Success) { *((WGPUAdapter*)userdata) = adapter; } else { printf("Request adapter failed: %.*s\n", (int)message.length, message.data); } } void platform_wgpu_request_adapter(WGPUInstance instance, const WGPURequestAdapterOptions* options, WGPUAdapter* out_adapter) { WGPURequestAdapterCallbackInfo cb = {}; cb.mode = WGPUCallbackMode_WaitAnyOnly; cb.callback = _platform_adapter_cb_native; cb.userdata1 = out_adapter; wgpuInstanceRequestAdapter(instance, options, cb); } static void _platform_device_cb_native(WGPURequestDeviceStatus status, WGPUDevice device, WGPUStringView message, void* userdata, void* userdata2) { (void)userdata2; if (status == WGPURequestDeviceStatus_Success) { *((WGPUDevice*)userdata) = device; } else { printf("Request device failed: %.*s\n", (int)message.length, message.data); } } void platform_wgpu_request_device(WGPUAdapter adapter, const WGPUDeviceDescriptor* descriptor, WGPUDevice* out_device) { WGPURequestDeviceCallbackInfo cb = {}; cb.mode = WGPUCallbackMode_WaitAnyOnly; cb.callback = _platform_device_cb_native; cb.userdata1 = out_device; wgpuAdapterRequestDevice(adapter, descriptor, cb); } #endif