| 1 | // Important: all XIM calls need to happen from the same thread! |
| 2 | |
| 3 | mod callbacks; |
| 4 | mod context; |
| 5 | mod inner; |
| 6 | mod input_method; |
| 7 | |
| 8 | use std::sync::mpsc::{Receiver, Sender}; |
| 9 | use std::sync::Arc; |
| 10 | |
| 11 | #[cfg (feature = "serde" )] |
| 12 | use serde::{Deserialize, Serialize}; |
| 13 | |
| 14 | use self::callbacks::*; |
| 15 | use self::context::ImeContext; |
| 16 | pub use self::context::ImeContextCreationError; |
| 17 | use self::inner::{close_im, ImeInner}; |
| 18 | use self::input_method::PotentialInputMethods; |
| 19 | use super::{ffi, util, XConnection, XError}; |
| 20 | |
| 21 | #[derive (Debug, Clone, PartialEq, Eq, Hash)] |
| 22 | #[cfg_attr (feature = "serde" , derive(Serialize, Deserialize))] |
| 23 | pub enum ImeEvent { |
| 24 | Enabled, |
| 25 | Start, |
| 26 | Update(String, usize), |
| 27 | End, |
| 28 | Disabled, |
| 29 | } |
| 30 | |
| 31 | pub type ImeReceiver = Receiver<ImeRequest>; |
| 32 | pub type ImeSender = Sender<ImeRequest>; |
| 33 | pub type ImeEventReceiver = Receiver<(ffi::Window, ImeEvent)>; |
| 34 | pub type ImeEventSender = Sender<(ffi::Window, ImeEvent)>; |
| 35 | |
| 36 | /// Request to control XIM handler from the window. |
| 37 | pub 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)] |
| 46 | pub(crate) enum ImeCreationError { |
| 47 | // Boxed to prevent large error type |
| 48 | OpenFailure(Box<PotentialInputMethods>), |
| 49 | SetDestroyCallbackFailed(#[allow (dead_code)] XError), |
| 50 | } |
| 51 | |
| 52 | pub(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 | |
| 59 | impl 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 | |
| 226 | impl 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 | } |
| 234 | |