xim_handler.rs

  1use std::default::Default;
  2
  3use x11rb::protocol::{Event, xproto};
  4use xim::{AHashMap, AttributeName, Client, ClientError, ClientHandler, InputStyle};
  5
  6pub enum XimCallbackEvent {
  7    XimXEvent(x11rb::protocol::Event),
  8    XimPreeditEvent(xproto::Window, String),
  9    XimCommitEvent(xproto::Window, String),
 10}
 11
 12pub struct XimHandler {
 13    pub im_id: u16,
 14    pub ic_id: u16,
 15    pub connected: bool,
 16    pub window: xproto::Window,
 17    pub last_callback_event: Option<XimCallbackEvent>,
 18}
 19
 20impl XimHandler {
 21    pub fn new() -> Self {
 22        Self {
 23            im_id: Default::default(),
 24            ic_id: Default::default(),
 25            connected: false,
 26            window: Default::default(),
 27            last_callback_event: None,
 28        }
 29    }
 30}
 31
 32impl<C: Client<XEvent = xproto::KeyPressEvent>> ClientHandler<C> for XimHandler {
 33    fn handle_connect(&mut self, client: &mut C) -> Result<(), ClientError> {
 34        client.open("C")
 35    }
 36
 37    fn handle_open(&mut self, client: &mut C, input_method_id: u16) -> Result<(), ClientError> {
 38        self.im_id = input_method_id;
 39
 40        client.get_im_values(input_method_id, &[AttributeName::QueryInputStyle])
 41    }
 42
 43    fn handle_get_im_values(
 44        &mut self,
 45        client: &mut C,
 46        input_method_id: u16,
 47        _attributes: AHashMap<AttributeName, Vec<u8>>,
 48    ) -> Result<(), ClientError> {
 49        let ic_attributes = client
 50            .build_ic_attributes()
 51            .push(AttributeName::InputStyle, InputStyle::PREEDIT_CALLBACKS)
 52            .push(AttributeName::ClientWindow, self.window)
 53            .push(AttributeName::FocusWindow, self.window)
 54            .build();
 55        client.create_ic(input_method_id, ic_attributes)
 56    }
 57
 58    fn handle_create_ic(
 59        &mut self,
 60        _client: &mut C,
 61        _input_method_id: u16,
 62        input_context_id: u16,
 63    ) -> Result<(), ClientError> {
 64        self.connected = true;
 65        self.ic_id = input_context_id;
 66        Ok(())
 67    }
 68
 69    fn handle_commit(
 70        &mut self,
 71        _client: &mut C,
 72        _input_method_id: u16,
 73        _input_context_id: u16,
 74        text: &str,
 75    ) -> Result<(), ClientError> {
 76        self.last_callback_event = Some(XimCallbackEvent::XimCommitEvent(
 77            self.window,
 78            String::from(text),
 79        ));
 80        Ok(())
 81    }
 82
 83    fn handle_forward_event(
 84        &mut self,
 85        _client: &mut C,
 86        _input_method_id: u16,
 87        _input_context_id: u16,
 88        _flag: xim::ForwardEventFlag,
 89        xev: C::XEvent,
 90    ) -> Result<(), ClientError> {
 91        match xev.response_type {
 92            x11rb::protocol::xproto::KEY_PRESS_EVENT => {
 93                self.last_callback_event = Some(XimCallbackEvent::XimXEvent(Event::KeyPress(xev)));
 94            }
 95            x11rb::protocol::xproto::KEY_RELEASE_EVENT => {
 96                self.last_callback_event =
 97                    Some(XimCallbackEvent::XimXEvent(Event::KeyRelease(xev)));
 98            }
 99            _ => {}
100        }
101        Ok(())
102    }
103
104    fn handle_close(&mut self, client: &mut C, _input_method_id: u16) -> Result<(), ClientError> {
105        client.disconnect()
106    }
107
108    fn handle_preedit_draw(
109        &mut self,
110        _client: &mut C,
111        _input_method_id: u16,
112        _input_context_id: u16,
113        _caret: i32,
114        _chg_first: i32,
115        _chg_len: i32,
116        _status: xim::PreeditDrawStatus,
117        preedit_string: &str,
118        _feedbacks: Vec<xim::Feedback>,
119    ) -> Result<(), ClientError> {
120        // XIMReverse: 1, XIMPrimary: 8, XIMTertiary: 32: selected text
121        // XIMUnderline: 2, XIMSecondary: 16: underlined text
122        // XIMHighlight: 4: normal text
123        // XIMVisibleToForward: 64, XIMVisibleToBackward: 128, XIMVisibleCenter: 256: text align position
124        // XIMPrimary, XIMHighlight, XIMSecondary, XIMTertiary are not specified,
125        // but interchangeable as above
126        // Currently there's no way to support these.
127        self.last_callback_event = Some(XimCallbackEvent::XimPreeditEvent(
128            self.window,
129            String::from(preedit_string),
130        ));
131        Ok(())
132    }
133}