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 dbg!("XIM: handle_connect called");
35 let result = client.open("C");
36 dbg!("XIM: handle_connect result", &result);
37 result
38 }
39
40 fn handle_open(&mut self, client: &mut C, input_method_id: u16) -> Result<(), ClientError> {
41 dbg!("XIM: handle_open called", input_method_id);
42 self.im_id = input_method_id;
43
44 let result = client.get_im_values(input_method_id, &[AttributeName::QueryInputStyle]);
45 dbg!("XIM: handle_open result", &result);
46 result
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(AttributeName::InputStyle, InputStyle::PREEDIT_CALLBACKS)
58 .push(AttributeName::ClientWindow, self.window)
59 .push(AttributeName::FocusWindow, self.window)
60 .build();
61 client.create_ic(input_method_id, ic_attributes)
62 }
63
64 fn handle_create_ic(
65 &mut self,
66 _client: &mut C,
67 _input_method_id: u16,
68 input_context_id: u16,
69 ) -> Result<(), ClientError> {
70 dbg!("XIM: handle_create_ic called", input_context_id);
71 self.connected = true;
72 self.ic_id = input_context_id;
73 dbg!("XIM: connection established", self.connected, self.ic_id);
74 Ok(())
75 }
76
77 fn handle_commit(
78 &mut self,
79 _client: &mut C,
80 _input_method_id: u16,
81 _input_context_id: u16,
82 text: &str,
83 ) -> Result<(), ClientError> {
84 dbg!("XIM: handle_commit called", text, self.window);
85 self.last_callback_event = Some(XimCallbackEvent::XimCommitEvent(
86 self.window,
87 String::from(text),
88 ));
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 dbg!("XIM: handle_forward_event called", xev.response_type);
101 match xev.response_type {
102 x11rb::protocol::xproto::KEY_PRESS_EVENT => {
103 dbg!("XIM: forwarding key press event");
104 self.last_callback_event = Some(XimCallbackEvent::XimXEvent(Event::KeyPress(xev)));
105 }
106 x11rb::protocol::xproto::KEY_RELEASE_EVENT => {
107 dbg!("XIM: forwarding key release event");
108 self.last_callback_event =
109 Some(XimCallbackEvent::XimXEvent(Event::KeyRelease(xev)));
110 }
111 _ => {
112 dbg!("XIM: ignoring event type", xev.response_type);
113 }
114 }
115 Ok(())
116 }
117
118 fn handle_close(&mut self, client: &mut C, _input_method_id: u16) -> Result<(), ClientError> {
119 dbg!("XIM: handle_close called");
120 // self.connected = false;
121 let result = client.disconnect();
122 dbg!("XIM: disconnect result", &result);
123 result
124 }
125
126 fn handle_preedit_draw(
127 &mut self,
128 _client: &mut C,
129 _input_method_id: u16,
130 _input_context_id: u16,
131 _caret: i32,
132 _chg_first: i32,
133 _chg_len: i32,
134 _status: xim::PreeditDrawStatus,
135 preedit_string: &str,
136 _feedbacks: Vec<xim::Feedback>,
137 ) -> Result<(), ClientError> {
138 dbg!(
139 "XIM: handle_preedit_draw called",
140 preedit_string,
141 self.window,
142 _caret,
143 _chg_first,
144 _chg_len
145 );
146 // XIMReverse: 1, XIMPrimary: 8, XIMTertiary: 32: selected text
147 // XIMUnderline: 2, XIMSecondary: 16: underlined text
148 // XIMHighlight: 4: normal text
149 // XIMVisibleToForward: 64, XIMVisibleToBackward: 128, XIMVisibleCenter: 256: text align position
150 // XIMPrimary, XIMHighlight, XIMSecondary, XIMTertiary are not specified,
151 // but interchangeable as above
152 // Currently there's no way to support these.
153 self.last_callback_event = Some(XimCallbackEvent::XimPreeditEvent(
154 self.window,
155 String::from(preedit_string),
156 ));
157 Ok(())
158 }
159}