use crate::{error::GlobalError, globals::ProvidesBoundGlobal};
use wayland_client::{
globals::{BindError, Global, GlobalList, GlobalListContents},
protocol::wl_registry,
Connection, Dispatch, Proxy, QueueHandle,
};
pub trait RegistryHandler<D>
where
D: ProvidesRegistryState,
{
fn new_global(
data: &mut D,
conn: &Connection,
qh: &QueueHandle<D>,
name: u32,
interface: &str,
version: u32,
) {
let _ = (data, conn, qh, name, interface, version);
}
fn remove_global(
data: &mut D,
conn: &Connection,
qh: &QueueHandle<D>,
name: u32,
interface: &str,
) {
let _ = (data, conn, qh, name, interface);
}
}
pub trait ProvidesRegistryState: Sized {
fn registry(&mut self) -> &mut RegistryState;
fn runtime_add_global(
&mut self,
conn: &Connection,
qh: &QueueHandle<Self>,
name: u32,
interface: &str,
version: u32,
);
fn runtime_remove_global(
&mut self,
conn: &Connection,
qh: &QueueHandle<Self>,
name: u32,
interface: &str,
);
}
#[derive(Debug)]
pub struct RegistryState {
registry: wl_registry::WlRegistry,
globals: Vec<Global>,
}
impl RegistryState {
pub fn new(global_list: &GlobalList) -> Self {
let registry = global_list.registry().clone();
let globals = global_list.contents().clone_list();
RegistryState { registry, globals }
}
pub fn registry(&self) -> &wl_registry::WlRegistry {
&self.registry
}
pub fn globals(&self) -> impl Iterator<Item = &Global> + '_ {
self.globals.iter()
}
pub fn globals_by_interface<'a>(
&'a self,
interface: &'a str,
) -> impl Iterator<Item = &'a Global> + 'a {
self.globals.iter().filter(move |g| g.interface == interface)
}
pub fn bind_one<I, D, U>(
&self,
qh: &QueueHandle<D>,
version: std::ops::RangeInclusive<u32>,
udata: U,
) -> Result<I, BindError>
where
D: Dispatch<I, U> + 'static,
I: Proxy + 'static,
U: Send + Sync + 'static,
{
bind_one(&self.registry, &self.globals, qh, version, udata)
}
pub fn bind_specific<I, D, U>(
&self,
qh: &QueueHandle<D>,
name: u32,
version: std::ops::RangeInclusive<u32>,
udata: U,
) -> Result<I, BindError>
where
D: Dispatch<I, U> + 'static,
I: Proxy + 'static,
U: Send + Sync + 'static,
{
let iface = I::interface();
if *version.end() > iface.version {
panic!("Maximum version ({}) was higher than the proxy's maximum version ({}); outdated wayland XML files?",
version.end(), iface.version);
}
for global in self.globals.iter().rev() {
if global.name != name || global.interface != iface.name {
continue;
}
if global.version < *version.start() {
return Err(BindError::UnsupportedVersion);
}
let version = global.version.min(*version.end());
let proxy = self.registry.bind(global.name, version, qh, udata);
log::debug!(target: "sctk", "Bound new global [{}] {} v{}", global.name, iface.name, version);
return Ok(proxy);
}
Err(BindError::NotPresent)
}
pub fn bind_all<I, D, U, F>(
&self,
qh: &QueueHandle<D>,
version: std::ops::RangeInclusive<u32>,
make_udata: F,
) -> Result<Vec<I>, BindError>
where
D: Dispatch<I, U> + 'static,
I: Proxy + 'static,
F: FnMut(u32) -> U,
U: Send + Sync + 'static,
{
bind_all(&self.registry, &self.globals, qh, version, make_udata)
}
}
#[macro_export]
macro_rules! delegate_registry {
($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
$crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty:
[
$crate::reexports::client::protocol::wl_registry::WlRegistry: $crate::reexports::client::globals::GlobalListContents
] => $crate::registry::RegistryState
);
};
}
impl<D> Dispatch<wl_registry::WlRegistry, GlobalListContents, D> for RegistryState
where
D: Dispatch<wl_registry::WlRegistry, GlobalListContents> + ProvidesRegistryState,
{
fn event(
state: &mut D,
_: &wl_registry::WlRegistry,
event: wl_registry::Event,
_: &GlobalListContents,
conn: &Connection,
qh: &QueueHandle<D>,
) {
match event {
wl_registry::Event::Global { name, interface, version } => {
let iface = interface.clone();
state.registry().globals.push(Global { name, interface, version });
state.runtime_add_global(conn, qh, name, &iface, version);
}
wl_registry::Event::GlobalRemove { name } => {
if let Some(i) = state.registry().globals.iter().position(|g| g.name == name) {
let global = state.registry().globals.swap_remove(i);
state.runtime_remove_global(conn, qh, name, &global.interface);
}
}
_ => unreachable!("wl_registry is frozen"),
}
}
}
#[derive(Debug)]
pub enum GlobalProxy<I> {
NotPresent,
Bound(I),
}
impl<I> From<Result<I, BindError>> for GlobalProxy<I> {
fn from(r: Result<I, BindError>) -> Self {
match r {
Ok(proxy) => GlobalProxy::Bound(proxy),
Err(_) => GlobalProxy::NotPresent,
}
}
}
impl<I: Proxy> GlobalProxy<I> {
pub fn get(&self) -> Result<&I, GlobalError> {
self.with_min_version(0)
}
pub fn with_min_version(&self, min_version: u32) -> Result<&I, GlobalError> {
match self {
GlobalProxy::Bound(proxy) => {
if proxy.version() < min_version {
Err(GlobalError::InvalidVersion {
name: I::interface().name,
required: min_version,
available: proxy.version(),
})
} else {
Ok(proxy)
}
}
GlobalProxy::NotPresent => Err(GlobalError::MissingGlobal(I::interface().name)),
}
}
}
#[derive(Debug)]
pub struct SimpleGlobal<I, const MAX_VERSION: u32> {
proxy: GlobalProxy<I>,
}
impl<I: Proxy + 'static, const MAX_VERSION: u32> SimpleGlobal<I, MAX_VERSION> {
pub fn bind<State>(globals: &GlobalList, qh: &QueueHandle<State>) -> Result<Self, BindError>
where
State: Dispatch<I, (), State> + 'static,
{
let proxy = globals.bind(qh, 0..=MAX_VERSION, ())?;
Ok(Self { proxy: GlobalProxy::Bound(proxy) })
}
pub fn get(&self) -> Result<&I, GlobalError> {
self.proxy.get()
}
pub fn with_min_version(&self, min_version: u32) -> Result<&I, GlobalError> {
self.proxy.with_min_version(min_version)
}
pub fn from_bound(proxy: I) -> Self {
Self { proxy: GlobalProxy::Bound(proxy) }
}
}
impl<I: Proxy + Clone, const MAX_VERSION: u32> ProvidesBoundGlobal<I, MAX_VERSION>
for SimpleGlobal<I, MAX_VERSION>
{
fn bound_global(&self) -> Result<I, GlobalError> {
self.proxy.get().cloned()
}
}
impl<D, I, const MAX_VERSION: u32> Dispatch<I, (), D> for SimpleGlobal<I, MAX_VERSION>
where
D: Dispatch<I, ()>,
I: Proxy,
{
fn event(_: &mut D, _: &I, _: <I as Proxy>::Event, _: &(), _: &Connection, _: &QueueHandle<D>) {
unreachable!("SimpleGlobal is not suitable for {} which has events", I::interface().name);
}
}
pub(crate) fn bind_all<I, D, U, F>(
registry: &wl_registry::WlRegistry,
globals: &[Global],
qh: &QueueHandle<D>,
version: std::ops::RangeInclusive<u32>,
mut make_udata: F,
) -> Result<Vec<I>, BindError>
where
D: Dispatch<I, U> + 'static,
I: Proxy + 'static,
F: FnMut(u32) -> U,
U: Send + Sync + 'static,
{
let iface = I::interface();
if *version.end() > iface.version {
panic!("Maximum version ({}) was higher than the proxy's maximum version ({}); outdated wayland XML files?",
version.end(), iface.version);
}
let mut rv = Vec::new();
for global in globals {
if global.interface != iface.name {
continue;
}
if global.version < *version.start() {
return Err(BindError::UnsupportedVersion);
}
let version = global.version.min(*version.end());
let udata = make_udata(global.name);
let proxy = registry.bind(global.name, version, qh, udata);
log::debug!(target: "sctk", "Bound new global [{}] {} v{}", global.name, iface.name, version);
rv.push(proxy);
}
Ok(rv)
}
pub(crate) fn bind_one<I, D, U>(
registry: &wl_registry::WlRegistry,
globals: &[Global],
qh: &QueueHandle<D>,
version: std::ops::RangeInclusive<u32>,
udata: U,
) -> Result<I, BindError>
where
D: Dispatch<I, U> + 'static,
I: Proxy + 'static,
U: Send + Sync + 'static,
{
let iface = I::interface();
if *version.end() > iface.version {
panic!("Maximum version ({}) of {} was higher than the proxy's maximum version ({}); outdated wayland XML files?",
version.end(), iface.name, iface.version);
}
if *version.end() < iface.version {
log::trace!(target: "sctk", "Version {} of {} is available; binding is currently limited to {}", iface.version, iface.name, version.end());
}
for global in globals {
if global.interface != iface.name {
continue;
}
if global.version < *version.start() {
return Err(BindError::UnsupportedVersion);
}
let version = global.version.min(*version.end());
let proxy = registry.bind(global.name, version, qh, udata);
log::debug!(target: "sctk", "Bound new global [{}] {} v{}", global.name, iface.name, version);
return Ok(proxy);
}
Err(BindError::NotPresent)
}
#[macro_export]
macro_rules! delegate_simple {
($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty:ty, $iface:ty, $max:expr) => {
$crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ $iface: () ]
=> $crate::registry::SimpleGlobal<$iface, $max>
);
};
}
#[macro_export]
macro_rules! registry_handlers {
($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $($ty:ty),* $(,)?) => {
fn runtime_add_global(
&mut self,
conn: &$crate::reexports::client::Connection,
qh: &$crate::reexports::client::QueueHandle<Self>,
name: u32,
interface: &str,
version: u32,
) {
$(
<$ty as $crate::registry::RegistryHandler<Self>>::new_global(self, conn, qh, name, interface, version);
)*
}
fn runtime_remove_global(
&mut self,
conn: &$crate::reexports::client::Connection,
qh: &$crate::reexports::client::QueueHandle<Self>,
name: u32,
interface: &str,
) {
$(
<$ty as $crate::registry::RegistryHandler<Self>>::remove_global(self, conn, qh, name, interface);
)*
}
}
}