xim_handler.rs

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