| 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 | |