1 // Important: all XIM calls need to happen from the same thread!
2 
3 mod inner;
4 mod input_method;
5 mod context;
6 mod callbacks;
7 
8 use std::sync::Arc;
9 use std::sync::mpsc::{Receiver, Sender};
10 
11 use super::{ffi, util, XConnection, XError};
12 
13 use self::inner::{close_im, ImeInner};
14 use self::input_method::PotentialInputMethods;
15 use self::context::{ImeContextCreationError, ImeContext};
16 use self::callbacks::*;
17 
18 pub type ImeReceiver = Receiver<(ffi::Window, i16, i16)>;
19 pub type ImeSender = Sender<(ffi::Window, i16, i16)>;
20 
21 #[derive(Debug)]
22 pub enum ImeCreationError {
23     OpenFailure(PotentialInputMethods),
24     SetDestroyCallbackFailed(XError),
25 }
26 
27 pub struct Ime {
28     xconn: Arc<XConnection>,
29     // The actual meat of this struct is boxed away, since it needs to have a fixed location in
30     // memory so we can pass a pointer to it around.
31     inner: Box<ImeInner>,
32 }
33 
34 impl Ime {
new(xconn: Arc<XConnection>) -> Result<Self, ImeCreationError>35     pub fn new(xconn: Arc<XConnection>) -> Result<Self, ImeCreationError> {
36         let potential_input_methods = PotentialInputMethods::new(&xconn);
37 
38         let (mut inner, client_data) = {
39             let mut inner = Box::new(ImeInner::new(
40                 xconn,
41                 potential_input_methods,
42             ));
43             let inner_ptr = Box::into_raw(inner);
44             let client_data = inner_ptr as _;
45             let destroy_callback = ffi::XIMCallback {
46                 client_data,
47                 callback: Some(xim_destroy_callback),
48             };
49             inner = unsafe { Box::from_raw(inner_ptr) };
50             inner.destroy_callback = destroy_callback;
51             (inner, client_data)
52         };
53 
54         let xconn = Arc::clone(&inner.xconn);
55 
56         let input_method = inner.potential_input_methods.open_im(&xconn, Some(&|| {
57             let _ = unsafe { set_instantiate_callback(&xconn, client_data) };
58         }));
59 
60         let is_fallback = input_method.is_fallback();
61         if let Some(input_method) = input_method.ok() {
62             inner.im = input_method.im;
63             inner.is_fallback = is_fallback;
64             unsafe {
65                 let result = set_destroy_callback(&xconn, input_method.im, &*inner)
66                     .map_err(ImeCreationError::SetDestroyCallbackFailed);
67                 if result.is_err() {
68                     let _ = close_im(&xconn, input_method.im);
69                 }
70                 result?;
71             }
72             Ok(Ime { xconn, inner })
73         } else {
74             Err(ImeCreationError::OpenFailure(inner.potential_input_methods))
75         }
76     }
77 
is_destroyed(&self) -> bool78     pub fn is_destroyed(&self) -> bool {
79         self.inner.is_destroyed
80     }
81 
82     // This pattern is used for various methods here:
83     // Ok(_) indicates that nothing went wrong internally
84     // Ok(true) indicates that the action was actually performed
85     // Ok(false) indicates that the action is not presently applicable
create_context(&mut self, window: ffi::Window) -> Result<bool, ImeContextCreationError>86     pub fn create_context(&mut self, window: ffi::Window)
87         -> Result<bool, ImeContextCreationError>
88     {
89         let context = if self.is_destroyed() {
90             // Create empty entry in map, so that when IME is rebuilt, this window has a context.
91             None
92         } else {
93             Some(unsafe { ImeContext::new(
94                 &self.inner.xconn,
95                 self.inner.im,
96                 window,
97                 None,
98             ) }?)
99         };
100         self.inner.contexts.insert(window, context);
101         Ok(!self.is_destroyed())
102     }
103 
get_context(&self, window: ffi::Window) -> Option<ffi::XIC>104     pub fn get_context(&self, window: ffi::Window) -> Option<ffi::XIC> {
105         if self.is_destroyed() {
106             return None;
107         }
108         if let Some(&Some(ref context)) = self.inner.contexts.get(&window) {
109             Some(context.ic)
110         } else {
111             None
112         }
113     }
114 
remove_context(&mut self, window: ffi::Window) -> Result<bool, XError>115     pub fn remove_context(&mut self, window: ffi::Window) -> Result<bool, XError> {
116         if let Some(Some(context)) = self.inner.contexts.remove(&window) {
117             unsafe {
118                 self.inner.destroy_ic_if_necessary(context.ic)?;
119             }
120             Ok(true)
121         } else {
122             Ok(false)
123         }
124     }
125 
focus(&mut self, window: ffi::Window) -> Result<bool, XError>126     pub fn focus(&mut self, window: ffi::Window) -> Result<bool, XError> {
127         if self.is_destroyed() {
128             return Ok(false);
129         }
130         if let Some(&mut Some(ref mut context)) = self.inner.contexts.get_mut(&window) {
131             context.focus(&self.xconn).map(|_| true)
132         } else {
133             Ok(false)
134         }
135     }
136 
unfocus(&mut self, window: ffi::Window) -> Result<bool, XError>137     pub fn unfocus(&mut self, window: ffi::Window) -> Result<bool, XError> {
138         if self.is_destroyed() {
139             return Ok(false);
140         }
141         if let Some(&mut Some(ref mut context)) = self.inner.contexts.get_mut(&window) {
142             context.unfocus(&self.xconn).map(|_| true)
143         } else {
144             Ok(false)
145         }
146     }
147 
send_xim_spot(&mut self, window: ffi::Window, x: i16, y: i16)148     pub fn send_xim_spot(&mut self, window: ffi::Window, x: i16, y: i16) {
149         if self.is_destroyed() {
150             return;
151         }
152         if let Some(&mut Some(ref mut context)) = self.inner.contexts.get_mut(&window) {
153             context.set_spot(&self.xconn, x as _, y as _);
154         }
155     }
156 }
157 
158 impl Drop for Ime {
drop(&mut self)159     fn drop(&mut self) {
160         unsafe {
161             let _ = self.inner.destroy_all_contexts_if_necessary();
162             let _ = self.inner.close_im_if_necessary();
163         }
164     }
165 }
166