wayland: Handle text input per-seat

When changing the text input mode on a window, only update the seats that currently hold keyboard focus on that window, otherwise, text input might be inadvertently enabled or disabled on a seat focused on another window.
This commit is contained in:
Frank Praznik
2025-07-19 10:08:56 -04:00
parent 8bd29f7ca3
commit 735f0cc300
4 changed files with 69 additions and 62 deletions

View File

@@ -35,6 +35,7 @@
#include "SDL_waylandwindow.h" #include "SDL_waylandwindow.h"
#include "SDL_waylandmouse.h" #include "SDL_waylandmouse.h"
#include "SDL_waylandclipboard.h" #include "SDL_waylandclipboard.h"
#include "SDL_waylandkeyboard.h"
#include "pointer-constraints-unstable-v1-client-protocol.h" #include "pointer-constraints-unstable-v1-client-protocol.h"
#include "relative-pointer-unstable-v1-client-protocol.h" #include "relative-pointer-unstable-v1-client-protocol.h"
@@ -1897,7 +1898,7 @@ static void keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
Wayland_DisplayUpdatePointerGrabs(seat->display, window); Wayland_DisplayUpdatePointerGrabs(seat->display, window);
// Update text input and IME focus. // Update text input and IME focus.
Wayland_UpdateTextInput(seat->display); Wayland_SeatUpdateTextInput(seat);
#ifdef SDL_USE_IME #ifdef SDL_USE_IME
if (!seat->text_input.zwp_text_input) { if (!seat->text_input.zwp_text_input) {
@@ -1979,7 +1980,7 @@ static void keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
seat->keyboard.pressed_modifiers = SDL_KMOD_NONE; seat->keyboard.pressed_modifiers = SDL_KMOD_NONE;
// Update text input and IME focus. // Update text input and IME focus.
Wayland_UpdateTextInput(seat->display); Wayland_SeatUpdateTextInput(seat);
#ifdef SDL_USE_IME #ifdef SDL_USE_IME
if (!seat->text_input.zwp_text_input && !window->keyboard_focus_count) { if (!seat->text_input.zwp_text_input && !window->keyboard_focus_count) {

View File

@@ -31,7 +31,6 @@
#include "SDL_waylandvideo.h" #include "SDL_waylandvideo.h"
#include "SDL_waylandwindow.h" #include "SDL_waylandwindow.h"
#include "SDL_waylanddatamanager.h" #include "SDL_waylanddatamanager.h"
#include "SDL_waylandkeyboard.h"
enum SDL_WaylandAxisEvent enum SDL_WaylandAxisEvent
{ {

View File

@@ -51,65 +51,59 @@ void Wayland_QuitKeyboard(SDL_VideoDevice *_this)
#endif #endif
} }
void Wayland_UpdateTextInput(SDL_VideoData *display) void Wayland_SeatUpdateTextInput(SDL_WaylandSeat *seat)
{ {
SDL_WaylandSeat *seat = NULL; if (seat->text_input.zwp_text_input) {
SDL_WindowData *focus = seat->keyboard.focus;
if (display->text_input_manager) { if (focus && focus->text_input_props.active) {
wl_list_for_each(seat, &display->seat_list, link) { SDL_Window *window = focus->sdlwindow;
SDL_WindowData *focus = seat->keyboard.focus;
if (seat->text_input.zwp_text_input) { // Enabling will reset all state, so don't do it redundantly.
if (focus && focus->text_input_props.active) { if (!seat->text_input.enabled) {
SDL_Window *window = focus->sdlwindow; seat->text_input.enabled = true;
zwp_text_input_v3_enable(seat->text_input.zwp_text_input);
// Enabling will reset all state, so don't do it redundantly. // Now that it's enabled, set the input properties
if (!seat->text_input.enabled) { zwp_text_input_v3_set_content_type(seat->text_input.zwp_text_input, focus->text_input_props.hint, focus->text_input_props.purpose);
seat->text_input.enabled = true; if (!SDL_RectEmpty(&window->text_input_rect)) {
zwp_text_input_v3_enable(seat->text_input.zwp_text_input); const SDL_Rect scaled_rect = {
(int)SDL_floor(window->text_input_rect.x / focus->pointer_scale.x),
(int)SDL_floor(window->text_input_rect.y / focus->pointer_scale.y),
(int)SDL_ceil(window->text_input_rect.w / focus->pointer_scale.x),
(int)SDL_ceil(window->text_input_rect.h / focus->pointer_scale.y)
};
const int scaled_cursor = (int)SDL_floor(window->text_input_cursor / focus->pointer_scale.x);
// Now that it's enabled, set the input properties SDL_copyp(&seat->text_input.text_input_rect, &scaled_rect);
zwp_text_input_v3_set_content_type(seat->text_input.zwp_text_input, focus->text_input_props.hint, focus->text_input_props.purpose); seat->text_input.text_input_cursor = scaled_cursor;
if (!SDL_RectEmpty(&window->text_input_rect)) {
const SDL_Rect scaled_rect = {
(int)SDL_floor(window->text_input_rect.x / focus->pointer_scale.x),
(int)SDL_floor(window->text_input_rect.y / focus->pointer_scale.y),
(int)SDL_ceil(window->text_input_rect.w / focus->pointer_scale.x),
(int)SDL_ceil(window->text_input_rect.h / focus->pointer_scale.y)
};
const int scaled_cursor = (int)SDL_floor(window->text_input_cursor / focus->pointer_scale.x);
SDL_copyp(&seat->text_input.text_input_rect, &scaled_rect); // Clamp the x value so it doesn't run too far past the end of the text input area.
seat->text_input.text_input_cursor = scaled_cursor; zwp_text_input_v3_set_cursor_rectangle(seat->text_input.zwp_text_input,
SDL_min(scaled_rect.x + scaled_cursor, scaled_rect.x + scaled_rect.w),
// Clamp the x value so it doesn't run too far past the end of the text input area. scaled_rect.y,
zwp_text_input_v3_set_cursor_rectangle(seat->text_input.zwp_text_input, 1,
SDL_min(scaled_rect.x + scaled_cursor, scaled_rect.x + scaled_rect.w), scaled_rect.h);
scaled_rect.y,
1,
scaled_rect.h);
}
zwp_text_input_v3_commit(seat->text_input.zwp_text_input);
if (seat->keyboard.xkb.compose_state) {
// Reset compose state so composite and dead keys don't carry over
WAYLAND_xkb_compose_state_reset(seat->keyboard.xkb.compose_state);
}
}
} else {
if (seat->text_input.enabled) {
seat->text_input.enabled = false;
SDL_zero(seat->text_input.text_input_rect);
seat->text_input.text_input_cursor = 0;
zwp_text_input_v3_disable(seat->text_input.zwp_text_input);
zwp_text_input_v3_commit(seat->text_input.zwp_text_input);
}
if (seat->keyboard.xkb.compose_state) {
// Reset compose state so composite and dead keys don't carry over
WAYLAND_xkb_compose_state_reset(seat->keyboard.xkb.compose_state);
}
} }
zwp_text_input_v3_commit(seat->text_input.zwp_text_input);
if (seat->keyboard.xkb.compose_state) {
// Reset compose state so composite and dead keys don't carry over
WAYLAND_xkb_compose_state_reset(seat->keyboard.xkb.compose_state);
}
}
} else {
if (seat->text_input.enabled) {
seat->text_input.enabled = false;
SDL_zero(seat->text_input.text_input_rect);
seat->text_input.text_input_cursor = 0;
zwp_text_input_v3_disable(seat->text_input.zwp_text_input);
zwp_text_input_v3_commit(seat->text_input.zwp_text_input);
}
if (seat->keyboard.xkb.compose_state) {
// Reset compose state so composite and dead keys don't carry over
WAYLAND_xkb_compose_state_reset(seat->keyboard.xkb.compose_state);
} }
} }
} }
@@ -182,12 +176,18 @@ bool Wayland_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_Prop
} }
wind->text_input_props.active = true; wind->text_input_props.active = true;
Wayland_UpdateTextInput(display);
SDL_WaylandSeat *seat;
wl_list_for_each (seat, &display->seat_list, link) {
if (seat->keyboard.focus == wind) {
Wayland_SeatUpdateTextInput(seat);
}
}
return true; return true;
} }
return false; return SDL_SetError("wayland: cannot enable text input; compositor lacks support for the required zwp_text_input_v3 protocol");
} }
bool Wayland_StopTextInput(SDL_VideoDevice *_this, SDL_Window *window) bool Wayland_StopTextInput(SDL_VideoDevice *_this, SDL_Window *window)
@@ -195,8 +195,15 @@ bool Wayland_StopTextInput(SDL_VideoDevice *_this, SDL_Window *window)
SDL_VideoData *display = _this->internal; SDL_VideoData *display = _this->internal;
if (display->text_input_manager) { if (display->text_input_manager) {
window->internal->text_input_props.active = false; SDL_WaylandSeat *seat;
Wayland_UpdateTextInput(display); SDL_WindowData *wind = window->internal;
wind->text_input_props.active = false;
wl_list_for_each (seat, &display->seat_list, link) {
if (seat->keyboard.focus == wind) {
Wayland_SeatUpdateTextInput(seat);
}
}
} }
#ifdef SDL_USE_IME #ifdef SDL_USE_IME
else { else {
@@ -212,10 +219,10 @@ bool Wayland_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window)
SDL_VideoData *internal = _this->internal; SDL_VideoData *internal = _this->internal;
if (internal->text_input_manager) { if (internal->text_input_manager) {
SDL_WaylandSeat *seat; SDL_WaylandSeat *seat;
SDL_WindowData *wind = window->internal;
wl_list_for_each (seat, &internal->seat_list, link) { wl_list_for_each (seat, &internal->seat_list, link) {
if (seat->text_input.zwp_text_input && seat->keyboard.focus == window->internal) { if (seat->text_input.zwp_text_input && seat->keyboard.focus == wind) {
SDL_WindowData *wind = window->internal;
const SDL_Rect scaled_rect = { const SDL_Rect scaled_rect = {
(int)SDL_floor(window->text_input_rect.x / wind->pointer_scale.x), (int)SDL_floor(window->text_input_rect.x / wind->pointer_scale.x),
(int)SDL_floor(window->text_input_rect.y / wind->pointer_scale.y), (int)SDL_floor(window->text_input_rect.y / wind->pointer_scale.y),

View File

@@ -28,7 +28,7 @@ extern void Wayland_QuitKeyboard(SDL_VideoDevice *_this);
extern bool Wayland_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props); extern bool Wayland_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props);
extern bool Wayland_StopTextInput(SDL_VideoDevice *_this, SDL_Window *window); extern bool Wayland_StopTextInput(SDL_VideoDevice *_this, SDL_Window *window);
extern bool Wayland_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window); extern bool Wayland_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window);
extern void Wayland_UpdateTextInput(SDL_VideoData *display); extern void Wayland_SeatUpdateTextInput(SDL_WaylandSeat *seat);
extern bool Wayland_HasScreenKeyboardSupport(SDL_VideoDevice *_this); extern bool Wayland_HasScreenKeyboardSupport(SDL_VideoDevice *_this);
#endif // SDL_waylandkeyboard_h_ #endif // SDL_waylandkeyboard_h_