1use crate::AHashMap;
7use alloc::vec::Vec;
8use std::ffi::CStr;
9use std::mem::MaybeUninit;
10use std::rc::Rc;
11use std::sync::Arc;
12use std::{convert::TryInto, os::raw::c_long};
13
14use crate::{
15 client::{handle_request, ClientCore, ClientError, ClientHandler},
16 Atoms,
17};
18use x11_dl::xlib;
19use xim_parser::{AttributeName, Request, XimWrite};
20
21impl<X: XlibRef> ClientCore for XlibClient<X> {
22 type XEvent = xlib::XKeyEvent;
23
24 #[inline]
25 fn ic_attributes(&self) -> &AHashMap<AttributeName, u16> {
26 &self.ic_attributes
27 }
28
29 #[inline]
30 fn im_attributes(&self) -> &AHashMap<AttributeName, u16> {
31 &self.im_attributes
32 }
33
34 #[inline]
35 fn serialize_event(&self, xev: &Self::XEvent) -> xim_parser::XEvent {
36 xim_parser::XEvent {
37 response_type: xev.type_ as u8,
38 detail: xev.keycode as u8,
39 sequence: xev.serial as _,
40 time: xev.time as u32,
41 root: xev.root as u32,
42 event: xev.window as u32,
43 child: xev.subwindow as u32,
44 root_x: xev.x_root as i16,
45 root_y: xev.y_root as i16,
46 event_x: xev.x as i16,
47 event_y: xev.y as i16,
48 state: xev.state as u16,
49 same_screen: xev.same_screen != 0,
50 }
51 }
52
53 #[inline]
54 fn deserialize_event(&self, xev: &xim_parser::XEvent) -> Self::XEvent {
55 xlib::XKeyEvent {
56 type_: xev.response_type as _,
57 keycode: xev.detail as _,
58 serial: xev.sequence as _,
59 time: xev.time as _,
60 root: xev.root as _,
61 window: xev.event as _,
62 subwindow: xev.child as _,
63 x_root: xev.root_x as _,
64 y_root: xev.root_y as _,
65 x: xev.event_x as _,
66 y: xev.event_y as _,
67 state: xev.state as _,
68 same_screen: xev.same_screen as i32,
69 display: self.display,
70 send_event: 0,
71 }
72 }
73
74 #[inline]
75 fn send_req(&mut self, req: xim_parser::Request) -> Result<(), ClientError> {
76 self.send_req_impl(req);
77 Ok(())
78 }
79
80 fn set_attrs(&mut self, ic_attrs: Vec<xim_parser::Attr>, im_attrs: Vec<xim_parser::Attr>) {
81 for im_attr in im_attrs {
82 self.im_attributes.insert(im_attr.name, im_attr.id);
83 }
84
85 for ic_attr in ic_attrs {
86 self.ic_attributes.insert(ic_attr.name, ic_attr.id);
87 }
88 }
89}
90
91impl<'a> XlibRef for &'a xlib::Xlib {
92 fn xlib(&self) -> &xlib::Xlib {
93 self
94 }
95}
96
97impl<X> XlibRef for Rc<X>
98where
99 X: XlibRef,
100{
101 fn xlib(&self) -> &xlib::Xlib {
102 (**self).xlib()
103 }
104}
105
106impl<X> XlibRef for Arc<X>
107where
108 X: XlibRef,
109{
110 fn xlib(&self) -> &xlib::Xlib {
111 (**self).xlib()
112 }
113}
114
115impl XlibRef for xlib::Xlib {
116 fn xlib(&self) -> &xlib::Xlib {
117 self
118 }
119}
120
121pub trait XlibRef {
122 fn xlib(&self) -> &xlib::Xlib;
123}
124
125pub struct XlibClient<X: XlibRef> {
126 x: X,
127 display: *mut xlib::Display,
128 im_window: xlib::Window,
129 server_owner_window: xlib::Window,
130 server_atom: xlib::Atom,
131 atoms: Atoms<xlib::Atom>,
132 transport_max: usize,
133 client_window: xlib::Window,
134 im_attributes: AHashMap<AttributeName, u16>,
135 ic_attributes: AHashMap<AttributeName, u16>,
136 buf: Vec<u8>,
137 sequence: u16,
138}
139
140impl<X: XlibRef> XlibClient<X> {
141 pub unsafe fn init(
147 x: X,
148 display: *mut xlib::Display,
149 im_name: Option<&str>,
150 ) -> Result<Self, ClientError> {
151 let xlib = x.xlib();
152 let root = (xlib.XDefaultRootWindow)(display);
153 let client_window = (xlib.XCreateSimpleWindow)(display, root, 0, 0, 1, 1, 0, 0, 0);
154
155 let var = std::env::var("XMODIFIERS").ok();
156 let var = var.as_ref().and_then(|n| n.strip_prefix("@im="));
157 let im_name = im_name.or(var).ok_or(ClientError::NoXimServer)?;
158
159 let atoms = Atoms::new_null::<ClientError, _>(|name| {
160 let atom = (xlib.XInternAtom)(display, name.as_ptr() as *const _, 0);
161 if atom == 0 {
162 Err(ClientError::InvalidReply)
163 } else {
164 Ok(atom)
165 }
166 })?;
167
168 let mut ty = MaybeUninit::uninit();
169 let mut format = MaybeUninit::uninit();
170 let mut items = MaybeUninit::uninit();
171 let mut bytes = MaybeUninit::uninit();
172 let mut prop = MaybeUninit::uninit();
173
174 let code = (xlib.XGetWindowProperty)(
175 display,
176 root,
177 atoms.XIM_SERVERS,
178 0,
179 i64::MAX,
180 xlib::False,
181 xlib::XA_ATOM,
182 ty.as_mut_ptr(),
183 format.as_mut_ptr(),
184 items.as_mut_ptr(),
185 bytes.as_mut_ptr(),
186 prop.as_mut_ptr(),
187 );
188
189 if code != 0 {
190 return Err(ClientError::InvalidReply);
191 }
192
193 let ty = ty.assume_init();
194 let format = format.assume_init();
195 let items = items.assume_init();
196 let _bytes = bytes.assume_init();
197 let prop = prop.assume_init() as *mut xlib::Atom;
198
199 if ty != xlib::XA_ATOM || format != 32 {
200 Err(ClientError::InvalidReply)
201 } else {
202 for i in 0..items {
203 let server_atom = prop.add(i as usize).read();
204 let server_owner = (xlib.XGetSelectionOwner)(display, server_atom);
205 let name_ptr = (xlib.XGetAtomName)(display, server_atom);
206 let name = CStr::from_ptr(name_ptr);
207 let name = match name.to_str() {
208 Ok(s) => s,
209 _ => continue,
210 };
211
212 if let Some(name) = name.strip_prefix("@server=") {
213 if name == im_name {
214 (xlib.XConvertSelection)(
215 display,
216 server_atom,
217 atoms.TRANSPORT,
218 atoms.TRANSPORT,
219 client_window,
220 xlib::CurrentTime,
221 );
222 (xlib.XFlush)(display);
223 (xlib.XFree)(name_ptr as _);
224 (xlib.XFree)(prop as _);
225
226 return Ok(Self {
227 atoms,
228 client_window,
229 server_atom,
230 server_owner_window: server_owner,
231 im_window: 0,
232 transport_max: 0,
233 display,
234 x,
235 ic_attributes: AHashMap::with_hasher(Default::default()),
236 im_attributes: AHashMap::with_hasher(Default::default()),
237 buf: Vec::with_capacity(1024),
238 sequence: 0,
239 });
240 }
241 } else {
242 (xlib.XFree)(name_ptr as _);
243 }
244 }
245
246 (xlib.XFree)(prop as _);
247
248 Err(ClientError::NoXimServer)
249 }
250 }
251
252 pub unsafe fn filter_event(
258 &mut self,
259 e: &xlib::XEvent,
260 handler: &mut impl ClientHandler<Self>,
261 ) -> Result<bool, ClientError> {
262 match e.get_type() {
263 xlib::SelectionNotify if e.selection.requestor == self.client_window => {
264 let mut ty = MaybeUninit::uninit();
265 let mut format = MaybeUninit::uninit();
266 let mut items = MaybeUninit::uninit();
267 let mut bytes = MaybeUninit::uninit();
268 let mut prop = MaybeUninit::uninit();
269 (self.x.xlib().XGetWindowProperty)(
270 self.display,
271 self.client_window,
272 self.atoms.TRANSPORT,
273 0,
274 i64::MAX,
275 xlib::True,
276 self.atoms.TRANSPORT,
277 ty.as_mut_ptr(),
278 format.as_mut_ptr(),
279 items.as_mut_ptr(),
280 bytes.as_mut_ptr(),
281 prop.as_mut_ptr(),
282 );
283
284 let _ty = ty.assume_init();
285 let _format = format.assume_init();
286 let items = items.assume_init();
287 let _bytes = bytes.assume_init();
288 let prop = prop.assume_init();
289
290 if e.selection.property == self.atoms.LOCALES {
291 self.xconnect();
293 } else if e.selection.property == self.atoms.TRANSPORT {
294 let transport = std::slice::from_raw_parts(prop, items as usize);
295
296 if !transport.starts_with(b"@transport=X/") {
297 return Err(ClientError::UnsupportedTransport);
298 }
299
300 (self.x.xlib().XConvertSelection)(
301 self.display,
302 self.server_atom,
303 self.atoms.LOCALES,
304 self.atoms.LOCALES,
305 self.client_window,
306 xlib::CurrentTime,
307 );
308 }
309
310 (self.x.xlib().XFree)(prop as _);
311
312 Ok(true)
313 }
314 xlib::ClientMessage if e.client_message.window == self.client_window => {
315 if e.client_message.message_type == self.atoms.XIM_XCONNECT {
316 let [im_window, major, minor, max, _]: [c_long; 5] =
317 e.client_message.data.as_longs().try_into().unwrap();
318
319 log::info!(
320 "XConnected server on {}, transport version: {}.{}, TRANSPORT_MAX: {}",
321 im_window,
322 major,
323 minor,
324 max
325 );
326
327 self.im_window = im_window as xlib::Window;
328 self.transport_max = max as usize;
329 self.send_req(Request::Connect {
330 client_major_protocol_version: 1,
331 client_minor_protocol_version: 0,
332 endian: xim_parser::Endian::Native,
333 client_auth_protocol_names: Vec::new(),
334 })?;
335
336 Ok(true)
337 } else if e.client_message.message_type == self.atoms.XIM_PROTOCOL {
338 self.handle_xim_protocol(&e.client_message, handler)?;
339 Ok(true)
340 } else {
341 Ok(false)
342 }
343 }
344 _ => Ok(false),
345 }
346 }
347
348 fn handle_xim_protocol(
349 &mut self,
350 msg: &xlib::XClientMessageEvent,
351 handler: &mut impl ClientHandler<Self>,
352 ) -> Result<(), ClientError> {
353 if msg.format == 32 {
354 let length = msg.data.get_long(0);
355 let atom = msg.data.get_long(1);
356
357 let mut ty = MaybeUninit::uninit();
358 let mut format = MaybeUninit::uninit();
359 let mut items = MaybeUninit::uninit();
360 let mut bytes = MaybeUninit::uninit();
361 let mut prop = MaybeUninit::uninit();
362
363 unsafe {
364 let code = (self.x.xlib().XGetWindowProperty)(
365 self.display,
366 msg.window,
367 atom as _,
368 0,
369 length,
370 xlib::True,
371 0,
372 ty.as_mut_ptr(),
373 format.as_mut_ptr(),
374 items.as_mut_ptr(),
375 bytes.as_mut_ptr(),
376 prop.as_mut_ptr(),
377 );
378
379 if code != 0 {
380 return Err(ClientError::InvalidReply);
381 }
382
383 let _ty = ty.assume_init();
384 let _format = format.assume_init();
385 let items = items.assume_init();
386 let _bytes = bytes.assume_init();
387 let prop = prop.assume_init();
388
389 if _bytes == 0 {
391 return Err(ClientError::InvalidReply);
392 }
393
394 let data = std::slice::from_raw_parts(prop, items as usize);
395
396 let req = xim_parser::read(data)?;
397
398 handle_request(self, handler, req)?;
399
400 (self.x.xlib().XFree)(prop as _);
401 }
402 } else if msg.format == 8 {
403 let bytes = msg.data.as_bytes();
404 let data: &[u8] =
405 unsafe { std::slice::from_raw_parts(bytes.as_ptr() as _, bytes.len()) };
406 let req = xim_parser::read(data)?;
407 handle_request(self, handler, req)?;
408 }
409
410 Ok(())
411 }
412
413 fn xconnect(&mut self) {
414 let mut ev = xlib::XClientMessageEvent {
415 display: self.display,
416 data: [self.client_window, 0, 0, 0, 0].into(),
417 format: 32,
418 message_type: self.atoms.XIM_XCONNECT,
419 serial: 0,
420 type_: xlib::ClientMessage,
421 send_event: xlib::True,
422 window: self.server_owner_window,
423 }
424 .into();
425
426 unsafe {
427 (self.x.xlib().XSendEvent)(
428 self.display,
429 self.server_owner_window,
430 xlib::False,
431 xlib::NoEventMask,
432 &mut ev,
433 );
434 }
435 }
436
437 fn send_req_impl(&mut self, req: Request) {
438 if log::log_enabled!(log::Level::Trace) {
439 log::trace!("->: {:?}", req);
440 } else {
441 log::debug!("->: {}", req.name());
442 }
443
444 self.buf.resize(req.size(), 0);
445 xim_parser::write(&req, &mut self.buf);
446
447 if self.buf.len() < self.transport_max {
448 if self.buf.len() > 20 {
449 todo!("multi-CM");
450 }
451 self.buf.resize(20, 0);
452 let buf: [u8; 20] = self.buf.as_slice().try_into().unwrap();
453 let mut ev = xlib::XClientMessageEvent {
454 type_: xlib::ClientMessage,
455 display: self.display,
456 message_type: self.atoms.XIM_PROTOCOL,
457 data: buf.into(),
458 format: 8,
459 serial: 0,
460 send_event: xlib::True,
461 window: self.im_window,
462 }
463 .into();
464 unsafe {
465 (self.x.xlib().XSendEvent)(
466 self.display,
467 self.im_window,
468 xlib::False,
469 xlib::NoEventMask,
470 &mut ev,
471 );
472 }
473 } else {
474 let name = alloc::format!("_XIM_DATA_{}\0", self.sequence);
475 self.sequence += 1;
476 let prop =
477 unsafe { (self.x.xlib().XInternAtom)(self.display, name.as_ptr().cast(), 0) };
478
479 unsafe {
480 (self.x.xlib().XChangeProperty)(
481 self.display,
482 self.im_window,
483 prop,
484 xlib::XA_STRING,
485 8,
486 xlib::PropModeAppend,
487 self.buf.as_ptr(),
488 self.buf.len() as _,
489 );
490 }
491 let mut ev = xlib::XClientMessageEvent {
492 type_: xlib::ClientMessage,
493 display: self.display,
494 message_type: self.atoms.XIM_PROTOCOL,
495 data: [self.buf.len() as _, prop, 0, 0, 0].into(),
496 format: 32,
497 serial: 0,
498 send_event: xlib::True,
499 window: self.im_window,
500 }
501 .into();
502 unsafe {
503 (self.x.xlib().XSendEvent)(
504 self.display,
505 self.im_window,
506 xlib::False,
507 xlib::NoEventMask,
508 &mut ev,
509 );
510 }
511 }
512 self.buf.clear();
513 }
514}