diff --git a/Cargo.toml b/Cargo.toml index 9650da5ab..d1703dd21 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ wayland-protocols-misc = { version = "0.3.6", features = ["client"] } wayland-protocols-wlr = { version = "0.3.1", features = ["client"] } wayland-scanner = "0.31.0" wayland-csd-frame = "0.3.0" +wl-input-method = { version = "3.0.0", git = "https://gitlab.freedesktop.org/dcz/wl-input-method", branch = "keyboard" } xkbcommon = { version = "0.8.0", optional = true, features = ["wayland"] } xkeysym = "0.2.0" diff --git a/src/lib.rs b/src/lib.rs index 6e345fda5..e0afd3512 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,7 +17,7 @@ pub mod reexports { pub use wayland_client as client; pub use wayland_csd_frame as csd_frame; pub use wayland_protocols as protocols; - pub use wayland_protocols_experimental as protocols_experimental; + pub use wl_input_method as protocols_experimental; pub use wayland_protocols_misc as protocols_misc; pub use wayland_protocols_wlr as protocols_wlr; } diff --git a/src/seat/input_method_v3.rs b/src/seat/input_method_v3.rs index bf9df6118..2f3daa369 100644 --- a/src/seat/input_method_v3.rs +++ b/src/seat/input_method_v3.rs @@ -13,23 +13,27 @@ use std::ops::Deref; use std::sync::{Arc, Mutex, MutexGuard, Weak}; use wayland_client::globals::{BindError, GlobalList}; +use wayland_client::protocol::wl_keyboard::WlKeyboard; use wayland_client::protocol::wl_seat::WlSeat; -use wayland_client::protocol::wl_surface; +use wayland_client::protocol::wl_surface::WlSurface; use wayland_client::WEnum; use wayland_client::{Connection, Dispatch, Proxy, QueueHandle}; use wayland_protocols::wp::text_input::zv3::client::zwp_text_input_v3::{ ChangeCause, ContentHint, ContentPurpose, }; -use wayland_protocols_experimental::input_method::v1::client as protocol; +use wl_input_method::input_method::v1::client as protocol; pub use protocol::xx_input_method_v1::XxInputMethodV1; +pub use protocol::xx_input_method_keyboard_v1::XxInputMethodKeyboardV1; pub use protocol::xx_input_popup_positioner_v1::XxInputPopupPositionerV1; pub use protocol::xx_input_popup_surface_v2::XxInputPopupSurfaceV2; use protocol::{ + xx_input_method_v1, + xx_input_method_keyboard_v1, xx_input_method_manager_v2::{self, XxInputMethodManagerV2}, - xx_input_method_v1, xx_input_popup_positioner_v1, xx_input_popup_surface_v2, + xx_input_popup_positioner_v1, xx_input_popup_surface_v2, }; pub use xx_input_popup_positioner_v1::{Anchor, Gravity}; @@ -59,7 +63,7 @@ impl InputMethodManager { where D: Dispatch + 'static, { - let manager = globals.bind(qh, 2..=2, GlobalData)?; + let manager = globals.bind(qh, 3..=3, GlobalData)?; Ok(Self { manager }) } @@ -238,6 +242,25 @@ impl InputMethod { surface, } } + + /// May cause a protocol error if there's a bound keyboard already. + pub fn keyboard_bind( + &self, + qh: &QueueHandle, + keyboard: &WlKeyboard, + surface: &WlSurface, + ) -> Keyboard + where + D: Dispatch + 'static, + { + let data = self.input_method.data::().unwrap(); + Keyboard(self.input_method.keyboard_bind( + keyboard, + surface, + qh, + KeyboardData { im: Arc::downgrade(&data.inner) }, + )) + } } #[derive(Debug)] @@ -281,6 +304,9 @@ pub struct InputMethodEventState { pub content_hint: ContentHint, pub text_change_cause: ChangeCause, pub active: Active, + /// A hash map of keyboards which reported a version. + /// A missing entry is equal to version 0, meaning inactive. + pub keyboards: HashMap, pub popups: HashMap, } @@ -292,6 +318,7 @@ impl Default for InputMethodEventState { content_purpose: ContentPurpose::Normal, text_change_cause: ChangeCause::InputMethod, active: Active::default(), + keyboards: Default::default(), popups: Default::default(), } } @@ -406,7 +433,7 @@ pub struct Popup { } impl Popup { - pub fn wl_surface(&self) -> &wl_surface::WlSurface { + pub fn wl_surface(&self) -> &WlSurface { self.surface.wl_surface() } @@ -536,6 +563,62 @@ impl PopupDataInner { } } +#[derive(Debug)] +pub struct Keyboard(XxInputMethodKeyboardV1); + +impl Keyboard { + /// May cause a protocol error if there's no bound keyboard. + pub fn unbind(&self) { + self.0.unbind(); + } + + /// May cause a protocol error on invalid serial. + pub fn filter( + &self, + serial: u32, + action: xx_input_method_keyboard_v1::FilterAction, + ) { + self.0.filter(serial, action); + } +} + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct KeyboardVersion(pub u32); + +impl Dispatch for Keyboard +where + D: Dispatch + InputMethodHandler, +{ + fn event( + _data: &mut D, + keyboard: &XxInputMethodKeyboardV1, + event: xx_input_method_keyboard_v1::Event, + data: &KeyboardData, + _conn: &Connection, + _qh: &QueueHandle, + ) { + if let Some(im) = data.im.upgrade() { + use xx_input_method_keyboard_v1::Event; + match event { + Event::NotifyVersion { version } => { + let mut im = im.lock().unwrap(); + im.pending_state.keyboards + .entry(keyboard.clone()) + .or_insert(KeyboardVersion(version)); + }, + _ => unreachable!(), + } + } else { + warn!("received event for a keyboard whose input method already disappeared"); + }; + } +} + +#[derive(Debug)] +pub struct KeyboardData { + im: Weak>, +} + #[macro_export] macro_rules! delegate_input_method_v3 { ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => { @@ -551,6 +634,9 @@ macro_rules! delegate_input_method_v3 { $crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ $crate::reexports::protocols_experimental::input_method::v1::client::xx_input_popup_positioner_v1::XxInputPopupPositionerV1: $crate::seat::input_method_v3::PositionerData ] => $crate::seat::input_method_v3::PopupPositioner); + $crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ + $crate::reexports::protocols_experimental::input_method::v1::client::xx_input_method_keyboard_v1::XxInputMethodKeyboardV1: $crate::seat::input_method_v3::KeyboardData + ] => $crate::seat::input_method_v3::Keyboard); }; } @@ -734,6 +820,15 @@ mod test { >, { } + + fn assert_is_keyboard_delegate() + where + T: wayland_client::Dispatch< + protocol::xx_input_method_keyboard_v1::XxInputMethodKeyboardV1, + KeyboardData, + >, + { + } #[test] fn test_valid_assignment() { @@ -741,5 +836,6 @@ mod test { assert_is_delegate::(); assert_is_popup_delegate::(); assert_is_positioner_delegate::(); + assert_is_keyboard_delegate::(); } }