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