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