1use std::ffi::CStr;
6use std::io::{Error as IOError, ErrorKind, IoSlice};
7use std::os::raw::c_int;
8#[cfg(unix)]
9use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
10use std::ptr::{null, null_mut};
11use std::sync::{atomic::Ordering, Mutex};
12
13use libc::c_void;
14
15use crate::connection::{
16 compute_length_field, Connection, ReplyOrError, RequestConnection, RequestKind,
17};
18use crate::cookie::{Cookie, CookieWithFds, VoidCookie};
19use crate::errors::DisplayParsingError;
20pub use crate::errors::{ConnectError, ConnectionError, ParseError, ReplyError, ReplyOrIdError};
21use crate::extension_manager::ExtensionManager;
22use crate::protocol::xproto::Setup;
23use crate::utils::{CSlice, RawFdContainer};
24use crate::x11_utils::{ExtensionInformation, TryParse, TryParseFd};
25
26use x11rb_protocol::{DiscardMode, SequenceNumber};
27
28mod atomic_u64;
29mod pending_errors;
30mod raw_ffi;
31
32use atomic_u64::AtomicU64;
33#[cfg(all(not(test), feature = "dl-libxcb"))]
34pub use raw_ffi::libxcb_library::load_libxcb;
35
36type Buffer = <XCBConnection as RequestConnection>::Buf;
37pub type RawEventAndSeqNumber = x11rb_protocol::RawEventAndSeqNumber<Buffer>;
39pub type BufWithFds = crate::connection::BufWithFds<Buffer>;
41
42#[allow(clippy::upper_case_acronyms)]
47#[derive(Debug)]
48pub struct XCBConnection<Conn = raw_ffi::XcbConnectionWrapper> {
49 conn: Conn,
50 setup: Setup,
51 ext_mgr: Mutex<ExtensionManager>,
52 errors: pending_errors::PendingErrors,
53 maximum_sequence_received: AtomicU64,
54}
55
56impl XCBConnection {
57 pub fn connect(dpy_name: Option<&CStr>) -> Result<(XCBConnection, usize), ConnectError> {
63 use libc::c_int;
64 unsafe {
65 let mut screen: c_int = 0;
66 let dpy_ptr = dpy_name.map_or(null(), |s| s.as_ptr());
67 let connection = raw_ffi::XcbConnectionWrapper::new(
68 raw_ffi::xcb_connect(dpy_ptr, &mut screen),
69 true,
70 );
71 let error = raw_ffi::xcb_connection_has_error(connection.as_ptr());
72 if error != 0 {
73 Err(Self::connect_error_from_c_error(error))
74 } else {
75 let setup = raw_ffi::xcb_get_setup(connection.as_ptr());
76 let conn = XCBConnection {
77 conn: connection,
79 setup: Self::parse_setup(setup)?,
80 ext_mgr: Default::default(),
81 errors: Default::default(),
82 maximum_sequence_received: AtomicU64::new(0),
83 };
84 Ok((conn, screen as usize))
85 }
86 }
87 }
88
89 pub unsafe fn from_raw_xcb_connection(
101 ptr: *mut c_void,
102 should_drop: bool,
103 ) -> Result<XCBConnection, ConnectError> {
104 Self::from_raw_xcb_connection_inner(raw_ffi::XcbConnectionWrapper::new(
105 ptr.cast(),
106 should_drop,
107 ))
108 }
109}
110
111impl<Conn: as_raw_xcb_connection::AsRawXcbConnection> XCBConnection<Conn> {
112 unsafe fn from_raw_xcb_connection_inner(
113 conn: Conn,
114 ) -> Result<XCBConnection<Conn>, ConnectError> {
115 let setup = raw_ffi::xcb_get_setup(conn.as_raw_xcb_connection().cast());
116 Ok(XCBConnection {
117 conn,
118 setup: Self::parse_setup(setup)?,
119 ext_mgr: Default::default(),
120 errors: Default::default(),
121 maximum_sequence_received: AtomicU64::new(0),
122 })
123 }
124
125 unsafe fn connection_error_from_connection(
126 c: *mut raw_ffi::xcb_connection_t,
127 ) -> ConnectionError {
128 Self::connection_error_from_c_error(raw_ffi::xcb_connection_has_error(c))
129 }
130
131 fn connection_error_from_c_error(error: c_int) -> ConnectionError {
132 use crate::xcb_ffi::raw_ffi::connection_errors::*;
133
134 assert_ne!(error, 0);
135 match error {
136 ERROR => IOError::new(ErrorKind::Other, ConnectionError::UnknownError).into(),
137 EXT_NOTSUPPORTED => ConnectionError::UnsupportedExtension,
138 MEM_INSUFFICIENT => ConnectionError::InsufficientMemory,
139 REQ_LEN_EXCEED => ConnectionError::MaximumRequestLengthExceeded,
140 FDPASSING_FAILED => ConnectionError::FdPassingFailed,
141 _ => ConnectionError::UnknownError,
142 }
144 }
145
146 fn connect_error_from_c_error(error: c_int) -> ConnectError {
147 use crate::xcb_ffi::raw_ffi::connection_errors::*;
148
149 assert_ne!(error, 0);
150 match error {
151 ERROR => IOError::new(ErrorKind::Other, ConnectionError::UnknownError).into(),
152 MEM_INSUFFICIENT => ConnectError::InsufficientMemory,
153 PARSE_ERR => DisplayParsingError::Unknown.into(),
154 INVALID_SCREEN => ConnectError::InvalidScreen,
155 _ => ConnectError::UnknownError,
156 }
158 }
159
160 pub fn from_existing_connection(conn: Conn) -> Result<Self, ConnectError> {
162 let setup = unsafe { raw_ffi::xcb_get_setup(conn.as_raw_xcb_connection().cast()) };
163 Ok(XCBConnection {
164 conn,
165 setup: unsafe { Self::parse_setup(setup) }?,
166 ext_mgr: Default::default(),
167 errors: Default::default(),
168 maximum_sequence_received: AtomicU64::new(0),
169 })
170 }
171
172 pub fn inner_connection(&self) -> &Conn {
176 &self.conn
177 }
178
179 fn as_ptr(&self) -> *mut raw_ffi::xcb_connection_t {
180 self.conn.as_raw_xcb_connection().cast()
181 }
182
183 unsafe fn parse_setup(setup: *const raw_ffi::xcb_setup_t) -> Result<Setup, ParseError> {
184 use std::slice::from_raw_parts;
185
186 let wrapper = from_raw_parts(setup as *const u8, 8);
189
190 let length = u16::from_ne_bytes([wrapper[6], wrapper[7]]);
192
193 let length = usize::from(length) * 4 + 8;
195
196 let slice = from_raw_parts(wrapper.as_ptr(), length);
197 let result = Setup::try_parse(slice)?.0;
198
199 Ok(result)
200 }
201
202 #[allow(clippy::useless_conversion)]
206 fn send_request(
207 &self,
208 bufs: &[IoSlice<'_>],
209 fds: Vec<RawFdContainer>,
210 has_reply: bool,
211 reply_has_fds: bool,
212 ) -> Result<SequenceNumber, ConnectionError> {
213 let mut storage = Default::default();
214 let new_bufs = compute_length_field(self, bufs, &mut storage)?;
215
216 let mut new_bufs_ffi = Vec::with_capacity(2 + new_bufs.len());
218 new_bufs_ffi.push(raw_ffi::iovec {
220 iov_base: null_mut(),
221 iov_len: 0,
222 });
223 new_bufs_ffi.push(raw_ffi::iovec {
224 iov_base: null_mut(),
225 iov_len: 0,
226 });
227 new_bufs_ffi.extend(new_bufs.iter().map(|ioslice| raw_ffi::iovec {
228 iov_base: ioslice.as_ptr() as _,
229 iov_len: ioslice.len().try_into().unwrap(),
230 }));
231
232 let protocol_request = raw_ffi::xcb_protocol_request_t {
234 count: new_bufs.len(),
235 ext: null_mut(), opcode: 0,
237 isvoid: u8::from(!has_reply),
238 };
239 let mut flags = raw_ffi::send_request_flags::RAW;
240 assert!(has_reply || !reply_has_fds);
241 flags |= raw_ffi::send_request_flags::CHECKED;
242 if reply_has_fds {
243 flags |= raw_ffi::send_request_flags::REPLY_FDS;
244 }
245
246 let seqno = if fds.is_empty() {
247 unsafe {
248 raw_ffi::xcb_send_request64(
249 self.as_ptr(),
250 flags,
251 &mut new_bufs_ffi[2],
252 &protocol_request,
253 )
254 }
255 } else {
256 #[cfg(unix)]
257 {
258 let mut fds: Vec<_> = fds.into_iter().map(RawFdContainer::into_raw_fd).collect();
260 let num_fds = fds.len().try_into().unwrap();
261 let fds_ptr = fds.as_mut_ptr();
262 unsafe {
263 raw_ffi::xcb_send_request_with_fds64(
264 self.as_ptr(),
265 flags,
266 &mut new_bufs_ffi[2],
267 &protocol_request,
268 num_fds,
269 fds_ptr,
270 )
271 }
272 }
273 #[cfg(not(unix))]
274 {
275 unreachable!("it is not possible to create a `RawFdContainer` on non-unix");
276 }
277 };
278 if seqno == 0 {
279 unsafe { Err(Self::connection_error_from_connection(self.as_ptr())) }
280 } else {
281 Ok(seqno)
282 }
283 }
284
285 pub fn has_error(&self) -> Option<ConnectionError> {
287 unsafe {
288 let error = raw_ffi::xcb_connection_has_error(self.as_ptr());
289 if error == 0 {
290 None
291 } else {
292 Some(Self::connection_error_from_c_error(error))
293 }
294 }
295 }
296
297 pub fn get_raw_xcb_connection(&self) -> *mut c_void {
302 self.as_ptr().cast()
303 }
304
305 fn poll_for_reply(&self, sequence: SequenceNumber) -> Result<Option<CSlice>, ()> {
311 unsafe {
312 let mut reply = null_mut();
313 let mut error = null_mut();
314 let found =
315 raw_ffi::xcb_poll_for_reply64(self.as_ptr(), sequence, &mut reply, &mut error);
316 if found == 0 {
317 return Err(());
318 }
319 assert_eq!(found, 1);
320 match (reply.is_null(), error.is_null()) {
321 (true, true) => Ok(None),
322 (true, false) => Ok(Some(self.wrap_error(error as _, sequence))),
323 (false, true) => Ok(Some(self.wrap_reply(reply as _, sequence))),
324 (false, false) => unreachable!(),
325 }
326 }
327 }
328
329 unsafe fn wrap_reply(&self, reply: *const u8, sequence: SequenceNumber) -> CSlice {
330 let _ = self
332 .maximum_sequence_received
333 .fetch_max(sequence, Ordering::Relaxed);
334
335 let header = CSlice::new(reply, 32);
336
337 let length_field = u32::from_ne_bytes(header[4..8].try_into().unwrap());
338 let length_field: usize = length_field
339 .try_into()
340 .expect("usize should have at least 32 bits");
341
342 let length = 32 + length_field * 4;
343 CSlice::new(header.into_ptr(), length)
344 }
345
346 unsafe fn wrap_error(&self, error: *const u8, sequence: SequenceNumber) -> CSlice {
347 let _ = self
349 .maximum_sequence_received
350 .fetch_max(sequence, Ordering::Relaxed);
351
352 CSlice::new(error, 32)
353 }
354
355 unsafe fn wrap_event(&self, event: *mut u8) -> Result<RawEventAndSeqNumber, ParseError> {
356 let header = CSlice::new(event, 36);
357 let mut length = 32;
358 let seqno = u32::from_ne_bytes([header[32], header[33], header[34], header[35]]);
360 let seqno = self.reconstruct_full_sequence(seqno);
361
362 if (*event & 0x7f) == super::protocol::xproto::GE_GENERIC_EVENT {
364 let length_field = u32::from_ne_bytes([header[4], header[5], header[6], header[7]]);
366 let length_field: usize = length_field
367 .try_into()
368 .or(Err(ParseError::ConversionFailed))?;
369 length += length_field * 4;
370 std::ptr::copy(event.add(36), event.add(32), length_field * 4);
373 }
374 Ok((CSlice::new(header.into_ptr(), length), seqno))
375 }
376
377 fn reconstruct_full_sequence(&self, seqno: u32) -> SequenceNumber {
383 reconstruct_full_sequence_impl(
384 self.maximum_sequence_received.load(Ordering::Relaxed),
385 seqno,
386 )
387 }
388}
389
390impl<Conn: as_raw_xcb_connection::AsRawXcbConnection> RequestConnection for XCBConnection<Conn> {
391 type Buf = CSlice;
392
393 fn send_request_with_reply<R>(
394 &self,
395 bufs: &[IoSlice<'_>],
396 fds: Vec<RawFdContainer>,
397 ) -> Result<Cookie<'_, Self, R>, ConnectionError>
398 where
399 R: TryParse,
400 {
401 Ok(Cookie::new(
402 self,
403 self.send_request(bufs, fds, true, false)?,
404 ))
405 }
406
407 fn send_request_with_reply_with_fds<R>(
408 &self,
409 bufs: &[IoSlice<'_>],
410 fds: Vec<RawFdContainer>,
411 ) -> Result<CookieWithFds<'_, Self, R>, ConnectionError>
412 where
413 R: TryParseFd,
414 {
415 Ok(CookieWithFds::new(
416 self,
417 self.send_request(bufs, fds, true, true)?,
418 ))
419 }
420
421 fn send_request_without_reply(
422 &self,
423 bufs: &[IoSlice<'_>],
424 fds: Vec<RawFdContainer>,
425 ) -> Result<VoidCookie<'_, Self>, ConnectionError> {
426 Ok(VoidCookie::new(
427 self,
428 self.send_request(bufs, fds, false, false)?,
429 ))
430 }
431
432 fn discard_reply(&self, sequence: SequenceNumber, _kind: RequestKind, mode: DiscardMode) {
433 match mode {
434 DiscardMode::DiscardReplyAndError => unsafe {
435 raw_ffi::xcb_discard_reply64(self.as_ptr(), sequence);
437 },
438 DiscardMode::DiscardReply => self.errors.discard_reply(sequence),
440 }
441 }
442
443 fn prefetch_extension_information(
444 &self,
445 extension_name: &'static str,
446 ) -> Result<(), ConnectionError> {
447 self.ext_mgr
448 .lock()
449 .unwrap()
450 .prefetch_extension_information(self, extension_name)
451 }
452
453 fn extension_information(
454 &self,
455 extension_name: &'static str,
456 ) -> Result<Option<ExtensionInformation>, ConnectionError> {
457 self.ext_mgr
458 .lock()
459 .unwrap()
460 .extension_information(self, extension_name)
461 }
462
463 fn wait_for_reply_or_raw_error(
464 &self,
465 sequence: SequenceNumber,
466 ) -> Result<ReplyOrError<CSlice>, ConnectionError> {
467 unsafe {
468 let mut error = null_mut();
469 let reply = raw_ffi::xcb_wait_for_reply64(self.as_ptr(), sequence, &mut error);
470 match (reply.is_null(), error.is_null()) {
471 (true, true) => Err(Self::connection_error_from_connection(self.as_ptr())),
472 (false, true) => Ok(ReplyOrError::Reply(self.wrap_reply(reply as _, sequence))),
473 (true, false) => Ok(ReplyOrError::Error(self.wrap_error(error as _, sequence))),
474 (false, false) => unreachable!(),
476 }
477 }
478 }
479
480 fn wait_for_reply(&self, sequence: SequenceNumber) -> Result<Option<CSlice>, ConnectionError> {
481 match self.wait_for_reply_or_raw_error(sequence)? {
482 ReplyOrError::Reply(reply) => Ok(Some(reply)),
483 ReplyOrError::Error(error) => {
484 self.errors.append_error((sequence, error));
485 Ok(None)
486 }
487 }
488 }
489
490 #[cfg(unix)]
491 fn wait_for_reply_with_fds_raw(
492 &self,
493 sequence: SequenceNumber,
494 ) -> Result<ReplyOrError<BufWithFds, Buffer>, ConnectionError> {
495 let buffer = match self.wait_for_reply_or_raw_error(sequence)? {
496 ReplyOrError::Reply(reply) => reply,
497 ReplyOrError::Error(error) => return Ok(ReplyOrError::Error(error)),
498 };
499
500 #[allow(clippy::cast_ptr_alignment)]
505 let fd_ptr = (unsafe { buffer.as_ptr().add(buffer.len()) }) as *const RawFd;
506
507 let fd_slice = unsafe { std::slice::from_raw_parts(fd_ptr, usize::from(buffer[1])) };
509 let fd_vec = fd_slice
510 .iter()
511 .map(|&fd| unsafe { OwnedFd::from_raw_fd(fd) })
512 .collect();
513
514 Ok(ReplyOrError::Reply((buffer, fd_vec)))
515 }
516
517 #[cfg(not(unix))]
518 fn wait_for_reply_with_fds_raw(
519 &self,
520 _sequence: SequenceNumber,
521 ) -> Result<ReplyOrError<BufWithFds, Buffer>, ConnectionError> {
522 unimplemented!("FD passing is currently only implemented on Unix-like systems")
523 }
524
525 fn check_for_raw_error(
526 &self,
527 sequence: SequenceNumber,
528 ) -> Result<Option<Buffer>, ConnectionError> {
529 let cookie = raw_ffi::xcb_void_cookie_t {
530 sequence: sequence as _,
531 };
532 let error = unsafe { raw_ffi::xcb_request_check(self.as_ptr(), cookie) };
533 if error.is_null() {
534 Ok(None)
535 } else {
536 unsafe { Ok(Some(self.wrap_error(error as _, sequence))) }
537 }
538 }
539
540 fn maximum_request_bytes(&self) -> usize {
541 4 * unsafe { raw_ffi::xcb_get_maximum_request_length(self.as_ptr()) as usize }
542 }
543
544 fn prefetch_maximum_request_bytes(&self) {
545 unsafe { raw_ffi::xcb_prefetch_maximum_request_length(self.as_ptr()) };
546 }
547
548 fn parse_error(&self, error: &[u8]) -> Result<crate::x11_utils::X11Error, ParseError> {
549 let ext_mgr = self.ext_mgr.lock().unwrap();
550 crate::x11_utils::X11Error::try_parse(error, &*ext_mgr)
551 }
552
553 fn parse_event(&self, event: &[u8]) -> Result<crate::protocol::Event, ParseError> {
554 let ext_mgr = self.ext_mgr.lock().unwrap();
555 crate::protocol::Event::parse(event, &*ext_mgr)
556 }
557}
558
559impl Connection for XCBConnection {
560 fn wait_for_raw_event_with_sequence(&self) -> Result<RawEventAndSeqNumber, ConnectionError> {
561 if let Some(error) = self.errors.get(self) {
562 return Ok((error.1, error.0));
563 }
564 unsafe {
565 let event = raw_ffi::xcb_wait_for_event(self.conn.as_ptr());
566 if event.is_null() {
567 return Err(Self::connection_error_from_connection(self.conn.as_ptr()));
568 }
569 Ok(self.wrap_event(event as _)?)
570 }
571 }
572
573 fn poll_for_raw_event_with_sequence(
574 &self,
575 ) -> Result<Option<RawEventAndSeqNumber>, ConnectionError> {
576 if let Some(error) = self.errors.get(self) {
577 return Ok(Some((error.1, error.0)));
578 }
579 unsafe {
580 let event = raw_ffi::xcb_poll_for_event(self.conn.as_ptr());
581 if event.is_null() {
582 let err = raw_ffi::xcb_connection_has_error(self.conn.as_ptr());
583 if err == 0 {
584 return Ok(None);
585 } else {
586 return Err(Self::connection_error_from_c_error(err));
587 }
588 }
589 Ok(Some(self.wrap_event(event as _)?))
590 }
591 }
592
593 fn flush(&self) -> Result<(), ConnectionError> {
594 let res = unsafe { raw_ffi::xcb_flush(self.conn.as_ptr()) };
596 if res != 0 {
597 Ok(())
598 } else {
599 unsafe { Err(Self::connection_error_from_connection(self.conn.as_ptr())) }
600 }
601 }
602
603 fn generate_id(&self) -> Result<u32, ReplyOrIdError> {
604 unsafe {
605 let id = raw_ffi::xcb_generate_id(self.conn.as_ptr());
606 if id == u32::MAX {
610 Err(Self::connection_error_from_connection(self.conn.as_ptr()).into())
611 } else {
612 Ok(id)
613 }
614 }
615 }
616
617 fn setup(&self) -> &Setup {
618 &self.setup
619 }
620}
621
622#[cfg(unix)]
623impl AsRawFd for XCBConnection {
624 fn as_raw_fd(&self) -> RawFd {
625 unsafe { raw_ffi::xcb_get_file_descriptor(self.conn.as_ptr()) }
626 }
627}
628
629#[cfg(unix)]
630impl AsFd for XCBConnection {
631 fn as_fd(&self) -> BorrowedFd<'_> {
632 unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
633 }
634}
635
636#[cfg(feature = "raw-window-handle")]
637unsafe impl raw_window_handle::HasRawDisplayHandle for XCBConnection {
638 #[inline]
639 fn raw_display_handle(&self) -> raw_window_handle::RawDisplayHandle {
640 let mut handle = raw_window_handle::XcbDisplayHandle::empty();
641 handle.connection = self.get_raw_xcb_connection();
642
643 raw_window_handle::RawDisplayHandle::Xcb(handle)
644 }
645}
646
647#[cfg(feature = "raw-window-handle")]
648unsafe impl raw_window_handle::HasRawWindowHandle
649 for crate::protocol::xproto::WindowWrapper<XCBConnection>
650{
651 #[inline]
652 fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle {
653 let mut handle = raw_window_handle::XcbWindowHandle::empty();
654 handle.window = self.window();
655
656 raw_window_handle::RawWindowHandle::Xcb(handle)
657 }
658}
659
660unsafe impl as_raw_xcb_connection::AsRawXcbConnection for XCBConnection {
662 fn as_raw_xcb_connection(&self) -> *mut as_raw_xcb_connection::xcb_connection_t {
663 self.get_raw_xcb_connection().cast()
664 }
665}
666
667fn reconstruct_full_sequence_impl(recent: SequenceNumber, value: u32) -> SequenceNumber {
671 let u32_max = SequenceNumber::from(u32::MAX);
673 let expanded_value = SequenceNumber::from(value) | (recent & !u32_max);
674
675 let step: SequenceNumber = SequenceNumber::from(1u8) << 32;
678
679 let result = [
685 expanded_value,
686 expanded_value + step,
687 expanded_value.wrapping_sub(step),
688 ]
689 .iter()
690 .copied()
691 .min_by_key(|&value| value.abs_diff(recent))
692 .unwrap();
693 assert_eq!(
695 result & SequenceNumber::from(u32::MAX),
696 SequenceNumber::from(value),
697 );
698 result
699}
700
701#[cfg(test)]
702mod test {
703 use super::XCBConnection;
704 use std::ffi::CString;
705
706 #[test]
707 fn xcb_connect_smoke_test() {
708 let str = CString::new("display name").unwrap();
712 let (_conn, screen) = XCBConnection::connect(Some(&str)).expect("Failed to 'connect'");
713 assert_eq!(screen, 0);
714 }
715
716 #[test]
717 fn reconstruct_full_sequence() {
718 use super::reconstruct_full_sequence_impl;
719 let max32 = u32::MAX;
720 let max32_: u64 = max32.into();
721 let max32_p1 = max32_ + 1;
722 let large_offset = max32_p1 * u64::from(u16::MAX);
723 for &(recent, value, expected) in &[
724 (0, 0, 0),
725 (0, 10, 10),
726 (0, max32, max32_),
729 (max32_, 0, max32_p1),
730 (max32_, 10, max32_p1 + 10),
731 (max32_, max32, max32_),
732 (max32_p1, 0, max32_p1),
733 (max32_p1, 10, max32_p1 + 10),
734 (max32_p1, max32, max32_),
735 (large_offset | 0xdead_cafe, 0, large_offset + max32_p1),
736 (large_offset | 0xdead_cafe, max32, large_offset + max32_),
737 (0xabcd_1234_5678, 0xf000_0000, 0xabcc_f000_0000),
738 (0xabcd_8765_4321, 0xf000_0000, 0xabcd_f000_0000),
739 ] {
740 let actual = reconstruct_full_sequence_impl(recent, value);
741 assert_eq!(
742 actual, expected,
743 "reconstruct({recent:x}, {value:x}) == {expected:x}, but was {actual:x}"
744 );
745 }
746 }
747}