From 866afd6bf20066c506a954d8ae1d28edc0b93a08 Mon Sep 17 00:00:00 2001 From: Daniel McNab <36049421+DJMcNab@users.noreply.github.com> Date: Sat, 29 Jul 2023 21:17:12 +0100 Subject: [PATCH] Start to sketch out more principled text input ownership --- src/backend/wayland/input/mod.rs | 103 +++++++++++++++++++++++++++++-- 1 file changed, 97 insertions(+), 6 deletions(-) diff --git a/src/backend/wayland/input/mod.rs b/src/backend/wayland/input/mod.rs index fbf74c83..4ef32bce 100644 --- a/src/backend/wayland/input/mod.rs +++ b/src/backend/wayland/input/mod.rs @@ -1,11 +1,15 @@ +use std::collections::HashMap; + use crate::{ - backend::shared::xkb::handle_xkb_key_event_full, text::Event, Counter, TextFieldToken, - WinHandler, + backend::shared::xkb::xkb_simulate_input, text::Event, Counter, TextFieldToken, WinHandler, }; use self::{keyboard::KeyboardState, text_input::InputState}; -use super::{window::WindowId, WaylandState}; +use super::{ + window::{WaylandWindowState, WindowId}, + WaylandState, +}; use keyboard_types::KeyState; use smithay_client_toolkit::{ @@ -129,18 +133,96 @@ impl TextFieldChange { /// - Pointer /// - Touch /// Plus: -/// - Text input (for ) +/// - Text input /// /// These are stored in a vector because we expect nearly all /// programs to only encounter a single seat, so we don't need the overhead of a HashMap. /// /// However, there's little harm in supporting multiple seats, so we may as well do so +/// +/// The SeatInfo is also the only system which can edit text input fields. +/// +/// Both the keyboard and text input want to handle text fields, so the seat handles ownership of this. +/// In particular, either the keyboard, or the text input system can "own" the input handling for the +/// focused text field, but not both. The main thing this impacts is whose state must be reset when the +/// other makes this claim. +/// +/// This state is stored in the window properties pub(super) struct SeatInfo { id: SeatName, seat: wl_seat::WlSeat, keyboard_state: Option, input_state: Option, keyboard_focused: Option, + + text_field_owner: TextFieldOwner, +} + +enum TextFieldOwner { + Keyboard, + TextInput, + Neither, +} + +/// The type used to store the set of active windows +type Windows = HashMap; + +pub(in crate::backend::wayland) struct TextInputProperties { + pub active_text_field: Option, + pub next_text_field: Option, + pub active_text_field_updated: bool, +} + +impl SeatInfo { + /// Called when the text input focused window might have changed (due to a keyboard focus leave event) + /// or a window being deleted + fn focus_changed(&mut self, new_focus: Option, windows: &mut Windows) { + if new_focus == self.keyboard_focused { + return; + } + if let Some(old_focus) = self.keyboard_focused { + let handler = self.handler(windows); + self.release_input(handler, token); + } + } + + fn update_active_text_input( + &mut self, + props: &TextInputProperties, + handler: &mut dyn WinHandler, + ) { + let next = props.next_text_field; + { + let previous = props.active_text_field; + if next != previous { + self.release_input(handler, previous); + } + } + } + + fn release_input(&mut self, handler: &mut dyn WinHandler, token: Option) { + match self.text_field_owner { + TextFieldOwner::Keyboard => { + let keyboard_state = self + .keyboard_state + .expect("Keyboard can only claim compose if available"); + let xkb_state = keyboard_state + .xkb_state + .expect("Keyboard can only claim if keymap available"); + } + TextFieldOwner::TextInput => {} + TextFieldOwner::Neither => {} + } + } + + /// Stop receiving events for the given keyboard + fn destroy_keyboard(&mut self) { + self.keyboard_state = None; + + if matches!(self.text_field_owner, TextFieldOwner::Keyboard) { + self.text_field_owner = TextFieldOwner::Neither; + } + } } impl SeatInfo { @@ -159,7 +241,7 @@ impl SeatInfo { // TODO: If the keyboard is removed from the seat whilst repeating, // this might not be true. Although at that point, repeat should be cancelled anyway, so should be fine .expect("Will have a keyboard if handling text input"); - let result = handle_xkb_key_event_full( + let result = xkb_simulate_input( &mut keyboard .xkb_state .as_mut() @@ -182,6 +264,15 @@ impl SeatInfo { } } +/// Get the text input information for the given window +fn handler<'a>( + windows: &'a mut Windows, + window: &WindowId, +) -> Option<(&'a dyn WinHandler, TextInputProperties)> { + let window = &mut *windows.get_mut(&window)?; + todo!() +} + /// Identifier for a seat #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub(super) struct SeatName(u64); @@ -276,7 +367,7 @@ impl SeatHandler for WaylandState { ) { let state = self.info_of_seat(&seat); match capability { - smithay_client_toolkit::seat::Capability::Keyboard => state.keyboard_state = None, + smithay_client_toolkit::seat::Capability::Keyboard => state.destroy_keyboard(), smithay_client_toolkit::seat::Capability::Pointer => {} smithay_client_toolkit::seat::Capability::Touch => {} it => tracing::info!(?seat, "Removed unknown seat capability {it}"),