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