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 }