x11rb/
x11_utils.rs

1//! Some utilities for working with X11.
2
3pub use x11rb_protocol::x11_utils::{
4    parse_request_header, BigRequests, ExtInfoProvider, ExtensionInformation, ReplyParsingFunction,
5    Request, RequestHeader, Serialize, TryParse, TryParseFd, X11Error,
6};
7
8/// A helper macro for managing atoms
9///
10/// In X11, one often has to work with many different atoms that are already known at compile time.
11/// This macro can simplify managing such a list of atoms.
12///
13/// The following macro invocation:
14/// ```
15/// # use x11rb::atom_manager;
16/// atom_manager! {
17///     /// A collection of Atoms.
18///     pub AtomCollection:
19///     /// A handle to a response from the X11 server.
20///     AtomCollectionCookie {
21///         _NET_WM_NAME,
22///         _NET_WM_ICON,
23///         ATOM_WITH_SPACES: b"ATOM WITH SPACES",
24///         WHATEVER,
25///     }
26/// }
27/// ```
28/// ...expands to this:
29/// ```
30/// # use x11rb::protocol::xproto::{Atom, ConnectionExt, InternAtomReply};
31/// # use x11rb::errors::{ConnectionError, ReplyError};
32/// # use x11rb::cookie::Cookie;
33/// #[allow(non_snake_case)]
34/// #[derive(Debug, Clone, Copy)]
35/// /// A collection of Atoms.
36/// pub struct AtomCollection {
37///     pub _NET_WM_NAME: Atom,
38///     pub _NET_WM_ICON: Atom,
39///     pub ATOM_WITH_SPACES: Atom,
40///     pub WHATEVER: Atom,
41/// }
42///
43/// #[allow(non_snake_case)]
44/// #[derive(Debug)]
45/// /// A handle to a response from the X11 server.
46/// struct AtomCollectionCookie<'c, C: ConnectionExt> {
47///     // please treat the actual members as private
48///     # phantom: std::marker::PhantomData<&'c C>,
49///     # _NET_WM_NAME: Cookie<'c, C, InternAtomReply>,
50///     # _NET_WM_ICON: Cookie<'c, C, InternAtomReply>,
51///     # ATOM_WITH_SPACES: Cookie<'c, C, InternAtomReply>,
52///     # WHATEVER: Cookie<'c, C, InternAtomReply>,
53/// }
54///
55/// impl AtomCollection {
56///     pub fn new<C: ConnectionExt>(
57///         conn: &C,
58///     ) -> Result<AtomCollectionCookie<'_, C>, ConnectionError> {
59///         // This is just an example for readability; the actual code is more efficient.
60///         Ok(AtomCollectionCookie {
61///             phantom: std::marker::PhantomData,
62///             _NET_WM_NAME: conn.intern_atom(false, b"_NET_WM_NAME")?,
63///             _NET_WM_ICON: conn.intern_atom(false, b"_NET_WM_ICON")?,
64///             ATOM_WITH_SPACES: conn.intern_atom(false, b"ATOM WITH SPACES")?,
65///             WHATEVER: conn.intern_atom(false, b"WHATEVER")?,
66///         })
67///     }
68/// }
69///
70/// impl<'c, C> AtomCollectionCookie<'c, C>
71/// where
72///     C: ConnectionExt,
73/// {
74///     pub fn reply(self) -> Result<AtomCollection, ReplyError> {
75///         // This is just an example for readability; the actual code is different.
76///         Ok(AtomCollection {
77///             _NET_WM_NAME: self._NET_WM_NAME.reply()?.atom,
78///             _NET_WM_ICON: self._NET_WM_ICON.reply()?.atom,
79///             ATOM_WITH_SPACES: self.ATOM_WITH_SPACES.reply()?.atom,
80///             WHATEVER: self.WHATEVER.reply()?.atom,
81///         })
82///     }
83/// }
84/// ```
85#[macro_export]
86macro_rules! atom_manager {
87    {
88        $(#[$struct_meta:meta])*
89        $vis:vis $struct_name:ident:
90        $(#[$cookie_meta:meta])*
91        $cookie_name:ident {
92            $($field_name:ident$(: $atom_value:expr)?,)*
93        }
94    } => {
95        // Cookie version
96        #[allow(non_snake_case)]
97        #[derive(Debug)]
98        $(#[$cookie_meta])*
99        $vis struct $cookie_name<'a, C: $crate::protocol::xproto::ConnectionExt> {
100            __private_phantom: ::std::marker::PhantomData<&'a C>,
101            __private_cookies: ::std::vec::Vec<$crate::cookie::Cookie<'a, C, $crate::protocol::xproto::InternAtomReply>>,
102        }
103
104        // Replies
105        #[allow(non_snake_case)]
106        #[derive(Debug, Clone, Copy)]
107        $(#[$struct_meta])*
108        $vis struct $struct_name {
109            $(
110                $vis $field_name: $crate::protocol::xproto::Atom,
111            )*
112        }
113
114        impl $struct_name {
115            $vis fn new<C: $crate::protocol::xproto::ConnectionExt>(
116                _conn: &C,
117            ) -> ::std::result::Result<$cookie_name<'_, C>, $crate::errors::ConnectionError> {
118                let names = [
119                    $($crate::__atom_manager_atom_value!($field_name$(: $atom_value)?),)*
120                ];
121                let cookies: ::std::result::Result<::std::vec::Vec<_>, _>
122                    = names.into_iter().map(|name| _conn.intern_atom(false, name)).collect();
123                Ok($cookie_name {
124                    __private_phantom: ::std::marker::PhantomData,
125                    __private_cookies: cookies?,
126                })
127            }
128        }
129
130        impl<'a, C: $crate::protocol::xproto::ConnectionExt> $cookie_name<'a, C> {
131            $vis fn reply(self) -> ::std::result::Result<$struct_name, $crate::errors::ReplyError> {
132                let mut replies = self.__private_cookies.into_iter();
133                Ok($struct_name {
134                    $(
135                        $field_name: replies.next().expect("new() should have constructed a Vec of the correct size").reply()?.atom,
136                    )*
137                })
138            }
139        }
140    }
141}
142
143#[doc(hidden)]
144#[macro_export]
145macro_rules! __atom_manager_atom_value {
146    ($field_name:ident) => {
147        stringify!($field_name).as_bytes()
148    };
149    ($field_name:ident: $atom_value:expr) => {
150        $atom_value
151    };
152}