1use alloc::format;
9use alloc::string::String;
10use alloc::vec::Vec;
11use std::{convert::TryInto, rc::Rc, sync::Arc};
12use x11rb::protocol::xproto::EventMask;
13
14#[cfg(feature = "x11rb-client")]
15use crate::client::{
16 handle_request as client_handle_request, ClientCore, ClientError, ClientHandler,
17};
18#[cfg(feature = "x11rb-server")]
19use crate::server::{ServerCore, ServerError, ServerHandler, XimConnection, XimConnections};
20#[cfg(feature = "x11rb-client")]
21use crate::AHashMap;
22#[cfg(feature = "x11rb-client")]
23use xim_parser::{Attr, AttributeName};
24
25use crate::Atoms;
26
27#[cfg(feature = "x11rb-xcb")]
28use x11rb::xcb_ffi::XCBConnection;
29
30#[allow(unused_imports)]
31use x11rb::{
32 connection::Connection,
33 errors::{ConnectError, ConnectionError, ParseError, ReplyError, ReplyOrIdError},
34 protocol::{
35 xproto::{
36 Atom, AtomEnum, ClientMessageEvent, ConnectionExt, KeyPressEvent, PropMode, Screen,
37 SelectionNotifyEvent, SelectionRequestEvent, Window, WindowClass, CLIENT_MESSAGE_EVENT,
38 SELECTION_NOTIFY_EVENT,
39 },
40 Event,
41 },
42 rust_connection::RustConnection,
43 wrapper::ConnectionExt as _,
44 COPY_DEPTH_FROM_PARENT, CURRENT_TIME,
45};
46
47use xim_parser::{Request, XimWrite};
48
49macro_rules! convert_error {
50 ($($ty:ty,)+) => {
51 $(
52 #[cfg(feature = "x11rb-client")]
53 impl From<$ty> for ClientError {
54 fn from(err: $ty) -> Self {
55 ClientError::Other(err.into())
56 }
57 }
58
59 #[cfg(feature = "x11rb-server")]
60 impl From<$ty> for ServerError {
61 fn from(err: $ty) -> Self {
62 ServerError::Other(err.into())
63 }
64 }
65 )+
66 };
67}
68
69convert_error!(
70 ConnectError,
71 ConnectionError,
72 ReplyError,
73 ReplyOrIdError,
74 ParseError,
75);
76
77pub trait HasConnection {
78 type Connection: Connection + ConnectionExt;
79
80 fn conn(&self) -> &Self::Connection;
81}
82
83#[cfg(feature = "x11rb-xcb")]
84impl HasConnection for XCBConnection {
85 type Connection = Self;
86
87 #[inline(always)]
88 fn conn(&self) -> &Self::Connection {
89 self
90 }
91}
92
93impl HasConnection for RustConnection {
94 type Connection = Self;
95
96 #[inline(always)]
97 fn conn(&self) -> &Self::Connection {
98 self
99 }
100}
101
102#[cfg(feature = "x11rb-client")]
103impl<C: HasConnection> HasConnection for X11rbClient<C> {
104 type Connection = C::Connection;
105
106 #[inline(always)]
107 fn conn(&self) -> &Self::Connection {
108 self.has_conn.conn()
109 }
110}
111
112#[cfg(feature = "x11rb-server")]
113impl<C: HasConnection> HasConnection for X11rbServer<C> {
114 type Connection = C::Connection;
115
116 #[inline(always)]
117 fn conn(&self) -> &Self::Connection {
118 self.has_conn.conn()
119 }
120}
121
122impl<'x, C: HasConnection> HasConnection for &'x C {
123 type Connection = C::Connection;
124
125 #[inline(always)]
126 fn conn(&self) -> &Self::Connection {
127 (**self).conn()
128 }
129}
130
131impl<C: HasConnection> HasConnection for Rc<C> {
132 type Connection = C::Connection;
133
134 #[inline(always)]
135 fn conn(&self) -> &Self::Connection {
136 (**self).conn()
137 }
138}
139
140impl<C: HasConnection> HasConnection for Arc<C> {
141 type Connection = C::Connection;
142
143 #[inline(always)]
144 fn conn(&self) -> &Self::Connection {
145 (**self).conn()
146 }
147}
148
149#[cfg(feature = "x11rb-server")]
150pub struct X11rbServer<C: HasConnection> {
151 has_conn: C,
152 locale_data: String,
153 im_win: Window,
154 atoms: Atoms<Atom>,
155 buf: Vec<u8>,
156 sequence: u16,
157}
158
159#[cfg(feature = "x11rb-server")]
160impl<C: HasConnection> X11rbServer<C> {
161 pub fn init(
162 has_conn: C,
163 screen_num: usize,
164 im_name: &str,
165 locales: &str,
166 ) -> Result<Self, ServerError> {
167 let im_name = format!("@server={}", im_name);
168 let conn = has_conn.conn();
169 let screen = &conn.setup().roots[screen_num];
170 let im_win = conn.generate_id()?;
171 conn.create_window(
172 COPY_DEPTH_FROM_PARENT,
173 im_win,
174 screen.root,
175 0,
176 0,
177 1,
178 1,
179 0,
180 WindowClass::INPUT_ONLY,
181 screen.root_visual,
182 &Default::default(),
183 )?;
184 let atoms = Atoms::new::<ServerError, _>(|name| {
185 Ok(conn.intern_atom(false, name.as_bytes())?.reply()?.atom)
186 })?;
187
188 let reply = conn
189 .get_property(
190 false,
191 screen.root,
192 atoms.XIM_SERVERS,
193 AtomEnum::ATOM,
194 0,
195 u32::MAX,
196 )?
197 .reply()?;
198
199 if reply.type_ != x11rb::NONE && (reply.type_ != u32::from(AtomEnum::ATOM)) {
200 return Err(ServerError::InvalidReply);
201 }
202
203 let server_name = conn.intern_atom(false, im_name.as_bytes())?.reply()?.atom;
204
205 let mut found = false;
206
207 if reply.type_ != x11rb::NONE {
208 for prop in reply.value32().ok_or(ServerError::InvalidReply)? {
209 if prop == server_name {
210 log::info!("Found previous XIM_SERVER it will overrided");
211 found = true;
212 }
213 }
214 }
215
216 conn.set_selection_owner(im_win, server_name, x11rb::CURRENT_TIME)?;
218
219 if !found {
220 conn.change_property32(
221 PropMode::PREPEND,
222 screen.root,
223 atoms.XIM_SERVERS,
224 AtomEnum::ATOM,
225 &[server_name],
226 )?;
227 }
228
229 conn.flush()?;
230
231 log::info!("Start server win: {}", im_win);
232
233 Ok(Self {
234 has_conn,
235 locale_data: format!("@locale={}", locales),
236 im_win,
237 atoms,
238 buf: Vec::with_capacity(1024),
239 sequence: 0,
240 })
241 }
242
243 pub fn filter_event<T>(
244 &mut self,
245 e: &Event,
246 connections: &mut XimConnections<T>,
247 handler: &mut impl ServerHandler<Self, InputContextData = T>,
248 ) -> Result<bool, ServerError> {
249 match e {
250 Event::SelectionRequest(req) if req.owner == self.im_win => {
251 if req.property == self.atoms.LOCALES {
252 log::trace!("Selection notify locale");
253 self.send_selection_notify(req, &self.locale_data)?;
254 } else if req.property == self.atoms.TRANSPORT {
255 log::trace!("Selection notify transport");
256 self.send_selection_notify(req, "@transport=X/")?;
257 }
258 Ok(true)
259 }
260 Event::ClientMessage(msg) => {
261 if msg.type_ == self.atoms.XIM_XCONNECT {
262 let com_win = self.conn().generate_id()?;
263 self.conn().create_window(
264 COPY_DEPTH_FROM_PARENT,
265 com_win,
266 self.im_win,
267 0,
268 0,
269 1,
270 1,
271 0,
272 WindowClass::INPUT_ONLY,
273 0,
274 &Default::default(),
275 )?;
276 let client_win = msg.data.as_data32()[0];
277 log::info!("XConnected with {}", client_win);
278 self.conn().send_event(
279 false,
280 client_win,
281 EventMask::NO_EVENT,
282 ClientMessageEvent {
283 format: 32,
284 type_: self.atoms.XIM_XCONNECT,
285 data: [com_win, 0, 0, 0, 0].into(),
286 response_type: CLIENT_MESSAGE_EVENT,
287 sequence: 0,
288 window: client_win,
289 },
290 )?;
291 self.conn().flush()?;
292 connections.new_connection(com_win, client_win);
293 } else if msg.type_ == self.atoms.XIM_PROTOCOL {
294 if let Some(connection) = connections.get_connection(msg.window) {
295 self.handle_xim_protocol(msg, connection, handler)?;
296 if connection.disconnected {
297 connections.remove_connection(msg.window);
298 }
299 } else {
300 log::warn!("Unknown connection");
301 }
302 }
303
304 Ok(true)
305 }
306 _ => Ok(false),
307 }
308 }
309
310 fn handle_xim_protocol<T>(
311 &mut self,
312 msg: &ClientMessageEvent,
313 connection: &mut XimConnection<T>,
314 handler: &mut impl ServerHandler<Self, InputContextData = T>,
315 ) -> Result<(), ServerError> {
316 if msg.format == 32 {
317 let [length, atom, ..] = msg.data.as_data32();
318 let data = self
319 .conn()
320 .get_property(true, msg.window, atom, AtomEnum::ANY, 0, length)?
321 .reply()?
322 .value;
323 let req = xim_parser::read(&data)?;
324 connection.handle_request(self, req, handler)
325 } else {
326 let req = xim_parser::read(&msg.data.as_data8())?;
327 connection.handle_request(self, req, handler)
328 }
329 }
330
331 fn send_selection_notify(
332 &self,
333 req: &SelectionRequestEvent,
334 data: &str,
335 ) -> Result<(), ServerError> {
336 let e = SelectionNotifyEvent {
337 response_type: SELECTION_NOTIFY_EVENT,
338 property: req.property,
339 time: req.time,
340 target: req.target,
341 selection: req.selection,
342 requestor: req.requestor,
343 sequence: 0,
344 };
345
346 self.conn().change_property8(
347 PropMode::REPLACE,
348 req.requestor,
349 req.property,
350 req.target,
351 data.as_bytes(),
352 )?;
353 self.conn()
354 .send_event(false, req.requestor, EventMask::NO_EVENT, e)?;
355 self.conn().flush()?;
356
357 Ok(())
358 }
359}
360
361#[cfg(feature = "x11rb-server")]
362impl<C: HasConnection> ServerCore for X11rbServer<C> {
363 type XEvent = KeyPressEvent;
364
365 fn send_req(&mut self, client_win: u32, req: Request) -> Result<(), ServerError> {
366 send_req_impl(
367 &self.has_conn,
368 &self.atoms,
369 client_win,
370 &mut self.buf,
371 &mut self.sequence,
372 20,
373 &req,
374 )
375 }
376
377 #[inline]
378 fn deserialize_event(&self, ev: &xim_parser::XEvent) -> Self::XEvent {
379 deserialize_event_impl(ev)
380 }
381}
382
383#[cfg(feature = "x11rb-client")]
384pub struct X11rbClient<C: HasConnection> {
385 has_conn: C,
386 server_owner_window: Window,
387 im_window: Window,
388 server_atom: Atom,
389 atoms: Atoms<Atom>,
390 transport_max: usize,
391 client_window: u32,
392 im_attributes: AHashMap<AttributeName, u16>,
393 ic_attributes: AHashMap<AttributeName, u16>,
394 sequence: u16,
395 buf: Vec<u8>,
396}
397
398#[cfg(feature = "x11rb-client")]
399impl<C: HasConnection> X11rbClient<C> {
400 pub fn init(
401 has_conn: C,
402 screen_num: usize,
403 im_name: Option<&str>,
404 ) -> Result<Self, ClientError> {
405 let conn = has_conn.conn();
406 let screen = &conn.setup().roots[screen_num];
407 let client_window = conn.generate_id()?;
408
409 conn.create_window(
410 COPY_DEPTH_FROM_PARENT,
411 client_window,
412 screen.root,
413 0,
414 0,
415 1,
416 1,
417 0,
418 WindowClass::INPUT_ONLY,
419 screen.root_visual,
420 &Default::default(),
421 )?;
422
423 let var = std::env::var("XMODIFIERS").ok();
424 let var = var.as_ref().and_then(|n| n.strip_prefix("@im="));
425 let im_name = im_name.or(var).ok_or(ClientError::NoXimServer)?;
426
427 log::info!("Try connect {}", im_name);
428
429 let atoms = Atoms::new::<ClientError, _>(|name| {
430 Ok(conn.intern_atom(false, name.as_bytes())?.reply()?.atom)
431 })?;
432 let server_reply = conn
433 .get_property(
434 false,
435 screen.root,
436 atoms.XIM_SERVERS,
437 AtomEnum::ATOM,
438 0,
439 u32::MAX,
440 )?
441 .reply()?;
442
443 if server_reply.type_ != u32::from(AtomEnum::ATOM) || server_reply.format != 32 {
444 Err(ClientError::InvalidReply)
445 } else {
446 for server_atom in server_reply.value32().ok_or(ClientError::InvalidReply)? {
447 let server_owner = conn.get_selection_owner(server_atom)?.reply()?.owner;
448 let name = conn.get_atom_name(server_atom)?.reply()?.name;
449
450 let name = match String::from_utf8(name) {
451 Ok(name) => name,
452 _ => continue,
453 };
454
455 if let Some(name) = name.strip_prefix("@server=") {
456 if name == im_name {
457 conn.convert_selection(
458 client_window,
459 server_atom,
460 atoms.TRANSPORT,
461 atoms.TRANSPORT,
462 CURRENT_TIME,
463 )?;
464
465 conn.flush()?;
466
467 return Ok(Self {
468 has_conn,
469 atoms,
470 server_atom,
471 server_owner_window: server_owner,
472 im_attributes: AHashMap::with_hasher(Default::default()),
473 ic_attributes: AHashMap::with_hasher(Default::default()),
474 im_window: x11rb::NONE,
475 transport_max: 20,
476 client_window,
477 sequence: 0,
478 buf: Vec::with_capacity(1024),
479 });
480 }
481 }
482 }
483
484 Err(ClientError::NoXimServer)
485 }
486 }
487
488 pub fn filter_event(
489 &mut self,
490 e: &Event,
491 handler: &mut impl ClientHandler<Self>,
492 ) -> Result<bool, ClientError> {
493 match e {
494 Event::SelectionNotify(e) if e.requestor == self.client_window => {
495 if e.property == self.atoms.LOCALES {
496 let _locale = self
498 .conn()
499 .get_property(
500 true,
501 self.client_window,
502 self.atoms.LOCALES,
503 self.atoms.LOCALES,
504 0,
505 u32::MAX,
506 )?
507 .reply()?;
508
509 self.xconnect()?;
510
511 Ok(true)
512 } else if e.property == self.atoms.TRANSPORT {
513 let transport = self
514 .conn()
515 .get_property(
516 true,
517 self.client_window,
518 self.atoms.TRANSPORT,
519 self.atoms.TRANSPORT,
520 0,
521 u32::MAX,
522 )?
523 .reply()?;
524
525 if !transport.value.starts_with(b"@transport=X/") {
526 return Err(ClientError::UnsupportedTransport);
527 }
528
529 self.conn().convert_selection(
530 self.client_window,
531 self.server_atom,
532 self.atoms.LOCALES,
533 self.atoms.LOCALES,
534 CURRENT_TIME,
535 )?;
536
537 self.conn().flush()?;
538
539 Ok(true)
540 } else {
541 Ok(false)
542 }
543 }
544 Event::ClientMessage(msg) if msg.window == self.client_window => {
545 if msg.type_ == self.atoms.XIM_XCONNECT {
546 let [im_window, major, minor, max, _] = msg.data.as_data32();
547 log::info!(
548 "XConnected server on {}, transport version: {}.{}, TRANSPORT_MAX: {}",
549 im_window,
550 major,
551 minor,
552 max
553 );
554 self.im_window = im_window;
555 self.transport_max = max as usize;
556 self.send_req(Request::Connect {
557 client_major_protocol_version: 1,
558 client_minor_protocol_version: 0,
559 endian: xim_parser::Endian::Native,
560 client_auth_protocol_names: Vec::new(),
561 })?;
562 Ok(true)
563 } else if msg.type_ == self.atoms.XIM_PROTOCOL {
564 self.handle_xim_protocol(msg, handler)?;
565 Ok(true)
566 } else {
567 Ok(false)
568 }
569 }
570 Event::Error(ref err)
571 if err.error_kind == x11rb::protocol::ErrorKind::Window
572 && err.bad_value == self.im_window =>
573 {
574 Err(ClientError::NoXimServer)
575 }
576 _ => Ok(false),
577 }
578 }
579
580 fn handle_xim_protocol(
581 &mut self,
582 msg: &ClientMessageEvent,
583 handler: &mut impl ClientHandler<Self>,
584 ) -> Result<(), ClientError> {
585 if msg.format == 32 {
586 let [length, atom, ..] = msg.data.as_data32();
587 let reply = self
588 .conn()
589 .get_property(true, msg.window, atom, AtomEnum::ANY, 0, length)?
590 .reply()?;
591 if reply.value_len == 0 {
593 return Err(ClientError::InvalidReply);
594 }
595 let data = reply.value;
596 let req = xim_parser::read(&data)?;
597 client_handle_request(self, handler, req)?;
598 } else if msg.format == 8 {
599 let data = msg.data.as_data8();
600 let req: xim_parser::Request = xim_parser::read(&data)?;
601 client_handle_request(self, handler, req)?;
602 }
603
604 Ok(())
605 }
606
607 fn xconnect(&mut self) -> Result<(), ClientError> {
608 self.conn().send_event(
609 false,
610 self.server_owner_window,
611 EventMask::NO_EVENT,
612 ClientMessageEvent {
613 data: [self.client_window, 0, 0, 0, 0].into(),
614 format: 32,
615 response_type: CLIENT_MESSAGE_EVENT,
616 sequence: 0,
617 type_: self.atoms.XIM_XCONNECT,
618 window: self.server_owner_window,
619 },
620 )?;
621
622 self.conn().flush()?;
623
624 Ok(())
625 }
626}
627
628#[cfg(feature = "x11rb-client")]
629impl<C: HasConnection> ClientCore for X11rbClient<C> {
630 type XEvent = KeyPressEvent;
631 fn set_attrs(&mut self, im_attrs: Vec<Attr>, ic_attrs: Vec<Attr>) {
632 for im_attr in im_attrs {
633 self.im_attributes.insert(im_attr.name, im_attr.id);
634 }
635
636 for ic_attr in ic_attrs {
637 self.ic_attributes.insert(ic_attr.name, ic_attr.id);
638 }
639 }
640
641 #[inline]
642 fn ic_attributes(&self) -> &AHashMap<AttributeName, u16> {
643 &self.ic_attributes
644 }
645
646 #[inline]
647 fn im_attributes(&self) -> &AHashMap<AttributeName, u16> {
648 &self.im_attributes
649 }
650
651 #[inline]
652 fn serialize_event(&self, xev: &Self::XEvent) -> xim_parser::XEvent {
653 xim_parser::XEvent {
654 response_type: xev.response_type,
655 detail: xev.detail,
656 sequence: xev.sequence,
657 time: xev.time,
658 root: xev.root,
659 event: xev.event,
660 child: xev.child,
661 root_x: xev.root_x,
662 root_y: xev.root_y,
663 event_x: xev.event_x,
664 event_y: xev.event_y,
665 state: xev.state.into(),
666 same_screen: xev.same_screen,
667 }
668 }
669
670 #[inline]
671 fn deserialize_event(&self, xev: &xim_parser::XEvent) -> Self::XEvent {
672 deserialize_event_impl(xev)
673 }
674
675 #[inline]
676 fn send_req(&mut self, req: Request) -> Result<(), ClientError> {
677 send_req_impl(
678 &self.has_conn,
679 &self.atoms,
680 self.im_window,
681 &mut self.buf,
682 &mut self.sequence,
683 self.transport_max,
684 &req,
685 )
686 }
687}
688
689fn send_req_impl<C: HasConnection, E: From<ConnectionError> + From<ReplyError>>(
690 c: &C,
691 atoms: &Atoms<Atom>,
692 target: Window,
693 buf: &mut Vec<u8>,
694 sequence: &mut u16,
695 transport_max: usize,
696 req: &Request,
697) -> Result<(), E> {
698 if log::log_enabled!(log::Level::Trace) {
699 log::trace!("->: {:?}", req);
700 } else {
701 log::debug!("->: {}", req.name());
702 }
703 buf.resize(req.size(), 0);
704 xim_parser::write(req, buf);
705
706 if buf.len() < transport_max {
707 if buf.len() > 20 {
708 todo!("multi-CM");
709 }
710 buf.resize(20, 0);
711 let buf: [u8; 20] = buf.as_slice().try_into().unwrap();
712 c.conn().send_event(
713 false,
714 target,
715 EventMask::NO_EVENT,
716 ClientMessageEvent {
717 response_type: CLIENT_MESSAGE_EVENT,
718 data: buf.into(),
719 format: 8,
720 sequence: 0,
721 type_: atoms.XIM_PROTOCOL,
722 window: target,
723 },
724 )?;
725 } else {
726 let prop = c
727 .conn()
728 .intern_atom(false, format!("_XIM_DATA_{}", sequence).as_bytes())?
729 .reply()?
730 .atom;
731 *sequence = sequence.wrapping_add(1);
732 c.conn().change_property(
733 PropMode::APPEND,
734 target,
735 prop,
736 AtomEnum::STRING,
737 8,
738 buf.len() as u32,
739 buf,
740 )?;
741 c.conn().send_event(
742 false,
743 target,
744 EventMask::NO_EVENT,
745 ClientMessageEvent {
746 data: [buf.len() as u32, prop, 0, 0, 0].into(),
747 format: 32,
748 sequence: 0,
749 response_type: CLIENT_MESSAGE_EVENT,
750 type_: atoms.XIM_PROTOCOL,
751 window: target,
752 },
753 )?;
754 }
755 buf.clear();
756 c.conn().flush()?;
757 Ok(())
758}
759
760#[inline]
761fn deserialize_event_impl(xev: &xim_parser::XEvent) -> KeyPressEvent {
762 KeyPressEvent {
763 response_type: xev.response_type,
764 detail: xev.detail,
765 sequence: xev.sequence,
766 time: xev.time,
767 root: xev.root,
768 event: xev.event,
769 child: xev.child,
770 root_x: xev.root_x,
771 root_y: xev.root_y,
772 event_x: xev.event_x,
773 event_y: xev.event_y,
774 state: xev.state.into(),
775 same_screen: xev.same_screen,
776 }
777}