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 
5 use std::convert::TryInto;
6 use std::ffi::CStr;
7 use std::io::{Error as IOError, ErrorKind, IoSlice};
8 use std::os::raw::c_int;
9 #[cfg(unix)]
10 use std::os::unix::io::{AsRawFd, RawFd};
11 use std::ptr::{null, null_mut};
12 use std::sync::{
13     atomic::{AtomicU64, Ordering},
14     Mutex,
15 };
16 
17 use libc::c_void;
18 
19 use crate::connection::{
20     compute_length_field, Connection, DiscardMode, ReplyOrError, RequestConnection, RequestKind,
21     SequenceNumber,
22 };
23 use crate::cookie::{Cookie, CookieWithFds, VoidCookie};
24 pub use crate::errors::{ConnectError, ConnectionError, ParseError, ReplyError, ReplyOrIdError};
25 use crate::extension_manager::ExtensionManager;
26 use crate::protocol::xproto::Setup;
27 use crate::utils::{CSlice, RawFdContainer};
28 use crate::x11_utils::{ExtensionInformation, TryParse, TryParseFd};
29 
30 mod pending_errors;
31 mod raw_ffi;
32 
33 #[cfg(all(not(test), feature = "dl-libxcb"))]
34 pub use raw_ffi::libxcb_library::load_libxcb;
35 
36 type Buffer = <XCBConnection as RequestConnection>::Buf;
37 /// The raw bytes of an event received by [`XCBConnection`] and its sequence number.
38 pub type RawEventAndSeqNumber = crate::connection::RawEventAndSeqNumber<Buffer>;
39 /// A combination of a buffer and a list of file descriptors for use by [`XCBConnection`].
40 pub 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 #[derive(Debug)]
47 pub struct XCBConnection {
48     conn: raw_ffi::XCBConnectionWrapper,
49     setup: Setup,
50     ext_mgr: Mutex<ExtensionManager>,
51     errors: pending_errors::PendingErrors,
52     maximum_sequence_received: AtomicU64,
53 }
54 
55 impl XCBConnection {
connection_error_from_connection( c: *mut raw_ffi::xcb_connection_t, ) -> ConnectionError56     unsafe fn connection_error_from_connection(
57         c: *mut raw_ffi::xcb_connection_t,
58     ) -> ConnectionError {
59         Self::connection_error_from_c_error(raw_ffi::xcb_connection_has_error(c))
60     }
61 
connection_error_from_c_error(error: c_int) -> ConnectionError62     fn connection_error_from_c_error(error: c_int) -> ConnectionError {
63         use crate::xcb_ffi::raw_ffi::connection_errors::*;
64 
65         assert_ne!(error, 0);
66         match error {
67             ERROR => IOError::new(ErrorKind::Other, ConnectionError::UnknownError).into(),
68             EXT_NOTSUPPORTED => ConnectionError::UnsupportedExtension,
69             MEM_INSUFFICIENT => ConnectionError::InsufficientMemory,
70             REQ_LEN_EXCEED => ConnectionError::MaximumRequestLengthExceeded,
71             FDPASSING_FAILED => ConnectionError::FDPassingFailed,
72             _ => ConnectionError::UnknownError,
73             // Not possible here: PARSE_ERR, INVALID_SCREEN
74         }
75     }
76 
connect_error_from_c_error(error: c_int) -> ConnectError77     fn connect_error_from_c_error(error: c_int) -> ConnectError {
78         use crate::xcb_ffi::raw_ffi::connection_errors::*;
79 
80         assert_ne!(error, 0);
81         match error {
82             ERROR => IOError::new(ErrorKind::Other, ConnectionError::UnknownError).into(),
83             MEM_INSUFFICIENT => ConnectError::InsufficientMemory,
84             PARSE_ERR => ConnectError::DisplayParsingError,
85             INVALID_SCREEN => ConnectError::InvalidScreen,
86             _ => ConnectError::UnknownError,
87             // Not possible here: EXT_NOTSUPPORTED, REQ_LEN_EXCEED, FDPASSING_FAILED
88         }
89     }
90 
91     /// Establish a new connection to an X11 server.
92     ///
93     /// If a `dpy_name` is provided, it describes the display that should be connected to, for
94     /// example `127.0.0.1:1`. If no value is provided, the `$DISPLAY` environment variable is
95     /// used.
connect(dpy_name: Option<&CStr>) -> Result<(XCBConnection, usize), ConnectError>96     pub fn connect(dpy_name: Option<&CStr>) -> Result<(XCBConnection, usize), ConnectError> {
97         use libc::c_int;
98         unsafe {
99             let mut screen: c_int = 0;
100             let dpy_ptr = dpy_name.map_or(null(), |s| s.as_ptr());
101             let connection = raw_ffi::XCBConnectionWrapper::new(
102                 raw_ffi::xcb_connect(dpy_ptr, &mut screen),
103                 true,
104             );
105             let error = raw_ffi::xcb_connection_has_error(connection.as_ptr());
106             if error != 0 {
107                 Err(Self::connect_error_from_c_error(error))
108             } else {
109                 let setup = raw_ffi::xcb_get_setup(connection.as_ptr());
110                 let conn = XCBConnection {
111                     // `xcb_connect` will never return null.
112                     conn: connection,
113                     setup: Self::parse_setup(setup)?,
114                     ext_mgr: Default::default(),
115                     errors: Default::default(),
116                     maximum_sequence_received: AtomicU64::new(0),
117                 };
118                 Ok((conn, screen as usize))
119             }
120         }
121     }
122 
123     /// Create a connection wrapper for a raw libxcb `xcb_connection_t`.
124     ///
125     /// `xcb_disconnect` is called on drop only if `should_drop` is `true`.
126     ///
127     /// # Safety
128     ///
129     /// If `should_drop` is `false`, the connection must live longer than the returned
130     /// `XCBConnection`. If `should_drop` is `true`, the returned `XCBConnection` will
131     /// take the ownership of the connection.
from_raw_xcb_connection( ptr: *mut c_void, should_drop: bool, ) -> Result<XCBConnection, ConnectError>132     pub unsafe fn from_raw_xcb_connection(
133         ptr: *mut c_void,
134         should_drop: bool,
135     ) -> Result<XCBConnection, ConnectError> {
136         let ptr = ptr as *mut raw_ffi::xcb_connection_t;
137         let setup = raw_ffi::xcb_get_setup(ptr);
138         Ok(XCBConnection {
139             conn: raw_ffi::XCBConnectionWrapper::new(ptr, should_drop),
140             setup: Self::parse_setup(setup)?,
141             ext_mgr: Default::default(),
142             errors: Default::default(),
143             maximum_sequence_received: AtomicU64::new(0),
144         })
145     }
146 
parse_setup(setup: *const raw_ffi::xcb_setup_t) -> Result<Setup, ParseError>147     unsafe fn parse_setup(setup: *const raw_ffi::xcb_setup_t) -> Result<Setup, ParseError> {
148         use std::slice::from_raw_parts;
149 
150         // We know that the setup information has at least eight bytes.
151         // Use a slice instead of Buffer::CSlice since we must not free() the xcb_setup_t that libxcb owns.
152         let wrapper = from_raw_parts(setup as *const u8, 8);
153 
154         // The length field is in the last two bytes
155         let length = u16::from_ne_bytes([wrapper[6], wrapper[7]]);
156 
157         // The length is in four-byte-units after the known header
158         let length = usize::from(length) * 4 + 8;
159 
160         let slice = from_raw_parts(wrapper.as_ptr(), length);
161         let result = Setup::try_parse(&*slice)?.0;
162 
163         Ok(result)
164     }
165 
166     // Slince the warning about ioslice.len().try_into().unwrap(). The target type is sometimes
167     // usize (where this warning is correct) and sometimes c_int (where we need the conversion). We
168     // need this here due to https://github.com/rust-lang/rust/issues/60681.
169     #[allow(clippy::useless_conversion)]
send_request( &self, bufs: &[IoSlice<'_>], fds: Vec<RawFdContainer>, has_reply: bool, reply_has_fds: bool, ) -> Result<SequenceNumber, ConnectionError>170     fn send_request(
171         &self,
172         bufs: &[IoSlice<'_>],
173         fds: Vec<RawFdContainer>,
174         has_reply: bool,
175         reply_has_fds: bool,
176     ) -> Result<SequenceNumber, ConnectionError> {
177         let mut storage = Default::default();
178         let new_bufs = compute_length_field(self, bufs, &mut storage)?;
179 
180         // Now wrap the buffers with IoSlice
181         let mut new_bufs_ffi = Vec::with_capacity(2 + new_bufs.len());
182         // XCB wants to access bufs[-1] and bufs[-2], so we need to add two empty items in front.
183         new_bufs_ffi.push(raw_ffi::iovec {
184             iov_base: null_mut(),
185             iov_len: 0,
186         });
187         new_bufs_ffi.push(raw_ffi::iovec {
188             iov_base: null_mut(),
189             iov_len: 0,
190         });
191         new_bufs_ffi.extend(new_bufs.iter().map(|ioslice| raw_ffi::iovec {
192             iov_base: ioslice.as_ptr() as _,
193             iov_len: ioslice.len().try_into().unwrap(),
194         }));
195 
196         // Set up the information that libxcb needs
197         let protocol_request = raw_ffi::xcb_protocol_request_t {
198             count: new_bufs.len(),
199             ext: null_mut(), // Not needed since we always use raw
200             opcode: 0,
201             isvoid: if has_reply { 0 } else { 1 },
202         };
203         let mut flags = raw_ffi::send_request_flags::RAW;
204         assert!(has_reply || !reply_has_fds);
205         flags |= raw_ffi::send_request_flags::CHECKED;
206         if reply_has_fds {
207             flags |= raw_ffi::send_request_flags::REPLY_FDS;
208         }
209 
210         let seqno = if fds.is_empty() {
211             unsafe {
212                 raw_ffi::xcb_send_request64(
213                     self.conn.as_ptr(),
214                     flags,
215                     &mut new_bufs_ffi[2],
216                     &protocol_request,
217                 )
218             }
219         } else {
220             #[cfg(unix)]
221             {
222                 // Convert the FDs into an array of ints. libxcb will close the FDs.
223                 let mut fds: Vec<_> = fds.into_iter().map(RawFdContainer::into_raw_fd).collect();
224                 let num_fds = fds.len().try_into().unwrap();
225                 let fds_ptr = fds.as_mut_ptr();
226                 unsafe {
227                     raw_ffi::xcb_send_request_with_fds64(
228                         self.conn.as_ptr(),
229                         flags,
230                         &mut new_bufs_ffi[2],
231                         &protocol_request,
232                         num_fds,
233                         fds_ptr,
234                     )
235                 }
236             }
237             #[cfg(not(unix))]
238             {
239                 unreachable!("it is not possible to create a `RawFdContainer` on non-unix");
240             }
241         };
242         if seqno == 0 {
243             unsafe { Err(Self::connection_error_from_connection(self.conn.as_ptr())) }
244         } else {
245             Ok(seqno)
246         }
247     }
248 
249     /// Check if the underlying XCB connection is in an error state.
has_error(&self) -> Option<ConnectionError>250     pub fn has_error(&self) -> Option<ConnectionError> {
251         unsafe {
252             let error = raw_ffi::xcb_connection_has_error(self.conn.as_ptr());
253             if error == 0 {
254                 None
255             } else {
256                 Some(Self::connection_error_from_c_error(error))
257             }
258         }
259     }
260 
261     /// Get access to the raw libxcb `xcb_connection_t`.
262     ///
263     /// The returned pointer is valid for as long as the original object was not dropped. No
264     /// ownerhsip is transferred.
get_raw_xcb_connection(&self) -> *mut c_void265     pub fn get_raw_xcb_connection(&self) -> *mut c_void {
266         self.conn.as_ptr() as _
267     }
268 
269     /// Check if a reply to the given request already received.
270     ///
271     /// Return Err(()) when the reply was not yet received. Returns Ok(None) when there can be no
272     /// reply. Returns Ok(buffer) with the reply if there is one (this buffer can be an error or a
273     /// reply).
poll_for_reply(&self, sequence: SequenceNumber) -> Result<Option<CSlice>, ()>274     fn poll_for_reply(&self, sequence: SequenceNumber) -> Result<Option<CSlice>, ()> {
275         unsafe {
276             let mut reply = null_mut();
277             let mut error = null_mut();
278             let found =
279                 raw_ffi::xcb_poll_for_reply64(self.conn.as_ptr(), sequence, &mut reply, &mut error);
280             if found == 0 {
281                 return Err(());
282             }
283             assert_eq!(found, 1);
284             match (reply.is_null(), error.is_null()) {
285                 (true, true) => Ok(None),
286                 (true, false) => Ok(Some(self.wrap_error(error as _, sequence))),
287                 (false, true) => Ok(Some(self.wrap_reply(reply as _, sequence))),
288                 (false, false) => unreachable!(),
289             }
290         }
291     }
292 
wrap_reply(&self, reply: *const u8, sequence: SequenceNumber) -> CSlice293     unsafe fn wrap_reply(&self, reply: *const u8, sequence: SequenceNumber) -> CSlice {
294         // Update our "max sequence number received" field
295         atomic_u64_max(&self.maximum_sequence_received, sequence);
296 
297         let header = CSlice::new(reply, 32);
298 
299         let length_field = u32::from_ne_bytes(header[4..8].try_into().unwrap());
300         let length_field: usize = length_field
301             .try_into()
302             .expect("usize should have at least 32 bits");
303 
304         let length = 32 + length_field * 4;
305         CSlice::new(header.into_ptr(), length)
306     }
307 
wrap_error(&self, error: *const u8, sequence: SequenceNumber) -> CSlice308     unsafe fn wrap_error(&self, error: *const u8, sequence: SequenceNumber) -> CSlice {
309         // Update our "max sequence number received" field
310         atomic_u64_max(&self.maximum_sequence_received, sequence);
311 
312         CSlice::new(error, 32)
313     }
314 
wrap_event(&self, event: *mut u8) -> Result<RawEventAndSeqNumber, ParseError>315     unsafe fn wrap_event(&self, event: *mut u8) -> Result<RawEventAndSeqNumber, ParseError> {
316         let header = CSlice::new(event, 36);
317         let mut length = 32;
318         // XCB inserts a uint32_t with the sequence number after the first 32 bytes.
319         let seqno = u32::from_ne_bytes([header[32], header[33], header[34], header[35]]);
320         let seqno = self.reconstruct_full_sequence(seqno);
321 
322         // The first byte contains the event type, check for XGE events
323         if (*event & 0x7f) == super::protocol::xproto::GE_GENERIC_EVENT {
324             // Read the length field of the event to get its length
325             let length_field = u32::from_ne_bytes([header[4], header[5], header[6], header[7]]);
326             let length_field: usize = length_field
327                 .try_into()
328                 .or(Err(ParseError::ConversionFailed))?;
329             length += length_field * 4;
330             // Discard the `full_sequence` field inserted by xcb at
331             // the 32-byte boundary.
332             std::ptr::copy(event.add(36), event.add(32), length_field * 4);
333         }
334         Ok((CSlice::new(header.into_ptr(), length), seqno))
335     }
336 
337     /// Reconstruct a full sequence number based on a partial value.
338     ///
339     /// The assumption for the algorithm here is that the given sequence number was received
340     /// recently. Thus, the maximum sequence number that was received so far is used to fill in the
341     /// missing bytes for the result.
reconstruct_full_sequence(&self, seqno: u32) -> SequenceNumber342     fn reconstruct_full_sequence(&self, seqno: u32) -> SequenceNumber {
343         reconstruct_full_sequence_impl(
344             self.maximum_sequence_received.load(Ordering::Relaxed),
345             seqno,
346         )
347     }
348 }
349 
350 impl RequestConnection for XCBConnection {
351     type Buf = CSlice;
352 
send_request_with_reply<R>( &self, bufs: &[IoSlice<'_>], fds: Vec<RawFdContainer>, ) -> Result<Cookie<'_, Self, R>, ConnectionError> where R: TryParse,353     fn send_request_with_reply<R>(
354         &self,
355         bufs: &[IoSlice<'_>],
356         fds: Vec<RawFdContainer>,
357     ) -> Result<Cookie<'_, Self, R>, ConnectionError>
358     where
359         R: TryParse,
360     {
361         Ok(Cookie::new(
362             self,
363             self.send_request(bufs, fds, true, false)?,
364         ))
365     }
366 
send_request_with_reply_with_fds<R>( &self, bufs: &[IoSlice<'_>], fds: Vec<RawFdContainer>, ) -> Result<CookieWithFds<'_, Self, R>, ConnectionError> where R: TryParseFd,367     fn send_request_with_reply_with_fds<R>(
368         &self,
369         bufs: &[IoSlice<'_>],
370         fds: Vec<RawFdContainer>,
371     ) -> Result<CookieWithFds<'_, Self, R>, ConnectionError>
372     where
373         R: TryParseFd,
374     {
375         Ok(CookieWithFds::new(
376             self,
377             self.send_request(bufs, fds, true, true)?,
378         ))
379     }
380 
send_request_without_reply( &self, bufs: &[IoSlice<'_>], fds: Vec<RawFdContainer>, ) -> Result<VoidCookie<'_, Self>, ConnectionError>381     fn send_request_without_reply(
382         &self,
383         bufs: &[IoSlice<'_>],
384         fds: Vec<RawFdContainer>,
385     ) -> Result<VoidCookie<'_, Self>, ConnectionError> {
386         Ok(VoidCookie::new(
387             self,
388             self.send_request(bufs, fds, false, false)?,
389         ))
390     }
391 
discard_reply(&self, sequence: SequenceNumber, _kind: RequestKind, mode: DiscardMode)392     fn discard_reply(&self, sequence: SequenceNumber, _kind: RequestKind, mode: DiscardMode) {
393         match mode {
394             DiscardMode::DiscardReplyAndError => unsafe {
395                 // libxcb can throw away everything for us
396                 raw_ffi::xcb_discard_reply64(self.conn.as_ptr(), sequence);
397             },
398             // We have to check for errors ourselves
399             DiscardMode::DiscardReply => self.errors.discard_reply(sequence),
400         }
401     }
402 
prefetch_extension_information( &self, extension_name: &'static str, ) -> Result<(), ConnectionError>403     fn prefetch_extension_information(
404         &self,
405         extension_name: &'static str,
406     ) -> Result<(), ConnectionError> {
407         self.ext_mgr
408             .lock()
409             .unwrap()
410             .prefetch_extension_information(self, extension_name)
411     }
412 
extension_information( &self, extension_name: &'static str, ) -> Result<Option<ExtensionInformation>, ConnectionError>413     fn extension_information(
414         &self,
415         extension_name: &'static str,
416     ) -> Result<Option<ExtensionInformation>, ConnectionError> {
417         self.ext_mgr
418             .lock()
419             .unwrap()
420             .extension_information(self, extension_name)
421     }
422 
wait_for_reply_or_raw_error( &self, sequence: SequenceNumber, ) -> Result<ReplyOrError<CSlice>, ConnectionError>423     fn wait_for_reply_or_raw_error(
424         &self,
425         sequence: SequenceNumber,
426     ) -> Result<ReplyOrError<CSlice>, ConnectionError> {
427         unsafe {
428             let mut error = null_mut();
429             let reply = raw_ffi::xcb_wait_for_reply64(self.conn.as_ptr(), sequence, &mut error);
430             match (reply.is_null(), error.is_null()) {
431                 (true, true) => Err(Self::connection_error_from_connection(self.conn.as_ptr())),
432                 (false, true) => Ok(ReplyOrError::Reply(self.wrap_reply(reply as _, sequence))),
433                 (true, false) => Ok(ReplyOrError::Error(self.wrap_error(error as _, sequence))),
434                 // At least one of these pointers must be NULL.
435                 (false, false) => unreachable!(),
436             }
437         }
438     }
439 
wait_for_reply(&self, sequence: SequenceNumber) -> Result<Option<CSlice>, ConnectionError>440     fn wait_for_reply(&self, sequence: SequenceNumber) -> Result<Option<CSlice>, ConnectionError> {
441         match self.wait_for_reply_or_raw_error(sequence)? {
442             ReplyOrError::Reply(reply) => Ok(Some(reply)),
443             ReplyOrError::Error(error) => {
444                 self.errors.append_error((sequence, error));
445                 Ok(None)
446             }
447         }
448     }
449 
450     #[cfg(unix)]
wait_for_reply_with_fds_raw( &self, sequence: SequenceNumber, ) -> Result<ReplyOrError<BufWithFds, Buffer>, ConnectionError>451     fn wait_for_reply_with_fds_raw(
452         &self,
453         sequence: SequenceNumber,
454     ) -> Result<ReplyOrError<BufWithFds, Buffer>, ConnectionError> {
455         let buffer = match self.wait_for_reply_or_raw_error(sequence)? {
456             ReplyOrError::Reply(reply) => reply,
457             ReplyOrError::Error(error) => return Ok(ReplyOrError::Error(error)),
458         };
459 
460         // Get a pointer to the array of integers where libxcb saved the FD numbers.
461         // libxcb saves the list of FDs after the data of the reply. Since the reply's
462         // length is encoded in "number of 4 bytes block", the following pointer is aligned
463         // correctly (if malloc() returned an aligned chunk, which it does).
464         #[allow(clippy::cast_ptr_alignment)]
465         let fd_ptr = (unsafe { buffer.as_ptr().add(buffer.len()) }) as *const RawFd;
466 
467         // The number of FDs is in the second byte (= buffer[1]) in all replies.
468         let fd_slice = unsafe { std::slice::from_raw_parts(fd_ptr, usize::from(buffer[1])) };
469         let fd_vec = fd_slice.iter().map(|&fd| RawFdContainer::new(fd)).collect();
470 
471         Ok(ReplyOrError::Reply((buffer, fd_vec)))
472     }
473 
474     #[cfg(not(unix))]
wait_for_reply_with_fds_raw( &self, _sequence: SequenceNumber, ) -> Result<ReplyOrError<BufWithFds, Buffer>, ConnectionError>475     fn wait_for_reply_with_fds_raw(
476         &self,
477         _sequence: SequenceNumber,
478     ) -> Result<ReplyOrError<BufWithFds, Buffer>, ConnectionError> {
479         unimplemented!("FD passing is currently only implemented on Unix-like systems")
480     }
481 
check_for_raw_error( &self, sequence: SequenceNumber, ) -> Result<Option<Buffer>, ConnectionError>482     fn check_for_raw_error(
483         &self,
484         sequence: SequenceNumber,
485     ) -> Result<Option<Buffer>, ConnectionError> {
486         let cookie = raw_ffi::xcb_void_cookie_t {
487             sequence: sequence as _,
488         };
489         let error = unsafe { raw_ffi::xcb_request_check(self.conn.as_ptr(), cookie) };
490         if error.is_null() {
491             Ok(None)
492         } else {
493             unsafe { Ok(Some(self.wrap_error(error as _, sequence))) }
494         }
495     }
496 
maximum_request_bytes(&self) -> usize497     fn maximum_request_bytes(&self) -> usize {
498         4 * unsafe { raw_ffi::xcb_get_maximum_request_length(self.conn.as_ptr()) as usize }
499     }
500 
prefetch_maximum_request_bytes(&self)501     fn prefetch_maximum_request_bytes(&self) {
502         unsafe { raw_ffi::xcb_prefetch_maximum_request_length(self.conn.as_ptr()) };
503     }
504 
parse_error(&self, error: &[u8]) -> Result<crate::x11_utils::X11Error, ParseError>505     fn parse_error(&self, error: &[u8]) -> Result<crate::x11_utils::X11Error, ParseError> {
506         let ext_mgr = self.ext_mgr.lock().unwrap();
507         crate::x11_utils::X11Error::try_parse(error, &*ext_mgr)
508     }
509 
parse_event(&self, event: &[u8]) -> Result<crate::protocol::Event, ParseError>510     fn parse_event(&self, event: &[u8]) -> Result<crate::protocol::Event, ParseError> {
511         let ext_mgr = self.ext_mgr.lock().unwrap();
512         crate::protocol::Event::parse(event, &*ext_mgr)
513     }
514 }
515 
516 impl Connection for XCBConnection {
wait_for_raw_event_with_sequence(&self) -> Result<RawEventAndSeqNumber, ConnectionError>517     fn wait_for_raw_event_with_sequence(&self) -> Result<RawEventAndSeqNumber, ConnectionError> {
518         if let Some(error) = self.errors.get(self) {
519             return Ok((error.1, error.0));
520         }
521         unsafe {
522             let event = raw_ffi::xcb_wait_for_event(self.conn.as_ptr());
523             if event.is_null() {
524                 return Err(Self::connection_error_from_connection(self.conn.as_ptr()));
525             }
526             Ok(self.wrap_event(event as _)?)
527         }
528     }
529 
poll_for_raw_event_with_sequence( &self, ) -> Result<Option<RawEventAndSeqNumber>, ConnectionError>530     fn poll_for_raw_event_with_sequence(
531         &self,
532     ) -> Result<Option<RawEventAndSeqNumber>, ConnectionError> {
533         if let Some(error) = self.errors.get(self) {
534             return Ok(Some((error.1, error.0)));
535         }
536         unsafe {
537             let event = raw_ffi::xcb_poll_for_event(self.conn.as_ptr());
538             if event.is_null() {
539                 let err = raw_ffi::xcb_connection_has_error(self.conn.as_ptr());
540                 if err == 0 {
541                     return Ok(None);
542                 } else {
543                     return Err(Self::connection_error_from_c_error(err));
544                 }
545             }
546             Ok(Some(self.wrap_event(event as _)?))
547         }
548     }
549 
flush(&self) -> Result<(), ConnectionError>550     fn flush(&self) -> Result<(), ConnectionError> {
551         // xcb_flush() returns 0 if the connection is in (or just entered) an error state, else 1.
552         let res = unsafe { raw_ffi::xcb_flush(self.conn.as_ptr()) };
553         if res != 0 {
554             Ok(())
555         } else {
556             unsafe { Err(Self::connection_error_from_connection(self.conn.as_ptr())) }
557         }
558     }
559 
generate_id(&self) -> Result<u32, ReplyOrIdError>560     fn generate_id(&self) -> Result<u32, ReplyOrIdError> {
561         unsafe {
562             let id = raw_ffi::xcb_generate_id(self.conn.as_ptr());
563             // XCB does not document the behaviour of `xcb_generate_id` when
564             // there is an error. Looking at its source code it seems that it
565             // returns `-1` (presumably `u32::max_value()`).
566             if id == u32::max_value() {
567                 Err(Self::connection_error_from_connection(self.conn.as_ptr()).into())
568             } else {
569                 Ok(id)
570             }
571         }
572     }
573 
setup(&self) -> &Setup574     fn setup(&self) -> &Setup {
575         &self.setup
576     }
577 }
578 
579 #[cfg(unix)]
580 impl AsRawFd for XCBConnection {
as_raw_fd(&self) -> RawFd581     fn as_raw_fd(&self) -> RawFd {
582         unsafe { raw_ffi::xcb_get_file_descriptor(self.conn.as_ptr()) }
583     }
584 }
585 
586 /// Atomically sets `value` to the maximum of `value` and `new`.
atomic_u64_max(value: &AtomicU64, new: u64)587 fn atomic_u64_max(value: &AtomicU64, new: u64) {
588     // If only AtomicU64::fetch_max were stable...
589     let mut old = value.load(Ordering::Relaxed);
590     loop {
591         if old >= new {
592             return;
593         }
594         match value.compare_exchange_weak(old, new, Ordering::Relaxed, Ordering::Relaxed) {
595             Ok(_) => return,
596             Err(val) => old = val,
597         }
598     }
599 }
600 
601 /// Reconstruct a partial sequence number based on a recently received 'full' sequence number.
602 ///
603 /// The new sequence number may be before or after the `recent` sequence number.
reconstruct_full_sequence_impl(recent: SequenceNumber, value: u32) -> SequenceNumber604 fn reconstruct_full_sequence_impl(recent: SequenceNumber, value: u32) -> SequenceNumber {
605     // Expand 'value' to a full sequence number. The high bits are copied from 'recent'.
606     let u32_max = SequenceNumber::from(u32::max_value());
607     let expanded_value = SequenceNumber::from(value) | (recent & !u32_max);
608 
609     // The "step size" is the difference between two sequence numbers that cannot be told apart
610     // from their truncated value.
611     let step: SequenceNumber = SequenceNumber::from(1u8) << 32;
612 
613     // There are three possible values for the returned sequence number:
614     // - The extended value
615     // - The extended value plus one step
616     // - The extended value minus one step
617     // Pick the value out of the possible values that is closest to `recent`.
618     let result = [
619         expanded_value,
620         expanded_value + step,
621         expanded_value.wrapping_sub(step),
622     ]
623     .iter()
624     .copied()
625     .min_by_key(|&value| {
626         if value > recent {
627             value - recent
628         } else {
629             recent - value
630         }
631     })
632     .unwrap();
633     // Just because: Check that the result matches the passed-in value in the low bits
634     assert_eq!(
635         result & SequenceNumber::from(u32::max_value()),
636         SequenceNumber::from(value),
637     );
638     result
639 }
640 
641 #[cfg(test)]
642 mod test {
643     use super::{atomic_u64_max, XCBConnection};
644     use std::ffi::CString;
645 
646     #[test]
xcb_connect_smoke_test()647     fn xcb_connect_smoke_test() {
648         // in cfg(test), raw_ffi does not call XCB, but instead uses a mock. This test calls into
649         // that mock and tests a bit of XCBConnection.
650 
651         let str = CString::new("display name").unwrap();
652         let (_conn, screen) = XCBConnection::connect(Some(&str)).expect("Failed to 'connect'");
653         assert_eq!(screen, 0);
654     }
655 
656     #[test]
u64_max()657     fn u64_max() {
658         use std::sync::atomic::{AtomicU64, Ordering};
659         let value = AtomicU64::new(0);
660 
661         atomic_u64_max(&value, 3);
662         assert_eq!(value.load(Ordering::Relaxed), 3);
663 
664         atomic_u64_max(&value, 7);
665         assert_eq!(value.load(Ordering::Relaxed), 7);
666 
667         atomic_u64_max(&value, 5);
668         assert_eq!(value.load(Ordering::Relaxed), 7);
669 
670         atomic_u64_max(&value, 7);
671         assert_eq!(value.load(Ordering::Relaxed), 7);
672 
673         atomic_u64_max(&value, 9);
674         assert_eq!(value.load(Ordering::Relaxed), 9);
675     }
676 
677     #[test]
reconstruct_full_sequence()678     fn reconstruct_full_sequence() {
679         use super::reconstruct_full_sequence_impl;
680         let max32 = u32::max_value();
681         let max32_: u64 = max32.into();
682         let max32_p1 = max32_ + 1;
683         let large_offset = max32_p1 * u64::from(u16::max_value());
684         for &(recent, value, expected) in &[
685             (0, 0, 0),
686             (0, 10, 10),
687             // This one is a special case: Technically, -1 is closer and should be reconstructed,
688             // but -1 does not fit into an unsigned integer.
689             (0, max32, max32_),
690             (max32_, 0, max32_p1),
691             (max32_, 10, max32_p1 + 10),
692             (max32_, max32, max32_),
693             (max32_p1, 0, max32_p1),
694             (max32_p1, 10, max32_p1 + 10),
695             (max32_p1, max32, max32_),
696             (large_offset | 0xdead_cafe, 0, large_offset + max32_p1),
697             (large_offset | 0xdead_cafe, max32, large_offset + max32_),
698             (0xabcd_1234_5678, 0xf000_0000, 0xabcc_f000_0000),
699             (0xabcd_8765_4321, 0xf000_0000, 0xabcd_f000_0000),
700         ] {
701             let actual = reconstruct_full_sequence_impl(recent, value);
702             assert_eq!(
703                 actual, expected,
704                 "reconstruct({:x}, {:x}) == {:x}, but was {:x}",
705                 recent, value, expected, actual,
706             );
707         }
708     }
709 }
710