1 | //! Some utilities for working with X11. |
2 | |
3 | pub 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 ] |
86 | macro_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 ] |
145 | macro_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 | } |
153 | |