use smithay_client_toolkit::{ error::GlobalError, globals::{GlobalData, ProvidesBoundGlobal}, reexports::{ client::{ globals::{BindError, GlobalList}, Connection, Dispatch, QueueHandle, }, protocols::wp::{ input_method::zv1::client::{ zwp_input_method_context_v1::{self, ZwpInputMethodContextV1}, zwp_input_method_v1::{self, ZwpInputMethodV1}, }, text_input::zv1::client::zwp_text_input_v1::{ContentHint, ContentPurpose}, }, }, }; pub trait ImeContextV1Handler { fn reset(&mut self); fn preferred_language(&mut self, lang: &str); fn content_type(&mut self, hint: ContentHint, purpose: ContentPurpose); fn commit_state(&mut self, serial: u32); /// button: e.g. BTN_LEFT / BTN_RIGHT /// index = position in preedit fn invoke_action(&mut self, button: u32, index: u32); fn set_surrounding_text(&mut self, text: &str, cursor: u32, anchor: u32); } pub trait ImeV1Handler { fn activate(&mut self, ctx: ImeContextV1); fn deactivate(&mut self, ctx: ImeContextV1); } #[derive(Debug)] pub struct ImeV1 { wl_ime: ZwpInputMethodV1, } #[derive(Debug)] pub struct ImeContextV1 { wl_ime_ctx: ZwpInputMethodContextV1, } impl From<ZwpInputMethodContextV1> for ImeContextV1 { fn from(wl_ime: ZwpInputMethodContextV1) -> Self { Self { wl_ime_ctx: wl_ime } } } impl ImeV1 { pub fn bind<State>(globals: &GlobalList, qh: &QueueHandle<State>) -> Result<Self, BindError> where State: Dispatch<ZwpInputMethodV1, GlobalData, State> + ImeContextV1Handler + 'static, { let wl_ime = globals.bind(qh, 1..=1, GlobalData)?; // Compositors must advertise Argb8888 and Xrgb8888, so let's reserve space for those formats. Ok(Self { wl_ime }) } #[allow(unused)] pub fn wl_ime(&self) -> &ZwpInputMethodV1 { &self.wl_ime } } impl ImeContextV1 { #[allow(unused)] pub fn wl_ime_ctx(&self) -> &ZwpInputMethodContextV1 { &self.wl_ime_ctx } } impl ProvidesBoundGlobal<ZwpInputMethodV1, 1> for ImeV1 { fn bound_global(&self) -> Result<ZwpInputMethodV1, GlobalError> { Ok(self.wl_ime.clone()) } } #[macro_export] macro_rules! delegate_ime_v1 { ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => { smithay_client_toolkit::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ smithay_client_toolkit::reexports::protocols::wp::input_method::zv1::client::zwp_input_method_v1::ZwpInputMethodV1: smithay_client_toolkit::globals::GlobalData ] => $crate::wayland::ime_v1::ImeV1 ); }; } #[macro_export] macro_rules! delegate_ime_context_v1 { ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => { smithay_client_toolkit::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ smithay_client_toolkit::reexports::protocols::wp::input_method::zv1::client::zwp_input_method_context_v1::ZwpInputMethodContextV1: smithay_client_toolkit::globals::GlobalData// $crate::wayland::ime_v1::ImeContextV1Data ] => $crate::wayland::ime_v1::ImeContextV1 ); }; } impl<D> Dispatch<ZwpInputMethodContextV1, GlobalData, D> for ImeContextV1 where D: Dispatch<ZwpInputMethodContextV1, GlobalData> + ImeContextV1Handler, { fn event( state: &mut D, _proxy: &ZwpInputMethodContextV1, event: zwp_input_method_context_v1::Event, _: &GlobalData, _: &Connection, _: &QueueHandle<D>, ) { log::debug!(target: "zwp_input_method_context_v1", "received {event:?}"); match event { zwp_input_method_context_v1::Event::Reset => { state.reset(); } zwp_input_method_context_v1::Event::ContentType { hint, purpose } => { let Some(hint) = ContentHint::from_bits(hint) else { log::debug!(target: "zwp_input_method_context_v1", "unknown hint: {hint:?}"); return; }; let Ok(purpose) = ContentPurpose::try_from(purpose) else { log::debug!(target: "zwp_input_method_context_v1", "unknown purpose: {purpose:?}"); return; }; state.content_type(hint, purpose); } zwp_input_method_context_v1::Event::CommitState { serial } => { state.commit_state(serial); } zwp_input_method_context_v1::Event::InvokeAction { button, index } => { state.invoke_action(button, index); } zwp_input_method_context_v1::Event::SurroundingText { text, cursor, anchor, } => { state.set_surrounding_text(&text, cursor, anchor); } zwp_input_method_context_v1::Event::PreferredLanguage { language } => { state.preferred_language(&language); } _ => log::debug!(target: "zwp_input_method_context_v1", "unknown event"), } } } impl<D> Dispatch<ZwpInputMethodV1, GlobalData, D> for ImeV1 where D: Dispatch<ZwpInputMethodV1, GlobalData> + ImeV1Handler, { fn event( state: &mut D, _proxy: &ZwpInputMethodV1, event: zwp_input_method_v1::Event, _data: &GlobalData, _conn: &Connection, _qh: &QueueHandle<D>, ) { match event { zwp_input_method_v1::Event::Activate { id } => state.activate(id.into()), zwp_input_method_v1::Event::Deactivate { context } => state.deactivate(context.into()), _ => log::debug!(target: "zwp_input_method_v1", "unknown event"), } } }