new input system
This commit is contained in:
parent
b3db40ebec
commit
ac2eb7fb0a
31 changed files with 3292 additions and 781 deletions
|
|
@ -56,6 +56,7 @@ module;
|
|||
module Crafter.Graphics:Window_impl;
|
||||
import :Window;
|
||||
import :Device;
|
||||
import :Gamepad;
|
||||
import :VulkanTransition;
|
||||
import :DescriptorHeapVulkan;
|
||||
import :RenderPass;
|
||||
|
|
@ -110,131 +111,15 @@ int create_shm_file(off_t size) {
|
|||
#endif
|
||||
|
||||
#ifdef CRAFTER_GRAPHICS_WINDOW_WIN32
|
||||
CrafterKeys vk_to_crafter_key(WPARAM vk)
|
||||
{
|
||||
switch (vk)
|
||||
{
|
||||
// Alphabet
|
||||
case 'A': return CrafterKeys::A;
|
||||
case 'B': return CrafterKeys::B;
|
||||
case 'C': return CrafterKeys::C;
|
||||
case 'D': return CrafterKeys::D;
|
||||
case 'E': return CrafterKeys::E;
|
||||
case 'F': return CrafterKeys::F;
|
||||
case 'G': return CrafterKeys::G;
|
||||
case 'H': return CrafterKeys::H;
|
||||
case 'I': return CrafterKeys::I;
|
||||
case 'J': return CrafterKeys::J;
|
||||
case 'K': return CrafterKeys::K;
|
||||
case 'L': return CrafterKeys::L;
|
||||
case 'M': return CrafterKeys::M;
|
||||
case 'N': return CrafterKeys::N;
|
||||
case 'O': return CrafterKeys::O;
|
||||
case 'P': return CrafterKeys::P;
|
||||
case 'Q': return CrafterKeys::Q;
|
||||
case 'R': return CrafterKeys::R;
|
||||
case 'S': return CrafterKeys::S;
|
||||
case 'T': return CrafterKeys::T;
|
||||
case 'U': return CrafterKeys::U;
|
||||
case 'V': return CrafterKeys::V;
|
||||
case 'W': return CrafterKeys::W;
|
||||
case 'X': return CrafterKeys::X;
|
||||
case 'Y': return CrafterKeys::Y;
|
||||
case 'Z': return CrafterKeys::Z;
|
||||
|
||||
// Numbers
|
||||
case '0': return CrafterKeys::_0;
|
||||
case '1': return CrafterKeys::_1;
|
||||
case '2': return CrafterKeys::_2;
|
||||
case '3': return CrafterKeys::_3;
|
||||
case '4': return CrafterKeys::_4;
|
||||
case '5': return CrafterKeys::_5;
|
||||
case '6': return CrafterKeys::_6;
|
||||
case '7': return CrafterKeys::_7;
|
||||
case '8': return CrafterKeys::_8;
|
||||
case '9': return CrafterKeys::_9;
|
||||
|
||||
// Function keys
|
||||
case VK_F1: return CrafterKeys::F1;
|
||||
case VK_F2: return CrafterKeys::F2;
|
||||
case VK_F3: return CrafterKeys::F3;
|
||||
case VK_F4: return CrafterKeys::F4;
|
||||
case VK_F5: return CrafterKeys::F5;
|
||||
case VK_F6: return CrafterKeys::F6;
|
||||
case VK_F7: return CrafterKeys::F7;
|
||||
case VK_F8: return CrafterKeys::F8;
|
||||
case VK_F9: return CrafterKeys::F9;
|
||||
case VK_F10: return CrafterKeys::F10;
|
||||
case VK_F11: return CrafterKeys::F11;
|
||||
case VK_F12: return CrafterKeys::F12;
|
||||
|
||||
// Control keys
|
||||
case VK_ESCAPE: return CrafterKeys::Escape;
|
||||
case VK_TAB: return CrafterKeys::Tab;
|
||||
case VK_RETURN: return CrafterKeys::Enter;
|
||||
case VK_SPACE: return CrafterKeys::Space;
|
||||
case VK_BACK: return CrafterKeys::Backspace;
|
||||
case VK_DELETE: return CrafterKeys::Delete;
|
||||
case VK_INSERT: return CrafterKeys::Insert;
|
||||
case VK_HOME: return CrafterKeys::Home;
|
||||
case VK_END: return CrafterKeys::End;
|
||||
case VK_PRIOR: return CrafterKeys::PageUp;
|
||||
case VK_NEXT: return CrafterKeys::PageDown;
|
||||
case VK_CAPITAL: return CrafterKeys::CapsLock;
|
||||
case VK_NUMLOCK: return CrafterKeys::NumLock;
|
||||
case VK_SCROLL: return CrafterKeys::ScrollLock;
|
||||
|
||||
// Modifiers
|
||||
case VK_SHIFT: return CrafterKeys::LeftShift;
|
||||
case VK_LSHIFT: return CrafterKeys::LeftShift;
|
||||
case VK_RSHIFT: return CrafterKeys::RightShift;
|
||||
case VK_CONTROL: return CrafterKeys::LeftCtrl;
|
||||
case VK_LCONTROL: return CrafterKeys::LeftCtrl;
|
||||
case VK_RCONTROL: return CrafterKeys::RightCtrl;
|
||||
case VK_LMENU: return CrafterKeys::LeftAlt;
|
||||
case VK_RMENU: return CrafterKeys::RightAlt;
|
||||
case VK_LWIN: return CrafterKeys::LeftSuper;
|
||||
case VK_RWIN: return CrafterKeys::RightSuper;
|
||||
|
||||
// Arrows
|
||||
case VK_UP: return CrafterKeys::Up;
|
||||
case VK_DOWN: return CrafterKeys::Down;
|
||||
case VK_LEFT: return CrafterKeys::Left;
|
||||
case VK_RIGHT: return CrafterKeys::Right;
|
||||
|
||||
// Keypad
|
||||
case VK_NUMPAD0: return CrafterKeys::keypad_0;
|
||||
case VK_NUMPAD1: return CrafterKeys::keypad_1;
|
||||
case VK_NUMPAD2: return CrafterKeys::keypad_2;
|
||||
case VK_NUMPAD3: return CrafterKeys::keypad_3;
|
||||
case VK_NUMPAD4: return CrafterKeys::keypad_4;
|
||||
case VK_NUMPAD5: return CrafterKeys::keypad_5;
|
||||
case VK_NUMPAD6: return CrafterKeys::keypad_6;
|
||||
case VK_NUMPAD7: return CrafterKeys::keypad_7;
|
||||
case VK_NUMPAD8: return CrafterKeys::keypad_8;
|
||||
case VK_NUMPAD9: return CrafterKeys::keypad_9;
|
||||
case VK_SEPARATOR: return CrafterKeys::keypad_enter;
|
||||
case VK_ADD: return CrafterKeys::keypad_plus;
|
||||
case VK_SUBTRACT: return CrafterKeys::keypad_minus;
|
||||
case VK_MULTIPLY: return CrafterKeys::keypad_multiply;
|
||||
case VK_DIVIDE: return CrafterKeys::keypad_divide;
|
||||
case VK_DECIMAL: return CrafterKeys::keypad_decimal;
|
||||
|
||||
// Punctuation
|
||||
case VK_OEM_3: return CrafterKeys::grave; // `
|
||||
case VK_OEM_MINUS: return CrafterKeys::minus; // -
|
||||
case VK_OEM_PLUS: return CrafterKeys::equal; // =
|
||||
case VK_OEM_4: return CrafterKeys::bracket_left; // [
|
||||
case VK_OEM_6: return CrafterKeys::bracket_right; // ]
|
||||
case VK_OEM_5: return CrafterKeys::backslash; //
|
||||
case VK_OEM_1: return CrafterKeys::semicolon; // ;
|
||||
case VK_OEM_7: return CrafterKeys::quote; // '
|
||||
case VK_OEM_COMMA:return CrafterKeys::comma; // ,
|
||||
case VK_OEM_PERIOD:return CrafterKeys::period; // .
|
||||
case VK_OEM_2: return CrafterKeys::slash; // /
|
||||
|
||||
default: throw std::runtime_error(std::format("Unkown VK {}", vk));
|
||||
}
|
||||
// Extract the layout-independent raw key code from a WM_KEY* lParam. Bits
|
||||
// 16-23 hold the PS/2 set-1 scancode byte; bit 24 is the extended-key flag
|
||||
// (the 0xE0-prefixed variants — RightCtrl, RightAlt, the cursor cluster,
|
||||
// keypad Enter/Slash, the Windows keys). We pack the extended flag into bit
|
||||
// 8 of the returned KeyCode so it round-trips with the compile-time
|
||||
// `Key(CrafterKeys::...)` table in :Keys.
|
||||
static inline KeyCode KeyCodeFromLParam(LPARAM lParam) {
|
||||
return ((KeyCode)((lParam >> 16) & 0xFF))
|
||||
| (((lParam >> 24) & 1u) << 8);
|
||||
}
|
||||
|
||||
// Define a window class name
|
||||
|
|
@ -266,28 +151,36 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
|||
PostQuitMessage(0);
|
||||
break;
|
||||
}
|
||||
case WM_SIZE: {
|
||||
// SIZE_MINIMIZED reports (0, 0) — Resize() short-circuits, so
|
||||
// we just propagate the values directly. WM_SIZE fires
|
||||
// synchronously during a drag-resize loop; the StartSync
|
||||
// pump runs WndProc between frames, so the swapchain is
|
||||
// never touched mid-Render.
|
||||
if (window) {
|
||||
window->Resize(LOWORD(lParam), HIWORD(lParam));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_KEYDOWN:
|
||||
case WM_SYSKEYDOWN: { // SYSKEYDOWN catches Alt combos, F10, etc.
|
||||
CrafterKeys crafterKey = vk_to_crafter_key(wParam);
|
||||
KeyCode code = KeyCodeFromLParam(lParam);
|
||||
bool isRepeat = (lParam & (1 << 30)) != 0;
|
||||
|
||||
if (isRepeat) {
|
||||
window->onKeyHold[(uint8_t)crafterKey].Invoke();
|
||||
window->onAnyKeyHold.Invoke(crafterKey);
|
||||
window->onRawKeyHold.Invoke(code);
|
||||
} else {
|
||||
window->heldkeys[(uint8_t)crafterKey] = true;
|
||||
window->onKeyDown[(uint8_t)crafterKey].Invoke();
|
||||
window->onAnyKeyDown.Invoke(crafterKey);
|
||||
window->heldKeys.insert(code);
|
||||
window->onRawKeyDown.Invoke(code);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case WM_KEYUP:
|
||||
case WM_SYSKEYUP: {
|
||||
CrafterKeys crafterKey = vk_to_crafter_key(wParam);
|
||||
window->heldkeys[(uint8_t)crafterKey] = false;
|
||||
window->onKeyUp[(uint8_t)crafterKey].Invoke();
|
||||
window->onAnyKeyUp.Invoke(crafterKey);
|
||||
KeyCode code = KeyCodeFromLParam(lParam);
|
||||
window->heldKeys.erase(code);
|
||||
window->onRawKeyUp.Invoke(code);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -510,6 +403,93 @@ Window::Window(std::uint32_t width, std::uint32_t height) : width(width), height
|
|||
currentMousePos = {0,0};
|
||||
}
|
||||
|
||||
void Window::Resize(std::uint32_t newWidth, std::uint32_t newHeight) {
|
||||
// Skip degenerate resizes. Win32 minimised windows give (0, 0); Wayland
|
||||
// sometimes echoes the current size in a configure.
|
||||
if (newWidth == 0 || newHeight == 0) return;
|
||||
if (newWidth == width && newHeight == height) return;
|
||||
// Win32 fires WM_SIZE synchronously inside CreateWindowEx (before the
|
||||
// constructor's CreateSwapchain). Defer the first resize to that
|
||||
// CreateSwapchain call instead of trying to recreate a non-existent
|
||||
// swapchain.
|
||||
if (swapChain == VK_NULL_HANDLE) {
|
||||
width = newWidth;
|
||||
height = newHeight;
|
||||
return;
|
||||
}
|
||||
|
||||
width = newWidth;
|
||||
height = newHeight;
|
||||
|
||||
// Caller (configure handler / WM_SIZE) runs between frames, but be
|
||||
// defensive: ensure no in-flight commands reference the old swapchain.
|
||||
Device::CheckVkResult(vkQueueWaitIdle(Device::queue));
|
||||
|
||||
#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND
|
||||
if (wpViewport) {
|
||||
wp_viewport_set_destination(wpViewport,
|
||||
static_cast<int>(std::ceil(width / scale)),
|
||||
static_cast<int>(std::ceil(height / scale)));
|
||||
}
|
||||
#endif
|
||||
|
||||
RecreateSwapchainAndImages();
|
||||
onResize.Invoke();
|
||||
}
|
||||
|
||||
void Window::RecreateSwapchainAndImages() {
|
||||
CreateSwapchain();
|
||||
|
||||
// CreateSwapchain leaves new swapchain images in VK_IMAGE_LAYOUT_UNDEFINED.
|
||||
// Render() barriers from PRESENT_SRC_KHR, so transition them now to
|
||||
// match. Mirrors the StartInit logic, on a one-shot command buffer.
|
||||
{
|
||||
VkCommandBufferAllocateInfo cba{
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
||||
.commandPool = Device::commandPool,
|
||||
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
||||
.commandBufferCount = 1,
|
||||
};
|
||||
VkCommandBuffer cmd = VK_NULL_HANDLE;
|
||||
Device::CheckVkResult(vkAllocateCommandBuffers(Device::device, &cba, &cmd));
|
||||
|
||||
VkCommandBufferBeginInfo cbi{
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
|
||||
};
|
||||
Device::CheckVkResult(vkBeginCommandBuffer(cmd, &cbi));
|
||||
|
||||
VkImageSubresourceRange range{
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = VK_REMAINING_MIP_LEVELS,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||
};
|
||||
for (std::uint32_t i = 0; i < numFrames; i++) {
|
||||
image_layout_transition(cmd,
|
||||
images[i],
|
||||
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||||
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
|
||||
0, 0,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
||||
range);
|
||||
}
|
||||
|
||||
Device::CheckVkResult(vkEndCommandBuffer(cmd));
|
||||
|
||||
VkSubmitInfo si{
|
||||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||
.commandBufferCount = 1,
|
||||
.pCommandBuffers = &cmd,
|
||||
};
|
||||
Device::CheckVkResult(vkQueueSubmit(Device::queue, 1, &si, VK_NULL_HANDLE));
|
||||
Device::CheckVkResult(vkQueueWaitIdle(Device::queue));
|
||||
vkFreeCommandBuffers(Device::device, Device::commandPool, 1, &cmd);
|
||||
}
|
||||
}
|
||||
|
||||
void Window::SetTitle(const std::string_view title) {
|
||||
#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND
|
||||
xdg_toplevel_set_title(xdgToplevel, title.data());
|
||||
|
|
@ -630,6 +610,7 @@ void Window::SetDefaultCursor() {
|
|||
void Window::StartSync() {
|
||||
#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND
|
||||
while (open && wl_display_dispatch(Device::display) != -1) {
|
||||
Gamepad::Tick();
|
||||
onBeforeUpdate.Invoke();
|
||||
}
|
||||
#endif
|
||||
|
|
@ -640,6 +621,7 @@ void Window::StartSync() {
|
|||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
Gamepad::Tick();
|
||||
onBeforeUpdate.Invoke();
|
||||
if(updating) {
|
||||
Update();
|
||||
|
|
@ -697,8 +679,23 @@ void Window::Update() {
|
|||
}
|
||||
|
||||
void Window::Render() {
|
||||
// Acquire the next image from the swap chain
|
||||
Device::CheckVkResult(vkAcquireNextImageKHR(Device::device, swapChain, UINT64_MAX, semaphores.presentComplete, (VkFence)nullptr, ¤tBuffer));
|
||||
// Acquire the next image from the swap chain. If the surface has
|
||||
// changed size out from under us (compositor/Win32 resize delivered
|
||||
// between Render calls), recreate and retry once.
|
||||
{
|
||||
VkResult acquire = vkAcquireNextImageKHR(Device::device, swapChain, UINT64_MAX,
|
||||
semaphores.presentComplete, (VkFence)nullptr, ¤tBuffer);
|
||||
if (acquire == VK_ERROR_OUT_OF_DATE_KHR) {
|
||||
Device::CheckVkResult(vkQueueWaitIdle(Device::queue));
|
||||
RecreateSwapchainAndImages();
|
||||
onResize.Invoke();
|
||||
acquire = vkAcquireNextImageKHR(Device::device, swapChain, UINT64_MAX,
|
||||
semaphores.presentComplete, (VkFence)nullptr, ¤tBuffer);
|
||||
}
|
||||
if (acquire != VK_SUBOPTIMAL_KHR) {
|
||||
Device::CheckVkResult(acquire);
|
||||
}
|
||||
}
|
||||
submitInfo.commandBufferCount = 1;
|
||||
submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
|
||||
|
||||
|
|
@ -822,8 +819,13 @@ void Window::Render() {
|
|||
}
|
||||
|
||||
VkResult result = vkQueuePresentKHR(Device::queue, &presentInfo);
|
||||
if(result == VK_SUBOPTIMAL_KHR) {
|
||||
CreateSwapchain();
|
||||
if (result == VK_SUBOPTIMAL_KHR || result == VK_ERROR_OUT_OF_DATE_KHR) {
|
||||
// Surface size changed mid-present. Drain the queue, rebuild the
|
||||
// swapchain, and let dependents (descriptors holding old image
|
||||
// handles) re-bind via onResize before the next frame.
|
||||
Device::CheckVkResult(vkQueueWaitIdle(Device::queue));
|
||||
RecreateSwapchainAndImages();
|
||||
onResize.Invoke();
|
||||
} else {
|
||||
Device::CheckVkResult(result);
|
||||
}
|
||||
|
|
@ -1067,8 +1069,13 @@ void Window::wl_surface_frame_done(void* data, struct wl_callback *cb, uint32_t
|
|||
}
|
||||
|
||||
|
||||
void Window::xdg_toplevel_configure(void*, xdg_toplevel*, std::int32_t, std::int32_t, wl_array*){
|
||||
|
||||
void Window::xdg_toplevel_configure(void* data, xdg_toplevel*, std::int32_t width, std::int32_t height, wl_array*){
|
||||
// xdg-shell batches state — width/height are pending until the matching
|
||||
// xdg_surface.configure arrives. Width/height are in surface-local
|
||||
// (logical DP) units; (0, 0) means "compositor has no preference".
|
||||
Window* window = reinterpret_cast<Window*>(data);
|
||||
window->pendingLogicalWidth = width;
|
||||
window->pendingLogicalHeight = height;
|
||||
}
|
||||
|
||||
void Window::xdg_toplevel_handle_close(void* data, xdg_toplevel*) {
|
||||
|
|
@ -1083,9 +1090,20 @@ void Window::xdg_surface_handle_configure(void* data, xdg_surface* xdg_surface,
|
|||
xdg_surface_ack_configure(xdg_surface, serial);
|
||||
|
||||
if (window->configured) {
|
||||
// If this isn't the first configure event we've received, we already
|
||||
// have a buffer attached, so no need to do anything. Commit the
|
||||
// surface to apply the configure acknowledgement.
|
||||
// Subsequent configure: if the toplevel asked for a new size
|
||||
// (non-zero, different from current), drive the resize end-to-end.
|
||||
// (0, 0) means "compositor has no preference, keep current size".
|
||||
// The swapchain may not exist yet on the very first frame between
|
||||
// the constructor's wait loop and CreateSwapchain — the Resize
|
||||
// guard against equal sizes already covers that path.
|
||||
if (window->pendingLogicalWidth > 0 && window->pendingLogicalHeight > 0 &&
|
||||
window->swapChain != VK_NULL_HANDLE) {
|
||||
std::uint32_t newWidth = static_cast<std::uint32_t>(
|
||||
std::ceil(window->pendingLogicalWidth * window->scale));
|
||||
std::uint32_t newHeight = static_cast<std::uint32_t>(
|
||||
std::ceil(window->pendingLogicalHeight * window->scale));
|
||||
window->Resize(newWidth, newHeight);
|
||||
}
|
||||
wl_surface_commit(window->surface);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue