fix(wayland): text input enables once per focus; touches route by their finger's window
enable resets the input method's state, so sending it with every caret
move (an application keeps the cursor rectangle current while typing)
dropped the composition at each keystroke: enable now goes once per
focus and a moved caret sends its rectangle alone; disable only takes
back an enable. The compositor's leave ends both, and a window still
showing composing text hears onPreedit("") then. A finger's up and motion
go to the window it went down on, not the window of the latest down. The
seat creates its pointer and keyboard once, as it already did the touch:
capabilities come again when a device appears or goes, and a second
keyboard proxy delivered every key twice.
This commit is contained in:
parent
c8b5739e1a
commit
1498a5bf4b
3 changed files with 32 additions and 10 deletions
|
|
@ -350,6 +350,7 @@ namespace {
|
||||||
if (free != nullptr) {
|
if (free != nullptr) {
|
||||||
free->used = true;
|
free->used = true;
|
||||||
free->id = id;
|
free->id = id;
|
||||||
|
free->window = nullptr;
|
||||||
}
|
}
|
||||||
return free;
|
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);
|
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*) {
|
void Device::text_input_leave(void*, zwp_text_input_v3*, wl_surface*) {
|
||||||
|
Window* window = textInputWindow;
|
||||||
textInputWindow = nullptr;
|
textInputWindow = nullptr;
|
||||||
textInputHasPreedit = textInputHasCommit = textInputHasDelete = false;
|
textInputHasPreedit = textInputHasCommit = textInputHasDelete = false;
|
||||||
TextStrings().preedit.clear();
|
TextStrings().preedit.clear();
|
||||||
|
if (window != nullptr) {
|
||||||
|
window->textInputEnabled_ = false;
|
||||||
|
if (!TextStrings().shown.empty()) window->onPreedit.Invoke(std::string_view());
|
||||||
|
}
|
||||||
TextStrings().shown.clear();
|
TextStrings().shown.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -427,10 +435,13 @@ void Device::text_input_done(void*, zwp_text_input_v3*, std::uint32_t) {
|
||||||
strings.commit.clear();
|
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) {
|
void Device::ApplyTextInput(Window* window) {
|
||||||
if (textInput == nullptr || window == nullptr || textInputWindow != window) return;
|
if (textInput == nullptr || window == nullptr || textInputWindow != window) return;
|
||||||
if (window->textInputActive_) {
|
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;
|
const bool phone = window->textInputPurpose_ == TextInputPurpose::Phone;
|
||||||
zwp_text_input_v3_set_content_type(textInput,
|
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),
|
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<std::int32_t>(window->textInputRect_[0] / s), static_cast<std::int32_t>(window->textInputRect_[1] / s),
|
static_cast<std::int32_t>(window->textInputRect_[0] / s), static_cast<std::int32_t>(window->textInputRect_[1] / s),
|
||||||
std::max(1, static_cast<std::int32_t>(window->textInputRect_[2] / s)), std::max(1, static_cast<std::int32_t>(window->textInputRect_[3] / s)));
|
std::max(1, static_cast<std::int32_t>(window->textInputRect_[2] / s)), std::max(1, static_cast<std::int32_t>(window->textInputRect_[3] / s)));
|
||||||
} else {
|
} else {
|
||||||
|
if (!window->textInputEnabled_) return;
|
||||||
|
window->textInputEnabled_ = false;
|
||||||
zwp_text_input_v3_disable(textInput);
|
zwp_text_input_v3_disable(textInput);
|
||||||
}
|
}
|
||||||
zwp_text_input_v3_commit(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)) {
|
if (TouchSlot* slot = SlotFor(id)) {
|
||||||
slot->x = pos.x;
|
slot->x = pos.x;
|
||||||
slot->y = pos.y;
|
slot->y = pos.y;
|
||||||
|
slot->window = window;
|
||||||
}
|
}
|
||||||
window->onTouchDown.Invoke({ id, pos });
|
window->onTouchDown.Invoke({ id, pos });
|
||||||
if (window->primaryTouchId < 0) {
|
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;
|
Window* window = touchWindow;
|
||||||
Vector<float, 2> pos = window->currentMousePos;
|
Vector<float, 2> pos = window->currentMousePos;
|
||||||
if (TouchSlot* slot = SlotFor(id)) {
|
if (TouchSlot* slot = SlotFor(id)) {
|
||||||
|
if (slot->window != nullptr) window = slot->window;
|
||||||
pos = Vector<float, 2>(slot->x, slot->y);
|
pos = Vector<float, 2>(slot->x, slot->y);
|
||||||
*slot = TouchSlot{};
|
*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) {
|
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;
|
if (touchWindow == nullptr) return;
|
||||||
Window* window = touchWindow;
|
Window* window = touchWindow;
|
||||||
|
TouchSlot* slot = SlotFor(id);
|
||||||
|
if (slot != nullptr && slot->window != nullptr) window = slot->window;
|
||||||
const Vector<float, 2> pos = Vector<float, 2>(wl_fixed_to_double(x), wl_fixed_to_double(y)) * window->scale;
|
const Vector<float, 2> pos = Vector<float, 2>(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->x = pos.x;
|
||||||
slot->y = pos.y;
|
slot->y = pos.y;
|
||||||
}
|
}
|
||||||
|
|
@ -743,19 +760,20 @@ void Device::touch_cancel(void*, wl_touch*) {
|
||||||
window->onTouchCancel.Invoke();
|
window->onTouchCancel.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Device::seat_handle_capabilities(void* data, wl_seat* seat, uint32_t capabilities) {
|
// Capabilities come again when a device appears or goes; each object is
|
||||||
seat = seat;
|
// created once, or every event would arrive once per copy.
|
||||||
if (capabilities & WL_SEAT_CAPABILITY_POINTER) {
|
void Device::seat_handle_capabilities(void*, wl_seat* seat, uint32_t capabilities) {
|
||||||
wl_pointer* pointer = wl_seat_get_pointer(seat);
|
if ((capabilities & WL_SEAT_CAPABILITY_POINTER) && seatPointer == nullptr) {
|
||||||
wl_pointer_add_listener(pointer, &pointer_listener, nullptr);
|
seatPointer = wl_seat_get_pointer(seat);
|
||||||
|
wl_pointer_add_listener(seatPointer, &pointer_listener, nullptr);
|
||||||
}
|
}
|
||||||
if ((capabilities & WL_SEAT_CAPABILITY_TOUCH) && wlTouch == nullptr) {
|
if ((capabilities & WL_SEAT_CAPABILITY_TOUCH) && wlTouch == nullptr) {
|
||||||
wlTouch = wl_seat_get_touch(seat);
|
wlTouch = wl_seat_get_touch(seat);
|
||||||
wl_touch_add_listener(wlTouch, &touch_listener, nullptr);
|
wl_touch_add_listener(wlTouch, &touch_listener, nullptr);
|
||||||
}
|
}
|
||||||
if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) {
|
if ((capabilities & WL_SEAT_CAPABILITY_KEYBOARD) && seatKeyboard == nullptr) {
|
||||||
wl_keyboard* keyboard = wl_seat_get_keyboard(seat);
|
seatKeyboard = wl_seat_get_keyboard(seat);
|
||||||
wl_keyboard_add_listener(keyboard, &keyboard_listener, nullptr);
|
wl_keyboard_add_listener(seatKeyboard, &keyboard_listener, nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,8 @@ export namespace Crafter {
|
||||||
inline static xkb_state* xkb_state;
|
inline static xkb_state* xkb_state;
|
||||||
inline static std::vector<Window*> windows;
|
inline static std::vector<Window*> windows;
|
||||||
inline static wl_pointer* wlPointer;
|
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 wl_touch* wlTouch = nullptr;
|
||||||
inline static Window* touchWindow = nullptr; // the window under the fingers
|
inline static Window* touchWindow = nullptr; // the window under the fingers
|
||||||
// The fingers down, by the compositor's id, in physical pixels. Plain
|
// The fingers down, by the compositor's id, in physical pixels. Plain
|
||||||
|
|
@ -70,6 +72,7 @@ export namespace Crafter {
|
||||||
float x;
|
float x;
|
||||||
float y;
|
float y;
|
||||||
bool used;
|
bool used;
|
||||||
|
Window* window;
|
||||||
};
|
};
|
||||||
inline static std::array<TouchSlot, 10> touchSlots{};
|
inline static std::array<TouchSlot, 10> touchSlots{};
|
||||||
static void ForgetTouches();
|
static void ForgetTouches();
|
||||||
|
|
|
||||||
|
|
@ -234,6 +234,7 @@ export namespace Crafter {
|
||||||
// mid-session (the hotspot only updates when set_cursor is recalled).
|
// mid-session (the hotspot only updates when set_cursor is recalled).
|
||||||
std::uint32_t lastPointerSerial_ = 0;
|
std::uint32_t lastPointerSerial_ = 0;
|
||||||
bool textInputActive_ = false;
|
bool textInputActive_ = false;
|
||||||
|
bool textInputEnabled_ = false; // enable sent and not taken back since
|
||||||
std::int32_t textInputRect_[4] = { 0, 0, 0, 0 };
|
std::int32_t textInputRect_[4] = { 0, 0, 0, 0 };
|
||||||
TextInputPurpose textInputPurpose_ = TextInputPurpose::Normal;
|
TextInputPurpose textInputPurpose_ = TextInputPurpose::Normal;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue