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

src/server/input_relay.rs (11K)

  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 
 25 unsafe fn connect_listener(
 26     signal: *mut ffi::wl_signal,
 27     listener: *mut ffi::wl_listener,
 28     callback: unsafe extern "C" fn(listener: *mut ffi::wl_listener, data: *mut std::ffi::c_void),
 29 ) {
 30     let wl_lis = listener as *mut WlListener;
 31     (*wl_lis).notify = Some(callback);
 32     wl_signal_add(signal, listener);
 33 }
 34 
 35 unsafe fn wl_listener_remove_safe(listener: *mut ffi::wl_listener) {
 36     let prev = (*listener).link.prev;
 37     let next = (*listener).link.next;
 38     if !prev.is_null() && !next.is_null() && prev != listener as *mut ffi::wl_list && next != listener as *mut ffi::wl_list {
 39         ffi::wl_list_remove(&mut (*listener).link);
 40         (*listener).link.prev = std::ptr::null_mut();
 41         (*listener).link.next = std::ptr::null_mut();
 42     }
 43 }
 44 
 45 impl InputRelay {
 46     pub unsafe fn init(&mut self, seat: *mut Seat) {
 47         self.seat = seat;
 48         ffi::wl_list_init(&mut self.text_inputs);
 49         ffi::wl_list_init(&mut self.input_popups);
 50         self.input_method = std::ptr::null_mut();
 51         self.text_input = std::ptr::null_mut();
 52         self.input_method_commit = std::mem::zeroed();
 53         self.grab_keyboard = std::mem::zeroed();
 54         self.input_method_destroy = std::mem::zeroed();
 55         self.input_method_new_popup = std::mem::zeroed();
 56         self.grab_keyboard_destroy = std::mem::zeroed();
 57     }
 58 
 59     pub unsafe fn new_input_method(&mut self, input_method: *mut ffi::wlr_input_method_v2) {
 60         let seat = crate::container_of!(self, Seat, relay);
 61         let seat_name = std::ffi::CStr::from_ptr(ffi::river_wlr_seat_get_name((*seat).wlr_seat))
 62             .to_str()
 63             .unwrap_or("unknown");
 64         log::debug!("new input method on seat {}", seat_name);
 65 
 66         if !self.input_method.is_null() {
 67             log::info!("seat {} already has an input method", seat_name);
 68             ffi::wlr_input_method_v2_send_unavailable(input_method);
 69             return;
 70         }
 71 
 72         self.input_method = input_method;
 73 
 74         connect_listener(&mut (*input_method).events.commit, &mut self.input_method_commit, handle_input_method_commit);
 75         connect_listener(&mut (*input_method).events.grab_keyboard, &mut self.grab_keyboard, handle_input_method_grab_keyboard);
 76         connect_listener(&mut (*input_method).events.destroy, &mut self.input_method_destroy, handle_input_method_destroy);
 77         connect_listener(&mut (*input_method).events.new_popup_surface, &mut self.input_method_new_popup, handle_input_method_new_popup);
 78 
 79         let focused_surface = (*seat).focused.surface();
 80         if !focused_surface.is_null() {
 81             self.focus(focused_surface);
 82         }
 83     }
 84 
 85     pub unsafe fn disable_text_input(&mut self) {
 86         assert!(!self.text_input.is_null());
 87         self.text_input = std::ptr::null_mut();
 88 
 89         if !self.input_method.is_null() {
 90             let mut pos = self.input_popups.next;
 91             let head_ptr = &self.input_popups as *const ffi::wl_list;
 92             while pos != head_ptr as *mut ffi::wl_list {
 93                 let next_pos = (*pos).next;
 94                 let popup = crate::container_of!(pos, crate::input_popup::InputPopup, link);
 95                 (*popup).update();
 96                 pos = next_pos;
 97             }
 98 
 99             ffi::wlr_input_method_v2_send_deactivate(self.input_method);
100             ffi::wlr_input_method_v2_send_done(self.input_method);
101         }
102     }
103 
104     pub unsafe fn send_input_method_state(&mut self) {
105         let input_method = self.input_method;
106         let wlr_text_input = (*self.text_input).wlr_text_input;
107 
108         let active_features = (*wlr_text_input).active_features;
109         let feature_surrounding = ffi::wlr_text_input_v3_features_WLR_TEXT_INPUT_V3_FEATURE_SURROUNDING_TEXT;
110         let feature_content = ffi::wlr_text_input_v3_features_WLR_TEXT_INPUT_V3_FEATURE_CONTENT_TYPE;
111 
112         if (active_features & feature_surrounding) != 0 {
113             let text = (*wlr_text_input).current.surrounding.text;
114             if !text.is_null() {
115                 ffi::wlr_input_method_v2_send_surrounding_text(
116                     input_method,
117                     text,
118                     (*wlr_text_input).current.surrounding.cursor,
119                     (*wlr_text_input).current.surrounding.anchor,
120                 );
121             }
122         }
123 
124         ffi::wlr_input_method_v2_send_text_change_cause(input_method, (*wlr_text_input).current.text_change_cause);
125 
126         if (active_features & feature_content) != 0 {
127             ffi::wlr_input_method_v2_send_content_type(
128                 input_method,
129                 (*wlr_text_input).current.content_type.hint,
130                 (*wlr_text_input).current.content_type.purpose,
131             );
132         }
133 
134         let mut pos = self.input_popups.next;
135         let head_ptr = &self.input_popups as *const ffi::wl_list;
136         while pos != head_ptr as *mut ffi::wl_list {
137             let next_pos = (*pos).next;
138             let popup = crate::container_of!(pos, crate::input_popup::InputPopup, link);
139             (*popup).update();
140             pos = next_pos;
141         }
142 
143         ffi::wlr_input_method_v2_send_done(input_method);
144     }
145 
146     pub unsafe fn focus(&mut self, new_focus: *mut ffi::wlr_surface) {
147         // Send leave events
148         let mut pos = self.text_inputs.next;
149         let head_ptr = &self.text_inputs as *const ffi::wl_list;
150         while pos != head_ptr as *mut ffi::wl_list {
151             let next_pos = (*pos).next;
152             let text_input = crate::container_of!(pos, TextInput, link);
153             let focused = (*(*text_input).wlr_text_input).focused_surface;
154             if !focused.is_null() {
155                 assert!(focused != new_focus);
156                 ffi::wlr_text_input_v3_send_leave((*text_input).wlr_text_input);
157             }
158             pos = next_pos;
159         }
160 
161         // Clear currently enabled text input
162         if !self.text_input.is_null() {
163             self.disable_text_input();
164         }
165 
166         // Send enter events if we have an input method
167         if !new_focus.is_null() && !self.input_method.is_null() {
168             let new_client = ffi::wl_resource_get_client(ffi::river_wlr_surface_get_resource(new_focus));
169             let mut pos = self.text_inputs.next;
170             while pos != head_ptr as *mut ffi::wl_list {
171                 let next_pos = (*pos).next;
172                 let text_input = crate::container_of!(pos, TextInput, link);
173                 let text_input_resource = (*(*text_input).wlr_text_input).resource;
174                 let client = ffi::wl_resource_get_client(text_input_resource);
175                 if client == new_client {
176                     ffi::wlr_text_input_v3_send_enter((*text_input).wlr_text_input, new_focus);
177                 }
178                 pos = next_pos;
179             }
180         }
181     }
182 }
183 
184 unsafe extern "C" fn handle_input_method_commit(listener: *mut ffi::wl_listener, _data: *mut std::ffi::c_void) {
185     let relay = crate::container_of!(listener, InputRelay, input_method_commit);
186     let input_method = (*relay).input_method;
187 
188     if !(*input_method).client_active {
189         return;
190     }
191 
192     let text_input = (*relay).text_input;
193     if text_input.is_null() {
194         return;
195     }
196 
197     let preedit_text = (*input_method).current.preedit.text;
198     if !preedit_text.is_null() {
199         ffi::wlr_text_input_v3_send_preedit_string(
200             (*text_input).wlr_text_input,
201             preedit_text,
202             (*input_method).current.preedit.cursor_begin,
203             (*input_method).current.preedit.cursor_end,
204         );
205     }
206 
207     let commit_text = (*input_method).current.commit_text;
208     if !commit_text.is_null() {
209         ffi::wlr_text_input_v3_send_commit_string((*text_input).wlr_text_input, commit_text);
210     }
211 
212     if (*input_method).current.delete.before_length != 0
213         || (*input_method).current.delete.after_length != 0
214     {
215         ffi::wlr_text_input_v3_send_delete_surrounding_text(
216             (*text_input).wlr_text_input,
217             (*input_method).current.delete.before_length,
218             (*input_method).current.delete.after_length,
219         );
220     }
221 
222     ffi::wlr_text_input_v3_send_done((*text_input).wlr_text_input);
223 }
224 
225 unsafe extern "C" fn handle_input_method_destroy(listener: *mut ffi::wl_listener, _data: *mut std::ffi::c_void) {
226     let relay = crate::container_of!(listener, InputRelay, input_method_destroy);
227 
228     wl_listener_remove_safe(&mut (*relay).input_method_commit);
229     wl_listener_remove_safe(&mut (*relay).grab_keyboard);
230     wl_listener_remove_safe(&mut (*relay).input_method_destroy);
231     wl_listener_remove_safe(&mut (*relay).input_method_new_popup);
232     (*relay).input_method = std::ptr::null_mut();
233 
234     (*relay).focus(std::ptr::null_mut());
235 
236     assert!((*relay).text_input.is_null());
237 }
238 
239 unsafe extern "C" fn handle_input_method_grab_keyboard(
240     listener: *mut ffi::wl_listener,
241     data: *mut std::ffi::c_void,
242 ) {
243     let relay = crate::container_of!(listener, InputRelay, grab_keyboard);
244     let keyboard_grab = data as *mut ffi::wlr_input_method_keyboard_grab_v2;
245     let seat = crate::container_of!(relay, Seat, relay);
246 
247     let active_keyboard = ffi::river_wlr_seat_get_keyboard((*seat).wlr_seat);
248     ffi::wlr_input_method_keyboard_grab_v2_set_keyboard(keyboard_grab, active_keyboard);
249 
250     connect_listener(
251         ffi::river_wlr_input_method_keyboard_grab_v2_get_destroy_signal(keyboard_grab),
252         &mut (*relay).grab_keyboard_destroy,
253         handle_input_method_grab_keyboard_destroy,
254     );
255 }
256 
257 unsafe extern "C" fn handle_input_method_new_popup(
258     listener: *mut ffi::wl_listener,
259     data: *mut std::ffi::c_void,
260 ) {
261     let relay = crate::container_of!(listener, InputRelay, input_method_new_popup);
262     let wlr_popup = data as *mut ffi::wlr_input_popup_surface_v2;
263 
264     if let Err(e) = crate::input_popup::InputPopup::create(wlr_popup, relay) {
265         log::error!("failed to create input popup: {}", e);
266     }
267 }
268 
269 unsafe extern "C" fn handle_input_method_grab_keyboard_destroy(
270     listener: *mut ffi::wl_listener,
271     _data: *mut std::ffi::c_void,
272 ) {
273     let relay = crate::container_of!(listener, InputRelay, grab_keyboard_destroy);
274     let input_method = (*relay).input_method;
275     let keyboard_grab = (*input_method).keyboard_grab;
276     wl_listener_remove_safe(&mut (*relay).grab_keyboard_destroy);
277 
278     let keyboard = ffi::river_wlr_input_method_keyboard_grab_v2_get_keyboard(keyboard_grab);
279     if !keyboard.is_null() {
280         let modifiers = ffi::river_wlr_keyboard_get_modifiers(keyboard);
281         ffi::wlr_seat_keyboard_notify_modifiers((*input_method).seat, modifiers);
282     }
283 }