1use std::default::Default;
2
3use x11rb::protocol::{xproto, Event};
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(
52 AttributeName::InputStyle,
53 InputStyle::PREEDIT_CALLBACKS
54 | InputStyle::STATUS_NOTHING
55 | InputStyle::PREEDIT_NONE,
56 )
57 .push(AttributeName::ClientWindow, self.window)
58 .push(AttributeName::FocusWindow, self.window)
59 .build();
60 client.create_ic(input_method_id, ic_attributes)
61 }
62
63 fn handle_create_ic(
64 &mut self,
65 _client: &mut C,
66 _input_method_id: u16,
67 input_context_id: u16,
68 ) -> Result<(), ClientError> {
69 self.connected = true;
70 self.ic_id = input_context_id;
71 Ok(())
72 }
73
74 fn handle_commit(
75 &mut self,
76 _client: &mut C,
77 _input_method_id: u16,
78 _input_context_id: u16,
79 text: &str,
80 ) -> Result<(), ClientError> {
81 self.last_callback_event = Some(XimCallbackEvent::XimCommitEvent(
82 self.window,
83 String::from(text),
84 ));
85 Ok(())
86 }
87
88 fn handle_forward_event(
89 &mut self,
90 _client: &mut C,
91 _input_method_id: u16,
92 _input_context_id: u16,
93 _flag: xim::ForwardEventFlag,
94 xev: C::XEvent,
95 ) -> Result<(), ClientError> {
96 match xev.response_type {
97 x11rb::protocol::xproto::KEY_PRESS_EVENT => {
98 self.last_callback_event = Some(XimCallbackEvent::XimXEvent(Event::KeyPress(xev)));
99 }
100 x11rb::protocol::xproto::KEY_RELEASE_EVENT => {
101 self.last_callback_event =
102 Some(XimCallbackEvent::XimXEvent(Event::KeyRelease(xev)));
103 }
104 _ => {}
105 }
106 Ok(())
107 }
108
109 fn handle_close(&mut self, client: &mut C, _input_method_id: u16) -> Result<(), ClientError> {
110 client.disconnect()
111 }
112
113 fn handle_destroy_ic(
114 &mut self,
115 client: &mut C,
116 input_method_id: u16,
117 _input_context_id: u16,
118 ) -> Result<(), ClientError> {
119 client.close(input_method_id)
120 }
121
122 fn handle_preedit_draw(
123 &mut self,
124 _client: &mut C,
125 _input_method_id: u16,
126 _input_context_id: u16,
127 _caret: i32,
128 _chg_first: i32,
129 _chg_len: i32,
130 _status: xim::PreeditDrawStatus,
131 preedit_string: &str,
132 _feedbacks: Vec<xim::Feedback>,
133 ) -> Result<(), ClientError> {
134 // XIMReverse: 1, XIMPrimary: 8, XIMTertiary: 32: selected text
135 // XIMUnderline: 2, XIMSecondary: 16: underlined text
136 // XIMHighlight: 4: normal text
137 // XIMVisibleToForward: 64, XIMVisibleToBackward: 128, XIMVisibleCenter: 256: text align position
138 // XIMPrimary, XIMHighlight, XIMSecondary, XIMTertiary are not specified,
139 // but interchangeable as above
140 // Currently there's no way to support these.
141 self.last_callback_event = Some(XimCallbackEvent::XimPreeditEvent(
142 self.window,
143 String::from(preedit_string),
144 ));
145 Ok(())
146 }
147}