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}