winit/platform_impl/linux/x11/ime/
mod.rs

1// Important: all XIM calls need to happen from the same thread!
2
3mod callbacks;
4mod context;
5mod inner;
6mod input_method;
7
8use std::sync::mpsc::{Receiver, Sender};
9use std::sync::Arc;
10
11#[cfg(feature = "serde")]
12use serde::{Deserialize, Serialize};
13use tracing::debug;
14
15use self::callbacks::*;
16use self::context::ImeContext;
17pub use self::context::ImeContextCreationError;
18use self::inner::{close_im, ImeInner};
19use self::input_method::{PotentialInputMethods, Style};
20use super::{ffi, util, XConnection, XError};
21
22#[derive(Debug, Clone, PartialEq, Eq, Hash)]
23#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
24pub enum ImeEvent {
25    Enabled,
26    Start,
27    Update(String, usize),
28    End,
29    Disabled,
30}
31
32pub type ImeReceiver = Receiver<ImeRequest>;
33pub type ImeSender = Sender<ImeRequest>;
34pub type ImeEventReceiver = Receiver<(ffi::Window, ImeEvent)>;
35pub type ImeEventSender = Sender<(ffi::Window, ImeEvent)>;
36
37/// Request to control XIM handler from the window.
38pub enum ImeRequest {
39    /// Set IME spot position for given `window_id`.
40    Position(ffi::Window, i16, i16),
41
42    /// Allow IME input for the given `window_id`.
43    Allow(ffi::Window, bool),
44}
45
46#[derive(Debug)]
47pub(crate) enum ImeCreationError {
48    // Boxed to prevent large error type
49    OpenFailure(Box<PotentialInputMethods>),
50    SetDestroyCallbackFailed(#[allow(dead_code)] XError),
51}
52
53pub(crate) struct Ime {
54    xconn: Arc<XConnection>,
55    // The actual meat of this struct is boxed away, since it needs to have a fixed location in
56    // memory so we can pass a pointer to it around.
57    inner: Box<ImeInner>,
58}
59
60impl Ime {
61    pub fn new(
62        xconn: Arc<XConnection>,
63        event_sender: ImeEventSender,
64    ) -> Result<Self, ImeCreationError> {
65        let potential_input_methods = PotentialInputMethods::new(&xconn);
66
67        let (mut inner, client_data) = {
68            let mut inner = Box::new(ImeInner::new(xconn, potential_input_methods, event_sender));
69            let inner_ptr = Box::into_raw(inner);
70            let client_data = inner_ptr as _;
71            let destroy_callback =
72                ffi::XIMCallback { client_data, callback: Some(xim_destroy_callback) };
73            inner = unsafe { Box::from_raw(inner_ptr) };
74            inner.destroy_callback = destroy_callback;
75            (inner, client_data)
76        };
77
78        let xconn = Arc::clone(&inner.xconn);
79
80        let input_method = inner.potential_input_methods.open_im(
81            &xconn,
82            Some(&|| {
83                let _ = unsafe { set_instantiate_callback(&xconn, client_data) };
84            }),
85        );
86
87        let is_fallback = input_method.is_fallback();
88        if let Some(input_method) = input_method.ok() {
89            inner.is_fallback = is_fallback;
90            unsafe {
91                let result = set_destroy_callback(&xconn, input_method.im, &inner)
92                    .map_err(ImeCreationError::SetDestroyCallbackFailed);
93                if result.is_err() {
94                    let _ = close_im(&xconn, input_method.im);
95                }
96                result?;
97            }
98            inner.im = Some(input_method);
99            Ok(Ime { xconn, inner })
100        } else {
101            Err(ImeCreationError::OpenFailure(Box::new(inner.potential_input_methods)))
102        }
103    }
104
105    pub fn is_destroyed(&self) -> bool {
106        self.inner.is_destroyed
107    }
108
109    // This pattern is used for various methods here:
110    // Ok(_) indicates that nothing went wrong internally
111    // Ok(true) indicates that the action was actually performed
112    // Ok(false) indicates that the action is not presently applicable
113    pub fn create_context(
114        &mut self,
115        window: ffi::Window,
116        with_preedit: bool,
117    ) -> Result<bool, ImeContextCreationError> {
118        let context = if self.is_destroyed() {
119            // Create empty entry in map, so that when IME is rebuilt, this window has a context.
120            None
121        } else {
122            let im = self.inner.im.as_ref().unwrap();
123            let style = if with_preedit { im.preedit_style } else { im.none_style };
124
125            let context = unsafe {
126                ImeContext::new(
127                    &self.inner.xconn,
128                    im.im,
129                    style,
130                    window,
131                    None,
132                    self.inner.event_sender.clone(),
133                )?
134            };
135
136            // Check the state on the context, since it could fail to enable or disable preedit.
137            let event = if matches!(style, Style::None(_)) {
138                if with_preedit {
139                    debug!("failed to create IME context with preedit support.")
140                }
141                ImeEvent::Disabled
142            } else {
143                if !with_preedit {
144                    debug!("failed to create IME context without preedit support.")
145                }
146                ImeEvent::Enabled
147            };
148
149            self.inner.event_sender.send((window, event)).expect("Failed to send enabled event");
150
151            Some(context)
152        };
153
154        self.inner.contexts.insert(window, context);
155        Ok(!self.is_destroyed())
156    }
157
158    pub fn get_context(&self, window: ffi::Window) -> Option<ffi::XIC> {
159        if self.is_destroyed() {
160            return None;
161        }
162        if let Some(Some(context)) = self.inner.contexts.get(&window) {
163            Some(context.ic)
164        } else {
165            None
166        }
167    }
168
169    pub fn remove_context(&mut self, window: ffi::Window) -> Result<bool, XError> {
170        if let Some(Some(context)) = self.inner.contexts.remove(&window) {
171            unsafe {
172                self.inner.destroy_ic_if_necessary(context.ic)?;
173            }
174            Ok(true)
175        } else {
176            Ok(false)
177        }
178    }
179
180    pub fn focus(&mut self, window: ffi::Window) -> Result<bool, XError> {
181        if self.is_destroyed() {
182            return Ok(false);
183        }
184        if let Some(&mut Some(ref mut context)) = self.inner.contexts.get_mut(&window) {
185            context.focus(&self.xconn).map(|_| true)
186        } else {
187            Ok(false)
188        }
189    }
190
191    pub fn unfocus(&mut self, window: ffi::Window) -> Result<bool, XError> {
192        if self.is_destroyed() {
193            return Ok(false);
194        }
195        if let Some(&mut Some(ref mut context)) = self.inner.contexts.get_mut(&window) {
196            context.unfocus(&self.xconn).map(|_| true)
197        } else {
198            Ok(false)
199        }
200    }
201
202    pub fn send_xim_spot(&mut self, window: ffi::Window, x: i16, y: i16) {
203        if self.is_destroyed() {
204            return;
205        }
206        if let Some(&mut Some(ref mut context)) = self.inner.contexts.get_mut(&window) {
207            context.set_spot(&self.xconn, x as _, y as _);
208        }
209    }
210
211    pub fn set_ime_allowed(&mut self, window: ffi::Window, allowed: bool) {
212        if self.is_destroyed() {
213            return;
214        }
215
216        if let Some(&mut Some(ref mut context)) = self.inner.contexts.get_mut(&window) {
217            if allowed == context.is_allowed() {
218                return;
219            }
220        }
221
222        // Remove context for that window.
223        let _ = self.remove_context(window);
224
225        // Create new context supporting IME input.
226        let _ = self.create_context(window, allowed);
227    }
228}
229
230impl Drop for Ime {
231    fn drop(&mut self) {
232        unsafe {
233            let _ = self.inner.destroy_all_contexts_if_necessary();
234            let _ = self.inner.close_im_if_necessary();
235        }
236    }
237}