diff --git a/implementations/Crafter.Graphics-Device.cpp b/implementations/Crafter.Graphics-Device.cpp index 138107c..d2344ce 100644 --- a/implementations/Crafter.Graphics-Device.cpp +++ b/implementations/Crafter.Graphics-Device.cpp @@ -441,11 +441,23 @@ void Device::keyboard_keymap(void *data, wl_keyboard *keyboard, uint32_t format, } void Device::keyboard_enter(void *data, wl_keyboard *keyboard, uint32_t serial, wl_surface *surface, wl_array *keys) { - + for (Window* window : windows) { + if (window->surface == surface) { + keyboardWindow = window; + return; + } + } + keyboardWindow = nullptr; } void Device::keyboard_leave(void *data, wl_keyboard *keyboard, uint32_t serial, wl_surface *surface) { - + // Releases happen elsewhere while the focus is away: nothing we still + // hold is held, and a repeat must not keep firing into a window that + // lost the keyboard. + if (keyboardWindow != nullptr) keyboardWindow->heldKeys.clear(); + keyboardWindow = nullptr; + keyRepeat.active = false; + keyRepeat.utf8.clear(); } void Device::keyboard_key(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { @@ -456,12 +468,16 @@ void Device::keyboard_key(void *data, wl_keyboard *keyboard, uint32_t serial, ui KeyCode code = key; xkb_keycode_t xkbKeycode = key + 8; + // No enter for any of our surfaces (a compositor quirk, or a key for a + // window already gone): nowhere to deliver it. + if (keyboardWindow == nullptr) return; + if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { - if (focusedWindow->heldKeys.contains(code)) { - focusedWindow->onRawKeyHold.Invoke(code); + if (keyboardWindow->heldKeys.contains(code)) { + keyboardWindow->onRawKeyHold.Invoke(code); } else { - focusedWindow->heldKeys.insert(code); - focusedWindow->onRawKeyDown.Invoke(code); + keyboardWindow->heldKeys.insert(code); + keyboardWindow->onRawKeyDown.Invoke(code); } std::string buf; @@ -471,7 +487,7 @@ void Device::keyboard_key(void *data, wl_keyboard *keyboard, uint32_t serial, ui if (n > 0 && (unsigned char)buf[0] >= 0x20 && buf[0] != 0x7f) { buf.resize(n); utf8 = buf; - focusedWindow->onTextInput.Invoke(utf8); + keyboardWindow->onTextInput.Invoke(utf8); } // Replace the active repeat with this key — most recent press wins, @@ -482,8 +498,8 @@ void Device::keyboard_key(void *data, wl_keyboard *keyboard, uint32_t serial, ui keyRepeat.pressTime = std::chrono::steady_clock::now(); keyRepeat.lastFireTime = keyRepeat.pressTime; } else { - focusedWindow->heldKeys.erase(code); - focusedWindow->onRawKeyUp.Invoke(code); + keyboardWindow->heldKeys.erase(code); + keyboardWindow->onRawKeyUp.Invoke(code); // If the released key was the one repeating, stop. Otherwise leave // the existing repeat alone (user pressed/released a modifier @@ -506,7 +522,7 @@ void Device::keyboard_repeat_info(void *data, wl_keyboard *keyboard, int32_t rat } void Device::TickKeyRepeats() { - if (!keyRepeat.active || !focusedWindow) return; + if (!keyRepeat.active || !keyboardWindow) return; if (keyRepeat.rate <= 0) return; auto now = std::chrono::steady_clock::now(); @@ -521,10 +537,10 @@ void Device::TickKeyRepeats() { // Catch up — emit one event per missed period so a paused frame doesn't // make the repeat permanently lag behind. while (now - keyRepeat.lastFireTime >= period) { - focusedWindow->onRawKeyDown.Invoke(keyRepeat.key); - focusedWindow->onRawKeyHold.Invoke(keyRepeat.key); + keyboardWindow->onRawKeyDown.Invoke(keyRepeat.key); + keyboardWindow->onRawKeyHold.Invoke(keyRepeat.key); if (!keyRepeat.utf8.empty()) { - focusedWindow->onTextInput.Invoke(keyRepeat.utf8); + keyboardWindow->onTextInput.Invoke(keyRepeat.utf8); } keyRepeat.lastFireTime += period; } diff --git a/interfaces/Crafter.Graphics-Device.cppm b/interfaces/Crafter.Graphics-Device.cppm index d461eb6..be42e9e 100644 --- a/interfaces/Crafter.Graphics-Device.cppm +++ b/interfaces/Crafter.Graphics-Device.cppm @@ -49,6 +49,11 @@ export namespace Crafter { inline static xkb_keymap* xkb_keymap; inline static wl_compositor* compositor = nullptr; inline static Window* focusedWindow = nullptr; + // The window with keyboard focus (wl_keyboard.enter/leave), apart from + // the pointer's: a key arrives without the pointer ever having entered + // (a touch phone, a key typed while the pointer hovers elsewhere), so the + // keyboard handlers route by this and never by focusedWindow. + inline static Window* keyboardWindow = nullptr; inline static wl_shm* shm = nullptr; inline static wp_viewporter* wpViewporter = nullptr; inline static xkb_context* xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);