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}