Skip to content

Commit 6cda60d

Browse files
implement screen for wayland. (#2076)
- refactors some of the wayland connection code out of the application startup.
1 parent 20c7e7b commit 6cda60d

File tree

12 files changed

+765
-146
lines changed

12 files changed

+765
-146
lines changed

druid-shell/src/backend/wayland/application.rs

Lines changed: 119 additions & 105 deletions
Large diffs are not rendered by default.

druid-shell/src/backend/wayland/dialog.rs

Lines changed: 0 additions & 31 deletions
This file was deleted.
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
use super::error;
2+
use std::collections::BTreeMap;
3+
use wayland_client as wlc;
4+
use wayland_client::protocol::wl_registry;
5+
use wayland_protocols::xdg_shell::client::xdg_wm_base;
6+
7+
#[derive(Clone)]
8+
pub struct GlobalEventSubscription {
9+
id: u64,
10+
sub: std::sync::Arc<dyn GlobalEventConsumer>,
11+
}
12+
13+
impl GlobalEventSubscription {
14+
fn with_id(mut self, id: u64) -> Self {
15+
self.id = id;
16+
self
17+
}
18+
}
19+
20+
impl GlobalEventConsumer for GlobalEventSubscription {
21+
fn consume<'a>(
22+
&self,
23+
event: &'a wlc::GlobalEvent,
24+
registry: &'a wlc::Attached<wl_registry::WlRegistry>,
25+
ctx: &'a wlc::DispatchData,
26+
) {
27+
self.sub.consume(event, registry, ctx)
28+
}
29+
}
30+
31+
impl<X> From<X> for GlobalEventSubscription
32+
where
33+
X: Fn(&wlc::GlobalEvent, &wlc::Attached<wl_registry::WlRegistry>, &wlc::DispatchData)
34+
+ GlobalEventConsumer
35+
+ 'static,
36+
{
37+
fn from(closure: X) -> Self {
38+
Self {
39+
id: 0,
40+
sub: std::sync::Arc::new(closure),
41+
}
42+
}
43+
}
44+
45+
impl<X> GlobalEventConsumer for X
46+
where
47+
X: Fn(&wlc::GlobalEvent, &wlc::Attached<wl_registry::WlRegistry>, &wlc::DispatchData) + 'static,
48+
{
49+
fn consume<'a>(
50+
&self,
51+
event: &'a wlc::GlobalEvent,
52+
registry: &'a wlc::Attached<wl_registry::WlRegistry>,
53+
ctx: &'a wlc::DispatchData,
54+
) {
55+
self(event, registry, ctx)
56+
}
57+
}
58+
59+
pub trait GlobalEventDispatch {
60+
fn subscribe(&self, sub: impl Into<GlobalEventSubscription>) -> GlobalEventSubscription;
61+
fn release(&self, s: &GlobalEventSubscription);
62+
}
63+
64+
pub trait GlobalEventConsumer {
65+
fn consume(
66+
&self,
67+
event: &wlc::GlobalEvent,
68+
registry: &wlc::Attached<wl_registry::WlRegistry>,
69+
ctx: &wlc::DispatchData,
70+
);
71+
}
72+
73+
pub(super) struct Dispatcher {
74+
incr: crate::Counter,
75+
subscriptions: std::cell::RefCell<BTreeMap<u64, GlobalEventSubscription>>,
76+
}
77+
78+
impl Default for Dispatcher {
79+
fn default() -> Self {
80+
Self {
81+
incr: crate::Counter::new(),
82+
subscriptions: std::cell::RefCell::new(BTreeMap::new()),
83+
}
84+
}
85+
}
86+
87+
impl GlobalEventConsumer for Dispatcher {
88+
fn consume<'a>(
89+
&self,
90+
event: &'a wlc::GlobalEvent,
91+
registry: &'a wlc::Attached<wl_registry::WlRegistry>,
92+
ctx: &'a wlc::DispatchData,
93+
) {
94+
// tracing::info!("global event initiated {:?} {:?}", registry, event);
95+
for (_, sub) in self.subscriptions.borrow().iter() {
96+
sub.consume(event, registry, ctx);
97+
}
98+
// tracing::info!("global event completed {:?} {:?}", registry, event);
99+
}
100+
}
101+
102+
impl GlobalEventDispatch for Dispatcher {
103+
fn subscribe(&self, sub: impl Into<GlobalEventSubscription>) -> GlobalEventSubscription {
104+
let sub = sub.into().with_id(self.incr.next());
105+
self.subscriptions.borrow_mut().insert(sub.id, sub.clone());
106+
sub
107+
}
108+
109+
fn release(&self, s: &GlobalEventSubscription) {
110+
self.subscriptions.borrow_mut().remove(&s.id);
111+
}
112+
}
113+
114+
pub(super) struct Environment {
115+
pub(super) display: wlc::Display,
116+
pub(super) registry: wlc::GlobalManager,
117+
pub(super) xdg_base: wlc::Main<xdg_wm_base::XdgWmBase>,
118+
pub(super) queue: std::rc::Rc<std::cell::RefCell<wlc::EventQueue>>,
119+
dispatcher: std::sync::Arc<Dispatcher>,
120+
}
121+
122+
// because we have the global environment we need to mark these as safe/send.
123+
// strictly speaking we should probably guard the access to the various fields
124+
// behind a mutex, but in practice we are not actually accessing across threads.
125+
unsafe impl Sync for Environment {}
126+
unsafe impl Send for Environment {}
127+
128+
impl GlobalEventDispatch for std::sync::Arc<Environment> {
129+
fn subscribe(&self, sub: impl Into<GlobalEventSubscription>) -> GlobalEventSubscription {
130+
self.dispatcher.subscribe(sub)
131+
}
132+
133+
fn release(&self, s: &GlobalEventSubscription) {
134+
self.dispatcher.release(s)
135+
}
136+
}
137+
138+
pub(super) fn new(dispatcher: Dispatcher) -> Result<std::sync::Arc<Environment>, error::Error> {
139+
let dispatcher = std::sync::Arc::new(dispatcher);
140+
let d = wlc::Display::connect_to_env()?;
141+
142+
let mut queue = d.create_event_queue();
143+
let handle = d.clone().attach(queue.token());
144+
let registry = wlc::GlobalManager::new_with_cb(&handle, {
145+
let dispatcher = dispatcher.clone();
146+
move |event, registry, ctx| {
147+
dispatcher.consume(&event, &registry, &ctx);
148+
}
149+
});
150+
151+
// do a round trip to make sure we have all the globals
152+
queue
153+
.sync_roundtrip(&mut (), |_, _, _| unreachable!())
154+
.map_err(error::Error::fatal)?;
155+
156+
let xdg_base = registry
157+
.instantiate_exact::<xdg_wm_base::XdgWmBase>(2)
158+
.map_err(|e| error::Error::global("xdg_wm_base", 2, e))?;
159+
160+
// We do this to make sure wayland knows we're still responsive.
161+
//
162+
// NOTE: This means that clients mustn't hold up the event loop, or else wayland might kill
163+
// your app's connection. Move *everything* to another thread, including e.g. file i/o,
164+
// computation, network, ... This is good practice for all back-ends: it will improve
165+
// responsiveness.
166+
xdg_base.quick_assign(|xdg_base, event, ctx| {
167+
tracing::info!(
168+
"global xdg_base events {:?} {:?} {:?}",
169+
xdg_base,
170+
event,
171+
ctx
172+
);
173+
match event {
174+
xdg_wm_base::Event::Ping { serial } => xdg_base.pong(serial),
175+
_ => (),
176+
}
177+
});
178+
179+
let env = std::sync::Arc::new(Environment {
180+
queue: std::rc::Rc::new(std::cell::RefCell::new(queue)),
181+
display: d,
182+
registry,
183+
xdg_base,
184+
dispatcher,
185+
});
186+
187+
return Ok(env);
188+
}
189+
190+
pub(super) fn global() -> Result<std::sync::Arc<Environment>, error::Error> {
191+
use lazy_static::lazy_static;
192+
lazy_static! {
193+
static ref _GLOBAL: std::sync::Mutex<Option<std::sync::Arc<Environment>>> =
194+
std::sync::Mutex::new(None,);
195+
}
196+
197+
let mut guard = _GLOBAL.lock().unwrap();
198+
if let Some(d) = &*guard {
199+
return Ok(d.clone());
200+
}
201+
202+
let env = new(Dispatcher::default())?;
203+
guard.replace(env.clone());
204+
205+
return Ok(env);
206+
}
207+
208+
#[allow(unused)]
209+
pub(super) fn print<'a>(reg: &'a wlc::GlobalManager) {
210+
let mut globals_list = reg.list();
211+
globals_list.sort_by(|(_, name1, version1), (_, name2, version2)| {
212+
name1.cmp(name2).then(version1.cmp(version2))
213+
});
214+
215+
for (id, name, version) in globals_list.into_iter() {
216+
tracing::debug!("{:?}@{:?} - {:?}", name, version, id);
217+
}
218+
}
219+
220+
pub(super) fn count<'a>(reg: &'a wlc::GlobalManager, i: &str) -> usize {
221+
reg.list()
222+
.iter()
223+
.filter(|(_, name, _)| name == i)
224+
.count()
225+
}

druid-shell/src/backend/wayland/error.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,15 @@ pub enum Error {
3232
Fatal(Arc<dyn StdError + 'static>),
3333
String(ErrorString),
3434
InvalidParent(u32),
35+
/// general error.
36+
Err(Arc<dyn StdError + 'static>),
3537
}
3638

3739
impl Error {
40+
pub fn error(e: impl StdError + 'static) -> Self {
41+
Self::Err(Arc::new(e))
42+
}
43+
3844
pub fn fatal(e: impl StdError + 'static) -> Self {
3945
Self::Fatal(Arc::new(e))
4046
}
@@ -62,6 +68,7 @@ impl fmt::Display for Error {
6268
name, version
6369
),
6470
Self::Fatal(e) => write!(f, "an unhandled error occurred: {:?}", e),
71+
Self::Err(e) => write!(f, "an unhandled error occurred: {:?}", e),
6572
Self::String(e) => e.fmt(f),
6673
Self::InvalidParent(id) => write!(f, "invalid parent window for popup: {:?}", id),
6774
}
@@ -74,6 +81,7 @@ impl std::error::Error for Error {
7481
Self::Connect(e) => Some(&**e),
7582
Self::Global { inner, .. } => Some(&**inner),
7683
Self::Fatal(e) => Some(&**e),
84+
Self::Err(e) => Some(&**e),
7785
Self::String(e) => Some(e),
7886
Self::InvalidParent(_) => None,
7987
}

druid-shell/src/backend/wayland/events.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ pub(crate) struct WaylandSource {
2424
impl WaylandSource {
2525
/// Wrap an `EventQueue` as a `WaylandSource`.
2626
pub fn new(appdata: std::sync::Arc<application::ApplicationData>) -> WaylandSource {
27-
let queue = appdata.event_queue.clone();
27+
let queue = appdata.wayland.queue.clone();
2828
let fd = queue.borrow().display().get_connection_fd();
2929
WaylandSource {
3030
appdata,

druid-shell/src/backend/wayland/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@
1616
1717
pub mod application;
1818
pub mod clipboard;
19+
mod display;
1920
pub mod error;
2021
mod events;
2122
pub mod keyboard;
2223
pub mod menu;
24+
mod outputs;
2325
pub mod pointers;
2426
pub mod screen;
2527
pub mod surfaces;

0 commit comments

Comments
 (0)