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 {
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            // Not possible here: PARSE_ERR, INVALID_SCREEN
75        }
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            // Not possible here: EXT_NOTSUPPORTED, REQ_LEN_EXCEED, FDPASSING_FAILED
89        }
90    }
91
92    /// Establish a new connection to an X11 server.
93    ///
94    /// If a `dpy_name` is provided, it describes the display that should be connected to, for
95    /// example `127.0.0.1:1`. If no value is provided, the `$DISPLAY` environment variable is
96    /// used.
97    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                    // `xcb_connect` will never return null.
113                    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    /// Create a connection wrapper for a raw libxcb `xcb_connection_t`.
125    ///
126    /// `xcb_disconnect` is called on drop only if `should_drop` is `true`.
127    /// If this function returns an `Err()` and `should_drop` was true, then
128    /// `xcb_disconnect` was already called.
129    ///
130    /// # Safety
131    ///
132    /// If `should_drop` is `false`, the connection must live longer than the returned
133    /// `XCBConnection`. If `should_drop` is `true`, the returned `XCBConnection` will
134    /// take the ownership of the connection.
135    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        // We know that the setup information has at least eight bytes.
155        // Use a slice instead of Buffer::CSlice since we must not free() the xcb_setup_t that libxcb owns.
156        let wrapper = from_raw_parts(setup as *const u8, 8);
157
158        // The length field is in the last two bytes
159        let length = u16::from_ne_bytes([wrapper[6], wrapper[7]]);
160
161        // The length is in four-byte-units after the known header
162        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    // Slince the warning about ioslice.len().try_into().unwrap(). The target type is sometimes
171    // usize (where this warning is correct) and sometimes c_int (where we need the conversion). We
172    // need this here due to https://github.com/rust-lang/rust/issues/60681.
173    #[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        // Now wrap the buffers with IoSlice
185        let mut new_bufs_ffi = Vec::with_capacity(2 + new_bufs.len());
186        // XCB wants to access bufs[-1] and bufs[-2], so we need to add two empty items in front.
187        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        // Set up the information that libxcb needs
201        let protocol_request = raw_ffi::xcb_protocol_request_t {
202            count: new_bufs.len(),
203            ext: null_mut(), // Not needed since we always use raw
204            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                // Convert the FDs into an array of ints. libxcb will close the FDs.
227                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    /// Check if the underlying XCB connection is in an error state.
254    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    /// Get access to the raw libxcb `xcb_connection_t`.
266    ///
267    /// The returned pointer is valid for as long as the original object was not dropped. No
268    /// ownerhsip is transferred.
269    pub fn get_raw_xcb_connection(&self) -> *mut c_void {
270        self.conn.as_ptr() as _
271    }
272
273    /// Check if a reply to the given request already received.
274    ///
275    /// Return Err(()) when the reply was not yet received. Returns Ok(None) when there can be no
276    /// reply. Returns Ok(buffer) with the reply if there is one (this buffer can be an error or a
277    /// reply).
278    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        // Update our "max sequence number received" field
299        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        // Update our "max sequence number received" field
316        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        // XCB inserts a uint32_t with the sequence number after the first 32 bytes.
327        let seqno = u32::from_ne_bytes([header[32], header[33], header[34], header[35]]);
328        let seqno = self.reconstruct_full_sequence(seqno);
329
330        // The first byte contains the event type, check for XGE events
331        if (*event & 0x7f) == super::protocol::xproto::GE_GENERIC_EVENT {
332            // Read the length field of the event to get its length
333            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            // Discard the `full_sequence` field inserted by xcb at
339            // the 32-byte boundary.
340            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    /// Reconstruct a full sequence number based on a partial value.
346    ///
347    /// The assumption for the algorithm here is that the given sequence number was received
348    /// recently. Thus, the maximum sequence number that was received so far is used to fill in the
349    /// missing bytes for the result.
350    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                // libxcb can throw away everything for us
404                raw_ffi::xcb_discard_reply64(self.conn.as_ptr(), sequence);
405            },
406            // We have to check for errors ourselves
407            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                // At least one of these pointers must be NULL.
443                (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        // Get a pointer to the array of integers where libxcb saved the FD numbers.
469        // libxcb saves the list of FDs after the data of the reply. Since the reply's
470        // length is encoded in "number of 4 bytes block", the following pointer is aligned
471        // correctly (if malloc() returned an aligned chunk, which it does).
472        #[allow(clippy::cast_ptr_alignment)]
473        let fd_ptr = (unsafe { buffer.as_ptr().add(buffer.len()) }) as *const RawFd;
474
475        // The number of FDs is in the second byte (= buffer[1]) in all replies.
476        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        // xcb_flush() returns 0 if the connection is in (or just entered) an error state, else 1.
563        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            // XCB does not document the behaviour of `xcb_generate_id` when
575            // there is an error. Looking at its source code it seems that it
576            // returns `-1` (presumably `u32::max_value()`).
577            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
604// SAFETY: We provide a valid xcb_connection_t that is valid for as long as required by the trait.
605unsafe 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
611/// Reconstruct a partial sequence number based on a recently received 'full' sequence number.
612///
613/// The new sequence number may be before or after the `recent` sequence number.
614fn reconstruct_full_sequence_impl(recent: SequenceNumber, value: u32) -> SequenceNumber {
615    // Expand 'value' to a full sequence number. The high bits are copied from 'recent'.
616    let u32_max = SequenceNumber::from(u32::max_value());
617    let expanded_value = SequenceNumber::from(value) | (recent & !u32_max);
618
619    // The "step size" is the difference between two sequence numbers that cannot be told apart
620    // from their truncated value.
621    let step: SequenceNumber = SequenceNumber::from(1u8) << 32;
622
623    // There are three possible values for the returned sequence number:
624    // - The extended value
625    // - The extended value plus one step
626    // - The extended value minus one step
627    // Pick the value out of the possible values that is closest to `recent`.
628    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    // Just because: Check that the result matches the passed-in value in the low bits
644    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        // in cfg(test), raw_ffi does not call XCB, but instead uses a mock. This test calls into
659        // that mock and tests a bit of XCBConnection.
660
661        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            // This one is a special case: Technically, -1 is closer and should be reconstructed,
677            // but -1 does not fit into an unsigned integer.
678            (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}