| 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))] |
| 13 | mod 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)))] |
| 20 | mod 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 |
| 54 | pub type RawFdContainer = raw_fd_container::RawFdContainer; |
| 55 | |
| 56 | mod 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 | |
| 197 | pub(crate) use pretty_printer::{pretty_print_bitmask, pretty_print_enum}; |
| 198 | |