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 {
49 conn: raw_ffi::XcbConnectionWrapper,
50 setup: Setup,
51 ext_mgr: Mutex<ExtensionManager>,
52 errors: pending_errors::PendingErrors,
53 maximum_sequence_received: AtomicU64,
54}
55
56impl XCBConnection {
57 unsafe fn connection_error_from_connection(
58 c: *mut raw_ffi::xcb_connection_t,
59 ) -> ConnectionError {
60 Self::connection_error_from_c_error(raw_ffi::xcb_connection_has_error(c))
61 }
62
63 fn connection_error_from_c_error(error: c_int) -> ConnectionError {
64 use crate::xcb_ffi::raw_ffi::connection_errors::*;
65
66 assert_ne!(error, 0);
67 match error {
68 ERROR => IOError::new(ErrorKind::Other, ConnectionError::UnknownError).into(),
69 EXT_NOTSUPPORTED => ConnectionError::UnsupportedExtension,
70 MEM_INSUFFICIENT => ConnectionError::InsufficientMemory,
71 REQ_LEN_EXCEED => ConnectionError::MaximumRequestLengthExceeded,
72 FDPASSING_FAILED => ConnectionError::FdPassingFailed,
73 _ => ConnectionError::UnknownError,
74 }
76 }
77
78 fn connect_error_from_c_error(error: c_int) -> ConnectError {
79 use crate::xcb_ffi::raw_ffi::connection_errors::*;
80
81 assert_ne!(error, 0);
82 match error {
83 ERROR => IOError::new(ErrorKind::Other, ConnectionError::UnknownError).into(),
84 MEM_INSUFFICIENT => ConnectError::InsufficientMemory,
85 PARSE_ERR => DisplayParsingError::Unknown.into(),
86 INVALID_SCREEN => ConnectError::InvalidScreen,
87 _ => ConnectError::UnknownError,
88 }
90 }
91
92 pub fn connect(dpy_name: Option<&CStr>) -> Result<(XCBConnection, usize), ConnectError> {
98 use libc::c_int;
99 unsafe {
100 let mut screen: c_int = 0;
101 let dpy_ptr = dpy_name.map_or(null(), |s| s.as_ptr());
102 let connection = raw_ffi::XcbConnectionWrapper::new(
103 raw_ffi::xcb_connect(dpy_ptr, &mut screen),
104 true,
105 );
106 let error = raw_ffi::xcb_connection_has_error(connection.as_ptr());
107 if error != 0 {
108 Err(Self::connect_error_from_c_error(error))
109 } else {
110 let setup = raw_ffi::xcb_get_setup(connection.as_ptr());
111 let conn = XCBConnection {
112 conn: connection,
114 setup: Self::parse_setup(setup)?,
115 ext_mgr: Default::default(),
116 errors: Default::default(),
117 maximum_sequence_received: AtomicU64::new(0),
118 };
119 Ok((conn, screen as usize))
120 }
121 }
122 }
123
124 pub unsafe fn from_raw_xcb_connection(
136 ptr: *mut c_void,
137 should_drop: bool,
138 ) -> Result<XCBConnection, ConnectError> {
139 let ptr = ptr as *mut raw_ffi::xcb_connection_t;
140 let conn = raw_ffi::XcbConnectionWrapper::new(ptr, should_drop);
141 let setup = raw_ffi::xcb_get_setup(ptr);
142 Ok(XCBConnection {
143 conn,
144 setup: Self::parse_setup(setup)?,
145 ext_mgr: Default::default(),
146 errors: Default::default(),
147 maximum_sequence_received: AtomicU64::new(0),
148 })
149 }
150
151 unsafe fn parse_setup(setup: *const raw_ffi::xcb_setup_t) -> Result<Setup, ParseError> {
152 use std::slice::from_raw_parts;
153
154 let wrapper = from_raw_parts(setup as *const u8, 8);
157
158 let length = u16::from_ne_bytes([wrapper[6], wrapper[7]]);
160
161 let length = usize::from(length) * 4 + 8;
163
164 let slice = from_raw_parts(wrapper.as_ptr(), length);
165 let result = Setup::try_parse(slice)?.0;
166
167 Ok(result)
168 }
169
170 #[allow(clippy::useless_conversion)]
174 fn send_request(
175 &self,
176 bufs: &[IoSlice<'_>],
177 fds: Vec<RawFdContainer>,
178 has_reply: bool,
179 reply_has_fds: bool,
180 ) -> Result<SequenceNumber, ConnectionError> {
181 let mut storage = Default::default();
182 let new_bufs = compute_length_field(self, bufs, &mut storage)?;
183
184 let mut new_bufs_ffi = Vec::with_capacity(2 + new_bufs.len());
186 new_bufs_ffi.push(raw_ffi::iovec {
188 iov_base: null_mut(),
189 iov_len: 0,
190 });
191 new_bufs_ffi.push(raw_ffi::iovec {
192 iov_base: null_mut(),
193 iov_len: 0,
194 });
195 new_bufs_ffi.extend(new_bufs.iter().map(|ioslice| raw_ffi::iovec {
196 iov_base: ioslice.as_ptr() as _,
197 iov_len: ioslice.len().try_into().unwrap(),
198 }));
199
200 let protocol_request = raw_ffi::xcb_protocol_request_t {
202 count: new_bufs.len(),
203 ext: null_mut(), opcode: 0,
205 isvoid: u8::from(!has_reply),
206 };
207 let mut flags = raw_ffi::send_request_flags::RAW;
208 assert!(has_reply || !reply_has_fds);
209 flags |= raw_ffi::send_request_flags::CHECKED;
210 if reply_has_fds {
211 flags |= raw_ffi::send_request_flags::REPLY_FDS;
212 }
213
214 let seqno = if fds.is_empty() {
215 unsafe {
216 raw_ffi::xcb_send_request64(
217 self.conn.as_ptr(),
218 flags,
219 &mut new_bufs_ffi[2],
220 &protocol_request,
221 )
222 }
223 } else {
224 #[cfg(unix)]
225 {
226 let mut fds: Vec<_> = fds.into_iter().map(RawFdContainer::into_raw_fd).collect();
228 let num_fds = fds.len().try_into().unwrap();
229 let fds_ptr = fds.as_mut_ptr();
230 unsafe {
231 raw_ffi::xcb_send_request_with_fds64(
232 self.conn.as_ptr(),
233 flags,
234 &mut new_bufs_ffi[2],
235 &protocol_request,
236 num_fds,
237 fds_ptr,
238 )
239 }
240 }
241 #[cfg(not(unix))]
242 {
243 unreachable!("it is not possible to create a `RawFdContainer` on non-unix");
244 }
245 };
246 if seqno == 0 {
247 unsafe { Err(Self::connection_error_from_connection(self.conn.as_ptr())) }
248 } else {
249 Ok(seqno)
250 }
251 }
252
253 pub fn has_error(&self) -> Option<ConnectionError> {
255 unsafe {
256 let error = raw_ffi::xcb_connection_has_error(self.conn.as_ptr());
257 if error == 0 {
258 None
259 } else {
260 Some(Self::connection_error_from_c_error(error))
261 }
262 }
263 }
264
265 pub fn get_raw_xcb_connection(&self) -> *mut c_void {
270 self.conn.as_ptr() as _
271 }
272
273 fn poll_for_reply(&self, sequence: SequenceNumber) -> Result<Option<CSlice>, ()> {
279 unsafe {
280 let mut reply = null_mut();
281 let mut error = null_mut();
282 let found =
283 raw_ffi::xcb_poll_for_reply64(self.conn.as_ptr(), sequence, &mut reply, &mut error);
284 if found == 0 {
285 return Err(());
286 }
287 assert_eq!(found, 1);
288 match (reply.is_null(), error.is_null()) {
289 (true, true) => Ok(None),
290 (true, false) => Ok(Some(self.wrap_error(error as _, sequence))),
291 (false, true) => Ok(Some(self.wrap_reply(reply as _, sequence))),
292 (false, false) => unreachable!(),
293 }
294 }
295 }
296
297 unsafe fn wrap_reply(&self, reply: *const u8, sequence: SequenceNumber) -> CSlice {
298 let _ = self
300 .maximum_sequence_received
301 .fetch_max(sequence, Ordering::Relaxed);
302
303 let header = CSlice::new(reply, 32);
304
305 let length_field = u32::from_ne_bytes(header[4..8].try_into().unwrap());
306 let length_field: usize = length_field
307 .try_into()
308 .expect("usize should have at least 32 bits");
309
310 let length = 32 + length_field * 4;
311 CSlice::new(header.into_ptr(), length)
312 }
313
314 unsafe fn wrap_error(&self, error: *const u8, sequence: SequenceNumber) -> CSlice {
315 let _ = self
317 .maximum_sequence_received
318 .fetch_max(sequence, Ordering::Relaxed);
319
320 CSlice::new(error, 32)
321 }
322
323 unsafe fn wrap_event(&self, event: *mut u8) -> Result<RawEventAndSeqNumber, ParseError> {
324 let header = CSlice::new(event, 36);
325 let mut length = 32;
326 let seqno = u32::from_ne_bytes([header[32], header[33], header[34], header[35]]);
328 let seqno = self.reconstruct_full_sequence(seqno);
329
330 if (*event & 0x7f) == super::protocol::xproto::GE_GENERIC_EVENT {
332 let length_field = u32::from_ne_bytes([header[4], header[5], header[6], header[7]]);
334 let length_field: usize = length_field
335 .try_into()
336 .or(Err(ParseError::ConversionFailed))?;
337 length += length_field * 4;
338 std::ptr::copy(event.add(36), event.add(32), length_field * 4);
341 }
342 Ok((CSlice::new(header.into_ptr(), length), seqno))
343 }
344
345 fn reconstruct_full_sequence(&self, seqno: u32) -> SequenceNumber {
351 reconstruct_full_sequence_impl(
352 self.maximum_sequence_received.load(Ordering::Relaxed),
353 seqno,
354 )
355 }
356}
357
358impl RequestConnection for XCBConnection {
359 type Buf = CSlice;
360
361 fn send_request_with_reply<R>(
362 &self,
363 bufs: &[IoSlice<'_>],
364 fds: Vec<RawFdContainer>,
365 ) -> Result<Cookie<'_, Self, R>, ConnectionError>
366 where
367 R: TryParse,
368 {
369 Ok(Cookie::new(
370 self,
371 self.send_request(bufs, fds, true, false)?,
372 ))
373 }
374
375 fn send_request_with_reply_with_fds<R>(
376 &self,
377 bufs: &[IoSlice<'_>],
378 fds: Vec<RawFdContainer>,
379 ) -> Result<CookieWithFds<'_, Self, R>, ConnectionError>
380 where
381 R: TryParseFd,
382 {
383 Ok(CookieWithFds::new(
384 self,
385 self.send_request(bufs, fds, true, true)?,
386 ))
387 }
388
389 fn send_request_without_reply(
390 &self,
391 bufs: &[IoSlice<'_>],
392 fds: Vec<RawFdContainer>,
393 ) -> Result<VoidCookie<'_, Self>, ConnectionError> {
394 Ok(VoidCookie::new(
395 self,
396 self.send_request(bufs, fds, false, false)?,
397 ))
398 }
399
400 fn discard_reply(&self, sequence: SequenceNumber, _kind: RequestKind, mode: DiscardMode) {
401 match mode {
402 DiscardMode::DiscardReplyAndError => unsafe {
403 raw_ffi::xcb_discard_reply64(self.conn.as_ptr(), sequence);
405 },
406 DiscardMode::DiscardReply => self.errors.discard_reply(sequence),
408 }
409 }
410
411 fn prefetch_extension_information(
412 &self,
413 extension_name: &'static str,
414 ) -> Result<(), ConnectionError> {
415 self.ext_mgr
416 .lock()
417 .unwrap()
418 .prefetch_extension_information(self, extension_name)
419 }
420
421 fn extension_information(
422 &self,
423 extension_name: &'static str,
424 ) -> Result<Option<ExtensionInformation>, ConnectionError> {
425 self.ext_mgr
426 .lock()
427 .unwrap()
428 .extension_information(self, extension_name)
429 }
430
431 fn wait_for_reply_or_raw_error(
432 &self,
433 sequence: SequenceNumber,
434 ) -> Result<ReplyOrError<CSlice>, ConnectionError> {
435 unsafe {
436 let mut error = null_mut();
437 let reply = raw_ffi::xcb_wait_for_reply64(self.conn.as_ptr(), sequence, &mut error);
438 match (reply.is_null(), error.is_null()) {
439 (true, true) => Err(Self::connection_error_from_connection(self.conn.as_ptr())),
440 (false, true) => Ok(ReplyOrError::Reply(self.wrap_reply(reply as _, sequence))),
441 (true, false) => Ok(ReplyOrError::Error(self.wrap_error(error as _, sequence))),
442 (false, false) => unreachable!(),
444 }
445 }
446 }
447
448 fn wait_for_reply(&self, sequence: SequenceNumber) -> Result<Option<CSlice>, ConnectionError> {
449 match self.wait_for_reply_or_raw_error(sequence)? {
450 ReplyOrError::Reply(reply) => Ok(Some(reply)),
451 ReplyOrError::Error(error) => {
452 self.errors.append_error((sequence, error));
453 Ok(None)
454 }
455 }
456 }
457
458 #[cfg(unix)]
459 fn wait_for_reply_with_fds_raw(
460 &self,
461 sequence: SequenceNumber,
462 ) -> Result<ReplyOrError<BufWithFds, Buffer>, ConnectionError> {
463 let buffer = match self.wait_for_reply_or_raw_error(sequence)? {
464 ReplyOrError::Reply(reply) => reply,
465 ReplyOrError::Error(error) => return Ok(ReplyOrError::Error(error)),
466 };
467
468 #[allow(clippy::cast_ptr_alignment)]
473 let fd_ptr = (unsafe { buffer.as_ptr().add(buffer.len()) }) as *const RawFd;
474
475 let fd_slice = unsafe { std::slice::from_raw_parts(fd_ptr, usize::from(buffer[1])) };
477 let fd_vec = fd_slice
478 .iter()
479 .map(|&fd| unsafe { OwnedFd::from_raw_fd(fd) })
480 .collect();
481
482 Ok(ReplyOrError::Reply((buffer, fd_vec)))
483 }
484
485 #[cfg(not(unix))]
486 fn wait_for_reply_with_fds_raw(
487 &self,
488 _sequence: SequenceNumber,
489 ) -> Result<ReplyOrError<BufWithFds, Buffer>, ConnectionError> {
490 unimplemented!("FD passing is currently only implemented on Unix-like systems")
491 }
492
493 fn check_for_raw_error(
494 &self,
495 sequence: SequenceNumber,
496 ) -> Result<Option<Buffer>, ConnectionError> {
497 let cookie = raw_ffi::xcb_void_cookie_t {
498 sequence: sequence as _,
499 };
500 let error = unsafe { raw_ffi::xcb_request_check(self.conn.as_ptr(), cookie) };
501 if error.is_null() {
502 Ok(None)
503 } else {
504 unsafe { Ok(Some(self.wrap_error(error as _, sequence))) }
505 }
506 }
507
508 fn maximum_request_bytes(&self) -> usize {
509 4 * unsafe { raw_ffi::xcb_get_maximum_request_length(self.conn.as_ptr()) as usize }
510 }
511
512 fn prefetch_maximum_request_bytes(&self) {
513 unsafe { raw_ffi::xcb_prefetch_maximum_request_length(self.conn.as_ptr()) };
514 }
515
516 fn parse_error(&self, error: &[u8]) -> Result<crate::x11_utils::X11Error, ParseError> {
517 let ext_mgr = self.ext_mgr.lock().unwrap();
518 crate::x11_utils::X11Error::try_parse(error, &*ext_mgr)
519 }
520
521 fn parse_event(&self, event: &[u8]) -> Result<crate::protocol::Event, ParseError> {
522 let ext_mgr = self.ext_mgr.lock().unwrap();
523 crate::protocol::Event::parse(event, &*ext_mgr)
524 }
525}
526
527impl Connection for XCBConnection {
528 fn wait_for_raw_event_with_sequence(&self) -> Result<RawEventAndSeqNumber, ConnectionError> {
529 if let Some(error) = self.errors.get(self) {
530 return Ok((error.1, error.0));
531 }
532 unsafe {
533 let event = raw_ffi::xcb_wait_for_event(self.conn.as_ptr());
534 if event.is_null() {
535 return Err(Self::connection_error_from_connection(self.conn.as_ptr()));
536 }
537 Ok(self.wrap_event(event as _)?)
538 }
539 }
540
541 fn poll_for_raw_event_with_sequence(
542 &self,
543 ) -> Result<Option<RawEventAndSeqNumber>, ConnectionError> {
544 if let Some(error) = self.errors.get(self) {
545 return Ok(Some((error.1, error.0)));
546 }
547 unsafe {
548 let event = raw_ffi::xcb_poll_for_event(self.conn.as_ptr());
549 if event.is_null() {
550 let err = raw_ffi::xcb_connection_has_error(self.conn.as_ptr());
551 if err == 0 {
552 return Ok(None);
553 } else {
554 return Err(Self::connection_error_from_c_error(err));
555 }
556 }
557 Ok(Some(self.wrap_event(event as _)?))
558 }
559 }
560
561 fn flush(&self) -> Result<(), ConnectionError> {
562 let res = unsafe { raw_ffi::xcb_flush(self.conn.as_ptr()) };
564 if res != 0 {
565 Ok(())
566 } else {
567 unsafe { Err(Self::connection_error_from_connection(self.conn.as_ptr())) }
568 }
569 }
570
571 fn generate_id(&self) -> Result<u32, ReplyOrIdError> {
572 unsafe {
573 let id = raw_ffi::xcb_generate_id(self.conn.as_ptr());
574 if id == u32::max_value() {
578 Err(Self::connection_error_from_connection(self.conn.as_ptr()).into())
579 } else {
580 Ok(id)
581 }
582 }
583 }
584
585 fn setup(&self) -> &Setup {
586 &self.setup
587 }
588}
589
590#[cfg(unix)]
591impl AsRawFd for XCBConnection {
592 fn as_raw_fd(&self) -> RawFd {
593 unsafe { raw_ffi::xcb_get_file_descriptor(self.conn.as_ptr()) }
594 }
595}
596
597#[cfg(unix)]
598impl AsFd for XCBConnection {
599 fn as_fd(&self) -> BorrowedFd<'_> {
600 unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
601 }
602}
603
604unsafe impl as_raw_xcb_connection::AsRawXcbConnection for XCBConnection {
606 fn as_raw_xcb_connection(&self) -> *mut as_raw_xcb_connection::xcb_connection_t {
607 self.get_raw_xcb_connection().cast()
608 }
609}
610
611fn reconstruct_full_sequence_impl(recent: SequenceNumber, value: u32) -> SequenceNumber {
615 let u32_max = SequenceNumber::from(u32::max_value());
617 let expanded_value = SequenceNumber::from(value) | (recent & !u32_max);
618
619 let step: SequenceNumber = SequenceNumber::from(1u8) << 32;
622
623 let result = [
629 expanded_value,
630 expanded_value + step,
631 expanded_value.wrapping_sub(step),
632 ]
633 .iter()
634 .copied()
635 .min_by_key(|&value| {
636 if value > recent {
637 value - recent
638 } else {
639 recent - value
640 }
641 })
642 .unwrap();
643 assert_eq!(
645 result & SequenceNumber::from(u32::max_value()),
646 SequenceNumber::from(value),
647 );
648 result
649}
650
651#[cfg(test)]
652mod test {
653 use super::XCBConnection;
654 use std::ffi::CString;
655
656 #[test]
657 fn xcb_connect_smoke_test() {
658 let str = CString::new("display name").unwrap();
662 let (_conn, screen) = XCBConnection::connect(Some(&str)).expect("Failed to 'connect'");
663 assert_eq!(screen, 0);
664 }
665
666 #[test]
667 fn reconstruct_full_sequence() {
668 use super::reconstruct_full_sequence_impl;
669 let max32 = u32::max_value();
670 let max32_: u64 = max32.into();
671 let max32_p1 = max32_ + 1;
672 let large_offset = max32_p1 * u64::from(u16::max_value());
673 for &(recent, value, expected) in &[
674 (0, 0, 0),
675 (0, 10, 10),
676 (0, max32, max32_),
679 (max32_, 0, max32_p1),
680 (max32_, 10, max32_p1 + 10),
681 (max32_, max32, max32_),
682 (max32_p1, 0, max32_p1),
683 (max32_p1, 10, max32_p1 + 10),
684 (max32_p1, max32, max32_),
685 (large_offset | 0xdead_cafe, 0, large_offset + max32_p1),
686 (large_offset | 0xdead_cafe, max32, large_offset + max32_),
687 (0xabcd_1234_5678, 0xf000_0000, 0xabcc_f000_0000),
688 (0xabcd_8765_4321, 0xf000_0000, 0xabcd_f000_0000),
689 ] {
690 let actual = reconstruct_full_sequence_impl(recent, value);
691 assert_eq!(
692 actual, expected,
693 "reconstruct({:x}, {:x}) == {:x}, but was {:x}",
694 recent, value, expected, actual,
695 );
696 }
697 }
698}