Files
Boundard/src/glfw_win32_stub.cpp
T
2026-08-16 13:32:51 +08:00

416 lines
12 KiB
C++

// Minimal GLFW-compatible Win32 backend for cross-compiling the example.
// It implements only the subset used by src/render.cpp.
#include <windows.h>
#include <windowsx.h>
#include <array>
#include <cstdint>
#include <vector>
#define GLFW_INCLUDE_VULKAN
#define VK_USE_PLATFORM_WIN32_KHR
#include <GLFW/glfw3.h>
#include <vulkan/vulkan.h>
#include <vulkan/vulkan_win32.h>
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<unsigned char, 1024> key_state{};
std::array<unsigned char, 8> 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<int>(vk - 'A');
}
if (vk >= '0' && vk <= '9') {
return GLFW_KEY_0 + static_cast<int>(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<int>((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<int>(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<GLFWwindow*>(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<double>(GET_X_LPARAM(lparam));
window->cursor_y = static_cast<double>(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<double>(GET_WHEEL_DELTA_WPARAM(wparam)) /
static_cast<double>(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<wchar_t> 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<LONG_PTR>(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<int>(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<int>(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<uint32_t>(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<PFN_vkCreateWin32SurfaceKHR>(
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"