git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

src/server/input_relay.rs (12K)

  1 // SPDX-FileCopyrightText: © 2021 The River Developers
  2 // SPDX-License-Identifier: GPL-3.0-only
  3 
  4 use crate::ffi;
  5 use crate::server::{WlListener, wl_signal_add};
  6 use crate::text_input::TextInput;
  7 use crate::seat::Seat;
  8 
  9 #[repr(C)]
 10 pub struct InputRelay {
 11     pub seat: *mut Seat,
 12     pub text_inputs: ffi::wl_list,
 13     pub input_method: *mut ffi::wlr_input_method_v2,
 14     pub input_popups: ffi::wl_list,
 15     pub text_input: *mut TextInput,
 16 
 17     pub input_method_commit: ffi::wl_listener,
 18     pub grab_keyboard: ffi::wl_listener,
 19     pub input_method_destroy: ffi::wl_listener,
 20     pub input_method_new_popup: ffi::wl_listener,
 21 
 22     pub grab_keyboard_destroy: ffi::wl_listener,
 23 
 24     /// The on-screen keyboard following a touched field (`osk.rs`).
 25     pub osk: crate::osk::Osk,
 26 }
 27 
 28 unsafe fn connect_listener(
 29     signal: *mut ffi::wl_signal,
 30     listener: *mut ffi::wl_listener,
 31     callback: unsafe extern "C" fn(listener: *mut ffi::wl_listener, data: *mut std::ffi::c_void),
 32 ) {
 33     let wl_lis = listener as *mut WlListener;
 34     (*wl_lis).notify = Some(callback);
 35     wl_signal_add(signal, listener);
 36 }
 37 
 38 unsafe fn wl_listener_remove_safe(listener: *mut ffi::wl_listener) {
 39     let prev = (*listener).link.prev;
 40     let next = (*listener).link.next;
 41     if !prev.is_null() && !next.is_null() && prev != listener as *mut ffi::wl_list && next != listener as *mut ffi::wl_list {
 42         ffi::wl_list_remove(&mut (*listener).link);
 43         (*listener).link.prev = std::ptr::null_mut();
 44         (*listener).link.next = std::ptr::null_mut();
 45     }
 46 }
 47 
 48 impl InputRelay {
 49     pub unsafe fn init(&mut self, seat: *mut Seat) {
 50         self.seat = seat;
 51         ffi::wl_list_init(&mut self.text_inputs);
 52         ffi::wl_list_init(&mut self.input_popups);
 53         self.input_method = std::ptr::null_mut();
 54         self.text_input = std::ptr::null_mut();
 55         self.input_method_commit = std::mem::zeroed();
 56         self.grab_keyboard = std::mem::zeroed();
 57         self.input_method_destroy = std::mem::zeroed();
 58         self.input_method_new_popup = std::mem::zeroed();
 59         self.grab_keyboard_destroy = std::mem::zeroed();
 60         // The relay starts as zeroed memory: write, don't assign over it.
 61         std::ptr::write(&mut self.osk, crate::osk::Osk::new((*seat).server));
 62     }
 63 
 64     pub unsafe fn new_input_method(&mut self, input_method: *mut ffi::wlr_input_method_v2) {
 65         let seat = crate::container_of!(self, Seat, relay);
 66         let seat_name = std::ffi::CStr::from_ptr(ffi::river_wlr_seat_get_name((*seat).wlr_seat))
 67             .to_str()
 68             .unwrap_or("unknown");
 69         log::debug!("new input method on seat {}", seat_name);
 70 
 71         if !self.input_method.is_null() {
 72             log::info!("seat {} already has an input method", seat_name);
 73             ffi::wlr_input_method_v2_send_unavailable(input_method);
 74             return;
 75         }
 76 
 77         self.input_method = input_method;
 78 
 79         connect_listener(&mut (*input_method).events.commit, &mut self.input_method_commit, handle_input_method_commit);
 80         connect_listener(&mut (*input_method).events.grab_keyboard, &mut self.grab_keyboard, handle_input_method_grab_keyboard);
 81         connect_listener(&mut (*input_method).events.destroy, &mut self.input_method_destroy, handle_input_method_destroy);
 82         connect_listener(&mut (*input_method).events.new_popup_surface, &mut self.input_method_new_popup, handle_input_method_new_popup);
 83 
 84         // Text inputs are entered whether or not an input method exists
 85         // (`focus`), so one that arrives late only needs telling about the
 86         // field already enabled.
 87         if !self.text_input.is_null() {
 88             ffi::wlr_input_method_v2_send_activate(input_method);
 89             self.send_input_method_state();
 90         }
 91     }
 92 
 93     pub unsafe fn disable_text_input(&mut self) {
 94         assert!(!self.text_input.is_null());
 95         self.text_input = std::ptr::null_mut();
 96         self.osk.field_gone();
 97 
 98         if !self.input_method.is_null() {
 99             let mut pos = self.input_popups.next;
100             let head_ptr = &self.input_popups as *const ffi::wl_list;
101             while pos != head_ptr as *mut ffi::wl_list {
102                 let next_pos = (*pos).next;
103                 let popup = crate::container_of!(pos, crate::input_popup::InputPopup, link);
104                 (*popup).update();
105                 pos = next_pos;
106             }
107 
108             ffi::wlr_input_method_v2_send_deactivate(self.input_method);
109             ffi::wlr_input_method_v2_send_done(self.input_method);
110         }
111     }
112 
113     pub unsafe fn send_input_method_state(&mut self) {
114         let input_method = self.input_method;
115         let wlr_text_input = (*self.text_input).wlr_text_input;
116 
117         let active_features = (*wlr_text_input).active_features;
118         let feature_surrounding = ffi::wlr_text_input_v3_features_WLR_TEXT_INPUT_V3_FEATURE_SURROUNDING_TEXT;
119         let feature_content = ffi::wlr_text_input_v3_features_WLR_TEXT_INPUT_V3_FEATURE_CONTENT_TYPE;
120 
121         if (active_features & feature_surrounding) != 0 {
122             let text = (*wlr_text_input).current.surrounding.text;
123             if !text.is_null() {
124                 ffi::wlr_input_method_v2_send_surrounding_text(
125                     input_method,
126                     text,
127                     (*wlr_text_input).current.surrounding.cursor,
128                     (*wlr_text_input).current.surrounding.anchor,
129                 );
130             }
131         }
132 
133         ffi::wlr_input_method_v2_send_text_change_cause(input_method, (*wlr_text_input).current.text_change_cause);
134 
135         if (active_features & feature_content) != 0 {
136             ffi::wlr_input_method_v2_send_content_type(
137                 input_method,
138                 (*wlr_text_input).current.content_type.hint,
139                 (*wlr_text_input).current.content_type.purpose,
140             );
141         }
142 
143         let mut pos = self.input_popups.next;
144         let head_ptr = &self.input_popups as *const ffi::wl_list;
145         while pos != head_ptr as *mut ffi::wl_list {
146             let next_pos = (*pos).next;
147             let popup = crate::container_of!(pos, crate::input_popup::InputPopup, link);
148             (*popup).update();
149             pos = next_pos;
150         }
151 
152         ffi::wlr_input_method_v2_send_done(input_method);
153     }
154 
155     /// Keyboard focus moved to `new_focus` (null: nowhere). Text inputs of
156     /// the client that lost it are left, and those of the client that got it
157     /// entered — whether or not an input method is running. river entered
158     /// them only with one, but then no client ever enabled a field, and the
159     /// on-screen keyboard (`osk.rs`) is driven by exactly that enable.
160     pub unsafe fn focus(&mut self, new_focus: *mut ffi::wlr_surface) {
161         let head_ptr = &self.text_inputs as *const ffi::wl_list as *mut ffi::wl_list;
162 
163         // Send leave events. A text input already on the new surface keeps
164         // it: a refocus of the same surface is not a leave.
165         let mut pos = self.text_inputs.next;
166         while pos != head_ptr {
167             let next_pos = (*pos).next;
168             let text_input = crate::container_of!(pos, TextInput, link);
169             let focused = (*(*text_input).wlr_text_input).focused_surface;
170             if !focused.is_null() && focused != new_focus {
171                 ffi::wlr_text_input_v3_send_leave((*text_input).wlr_text_input);
172             }
173             pos = next_pos;
174         }
175 
176         // Clear the enabled text input unless its surface kept focus
177         if !self.text_input.is_null() {
178             let focused = (*(*self.text_input).wlr_text_input).focused_surface;
179             if focused.is_null() || focused != new_focus {
180                 self.disable_text_input();
181             }
182         }
183 
184         if new_focus.is_null() {
185             return;
186         }
187         let new_client = ffi::wl_resource_get_client(ffi::river_wlr_surface_get_resource(new_focus));
188         let mut pos = self.text_inputs.next;
189         while pos != head_ptr {
190             let next_pos = (*pos).next;
191             let text_input = crate::container_of!(pos, TextInput, link);
192             let wlr_text_input = (*text_input).wlr_text_input;
193             if (*wlr_text_input).focused_surface.is_null()
194                 && ffi::wl_resource_get_client((*wlr_text_input).resource) == new_client
195             {
196                 ffi::wlr_text_input_v3_send_enter(wlr_text_input, new_focus);
197             }
198             pos = next_pos;
199         }
200     }
201 }
202 
203 unsafe extern "C" fn handle_input_method_commit(listener: *mut ffi::wl_listener, _data: *mut std::ffi::c_void) {
204     let relay = crate::container_of!(listener, InputRelay, input_method_commit);
205     let input_method = (*relay).input_method;
206 
207     if !(*input_method).client_active {
208         return;
209     }
210 
211     let text_input = (*relay).text_input;
212     if text_input.is_null() {
213         return;
214     }
215 
216     let preedit_text = (*input_method).current.preedit.text;
217     if !preedit_text.is_null() {
218         ffi::wlr_text_input_v3_send_preedit_string(
219             (*text_input).wlr_text_input,
220             preedit_text,
221             (*input_method).current.preedit.cursor_begin,
222             (*input_method).current.preedit.cursor_end,
223         );
224     }
225 
226     let commit_text = (*input_method).current.commit_text;
227     if !commit_text.is_null() {
228         ffi::wlr_text_input_v3_send_commit_string((*text_input).wlr_text_input, commit_text);
229     }
230 
231     if (*input_method).current.delete.before_length != 0
232         || (*input_method).current.delete.after_length != 0
233     {
234         ffi::wlr_text_input_v3_send_delete_surrounding_text(
235             (*text_input).wlr_text_input,
236             (*input_method).current.delete.before_length,
237             (*input_method).current.delete.after_length,
238         );
239     }
240 
241     ffi::wlr_text_input_v3_send_done((*text_input).wlr_text_input);
242 }
243 
244 unsafe extern "C" fn handle_input_method_destroy(listener: *mut ffi::wl_listener, _data: *mut std::ffi::c_void) {
245     let relay = crate::container_of!(listener, InputRelay, input_method_destroy);
246 
247     wl_listener_remove_safe(&mut (*relay).input_method_commit);
248     wl_listener_remove_safe(&mut (*relay).grab_keyboard);
249     wl_listener_remove_safe(&mut (*relay).input_method_destroy);
250     wl_listener_remove_safe(&mut (*relay).input_method_new_popup);
251     (*relay).input_method = std::ptr::null_mut();
252     // The text inputs stay entered and enabled: they never depended on the
253     // input method (`InputRelay::focus`).
254 }
255 
256 unsafe extern "C" fn handle_input_method_grab_keyboard(
257     listener: *mut ffi::wl_listener,
258     data: *mut std::ffi::c_void,
259 ) {
260     let relay = crate::container_of!(listener, InputRelay, grab_keyboard);
261     let keyboard_grab = data as *mut ffi::wlr_input_method_keyboard_grab_v2;
262     let seat = crate::container_of!(relay, Seat, relay);
263 
264     let active_keyboard = ffi::river_wlr_seat_get_keyboard((*seat).wlr_seat);
265     ffi::wlr_input_method_keyboard_grab_v2_set_keyboard(keyboard_grab, active_keyboard);
266 
267     connect_listener(
268         ffi::river_wlr_input_method_keyboard_grab_v2_get_destroy_signal(keyboard_grab),
269         &mut (*relay).grab_keyboard_destroy,
270         handle_input_method_grab_keyboard_destroy,
271     );
272 }
273 
274 unsafe extern "C" fn handle_input_method_new_popup(
275     listener: *mut ffi::wl_listener,
276     data: *mut std::ffi::c_void,
277 ) {
278     let relay = crate::container_of!(listener, InputRelay, input_method_new_popup);
279     let wlr_popup = data as *mut ffi::wlr_input_popup_surface_v2;
280 
281     if let Err(e) = crate::input_popup::InputPopup::create(wlr_popup, relay) {
282         log::error!("failed to create input popup: {}", e);
283     }
284 }
285 
286 unsafe extern "C" fn handle_input_method_grab_keyboard_destroy(
287     listener: *mut ffi::wl_listener,
288     _data: *mut std::ffi::c_void,
289 ) {
290     let relay = crate::container_of!(listener, InputRelay, grab_keyboard_destroy);
291     let input_method = (*relay).input_method;
292     let keyboard_grab = (*input_method).keyboard_grab;
293     wl_listener_remove_safe(&mut (*relay).grab_keyboard_destroy);
294 
295     let keyboard = ffi::river_wlr_input_method_keyboard_grab_v2_get_keyboard(keyboard_grab);
296     if !keyboard.is_null() {
297         let modifiers = ffi::river_wlr_keyboard_get_modifiers(keyboard);
298         ffi::wlr_seat_keyboard_notify_modifiers((*input_method).seat, modifiers);
299     }
300 }