1//! Utility functions that are not specific to X11.
2//!
3//! # RawFdContainer
4//!
5//! [`RawFdContainer`] is a variant of [`std::os::unix::io::RawFd`] with ownership semantics. This
6//! means that the `RawFd` will be closed when the `RawFdContainer` is dropped.
7//!
8//! On non-`cfg(unix)`-systems, this is an empty type without methods. It still exists as a type so
9//! that it can appear in interfaces, but it is not actually possible to construct an instance of
10//! `RawFdContainer`.
11
12#[cfg(all(feature = "std", unix))]
13mod raw_fd_container {
14 use std::os::unix::io::OwnedFd;
15
16 pub(crate) type RawFdContainer = OwnedFd;
17}
18
19#[cfg(not(all(feature = "std", unix)))]
20mod raw_fd_container {
21 use core::convert::Infallible;
22
23 #[derive(Debug)]
24 #[doc(hidden)]
25 pub struct RawFdContainer(Infallible);
26
27 impl Drop for RawFdContainer {
28 fn drop(&mut self) {
29 // This function exists for symmetry with cfg(unix)
30 match self.0 {}
31 }
32 }
33}
34
35/// A type representative of the file descriptors as they are sent to and from the X server.
36///
37/// On `cfg(unix)` platforms, this is a type alias for [`std::os::unix::io::OwnedFd`]. See the
38/// documentation for that type for more information on how it should be used. In most cases it
39/// can be cast into a [`File`] or [`UnixStream`], or otherwise downgraded into the actual
40/// underlying file descriptor.
41///
42/// On non-Unix platforms, this is an uninhabited type in the same vogue as [`Void`]. As handle
43/// passing is an undefined operation on non-Unix implementations of the X11 protocol, instances
44/// of this type cannot exist. No operations can be called on this type. If handle passing is ever
45/// added to any reference implementation of the X11 protocol, this type will be changed to
46/// something that can be used to represent the file descriptors.
47///
48/// Consumers of this type should be careful to check for `cfg(unix)` before using it in any
49/// meaningful way. Otherwise, the program will not compile on non-Unix platforms.
50///
51/// [`File`]: std::fs::File
52/// [`UnixStream`]: std::os::unix::net::UnixStream
53/// [`Void`]: https://docs.rs/void/latest/void/enum.Void.html
54pub type RawFdContainer = raw_fd_container::RawFdContainer;
55
56mod pretty_printer {
57 use core::fmt::{Debug, Formatter, Result};
58
59 /// A helper to pretty-print an enumeration value.
60 ///
61 /// This function prints the given number. If it matches one of the provided variants, that
62 /// match is used. Otherwise, the number is printed as a decimal.
63 ///
64 /// In alternate mode, the second string in the given array is used, else the first.
65 pub(crate) fn pretty_print_enum(
66 fmt: &mut Formatter<'_>,
67 value: u32,
68 cases: &[(u32, &str, &str)],
69 ) -> Result {
70 for (variant, name1, name2) in cases {
71 if &value == variant {
72 if fmt.alternate() {
73 return fmt.write_str(name2);
74 } else {
75 return fmt.write_str(name1);
76 }
77 }
78 }
79 Debug::fmt(&value, fmt)
80 }
81
82 /// A helper to pretty-print a bitmask.
83 ///
84 /// This function prints the given number. All bit-matches with the given variants are printed.
85 /// Any left-over number is printed as a decimal.
86 ///
87 /// In alternate mode, the second string in the given array is used, else the first.
88 pub(crate) fn pretty_print_bitmask(
89 fmt: &mut Formatter<'_>,
90 value: u32,
91 cases: &[(u32, &str, &str)],
92 ) -> Result {
93 // First, figure out if there are any bits not covered by any case
94 let known_bits = cases.iter().fold(0, |acc, (value, _, _)| acc | value);
95 let remaining = value & !known_bits;
96 let mut already_printed = if value == 0 || remaining != 0 {
97 Debug::fmt(&remaining, fmt)?;
98 true
99 } else {
100 false
101 };
102 for (variant, name1, name2) in cases {
103 if variant & value != 0 {
104 if already_printed {
105 fmt.write_str(" | ")?;
106 }
107 already_printed = true;
108 if fmt.alternate() {
109 fmt.write_str(name2)?;
110 } else {
111 fmt.write_str(name1)?;
112 }
113 }
114 }
115 Ok(())
116 }
117
118 #[cfg(test)]
119 mod test {
120 use super::{pretty_print_bitmask, pretty_print_enum};
121 use alloc::format;
122 use core::fmt::{Display, Formatter, Result};
123
124 type CallbackType = fn(&mut Formatter<'_>, u32, &[(u32, &str, &str)]) -> Result;
125
126 struct CallbackFormating<'a, 'b> {
127 callback: CallbackType,
128 value: u32,
129 cases: &'a [(u32, &'b str, &'b str)],
130 }
131
132 fn new_enum<'a, 'b>(
133 value: u32,
134 cases: &'a [(u32, &'b str, &'b str)],
135 ) -> CallbackFormating<'a, 'b> {
136 CallbackFormating {
137 callback: pretty_print_enum,
138 value,
139 cases,
140 }
141 }
142
143 fn new_bitmask<'a, 'b>(
144 value: u32,
145 cases: &'a [(u32, &'b str, &'b str)],
146 ) -> CallbackFormating<'a, 'b> {
147 CallbackFormating {
148 callback: pretty_print_bitmask,
149 value,
150 cases,
151 }
152 }
153
154 impl Display for CallbackFormating<'_, '_> {
155 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
156 (self.callback)(f, self.value, self.cases)
157 }
158 }
159
160 #[test]
161 fn test_enum() {
162 let cases = [(0, "zero", "ZERO"), (42, "the answer", "ANSWER")];
163 let printer = new_enum(0, &cases);
164 assert_eq!(&format!("{}", printer), "zero");
165 assert_eq!(&format!("{:#}", printer), "ZERO");
166 let printer = new_enum(1, &cases);
167 assert_eq!(&format!("{}", printer), "1");
168 assert_eq!(&format!("{:#}", printer), "1");
169 let printer = new_enum(42, &cases);
170 assert_eq!(&format!("{}", printer), "the answer");
171 assert_eq!(&format!("{:#}", printer), "ANSWER");
172 }
173
174 #[test]
175 fn test_bitmask() {
176 let bits = [
177 (1 << 5, "b5", "B5"),
178 (1 << 1, "b1", "B1"),
179 (1 << 0, "unused", "UNUSED"),
180 ];
181 let printer = new_bitmask(8, &bits);
182 assert_eq!(&format!("{}", printer), "8");
183 assert_eq!(&format!("{:#}", printer), "8");
184 let printer = new_bitmask(32, &bits);
185 assert_eq!(&format!("{}", printer), "b5");
186 assert_eq!(&format!("{:#}", printer), "B5");
187 let printer = new_bitmask(34, &bits);
188 assert_eq!(&format!("{}", printer), "b5 | b1");
189 assert_eq!(&format!("{:#}", printer), "B5 | B1");
190 let printer = new_bitmask(42, &bits);
191 assert_eq!(&format!("{}", printer), "8 | b5 | b1");
192 assert_eq!(&format!("{:#}", printer), "8 | B5 | B1");
193 }
194 }
195}
196
197pub(crate) use pretty_printer::{pretty_print_bitmask, pretty_print_enum};
198