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