// Minimal GLFW-compatible Win32 backend for cross-compiling the example. // It implements only the subset used by src/render.cpp. #include #include #include #include #include #define GLFW_INCLUDE_VULKAN #define VK_USE_PLATFORM_WIN32_KHR #include #include #include struct GLFWwindow { HWND handle = nullptr; void* user_pointer = nullptr; bool should_close = false; int width = 0; int height = 0; GLFWkeyfun key_callback = nullptr; GLFWmousebuttonfun mouse_button_callback = nullptr; GLFWcursorposfun cursor_pos_callback = nullptr; GLFWscrollfun scroll_callback = nullptr; std::array key_state{}; std::array mouse_button_state{}; double cursor_x = 0.0; double cursor_y = 0.0; }; namespace { constexpr wchar_t kWindowClassName[] = L"OpenCGE_GLFWStub"; HINSTANCE g_instance = nullptr; bool g_initialized = false; bool g_resizable = true; int map_key(WPARAM vk) { if (vk >= 'A' && vk <= 'Z') { return GLFW_KEY_A + static_cast(vk - 'A'); } if (vk >= '0' && vk <= '9') { return GLFW_KEY_0 + static_cast(vk - '0'); } switch (vk) { case VK_ESCAPE: return GLFW_KEY_ESCAPE; case VK_LEFT: return GLFW_KEY_LEFT; case VK_RIGHT: return GLFW_KEY_RIGHT; case VK_UP: return GLFW_KEY_UP; case VK_DOWN: return GLFW_KEY_DOWN; case VK_SPACE: return GLFW_KEY_SPACE; case VK_RETURN: return GLFW_KEY_ENTER; case VK_TAB: return GLFW_KEY_TAB; case VK_BACK: return GLFW_KEY_BACKSPACE; case VK_DELETE: return GLFW_KEY_DELETE; case VK_LSHIFT: return GLFW_KEY_LEFT_SHIFT; case VK_RSHIFT: return GLFW_KEY_RIGHT_SHIFT; case VK_LCONTROL: return GLFW_KEY_LEFT_CONTROL; case VK_RCONTROL: return GLFW_KEY_RIGHT_CONTROL; case VK_LMENU: return GLFW_KEY_LEFT_ALT; case VK_RMENU: return GLFW_KEY_RIGHT_ALT; default: break; } return GLFW_KEY_UNKNOWN; } int current_mods() { int mods = 0; if (GetKeyState(VK_SHIFT) & 0x8000) { mods |= GLFW_MOD_SHIFT; } if (GetKeyState(VK_CONTROL) & 0x8000) { mods |= GLFW_MOD_CONTROL; } if (GetKeyState(VK_MENU) & 0x8000) { mods |= GLFW_MOD_ALT; } if ((GetKeyState(VK_LWIN) & 0x8000) || (GetKeyState(VK_RWIN) & 0x8000)) { mods |= GLFW_MOD_SUPER; } return mods; } void handle_key(GLFWwindow* window, UINT message, WPARAM wparam, LPARAM lparam) { if (window == nullptr || window->key_callback == nullptr) { return; } const int key = map_key(wparam); const int scancode = static_cast((lparam >> 16) & 0x1ff); const int action = message == WM_KEYDOWN || message == WM_SYSKEYDOWN ? (((lparam & (1U << 30)) != 0) ? GLFW_REPEAT : GLFW_PRESS) : GLFW_RELEASE; if (key >= 0 && key < static_cast(window->key_state.size())) { window->key_state[key] = action == GLFW_RELEASE ? GLFW_RELEASE : GLFW_PRESS; } window->key_callback(window, key, scancode, action, current_mods()); } int map_mouse_button(UINT message, WPARAM wparam) { switch (message) { case WM_LBUTTONDOWN: case WM_LBUTTONUP: return GLFW_MOUSE_BUTTON_LEFT; case WM_RBUTTONDOWN: case WM_RBUTTONUP: return GLFW_MOUSE_BUTTON_RIGHT; case WM_MBUTTONDOWN: case WM_MBUTTONUP: return GLFW_MOUSE_BUTTON_MIDDLE; case WM_XBUTTONDOWN: case WM_XBUTTONUP: return GET_XBUTTON_WPARAM(wparam) == XBUTTON1 ? GLFW_MOUSE_BUTTON_4 : GLFW_MOUSE_BUTTON_5; default: return -1; } } void handle_mouse_button(GLFWwindow* window, UINT message, WPARAM wparam) { if (window == nullptr || window->mouse_button_callback == nullptr) { return; } const int button = map_mouse_button(message, wparam); if (button < 0) { return; } const bool is_down = message == WM_LBUTTONDOWN || message == WM_RBUTTONDOWN || message == WM_MBUTTONDOWN || message == WM_XBUTTONDOWN; window->mouse_button_state[button] = is_down ? GLFW_PRESS : GLFW_RELEASE; window->mouse_button_callback(window, button, is_down ? GLFW_PRESS : GLFW_RELEASE, current_mods()); } LRESULT CALLBACK window_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { auto* window = reinterpret_cast(GetWindowLongPtrW(hwnd, GWLP_USERDATA)); switch (message) { case WM_CLOSE: if (window != nullptr) { window->should_close = true; } return 0; case WM_KEYDOWN: case WM_SYSKEYDOWN: case WM_KEYUP: case WM_SYSKEYUP: handle_key(window, message, wparam, lparam); return 0; case WM_LBUTTONDOWN: case WM_LBUTTONUP: case WM_RBUTTONDOWN: case WM_RBUTTONUP: case WM_MBUTTONDOWN: case WM_MBUTTONUP: case WM_XBUTTONDOWN: case WM_XBUTTONUP: handle_mouse_button(window, message, wparam); return 0; case WM_MOUSEMOVE: if (window != nullptr) { window->cursor_x = static_cast(GET_X_LPARAM(lparam)); window->cursor_y = static_cast(GET_Y_LPARAM(lparam)); if (window->cursor_pos_callback != nullptr) { window->cursor_pos_callback(window, window->cursor_x, window->cursor_y); } } return 0; case WM_MOUSEWHEEL: if (window != nullptr && window->scroll_callback != nullptr) { const double delta = static_cast(GET_WHEEL_DELTA_WPARAM(wparam)) / static_cast(WHEEL_DELTA); window->scroll_callback(window, 0.0, delta); } return 0; default: break; } return DefWindowProcW(hwnd, message, wparam, lparam); } } // namespace extern "C" { int glfwInit(void) { if (g_initialized) { return GLFW_TRUE; } g_instance = GetModuleHandleW(nullptr); WNDCLASSEXW wc{}; wc.cbSize = sizeof(wc); wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; wc.lpfnWndProc = window_proc; wc.hInstance = g_instance; wc.hCursor = LoadCursorW(nullptr, MAKEINTRESOURCEW(32512)); wc.lpszClassName = kWindowClassName; if (RegisterClassExW(&wc) == 0) { return GLFW_FALSE; } g_initialized = true; return GLFW_TRUE; } void glfwTerminate(void) { g_initialized = false; } void glfwWindowHint(int hint, int value) { if (hint == GLFW_RESIZABLE) { g_resizable = value != GLFW_FALSE; } } GLFWwindow* glfwCreateWindow(int width, int height, const char* title, GLFWmonitor*, GLFWwindow*) { if (!g_initialized) { return nullptr; } const int wide_length = MultiByteToWideChar(CP_UTF8, 0, title, -1, nullptr, 0); std::vector wide_title(wide_length); MultiByteToWideChar(CP_UTF8, 0, title, -1, wide_title.data(), wide_length); DWORD style = WS_OVERLAPPEDWINDOW; if (!g_resizable) { style &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX); } RECT rect{0, 0, width, height}; AdjustWindowRectEx(&rect, style, FALSE, 0); HWND hwnd = CreateWindowExW( 0, kWindowClassName, wide_title.data(), style, CW_USEDEFAULT, CW_USEDEFAULT, rect.right - rect.left, rect.bottom - rect.top, nullptr, nullptr, g_instance, nullptr); if (hwnd == nullptr) { return nullptr; } auto* window = new GLFWwindow(); window->handle = hwnd; window->width = width; window->height = height; SetWindowLongPtrW(hwnd, GWLP_USERDATA, reinterpret_cast(window)); ShowWindow(hwnd, SW_SHOW); UpdateWindow(hwnd); return window; } void glfwDestroyWindow(GLFWwindow* window) { if (window == nullptr) { return; } if (window->handle != nullptr) { DestroyWindow(window->handle); } delete window; } void glfwSetWindowUserPointer(GLFWwindow* window, void* pointer) { if (window != nullptr) { window->user_pointer = pointer; } } void* glfwGetWindowUserPointer(GLFWwindow* window) { return window != nullptr ? window->user_pointer : nullptr; } GLFWkeyfun glfwSetKeyCallback(GLFWwindow* window, GLFWkeyfun callback) { if (window == nullptr) { return nullptr; } GLFWkeyfun previous = window->key_callback; window->key_callback = callback; return previous; } GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* window, GLFWmousebuttonfun callback) { if (window == nullptr) { return nullptr; } GLFWmousebuttonfun previous = window->mouse_button_callback; window->mouse_button_callback = callback; return previous; } GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* window, GLFWcursorposfun callback) { if (window == nullptr) { return nullptr; } GLFWcursorposfun previous = window->cursor_pos_callback; window->cursor_pos_callback = callback; return previous; } GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun callback) { if (window == nullptr) { return nullptr; } GLFWscrollfun previous = window->scroll_callback; window->scroll_callback = callback; return previous; } void glfwPollEvents(void) { MSG msg{}; while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessageW(&msg); } } int glfwWindowShouldClose(GLFWwindow* window) { return window != nullptr && window->should_close ? GLFW_TRUE : GLFW_FALSE; } int glfwGetKey(GLFWwindow* window, int key) { if (window == nullptr || key < 0 || key >= static_cast(window->key_state.size())) { return GLFW_RELEASE; } return window->key_state[key] != 0 ? GLFW_PRESS : GLFW_RELEASE; } int glfwGetMouseButton(GLFWwindow* window, int button) { if (window == nullptr || button < 0 || button >= static_cast(window->mouse_button_state.size())) { return GLFW_RELEASE; } return window->mouse_button_state[button] != 0 ? GLFW_PRESS : GLFW_RELEASE; } void glfwGetCursorPos(GLFWwindow* window, double* xpos, double* ypos) { if (xpos != nullptr) { *xpos = window != nullptr ? window->cursor_x : 0.0; } if (ypos != nullptr) { *ypos = window != nullptr ? window->cursor_y : 0.0; } } void glfwGetFramebufferSize(GLFWwindow* window, int* width, int* height) { if (window == nullptr || window->handle == nullptr) { return; } RECT client{}; GetClientRect(window->handle, &client); if (width != nullptr) { *width = client.right - client.left; } if (height != nullptr) { *height = client.bottom - client.top; } } const char** glfwGetRequiredInstanceExtensions(uint32_t* count) { static const char* extensions[] = { VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_WIN32_SURFACE_EXTENSION_NAME, }; if (count != nullptr) { *count = static_cast(sizeof(extensions) / sizeof(extensions[0])); } return extensions; } VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface) { if (instance == VK_NULL_HANDLE || window == nullptr || surface == nullptr) { return VK_ERROR_INITIALIZATION_FAILED; } auto create_surface = reinterpret_cast( vkGetInstanceProcAddr(instance, "vkCreateWin32SurfaceKHR")); if (create_surface == nullptr) { return VK_ERROR_EXTENSION_NOT_PRESENT; } VkWin32SurfaceCreateInfoKHR info{}; info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; info.hinstance = g_instance; info.hwnd = window->handle; return create_surface(instance, &info, allocator, surface); } } // extern "C"