use std::sync::{Arc, Mutex};
use crate::protocol::wl_display;
use crate::protocol::wl_registry;
use crate::{Attached, DispatchData, Interface, Main, Proxy};
struct Inner {
list: Vec<(u32, String, u32)>,
}
#[derive(Clone)]
pub struct GlobalManager {
inner: Arc<Mutex<Inner>>,
registry: Main<wl_registry::WlRegistry>,
}
#[derive(Debug, PartialEq)]
pub enum GlobalError {
Missing,
VersionTooLow(u32),
}
impl ::std::error::Error for GlobalError {}
impl ::std::fmt::Display for GlobalError {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
match *self {
GlobalError::Missing => f.write_str("The requested global was missing."),
GlobalError::VersionTooLow(_) => {
f.write_str("The requested global's version is too low.")
}
}
}
}
pub enum GlobalEvent {
New {
id: u32,
interface: String,
version: u32,
},
Removed {
id: u32,
interface: String,
},
}
impl GlobalManager {
pub fn new(display: &Attached<wl_display::WlDisplay>) -> GlobalManager {
let inner = Arc::new(Mutex::new(Inner { list: Vec::new() }));
let inner_clone = inner.clone();
let registry = display
.as_ref()
.send::<wl_registry::WlRegistry>(wl_display::Request::GetRegistry {}, None)
.unwrap();
registry.quick_assign(move |_proxy, msg, _data| {
let mut inner = inner.lock().unwrap();
match msg {
wl_registry::Event::Global { name, interface, version } => {
inner.list.push((name, interface, version));
}
wl_registry::Event::GlobalRemove { name } => {
inner.list.retain(|&(n, _, _)| n != name);
}
}
});
GlobalManager { inner: inner_clone, registry }
}
pub fn new_with_cb<F>(
display: &Attached<wl_display::WlDisplay>,
mut callback: F,
) -> GlobalManager
where
F: FnMut(GlobalEvent, Attached<wl_registry::WlRegistry>, DispatchData) + 'static,
{
let inner = Arc::new(Mutex::new(Inner { list: Vec::new() }));
let inner_clone = inner.clone();
let registry = display
.as_ref()
.send::<wl_registry::WlRegistry>(wl_display::Request::GetRegistry {}, None)
.unwrap();
registry.quick_assign(move |proxy, msg, data| {
let mut inner = inner.lock().unwrap();
let inner = &mut *inner;
match msg {
wl_registry::Event::Global {
name,
interface,
version,
} => {
inner.list.push((name, interface.clone(), version));
callback(
GlobalEvent::New {
id: name,
interface,
version,
},
(*proxy).clone(),
data,
);
}
wl_registry::Event::GlobalRemove { name } => {
if let Some((i, _)) = inner.list.iter().enumerate().find(|&(_, &(n, _, _))| n == name) {
let (id, interface, _) = inner.list.swap_remove(i);
callback(GlobalEvent::Removed { id, interface }, (*proxy).clone(), data);
} else {
panic!(
"Wayland protocol error: the server removed non-existing global \"{}\".",
name
);
}
}
}
});
GlobalManager { inner: inner_clone, registry }
}
pub fn instantiate_exact<I>(&self, version: u32) -> Result<Main<I>, GlobalError>
where
I: Interface + AsRef<Proxy<I>> + From<Proxy<I>>,
{
let inner = self.inner.lock().unwrap();
for &(id, ref interface, server_version) in &inner.list {
if interface == I::NAME {
if version > server_version {
return Err(GlobalError::VersionTooLow(server_version));
} else {
return Ok(self.registry.bind::<I>(version, id));
}
}
}
Err(GlobalError::Missing)
}
pub fn instantiate_range<I>(
&self,
min_version: u32,
max_version: u32,
) -> Result<Main<I>, GlobalError>
where
I: Interface + AsRef<Proxy<I>> + From<Proxy<I>>,
{
let inner = self.inner.lock().unwrap();
for &(id, ref interface, version) in &inner.list {
if interface == I::NAME {
if version >= min_version {
let version = ::std::cmp::min(version, max_version);
return Ok(self.registry.bind::<I>(version, id));
} else {
return Err(GlobalError::VersionTooLow(version));
}
}
}
Err(GlobalError::Missing)
}
pub fn list(&self) -> Vec<(u32, String, u32)> {
self.inner.lock().unwrap().list.clone()
}
}
pub trait GlobalImplementor<I: Interface + AsRef<Proxy<I>> + From<Proxy<I>>> {
fn new_global(&mut self, global: Main<I>, data: DispatchData);
fn error(&mut self, _version: u32, _data: DispatchData) {}
}
impl<F, I: Interface> GlobalImplementor<I> for F
where
I: Interface + AsRef<Proxy<I>> + From<Proxy<I>>,
F: FnMut(Main<I>, DispatchData),
{
fn new_global(&mut self, global: Main<I>, data: DispatchData) {
(*self)(global, data)
}
}
#[macro_export]
macro_rules! global_filter {
($([$interface:ty, $version:expr, $callback:expr]),*) => {
{
use $crate::protocol::wl_registry;
use $crate::{GlobalEvent, Interface, Attached, GlobalImplementor, DispatchData};
type Callback = Box<dyn FnMut(u32, u32, Attached<wl_registry::WlRegistry>, DispatchData<'_>)>;
let mut callbacks: Vec<(&'static str, Callback)> = Vec::new();
$({
let mut cb = { $callback };
callbacks.push((
<$interface as Interface>::NAME,
Box::new(move |id, version, registry: Attached<wl_registry::WlRegistry>, ddata: DispatchData| {
if version < $version {
GlobalImplementor::<$interface>::error(&mut cb, version, ddata);
} else {
let proxy = registry.bind::<$interface>(version, id);
GlobalImplementor::<$interface>::new_global(&mut cb, proxy, ddata);
}
}) as Box<_>
));
})*
move |event: GlobalEvent, registry: Attached<wl_registry::WlRegistry>, ddata| {
if let GlobalEvent::New { id, interface, version } = event {
for &mut (iface, ref mut cb) in &mut callbacks {
if iface == interface {
cb(id, version, registry, ddata);
break;
}
}
}
}
}
}
}