wayland_protocols_async/zwp_input_method_v2/
handler.rs

1use tokio::{sync::mpsc::{Sender, Receiver}, io::unix::AsyncFd};
2use wayland_client::{EventQueue, protocol::{wl_seat::{WlSeat, self}, wl_registry::{WlRegistry, self}, wl_output::{WlOutput, self}}, globals::{self, GlobalListContents}, Connection, QueueHandle, Dispatch, WEnum, backend::protocol::WEnumError};
3use wayland_protocols::wp::text_input::zv3::client::zwp_text_input_v3::{ChangeCause, ContentHint, ContentPurpose};
4use wayland_protocols_misc::zwp_input_method_v2::client::{zwp_input_method_manager_v2::ZwpInputMethodManagerV2, zwp_input_method_v2::{self, ZwpInputMethodV2}};
5
6#[derive(Debug)]
7pub enum InputMethodMessage {
8    CommitString { text: String },
9    SetPreeditString { text: String, cursor_begin: i32, cursor_end: i32 },
10    DeleteSurroundingText { before_length: u32, after_length: u32 },
11    Commit { serial: u32 },
12    // GetInputPopupSurface,
13    // GrabKeyboard,
14}
15
16#[derive(Debug)]
17pub enum InputMethodEvent {
18    Activate,
19    Deactivate,
20    Done,
21    SurroundingText { text: String, cursor: u32, anchor: u32 },
22    TextChangeCause { cause: Result<ChangeCause, WEnumError> },
23    ContentType { hint: Result<ContentHint, WEnumError>, purpose: Result<ContentPurpose, WEnumError>},
24    Unavailable,
25}
26
27pub struct InputMethodState {
28    event_tx: Sender<InputMethodEvent>,
29    input_method: ZwpInputMethodV2,
30    seat: Option<WlSeat>,
31    output: Option<WlOutput>,
32}
33
34pub struct InputMethodHandler {
35    event_queue: EventQueue<InputMethodState>,
36    state: InputMethodState,
37}
38
39impl InputMethodHandler {
40    pub fn new(event_tx: Sender<InputMethodEvent>) -> Self {
41        let conn = wayland_client::Connection::connect_to_env()
42            .map_err(|_| "could not connect to wayland socket, try setting WAYLAND_DISPLAY.")
43            .unwrap();
44        let (globals, mut event_queue) = globals::registry_queue_init::<InputMethodState>(&conn).unwrap();
45        let qh = event_queue.handle();
46
47        let _input_manager = globals
48            .bind::<ZwpInputMethodManagerV2, _, _>(&qh, core::ops::RangeInclusive::new(1, 1), ())
49            .map_err(|_| "compositor does not implement input method manager (v1).").unwrap();
50        let seat = globals
51            .bind::<WlSeat, _, _>(&qh, core::ops::RangeInclusive::new(1, 1), ())
52            .map_err(|_| "failed to retrieve the seat from global.")
53            .unwrap();
54        let output = globals
55            .bind::<WlOutput, _, _>(&qh, core::ops::RangeInclusive::new(1, 1), ())
56            .map_err(|_| "failed to retrieve the output from global.")
57            .unwrap();
58
59        // bind the input method
60        let input_method = _input_manager.get_input_method(&seat, &qh, ());
61
62        let mut state = InputMethodState {
63            event_tx,
64            input_method,
65            seat: Some(seat),
66            output: Some(output),
67        };
68
69        event_queue.roundtrip(&mut state).unwrap();
70    
71        InputMethodHandler {
72            event_queue,
73            state,
74        }
75    }
76
77    pub async fn run(&mut self, mut msg_rx: Receiver<InputMethodMessage>) {
78        let event_queue = &mut self.event_queue;
79        let mut input_method_state = &mut self.state;
80    
81        loop {
82            // This would be required if other threads were reading from the socket.
83            event_queue.dispatch_pending(&mut input_method_state).unwrap();
84            let read_guard = event_queue.prepare_read().unwrap();
85            let fd = read_guard.connection_fd();
86            let async_fd = AsyncFd::new(fd).unwrap();
87    
88            tokio::select! {
89                async_guard = async_fd.readable() => {
90                    async_guard.unwrap().clear_ready();
91                    // Drop the async_fd since it's holding a reference to the read_guard,
92                    // which is dropped on read. We don't need to read from it anyways.
93                    std::mem::drop(async_fd);
94                    // This should not block because we already ensured readiness
95                    let event = read_guard.read();
96                    match event {
97                        // There are events but another thread processed them, we don't need to dispatch
98                        Ok(0) => {},
99                        // We have some events
100                        Ok(_) => {
101                            event_queue.dispatch_pending(&mut input_method_state).unwrap();
102                        },
103                        // No events to receive
104                        Err(_) => {}
105                        // Err(e) => eprintln!("error reading event {}", e),
106                    }
107                },
108                msg = msg_rx.recv() => {
109                    if msg.is_none() {
110                        continue;
111                    }
112                    match msg.unwrap() {
113                        InputMethodMessage::CommitString{ text } => {
114                            input_method_state.input_method.commit_string(text)
115                        },
116                        InputMethodMessage::SetPreeditString { text, cursor_begin, cursor_end } => {
117                            input_method_state.input_method.set_preedit_string(text, cursor_begin, cursor_end)
118                        },
119                        InputMethodMessage::DeleteSurroundingText { before_length, after_length } => {
120                            input_method_state.input_method.delete_surrounding_text(before_length, after_length)
121                        },
122                        InputMethodMessage::Commit { serial } => {
123                            input_method_state.input_method.commit(serial)
124                        },
125                    }
126                }
127            }
128    
129            // Send any new messages to the socket.
130            let _ = event_queue.flush().expect("wayland connection closed");
131        }
132    }
133}
134
135impl InputMethodState {
136    fn dispatch_event(&self, event: InputMethodEvent) {
137        let tx: Sender<InputMethodEvent> = self.event_tx.clone();
138        tokio::task::spawn(async move {
139            let _ = tx.send(event).await;
140        });
141    }
142}
143
144impl Dispatch<WlRegistry, GlobalListContents> for InputMethodState {
145    fn event(
146        _: &mut Self,
147        registry: &wl_registry::WlRegistry,
148        event: wl_registry::Event,
149        _: &GlobalListContents,
150        _: &Connection,
151        qh: &QueueHandle<InputMethodState>,
152    ) {
153        if let wl_registry::Event::Global {
154            name,
155            interface,
156            version,
157        } = event
158        {
159            if interface == "wl_seat" {
160                registry.bind::<WlSeat, (), InputMethodState>(name, version, qh, ());
161            }
162        }
163    }
164}
165
166impl Dispatch<WlSeat, ()> for InputMethodState {
167    fn event(
168        state: &mut Self,
169        seat: &WlSeat,
170        event: wl_seat::Event,
171        _: &(),
172        _: &Connection,
173        _: &QueueHandle<InputMethodState>,
174    ) {
175        if let wl_seat::Event::Name { .. } = event {
176            state.seat = Some(seat.to_owned());
177        }
178    }
179}
180
181impl Dispatch<WlOutput, ()> for InputMethodState {
182    fn event(
183        state: &mut Self,
184        output: &WlOutput,
185        event: wl_output::Event,
186        _: &(),
187        _: &Connection,
188        _: &QueueHandle<InputMethodState>,
189    ) {
190        if let wl_output::Event::Name { .. } = event {
191            state.output = Some(output.to_owned());
192        }
193    }
194}
195
196impl Dispatch<ZwpInputMethodManagerV2, ()> for InputMethodState {
197    fn event(
198        _: &mut Self,
199        _: &ZwpInputMethodManagerV2,
200        event: <ZwpInputMethodManagerV2 as wayland_client::Proxy>::Event,
201        _: &(),
202        _: &Connection,
203        _: &QueueHandle<Self>,
204    ) {
205        match event {
206            _ => (),
207        }
208    }
209}
210
211
212impl Dispatch<ZwpInputMethodV2, ()> for InputMethodState {
213    fn event(
214        state: &mut Self,
215        _: &ZwpInputMethodV2,
216        event: zwp_input_method_v2::Event,
217        _: &(),
218        _: &Connection,
219        _: &QueueHandle<InputMethodState>,
220    ) {
221        match event {
222            zwp_input_method_v2::Event::Activate =>
223                state.dispatch_event(InputMethodEvent::Activate),
224            zwp_input_method_v2::Event::Deactivate =>
225                state.dispatch_event(InputMethodEvent::Deactivate),
226            zwp_input_method_v2::Event::Done =>
227                state.dispatch_event(InputMethodEvent::Done),
228            zwp_input_method_v2::Event::SurroundingText { text, cursor, anchor } =>
229                state.dispatch_event(InputMethodEvent::SurroundingText { text, cursor, anchor }),
230            zwp_input_method_v2::Event::TextChangeCause { cause } => {
231                state.dispatch_event(InputMethodEvent::TextChangeCause {
232                    cause: cause.into_result()
233                })
234            },
235            zwp_input_method_v2::Event::ContentType { hint, purpose } => {
236                state.dispatch_event(InputMethodEvent::ContentType {
237                    hint: hint.into_result(),
238                    purpose: purpose.into_result(),
239                })
240            },
241            zwp_input_method_v2::Event::Unavailable =>
242            state.dispatch_event(InputMethodEvent::Unavailable),
243            _ => {}
244        }
245    }
246}