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