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}