diff --git a/implementations/Crafter.Graphics-Device.cpp b/implementations/Crafter.Graphics-Device.cpp index 8258218..f9414c6 100644 --- a/implementations/Crafter.Graphics-Device.cpp +++ b/implementations/Crafter.Graphics-Device.cpp @@ -350,6 +350,7 @@ namespace { if (free != nullptr) { free->used = true; free->id = id; + free->window = nullptr; } return free; } @@ -384,10 +385,17 @@ void Device::text_input_enter(void*, zwp_text_input_v3*, wl_surface* surface) { if (textInputWindow != nullptr && textInputWindow->textInputActive_) ApplyTextInput(textInputWindow); } +// The compositor's leave ends the enable and the composition: the window +// hears that its composing text is gone, and the next enter enables anew. void Device::text_input_leave(void*, zwp_text_input_v3*, wl_surface*) { + Window* window = textInputWindow; textInputWindow = nullptr; textInputHasPreedit = textInputHasCommit = textInputHasDelete = false; TextStrings().preedit.clear(); + if (window != nullptr) { + window->textInputEnabled_ = false; + if (!TextStrings().shown.empty()) window->onPreedit.Invoke(std::string_view()); + } TextStrings().shown.clear(); } @@ -427,10 +435,13 @@ void Device::text_input_done(void*, zwp_text_input_v3*, std::uint32_t) { strings.commit.clear(); } +// enable resets the input method's state, so it goes once per focus; a +// caret that moved sends its rectangle alone. void Device::ApplyTextInput(Window* window) { if (textInput == nullptr || window == nullptr || textInputWindow != window) return; if (window->textInputActive_) { - zwp_text_input_v3_enable(textInput); + if (!window->textInputEnabled_) zwp_text_input_v3_enable(textInput); + window->textInputEnabled_ = true; const bool phone = window->textInputPurpose_ == TextInputPurpose::Phone; zwp_text_input_v3_set_content_type(textInput, phone ? ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE : (ZWP_TEXT_INPUT_V3_CONTENT_HINT_COMPLETION | ZWP_TEXT_INPUT_V3_CONTENT_HINT_SPELLCHECK | ZWP_TEXT_INPUT_V3_CONTENT_HINT_AUTO_CAPITALIZATION), @@ -440,6 +451,8 @@ void Device::ApplyTextInput(Window* window) { static_cast(window->textInputRect_[0] / s), static_cast(window->textInputRect_[1] / s), std::max(1, static_cast(window->textInputRect_[2] / s)), std::max(1, static_cast(window->textInputRect_[3] / s))); } else { + if (!window->textInputEnabled_) return; + window->textInputEnabled_ = false; zwp_text_input_v3_disable(textInput); } zwp_text_input_v3_commit(textInput); @@ -685,6 +698,7 @@ void Device::touch_down(void*, wl_touch*, std::uint32_t serial, std::uint32_t, w if (TouchSlot* slot = SlotFor(id)) { slot->x = pos.x; slot->y = pos.y; + slot->window = window; } window->onTouchDown.Invoke({ id, pos }); if (window->primaryTouchId < 0) { @@ -701,6 +715,7 @@ void Device::touch_up(void*, wl_touch*, std::uint32_t, std::uint32_t, std::int32 Window* window = touchWindow; Vector pos = window->currentMousePos; if (TouchSlot* slot = SlotFor(id)) { + if (slot->window != nullptr) window = slot->window; pos = Vector(slot->x, slot->y); *slot = TouchSlot{}; } @@ -716,8 +731,10 @@ void Device::touch_up(void*, wl_touch*, std::uint32_t, std::uint32_t, std::int32 void Device::touch_motion(void*, wl_touch*, std::uint32_t, std::int32_t id, wl_fixed_t x, wl_fixed_t y) { if (touchWindow == nullptr) return; Window* window = touchWindow; + TouchSlot* slot = SlotFor(id); + if (slot != nullptr && slot->window != nullptr) window = slot->window; const Vector pos = Vector(wl_fixed_to_double(x), wl_fixed_to_double(y)) * window->scale; - if (TouchSlot* slot = SlotFor(id)) { + if (slot != nullptr) { slot->x = pos.x; slot->y = pos.y; } @@ -743,19 +760,20 @@ void Device::touch_cancel(void*, wl_touch*) { window->onTouchCancel.Invoke(); } -void Device::seat_handle_capabilities(void* data, wl_seat* seat, uint32_t capabilities) { - seat = seat; - if (capabilities & WL_SEAT_CAPABILITY_POINTER) { - wl_pointer* pointer = wl_seat_get_pointer(seat); - wl_pointer_add_listener(pointer, &pointer_listener, nullptr); +// Capabilities come again when a device appears or goes; each object is +// created once, or every event would arrive once per copy. +void Device::seat_handle_capabilities(void*, wl_seat* seat, uint32_t capabilities) { + if ((capabilities & WL_SEAT_CAPABILITY_POINTER) && seatPointer == nullptr) { + seatPointer = wl_seat_get_pointer(seat); + wl_pointer_add_listener(seatPointer, &pointer_listener, nullptr); } if ((capabilities & WL_SEAT_CAPABILITY_TOUCH) && wlTouch == nullptr) { wlTouch = wl_seat_get_touch(seat); wl_touch_add_listener(wlTouch, &touch_listener, nullptr); } - if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) { - wl_keyboard* keyboard = wl_seat_get_keyboard(seat); - wl_keyboard_add_listener(keyboard, &keyboard_listener, nullptr); + if ((capabilities & WL_SEAT_CAPABILITY_KEYBOARD) && seatKeyboard == nullptr) { + seatKeyboard = wl_seat_get_keyboard(seat); + wl_keyboard_add_listener(seatKeyboard, &keyboard_listener, nullptr); } } diff --git a/interfaces/Crafter.Graphics-Device.cppm b/interfaces/Crafter.Graphics-Device.cppm index 7fbad00..30e7959 100644 --- a/interfaces/Crafter.Graphics-Device.cppm +++ b/interfaces/Crafter.Graphics-Device.cppm @@ -61,6 +61,8 @@ export namespace Crafter { inline static xkb_state* xkb_state; inline static std::vector windows; inline static wl_pointer* wlPointer; + inline static wl_pointer* seatPointer = nullptr; + inline static wl_keyboard* seatKeyboard = nullptr; inline static wl_touch* wlTouch = nullptr; inline static Window* touchWindow = nullptr; // the window under the fingers // The fingers down, by the compositor's id, in physical pixels. Plain @@ -70,6 +72,7 @@ export namespace Crafter { float x; float y; bool used; + Window* window; }; inline static std::array touchSlots{}; static void ForgetTouches(); diff --git a/interfaces/Crafter.Graphics-Window.cppm b/interfaces/Crafter.Graphics-Window.cppm index 03a5710..1426cd0 100644 --- a/interfaces/Crafter.Graphics-Window.cppm +++ b/interfaces/Crafter.Graphics-Window.cppm @@ -234,6 +234,7 @@ export namespace Crafter { // mid-session (the hotspot only updates when set_cursor is recalled). std::uint32_t lastPointerSerial_ = 0; bool textInputActive_ = false; + bool textInputEnabled_ = false; // enable sent and not taken back since std::int32_t textInputRect_[4] = { 0, 0, 0, 0 }; TextInputPurpose textInputPurpose_ = TextInputPurpose::Normal;