smithay/wayland/input_method/
mod.rs

1//! Utilities for input method support
2//!
3//! This module provides you with utilities to handle input methods,
4//! it must be used in conjunction with the text input module to work.
5//!
6//! ```
7//! use smithay::{
8//!     delegate_seat, delegate_input_method_manager, delegate_text_input_manager,
9//! #   delegate_compositor,
10//! };
11//! use smithay::input::{Seat, SeatState, SeatHandler, pointer::CursorImageStatus};
12//! # use smithay::wayland::compositor::{CompositorHandler, CompositorState, CompositorClientState};
13//! use smithay::wayland::input_method::{InputMethodManagerState, InputMethodHandler, PopupSurface};
14//! use smithay::wayland::text_input::TextInputManagerState;
15//! use smithay::reexports::wayland_server::{Display, protocol::wl_surface::WlSurface};
16//! # use smithay::reexports::wayland_server::Client;
17//! use smithay::utils::{Rectangle, Logical};
18//!
19//! # struct State { seat_state: SeatState<Self> };
20//!
21//! delegate_seat!(State);
22//! # delegate_compositor!(State);
23//!
24//! impl InputMethodHandler for State {
25//!     fn new_popup(&mut self, surface: PopupSurface) {}
26//!     fn dismiss_popup(&mut self, surface: PopupSurface) {}
27//!     fn popup_repositioned(&mut self, surface: PopupSurface) {}
28//!     fn parent_geometry(&self, parent: &WlSurface) -> Rectangle<i32, Logical> {
29//!         Rectangle::default()
30//!     }
31//! }
32//!
33//! // Delegate input method handling for State to InputMethodManagerState.
34//! delegate_input_method_manager!(State);
35//!
36//! delegate_text_input_manager!(State);
37//!
38//! # let mut display = wayland_server::Display::<State>::new().unwrap();
39//! # let display_handle = display.handle();
40//!
41//! let mut seat_state = SeatState::<State>::new();
42//!
43//! // implement the required traits
44//! impl SeatHandler for State {
45//!     type KeyboardFocus = WlSurface;
46//!     type PointerFocus = WlSurface;
47//!     type TouchFocus = WlSurface;
48//!     fn seat_state(&mut self) -> &mut SeatState<Self> {
49//!         &mut self.seat_state
50//!     }
51//!     fn focus_changed(&mut self, seat: &Seat<Self>, focused: Option<&WlSurface>) { unimplemented!() }
52//!     fn cursor_image(&mut self, seat: &Seat<Self>, image: CursorImageStatus) { unimplemented!() }
53//! }
54//!
55//! # impl CompositorHandler for State {
56//! #     fn compositor_state(&mut self) -> &mut CompositorState { unimplemented!() }
57//! #     fn client_compositor_state<'a>(&self, client: &'a Client) -> &'a CompositorClientState { unimplemented!() }
58//! #     fn commit(&mut self, surface: &WlSurface) {}
59//! # }
60//!
61//! // Add the seat state to your state and create manager globals
62//! InputMethodManagerState::new::<State, _>(&display_handle, |_client| true);
63//! // Add text input capabilities, needed for the input method to work
64//! TextInputManagerState::new::<State>(&display_handle);
65//!
66//! ```
67
68use wayland_server::{
69    backend::GlobalId, protocol::wl_surface::WlSurface, Client, DataInit, Dispatch, DisplayHandle,
70    GlobalDispatch, New,
71};
72
73use wayland_protocols_misc::zwp_input_method_v2::server::{
74    zwp_input_method_manager_v2::{self, ZwpInputMethodManagerV2},
75    zwp_input_method_v2::ZwpInputMethodV2,
76};
77
78use crate::{
79    input::{Seat, SeatHandler},
80    utils::{Logical, Rectangle},
81};
82
83pub use input_method_handle::{InputMethodHandle, InputMethodUserData};
84pub use input_method_keyboard_grab::{InputMethodKeyboardGrab, InputMethodKeyboardUserData};
85pub use input_method_popup_surface::InputMethodPopupSurfaceUserData;
86
87use super::text_input::TextInputHandle;
88
89const MANAGER_VERSION: u32 = 1;
90
91/// The role of the input method popup.
92pub const INPUT_POPUP_SURFACE_ROLE: &str = "zwp_input_popup_surface_v2";
93
94mod input_method_handle;
95mod input_method_keyboard_grab;
96mod input_method_popup_surface;
97pub use input_method_popup_surface::{PopupParent, PopupSurface};
98
99/// Adds input method popup to compositor state
100pub trait InputMethodHandler {
101    /// Add a popup surface to compositor state.
102    fn new_popup(&mut self, surface: PopupSurface);
103
104    /// Dismiss a popup surface from the compositor state.
105    fn dismiss_popup(&mut self, surface: PopupSurface);
106
107    /// Popup location has changed.
108    fn popup_repositioned(&mut self, surface: PopupSurface);
109
110    /// Sets the parent location so the popup surface can be placed correctly
111    fn parent_geometry(&self, parent: &WlSurface) -> Rectangle<i32, Logical>;
112}
113
114/// Extends [Seat] with input method functionality
115pub trait InputMethodSeat {
116    /// Get an input method associated with this seat
117    fn input_method(&self) -> &InputMethodHandle;
118}
119
120impl<D: SeatHandler + 'static> InputMethodSeat for Seat<D> {
121    fn input_method(&self) -> &InputMethodHandle {
122        let user_data = self.user_data();
123        user_data.insert_if_missing(InputMethodHandle::default);
124        user_data.get::<InputMethodHandle>().unwrap()
125    }
126}
127
128/// Data associated with a InputMethodManager global.
129#[allow(missing_debug_implementations)]
130pub struct InputMethodManagerGlobalData {
131    filter: Box<dyn for<'c> Fn(&'c Client) -> bool + Send + Sync>,
132}
133
134/// State of wp misc input method protocol
135#[derive(Debug)]
136pub struct InputMethodManagerState {
137    global: GlobalId,
138}
139
140impl InputMethodManagerState {
141    /// Initialize a text input manager global.
142    pub fn new<D, F>(display: &DisplayHandle, filter: F) -> Self
143    where
144        D: GlobalDispatch<ZwpInputMethodManagerV2, InputMethodManagerGlobalData>,
145        D: Dispatch<ZwpInputMethodManagerV2, ()>,
146        D: Dispatch<ZwpInputMethodV2, InputMethodUserData<D>>,
147        D: SeatHandler,
148        D: 'static,
149        F: for<'c> Fn(&'c Client) -> bool + Send + Sync + 'static,
150    {
151        let data = InputMethodManagerGlobalData {
152            filter: Box::new(filter),
153        };
154        let global = display.create_global::<D, ZwpInputMethodManagerV2, _>(MANAGER_VERSION, data);
155
156        Self { global }
157    }
158
159    /// Get the id of ZwpTextInputManagerV3 global
160    pub fn global(&self) -> GlobalId {
161        self.global.clone()
162    }
163}
164
165impl<D> GlobalDispatch<ZwpInputMethodManagerV2, InputMethodManagerGlobalData, D> for InputMethodManagerState
166where
167    D: GlobalDispatch<ZwpInputMethodManagerV2, InputMethodManagerGlobalData>,
168    D: Dispatch<ZwpInputMethodManagerV2, ()>,
169    D: Dispatch<ZwpInputMethodV2, InputMethodUserData<D>>,
170    D: SeatHandler,
171    D: 'static,
172{
173    fn bind(
174        _: &mut D,
175        _: &DisplayHandle,
176        _: &Client,
177        resource: New<ZwpInputMethodManagerV2>,
178        _: &InputMethodManagerGlobalData,
179        data_init: &mut DataInit<'_, D>,
180    ) {
181        data_init.init(resource, ());
182    }
183
184    fn can_view(client: Client, global_data: &InputMethodManagerGlobalData) -> bool {
185        (global_data.filter)(&client)
186    }
187}
188
189impl<D> Dispatch<ZwpInputMethodManagerV2, (), D> for InputMethodManagerState
190where
191    D: Dispatch<ZwpInputMethodManagerV2, ()>,
192    D: Dispatch<ZwpInputMethodV2, InputMethodUserData<D>>,
193    D: SeatHandler + InputMethodHandler,
194    D: 'static,
195{
196    fn request(
197        _state: &mut D,
198        _client: &Client,
199        _: &ZwpInputMethodManagerV2,
200        request: zwp_input_method_manager_v2::Request,
201        _: &(),
202        _dh: &DisplayHandle,
203        data_init: &mut DataInit<'_, D>,
204    ) {
205        match request {
206            zwp_input_method_manager_v2::Request::GetInputMethod { seat, input_method } => {
207                let seat = Seat::<D>::from_resource(&seat).unwrap();
208
209                let user_data = seat.user_data();
210                user_data.insert_if_missing(TextInputHandle::default);
211                user_data.insert_if_missing(InputMethodHandle::default);
212                let handle = user_data.get::<InputMethodHandle>().unwrap();
213                let text_input_handle = user_data.get::<TextInputHandle>().unwrap();
214                text_input_handle.with_focused_text_input(|ti, surface| {
215                    ti.enter(surface);
216                });
217                let keyboard_handle = seat.get_keyboard().unwrap();
218                let instance = data_init.init(
219                    input_method,
220                    InputMethodUserData {
221                        handle: handle.clone(),
222                        text_input_handle: text_input_handle.clone(),
223                        keyboard_handle,
224                        popup_geometry_callback: D::parent_geometry,
225                        popup_repositioned: D::popup_repositioned,
226                        new_popup: D::new_popup,
227                        dismiss_popup: D::dismiss_popup,
228                    },
229                );
230                handle.add_instance(&instance);
231            }
232            zwp_input_method_manager_v2::Request::Destroy => {
233                // Nothing to do
234            }
235            _ => unreachable!(),
236        }
237    }
238}
239
240#[allow(missing_docs)] // TODO
241#[macro_export]
242macro_rules! delegate_input_method_manager {
243    ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
244        $crate::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
245            $crate::reexports::wayland_protocols_misc::zwp_input_method_v2::server::zwp_input_method_manager_v2::ZwpInputMethodManagerV2:
246            $crate::wayland::input_method::InputMethodManagerGlobalData
247        ] => $crate::wayland::input_method::InputMethodManagerState);
248
249        $crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
250            $crate::reexports::wayland_protocols_misc::zwp_input_method_v2::server::zwp_input_method_manager_v2::ZwpInputMethodManagerV2: ()
251        ] => $crate::wayland::input_method::InputMethodManagerState);
252        $crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
253            $crate::reexports::wayland_protocols_misc::zwp_input_method_v2::server::zwp_input_method_v2::ZwpInputMethodV2:
254            $crate::wayland::input_method::InputMethodUserData<Self>
255        ] => $crate::wayland::input_method::InputMethodManagerState);
256        $crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
257            $crate::reexports::wayland_protocols_misc::zwp_input_method_v2::server::zwp_input_method_keyboard_grab_v2::ZwpInputMethodKeyboardGrabV2:
258            $crate::wayland::input_method::InputMethodKeyboardUserData<Self>
259        ] => $crate::wayland::input_method::InputMethodManagerState);
260        $crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
261            $crate::reexports::wayland_protocols_misc::zwp_input_method_v2::server::zwp_input_popup_surface_v2::ZwpInputPopupSurfaceV2:
262            $crate::wayland::input_method::InputMethodPopupSurfaceUserData
263        ] => $crate::wayland::input_method::InputMethodManagerState);
264    };
265}