1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/// This macro allows you to create a simple wayland environment handler.
///
/// It will define a struct which upon creation communicates with the server
/// to fetch the list of global objects and instanciate them with the newest
/// interface version supported by both the server and the client library.
///
/// This struct comes with a static constructor `init(display)`, which takes
/// a display, and returns the created struct and an `EventIterator` associated
/// with the display and all the global objects.
///
/// Note that none of the events associated with the newly created objects are
/// dispatched (expect for the registry), allowing you to change the event iterators
/// associated with them before dispatching them, if you want to.
///
/// The struct has these public fields:
///
/// - `display`: the `WlDisplay` provided as argument
/// - `registry`: a instance of the `WlRegistry` associated
/// - `globals`: a `Vec` containing the globals advertized by the server, in the format
///   `(global_id, interface_name, version)`
///   Note here that `version` is the version advertized by the server.
/// - One field for each of the objects you specified, of type `Option<T, u32>`, in the format
///   `(proxy, version)`. The value is `None` if this global was not advertized by the server.
///
/// Note that:
///
/// - If you specify several objects of the same interface, only the first one will be
///   populated.
/// - If a global is advertized several times (like `wl_seat` or `wl_output` can be), only
///   the first one will be automatically bound (but all will still be listed in the `globals`
///   list).
///
/// The struct also provides two methods:
///
/// - `fn rebind<T: Proxy>(&self) -> Option<(T, u32)>` which will try to bind once more a global
///   (this allows you to effectively clone a global, and is perfectly legal). It will match
///   the first global of that type that was encountered. Returns `None` if this global type was
///   not encountered.
/// - `fn rebind_id<T: Proxy>(&self, id: u32) -> Option<(T, u32)>` which will try to bind once
///   more a global with given id as listed in `globals`. Returns `None` if given id is not known
///   or if its interface does not match with the provided type.
///
/// Example of use:
///
/// ```no_run
/// # #![allow(dead_code)]
/// #[macro_use] extern crate wayland_client;
///
/// use wayland_client::wayland::get_display;
/// use wayland_client::wayland::compositor::WlCompositor;
/// use wayland_client::wayland::shell::WlShell;
///
/// wayland_env!(WaylandEnv,
///     compositor: WlCompositor,
///     shell: WlShell
/// );
///
/// fn main() {
///     let display = get_display().expect("Unable to connect to waylans server.");
///     let (env, iter) = WaylandEnv::init(display);
///     let shell = match env.shell {
///         Some((ref comp, version)) if version >= 2 => comp,
///         _ => panic!("This app requires the wayland interface wl_shell of version >= 2.")
///     };
///     // etc...
/// }
/// ```
#[macro_export]
macro_rules! wayland_env {
    ($structname: ident, $($name: ident : $interface: ty),*) => (
        struct $structname {
            pub display: $crate::wayland::WlDisplay,
            pub registry: $crate::wayland::WlRegistry,
            pub globals: Vec<(u32, String, u32)>,
            $(
                pub $name : Option<($interface, u32)>,
            )*
        }

        impl $structname {
            pub fn init(mut display: $crate::wayland::WlDisplay) -> ($structname, $crate::EventIterator) {
                use $crate::{Proxy, Event};
                use $crate::wayland::{WaylandProtocolEvent, WlRegistryEvent};

                let mut iter = $crate::EventIterator::new();
                display.set_evt_iterator(&iter);
                let registry = display.get_registry();
                match display.sync_roundtrip() {
                    Ok(_) => {},
                    Err(e) => panic!("Roundtrip with wayland server failed: {:?}", e)
                }

                let mut env = $structname {
                    display: display,
                    registry: registry,
                    globals: Vec::new(),
                    $(
                        $name: None,
                    )*
                };

                for evt in &mut iter {
                    match evt {
                        Event::Wayland(WaylandProtocolEvent::WlRegistry(
                            _, WlRegistryEvent::Global(name, interface, version)
                        )) => {
                            env.handle_global(name, interface, version)
                        }
                        _ => {}
                    }
                }

                (env, iter)
            }

            #[allow(dead_code)]
            pub fn rebind<T: $crate::Proxy>(&self) -> Option<(T, u32)> {
                use $crate::Proxy;
                let t_interface = <T as Proxy>::interface_name();
                for &(name, ref interface, version) in &self.globals {
                    if interface == t_interface {
                        let chosen_version = ::std::cmp::min(version, <T as Proxy>::version());
                        let proxy = unsafe { self.registry.bind::<T>(name, chosen_version) };
                        return Some((proxy, chosen_version))
                    }
                }
                return None
            }

            #[allow(dead_code)]
            pub fn rebind_id<T: $crate::Proxy>(&self, id: u32) -> Option<(T, u32)> {
                use $crate::Proxy;
                let t_interface = <T as Proxy>::interface_name();
                for &(name, ref interface, version) in &self.globals {
                    if name == id && interface == t_interface {
                        let chosen_version = ::std::cmp::min(version, <T as Proxy>::version());
                        let proxy = unsafe { self.registry.bind::<T>(name, chosen_version) };
                        return Some((proxy, chosen_version))
                    }
                }
                return None
            }

            fn handle_global(&mut self, name: u32, interface: String, version: u32) {
                use $crate::Proxy;
                match interface {
                    $(
                        ref s if &s[..] == <$interface as Proxy>::interface_name() => {
                            let chosen_version = ::std::cmp::min(version, <$interface as Proxy>::version());
                            if self.$name.is_none() {
                                let proxy = unsafe { self.registry.bind::<$interface>(name, chosen_version) };
                                self.$name = Some((proxy, chosen_version));
                            }
                        }
                    )*
                    _ => {}
                }
                self.globals.push((name, interface, version));
            }
        }
    )
}