winit/platform_impl/linux/x11/ime/
callbacks.rs
1use std::collections::HashMap;
2use std::os::raw::c_char;
3use std::ptr;
4use std::sync::Arc;
5
6use super::context::{ImeContext, ImeContextCreationError};
7use super::inner::{close_im, ImeInner};
8use super::input_method::PotentialInputMethods;
9use super::{ffi, XConnection, XError};
10
11pub(crate) unsafe fn xim_set_callback(
12 xconn: &Arc<XConnection>,
13 xim: ffi::XIM,
14 field: *const c_char,
15 callback: *mut ffi::XIMCallback,
16) -> Result<(), XError> {
17 unsafe { (xconn.xlib.XSetIMValues)(xim, field, callback, ptr::null_mut::<()>()) };
20 xconn.check_errors()
21}
22
23pub(crate) unsafe fn set_instantiate_callback(
30 xconn: &Arc<XConnection>,
31 client_data: ffi::XPointer,
32) -> Result<(), XError> {
33 unsafe {
34 (xconn.xlib.XRegisterIMInstantiateCallback)(
35 xconn.display,
36 ptr::null_mut(),
37 ptr::null_mut(),
38 ptr::null_mut(),
39 Some(xim_instantiate_callback),
40 client_data,
41 )
42 };
43 xconn.check_errors()
44}
45
46pub(crate) unsafe fn unset_instantiate_callback(
47 xconn: &Arc<XConnection>,
48 client_data: ffi::XPointer,
49) -> Result<(), XError> {
50 unsafe {
51 (xconn.xlib.XUnregisterIMInstantiateCallback)(
52 xconn.display,
53 ptr::null_mut(),
54 ptr::null_mut(),
55 ptr::null_mut(),
56 Some(xim_instantiate_callback),
57 client_data,
58 )
59 };
60 xconn.check_errors()
61}
62
63pub(crate) unsafe fn set_destroy_callback(
64 xconn: &Arc<XConnection>,
65 im: ffi::XIM,
66 inner: &ImeInner,
67) -> Result<(), XError> {
68 unsafe {
69 xim_set_callback(
70 xconn,
71 im,
72 ffi::XNDestroyCallback_0.as_ptr() as *const _,
73 &inner.destroy_callback as *const _ as *mut _,
74 )
75 }
76}
77
78#[derive(Debug)]
79#[allow(clippy::enum_variant_names)]
80enum ReplaceImError {
81 MethodOpenFailed(#[allow(dead_code)] Box<PotentialInputMethods>),
83 ContextCreationFailed(#[allow(dead_code)] ImeContextCreationError),
84 SetDestroyCallbackFailed(#[allow(dead_code)] XError),
85}
86
87unsafe fn replace_im(inner: *mut ImeInner) -> Result<(), ReplaceImError> {
91 let xconn = unsafe { &(*inner).xconn };
92
93 let (new_im, is_fallback) = {
94 let new_im = unsafe { (*inner).potential_input_methods.open_im(xconn, None) };
95 let is_fallback = new_im.is_fallback();
96 (
97 new_im.ok().ok_or_else(|| {
98 ReplaceImError::MethodOpenFailed(Box::new(unsafe {
99 (*inner).potential_input_methods.clone()
100 }))
101 })?,
102 is_fallback,
103 )
104 };
105
106 {
109 let result = unsafe { set_destroy_callback(xconn, new_im.im, &*inner) };
110 if result.is_err() {
111 let _ = unsafe { close_im(xconn, new_im.im) };
112 }
113 result
114 }
115 .map_err(ReplaceImError::SetDestroyCallbackFailed)?;
116
117 let mut new_contexts = HashMap::new();
118 for (window, old_context) in unsafe { (*inner).contexts.iter() } {
119 let spot = old_context.as_ref().map(|old_context| old_context.ic_spot);
120
121 let is_allowed =
123 old_context.as_ref().map(|old_context| old_context.is_allowed()).unwrap_or_default();
124
125 let style = if is_allowed { new_im.preedit_style } else { new_im.none_style };
128
129 let new_context = {
130 let result = unsafe {
131 ImeContext::new(
132 xconn,
133 new_im.im,
134 style,
135 *window,
136 spot,
137 (*inner).event_sender.clone(),
138 )
139 };
140 if result.is_err() {
141 let _ = unsafe { close_im(xconn, new_im.im) };
142 }
143 result.map_err(ReplaceImError::ContextCreationFailed)?
144 };
145 new_contexts.insert(*window, Some(new_context));
146 }
147
148 unsafe {
150 let _ = (*inner).destroy_all_contexts_if_necessary();
151 let _ = (*inner).close_im_if_necessary();
152 (*inner).im = Some(new_im);
153 (*inner).contexts = new_contexts;
154 (*inner).is_destroyed = false;
155 (*inner).is_fallback = is_fallback;
156 }
157 Ok(())
158}
159
160pub unsafe extern "C" fn xim_instantiate_callback(
161 _display: *mut ffi::Display,
162 client_data: ffi::XPointer,
163 _call_data: ffi::XPointer,
165) {
166 let inner: *mut ImeInner = client_data as _;
167 if !inner.is_null() {
168 let xconn = unsafe { &(*inner).xconn };
169 match unsafe { replace_im(inner) } {
170 Ok(()) => unsafe {
171 let _ = unset_instantiate_callback(xconn, client_data);
172 (*inner).is_fallback = false;
173 },
174 Err(err) => unsafe {
175 if (*inner).is_destroyed {
176 panic!("Failed to reopen input method: {err:?}");
178 }
179 },
180 }
181 }
182}
183
184pub unsafe extern "C" fn xim_destroy_callback(
188 _xim: ffi::XIM,
189 client_data: ffi::XPointer,
190 _call_data: ffi::XPointer,
192) {
193 let inner: *mut ImeInner = client_data as _;
194 if !inner.is_null() {
195 unsafe { (*inner).is_destroyed = true };
196 let xconn = unsafe { &(*inner).xconn };
197 if unsafe { !(*inner).is_fallback } {
198 let _ = unsafe { set_instantiate_callback(xconn, client_data) };
199 match unsafe { replace_im(inner) } {
201 Ok(()) => unsafe { (*inner).is_fallback = true },
202 Err(err) => {
203 panic!("Failed to open fallback input method: {err:?}");
205 },
206 }
207 }
208 }
209}