Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Delegated dispatchers #699

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion deny.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@ allow = [
# Fedora believes it is a free license: https://fedoraproject.org/wiki/Licensing/Unicode
# Google categorizes the license under "notice" which means it can be used (with some exceptions like
# notices or advertising clauses): https://opensource.google/documentation/reference/thirdparty/licenses#notice
"Unicode-DFS-2016",
"Unicode-3.0",
]
2 changes: 1 addition & 1 deletion wayland-backend/src/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ pub fn print_send_message<Id: Display, Fd: AsRawFd>(

pub(crate) struct DisplaySlice<'a, D>(pub &'a [D]);

impl<'a, D: Display> Display for DisplaySlice<'a, D> {
impl<D: Display> Display for DisplaySlice<'_, D> {
#[cfg_attr(coverage, coverage(off))]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut it = self.0.iter();
Expand Down
10 changes: 10 additions & 0 deletions wayland-client/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

## Unreleased

#### Breaking changes

- `QueueHandle::make_data` now accepts additional `DelegateTo` generic,
therefore allowing users to dispatch events to types different than main `State`
- `delegate_dispatch` Removed in favour of `DelegateTo` generic on `QueueHandle::make_data`

#### Additions

- `globals::registry_queue_init_delegated` and `GlobalList::bind_delegated`

## 0.31.7 -- 2024-10-23

- Updated Wayland core protocol to 1.23
Expand Down
122 changes: 122 additions & 0 deletions wayland-client/examples/delegated.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#![allow(clippy::single_match)]

use wayland_client::{
protocol::{
wl_compositor::{self, WlCompositor},
wl_display::{self, WlDisplay},
wl_registry::{self, WlRegistry},
},
Connection, Dispatch, Proxy, QueueHandle,
};

/// A demonstration of how delegateing can make implementing protocols an implementation detail
///
/// Users of this module only need to implement `RegistryHandler` trait on their state,
/// Implementation of `Dispatch` can remain an internal detail of the module.
///
/// In a way you can pretend that everything inside of this submodule is a library / different crate
mod delegated {
use super::*;

pub trait RegistryHandler: 'static {
fn state(&mut self) -> &mut Registry;
fn new_global(&mut self, name: u32, interface: &str, version: u32);
}

pub struct Registry {
wl_registry: WlRegistry,
}

impl Registry {
/// Create a [`WlRegistry`] object, and handle it's events internally
/// It can use [`RegistryHandler`] trait to callback to your `D` state.
pub fn new<D: RegistryHandler>(qh: &QueueHandle<D>, display: &WlDisplay) -> Self {
// Let's construct a `WlRegistry` object that dispatches it's events to our
// `Registry::event` rather than to `D`,
// that way it can remain an implementation detail
let data = qh.make_data::<WlRegistry, _, Self>(());
let wl_registry =
display.send_constructor(wl_display::Request::GetRegistry {}, data).unwrap();

Self { wl_registry }
}

pub fn wl_registry(&self) -> WlRegistry {
self.wl_registry.clone()
}
}

impl<D: RegistryHandler> Dispatch<WlRegistry, (), D> for Registry {
/// Called whenever an object created via `make_data<WlRegistry, _, Registry>`
/// receives a server event
fn event(
state: &mut D,
_: &wl_registry::WlRegistry,
event: wl_registry::Event,
_: &(),
_: &Connection,
_: &QueueHandle<D>,
) {
let _state = state.state();

if let wl_registry::Event::Global { name, interface, version } = event {
// Let's callback the user of this abstraction, informing them about new global
state.new_global(name, &interface, version);
}
}
}
}

struct AppData {
registry: delegated::Registry,
qh: QueueHandle<Self>,
}

impl delegated::RegistryHandler for AppData {
fn state(&mut self) -> &mut delegated::Registry {
&mut self.registry
}

// Even tho we did not implement WlRegistry, `delegated::Registry` implemented it for us,
// and will call this method whenever new globals appear
fn new_global(&mut self, name: u32, interface: &str, version: u32) {
println!("[{}] {} (v{})", name, interface, version);

match interface {
"wl_compositor" => {
self.registry.wl_registry().bind(name, version, &self.qh, ());
}
_ => {}
}
}
}

impl Dispatch<WlCompositor, ()> for AppData {
fn event(
_state: &mut Self,
_proxy: &WlCompositor,
_event: wl_compositor::Event,
_data: &(),
_conn: &Connection,
_qhandle: &QueueHandle<Self>,
) {
}
}

fn main() {
let conn = Connection::connect_to_env().unwrap();

let display = conn.display();

let mut event_queue = conn.new_event_queue::<AppData>();
let qh = event_queue.handle();

// Let's ask `delegated::Registry` to implement `WlRegistry` for us, only calling us back whenever
// necessary via `RegistryHandler` trait
let registry = delegated::Registry::new(&qh, &display);

let mut app = AppData { registry, qh: qh.clone() };

println!("Advertized globals:");
event_queue.roundtrip(&mut app).unwrap();
}
Loading
Loading