xim_handler.rs

  1use std::cell::RefCell;
  2use std::default::Default;
  3use std::rc::Rc;
  4
  5use calloop::channel;
  6
  7use x11rb::protocol::{xproto, Event};
  8use xim::{AHashMap, AttributeName, Client, ClientError, ClientHandler, InputStyle, Point};
  9
 10use crate::{Keystroke, PlatformInput, X11ClientState};
 11
 12pub enum XimCallbackEvent {
 13    XimXEvent(x11rb::protocol::Event),
 14    XimPreeditEvent(xproto::Window, String),
 15    XimCommitEvent(xproto::Window, String),
 16}
 17
 18pub struct XimHandler {
 19    pub im_id: u16,
 20    pub ic_id: u16,
 21    pub xim_tx: channel::Sender<XimCallbackEvent>,
 22    pub connected: bool,
 23    pub window: xproto::Window,
 24}
 25
 26impl XimHandler {
 27    pub fn new(xim_tx: channel::Sender<XimCallbackEvent>) -> Self {
 28        Self {
 29            im_id: Default::default(),
 30            ic_id: Default::default(),
 31            xim_tx,
 32            connected: false,
 33            window: Default::default(),
 34        }
 35    }
 36}
 37
 38impl<C: Client<XEvent = xproto::KeyPressEvent>> ClientHandler<C> for XimHandler {
 39    fn handle_connect(&mut self, client: &mut C) -> Result<(), ClientError> {
 40        client.open("C")
 41    }
 42
 43    fn handle_open(&mut self, client: &mut C, input_method_id: u16) -> Result<(), ClientError> {
 44        self.im_id = input_method_id;
 45
 46        client.get_im_values(input_method_id, &[AttributeName::QueryInputStyle])
 47    }
 48
 49    fn handle_get_im_values(
 50        &mut self,
 51        client: &mut C,
 52        input_method_id: u16,
 53        _attributes: AHashMap<AttributeName, Vec<u8>>,
 54    ) -> Result<(), ClientError> {
 55        let ic_attributes = client
 56            .build_ic_attributes()
 57            .push(
 58                AttributeName::InputStyle,
 59                InputStyle::PREEDIT_CALLBACKS
 60                    | InputStyle::STATUS_NOTHING
 61                    | InputStyle::PREEDIT_NONE,
 62            )
 63            .push(AttributeName::ClientWindow, self.window)
 64            .push(AttributeName::FocusWindow, self.window)
 65            .build();
 66        client.create_ic(input_method_id, ic_attributes)
 67    }
 68
 69    fn handle_create_ic(
 70        &mut self,
 71        _client: &mut C,
 72        _input_method_id: u16,
 73        input_context_id: u16,
 74    ) -> Result<(), ClientError> {
 75        self.connected = true;
 76        self.ic_id = input_context_id;
 77        Ok(())
 78    }
 79
 80    fn handle_commit(
 81        &mut self,
 82        _client: &mut C,
 83        _input_method_id: u16,
 84        _input_context_id: u16,
 85        text: &str,
 86    ) -> Result<(), ClientError> {
 87        self.xim_tx.send(XimCallbackEvent::XimCommitEvent(
 88            self.window,
 89            String::from(text),
 90        ));
 91        Ok(())
 92    }
 93
 94    fn handle_forward_event(
 95        &mut self,
 96        _client: &mut C,
 97        _input_method_id: u16,
 98        _input_context_id: u16,
 99        _flag: xim::ForwardEventFlag,
100        xev: C::XEvent,
101    ) -> Result<(), ClientError> {
102        match (xev.response_type) {
103            x11rb::protocol::xproto::KEY_PRESS_EVENT => {
104                self.xim_tx
105                    .send(XimCallbackEvent::XimXEvent(Event::KeyPress(xev)));
106            }
107            x11rb::protocol::xproto::KEY_RELEASE_EVENT => {
108                self.xim_tx
109                    .send(XimCallbackEvent::XimXEvent(Event::KeyRelease(xev)));
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        let mark_range = self.xim_tx.send(XimCallbackEvent::XimPreeditEvent(
149            self.window,
150            String::from(preedit_string),
151        ));
152        Ok(())
153    }
154}