x11rb/xcb_ffi/
mod.rs

1//! A FFI-based connection to an X11 server, using libxcb.
2//!
3//! This module is only available when the `allow-unsafe-code` feature is enabled.
4
5use 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;
37/// The raw bytes of an event received by [`XCBConnection`] and its sequence number.
38pub type RawEventAndSeqNumber = x11rb_protocol::RawEventAndSeqNumber<Buffer>;
39/// A combination of a buffer and a list of file descriptors for use by [`XCBConnection`].
40pub type BufWithFds = crate::connection::BufWithFds<Buffer>;
41
42/// A connection to an X11 server.
43///
44/// This type wraps `*mut xcb_connection_t` that is provided by libxcb. It provides a rust
45/// interface to this C library.
46#[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    /// Establish a new connection to an X11 server.
58    ///
59    /// If a `dpy_name` is provided, it describes the display that should be connected to, for
60    /// example `127.0.0.1:1`. If no value is provided, the `$DISPLAY` environment variable is
61    /// used.
62    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                    // `xcb_connect` will never return null.
78                    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    /// Create a connection wrapper for a raw libxcb `xcb_connection_t`.
90    ///
91    /// `xcb_disconnect` is called on drop only if `should_drop` is `true`.
92    /// If this function returns an `Err()` and `should_drop` was true, then
93    /// `xcb_disconnect` was already called.
94    ///
95    /// # Safety
96    ///
97    /// If `should_drop` is `false`, the connection must live longer than the returned
98    /// `XCBConnection`. If `should_drop` is `true`, the returned `XCBConnection` will
99    /// take the ownership of the connection.
100    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            // Not possible here: PARSE_ERR, INVALID_SCREEN
143        }
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            // Not possible here: EXT_NOTSUPPORTED, REQ_LEN_EXCEED, FDPASSING_FAILED
157        }
158    }
159
160    /// Create an `XCBConnection` from an object that implements `AsRawXcbConnection`.
161    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    /// Get a reference to the inner managed connection.
173    ///
174    /// It is discouraged to use this inner connection for fetching events.
175    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        // We know that the setup information has at least eight bytes.
187        // Use a slice instead of Buffer::CSlice since we must not free() the xcb_setup_t that libxcb owns.
188        let wrapper = from_raw_parts(setup as *const u8, 8);
189
190        // The length field is in the last two bytes
191        let length = u16::from_ne_bytes([wrapper[6], wrapper[7]]);
192
193        // The length is in four-byte-units after the known header
194        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    // Slince the warning about ioslice.len().try_into().unwrap(). The target type is sometimes
203    // usize (where this warning is correct) and sometimes c_int (where we need the conversion). We
204    // need this here due to https://github.com/rust-lang/rust/issues/60681.
205    #[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        // Now wrap the buffers with IoSlice
217        let mut new_bufs_ffi = Vec::with_capacity(2 + new_bufs.len());
218        // XCB wants to access bufs[-1] and bufs[-2], so we need to add two empty items in front.
219        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        // Set up the information that libxcb needs
233        let protocol_request = raw_ffi::xcb_protocol_request_t {
234            count: new_bufs.len(),
235            ext: null_mut(), // Not needed since we always use raw
236            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                // Convert the FDs into an array of ints. libxcb will close the FDs.
259                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    /// Check if the underlying XCB connection is in an error state.
286    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    /// Get access to the raw libxcb `xcb_connection_t`.
298    ///
299    /// The returned pointer is valid for as long as the original object was not dropped. No
300    /// ownerhsip is transferred.
301    pub fn get_raw_xcb_connection(&self) -> *mut c_void {
302        self.as_ptr().cast()
303    }
304
305    /// Check if a reply to the given request already received.
306    ///
307    /// Return Err(()) when the reply was not yet received. Returns Ok(None) when there can be no
308    /// reply. Returns Ok(buffer) with the reply if there is one (this buffer can be an error or a
309    /// reply).
310    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        // Update our "max sequence number received" field
331        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        // Update our "max sequence number received" field
348        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        // XCB inserts a uint32_t with the sequence number after the first 32 bytes.
359        let seqno = u32::from_ne_bytes([header[32], header[33], header[34], header[35]]);
360        let seqno = self.reconstruct_full_sequence(seqno);
361
362        // The first byte contains the event type, check for XGE events
363        if (*event & 0x7f) == super::protocol::xproto::GE_GENERIC_EVENT {
364            // Read the length field of the event to get its length
365            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            // Discard the `full_sequence` field inserted by xcb at
371            // the 32-byte boundary.
372            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    /// Reconstruct a full sequence number based on a partial value.
378    ///
379    /// The assumption for the algorithm here is that the given sequence number was received
380    /// recently. Thus, the maximum sequence number that was received so far is used to fill in the
381    /// missing bytes for the result.
382    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                // libxcb can throw away everything for us
436                raw_ffi::xcb_discard_reply64(self.as_ptr(), sequence);
437            },
438            // We have to check for errors ourselves
439            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                // At least one of these pointers must be NULL.
475                (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        // Get a pointer to the array of integers where libxcb saved the FD numbers.
501        // libxcb saves the list of FDs after the data of the reply. Since the reply's
502        // length is encoded in "number of 4 bytes block", the following pointer is aligned
503        // correctly (if malloc() returned an aligned chunk, which it does).
504        #[allow(clippy::cast_ptr_alignment)]
505        let fd_ptr = (unsafe { buffer.as_ptr().add(buffer.len()) }) as *const RawFd;
506
507        // The number of FDs is in the second byte (= buffer[1]) in all replies.
508        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        // xcb_flush() returns 0 if the connection is in (or just entered) an error state, else 1.
595        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            // XCB does not document the behaviour of `xcb_generate_id` when
607            // there is an error. Looking at its source code it seems that it
608            // returns `-1` (presumably `u32::MAX`).
609            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
660// SAFETY: We provide a valid xcb_connection_t that is valid for as long as required by the trait.
661unsafe 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
667/// Reconstruct a partial sequence number based on a recently received 'full' sequence number.
668///
669/// The new sequence number may be before or after the `recent` sequence number.
670fn reconstruct_full_sequence_impl(recent: SequenceNumber, value: u32) -> SequenceNumber {
671    // Expand 'value' to a full sequence number. The high bits are copied from 'recent'.
672    let u32_max = SequenceNumber::from(u32::MAX);
673    let expanded_value = SequenceNumber::from(value) | (recent & !u32_max);
674
675    // The "step size" is the difference between two sequence numbers that cannot be told apart
676    // from their truncated value.
677    let step: SequenceNumber = SequenceNumber::from(1u8) << 32;
678
679    // There are three possible values for the returned sequence number:
680    // - The extended value
681    // - The extended value plus one step
682    // - The extended value minus one step
683    // Pick the value out of the possible values that is closest to `recent`.
684    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    // Just because: Check that the result matches the passed-in value in the low bits
694    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        // in cfg(test), raw_ffi does not call XCB, but instead uses a mock. This test calls into
709        // that mock and tests a bit of XCBConnection.
710
711        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            // This one is a special case: Technically, -1 is closer and should be reconstructed,
727            // but -1 does not fit into an unsigned integer.
728            (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}