#[expect(unused_imports)]
use crate::Connection;
#[expect(unused_imports)]
use crate::proxy;
use {
crate::{
DispatchLock, Queue,
builder::prelude::{CreateEventHandler, EventHandler, UntypedBorrowedProxy},
ffi::{wl_argument, wl_interface, wl_message},
proxy::{
OwnedProxy, get_owned,
low_level::{
OwnedProxyRegistry, ProxyDataDestruction,
owned::{UntypedOwnedProxyData, event_handler_func},
},
},
utils::{
on_drop::abort_on_panic,
sync_cell::{SyncCell, SyncUnsafeCell},
},
},
parking_lot::{Condvar, Mutex},
run_on_drop::on_drop,
std::{
any::TypeId,
ffi::{c_int, c_void},
future::poll_fn,
marker::PhantomData,
mem,
pin::pin,
ptr::NonNull,
sync::{Arc, atomic::Ordering::Relaxed},
},
};
#[cfg(test)]
mod tests;
pub struct Scope<'scope, 'env: 'scope> {
pub(super) data: Arc<ScopeData>,
_inv_scope: PhantomData<&'scope mut &'scope mut ()>,
_inv_env: PhantomData<&'env mut &'env mut ()>,
}
pub(super) struct ScopeData {
queue: Queue,
may_dispatch: SyncCell<bool>,
defer_destruction: SyncCell<bool>,
live_event_handlers: SyncCell<u64>,
awaiting_last_destruction: SyncCell<bool>,
last_event_handler_destroyed: Mutex<bool>,
last_event_handler_condvar: Condvar,
pub(super) registry: OwnedProxyRegistry,
destructions: SyncUnsafeCell<Vec<ProxyDataDestruction>>,
}
impl Queue {
pub fn dispatch_scope_blocking<'env, T, F>(&self, f: F) -> T
where
F: for<'scope> FnOnce(&'scope Scope<'scope, 'env>) -> T,
{
let scope = Scope::new(self);
{
let _lock = self.lock_dispatch();
unsafe {
scope.data.may_dispatch.set(true);
}
}
let _cleanup = on_drop(|| {
let lock = abort_on_panic(|| self.lock_dispatch());
unsafe {
scope.data.may_dispatch.set(false);
}
unsafe {
scope.drop(Some(lock));
}
});
f(&scope)
}
pub async fn dispatch_scope_async<'env, T, F>(&self, f: F) -> T
where
F: for<'scope> AsyncFnOnce(&'scope Scope<'scope, 'env>) -> T,
{
let scope = Scope::new(self);
let _cleanup = on_drop(|| {
unsafe {
scope.drop(None);
}
});
let mut fut = pin!(f(&scope));
poll_fn(|ctx| {
{
let _lock = self.lock_dispatch();
unsafe {
scope.data.may_dispatch.set(true);
}
}
let _cleanup = on_drop(|| {
let _lock = abort_on_panic(|| self.lock_dispatch());
unsafe {
scope.data.may_dispatch.set(false);
}
unsafe {
scope.run_destructions(true);
}
});
fut.as_mut().poll(ctx)
})
.await
}
}
impl<'scope> Scope<'scope, '_> {
fn new(queue: &Queue) -> Self {
Scope {
data: Arc::new(ScopeData {
queue: queue.clone(),
may_dispatch: SyncCell::new(false),
defer_destruction: SyncCell::new(true),
live_event_handlers: SyncCell::new(0),
awaiting_last_destruction: SyncCell::new(false),
last_event_handler_destroyed: Default::default(),
last_event_handler_condvar: Default::default(),
registry: Default::default(),
destructions: SyncUnsafeCell::new(Vec::new()),
}),
_inv_scope: Default::default(),
_inv_env: Default::default(),
}
}
unsafe fn drop(&'scope self, lock: Option<DispatchLock<'_>>) {
let lock = lock.unwrap_or_else(|| self.data.queue.lock_dispatch());
unsafe {
self.data.defer_destruction.set(false);
}
let reset_defer_destruction = on_drop(|| {
self.data.queue.run_locked(|| {
unsafe {
self.data.defer_destruction.set(true);
}
});
});
unsafe {
self.data.registry.destroy_event_handlers();
}
unsafe {
self.run_destructions(false);
}
let live_event_handlers = unsafe { self.data.live_event_handlers.get() };
if live_event_handlers > 0 {
unsafe {
self.data.awaiting_last_destruction.set(true);
}
drop(lock);
let mut done = self.data.last_event_handler_destroyed.lock();
while !*done {
self.data.last_event_handler_condvar.wait(&mut done);
}
}
reset_defer_destruction.forget();
}
unsafe fn run_destructions(&self, re_use_memory: bool) {
let mut stash = vec![];
mem::swap(&mut stash, unsafe { &mut *self.data.destructions.get() });
for destruction in stash.drain(..) {
unsafe {
destruction.run();
}
}
if re_use_memory {
mem::swap(&mut stash, unsafe { &mut *self.data.destructions.get() });
assert!(stash.is_empty());
}
}
#[inline]
pub fn set_event_handler<P, H>(&'scope self, proxy: &P, handler: H)
where
P: OwnedProxy,
P::Api: CreateEventHandler<H>,
<P::Api as CreateEventHandler<H>>::EventHandler: Send + 'scope,
{
unsafe {
set_event_handler(self, proxy, P::Api::create_event_handler(handler));
}
}
#[inline]
pub fn set_event_handler_local<P, H>(&'scope self, proxy: &P, handler: H)
where
P: OwnedProxy,
P::Api: CreateEventHandler<H>,
<P::Api as CreateEventHandler<H>>::EventHandler: 'scope,
{
if self.data.queue.is_non_local() {
panic!("Queue is not a local queue");
}
unsafe {
set_event_handler(self, proxy, P::Api::create_event_handler(handler));
}
}
}
struct ScopeEventHandler<H, D> {
event_handler: H,
_on_drop: D,
}
unsafe impl<H, D> EventHandler for ScopeEventHandler<H, D>
where
H: EventHandler,
{
const WL_INTERFACE: &'static wl_interface = H::WL_INTERFACE;
#[inline]
fn mutable_type() -> Option<(TypeId, &'static str)> {
H::mutable_type()
}
#[inline]
unsafe fn handle_event(
&self,
queue: &Queue,
data: *mut u8,
slf: &UntypedBorrowedProxy,
opcode: u32,
args: *mut wl_argument,
) {
unsafe {
self.event_handler
.handle_event(queue, data, slf, opcode, args);
}
}
}
unsafe fn set_event_handler<'scope, P, H>(
scope: &'scope Scope<'scope, '_>,
proxy: &P,
event_handler: H,
) where
P: OwnedProxy,
H: EventHandler + 'scope,
{
if mem::needs_drop::<H>() {
let data = scope.data.clone();
let event_handler = ScopeEventHandler {
event_handler,
_on_drop: on_drop(move || {
let d = &*data;
let leh = &d.live_event_handlers;
let live = unsafe { leh.get() };
unsafe {
leh.set(live - 1);
}
let awaiting_last_destruction = unsafe { d.awaiting_last_destruction.get() };
if live == 1 && awaiting_last_destruction {
*d.last_event_handler_destroyed.lock() = true;
d.last_event_handler_condvar.notify_all();
}
}),
};
scope.data.queue.run_locked(|| {
let leh = &scope.data.live_event_handlers;
unsafe {
leh.set(leh.get() + 1);
}
});
unsafe {
set_event_handler2(scope, proxy, event_handler);
}
} else {
unsafe {
set_event_handler2(scope, proxy, event_handler);
}
}
}
unsafe fn set_event_handler2<'scope, P, H>(
scope: &'scope Scope<'scope, '_>,
proxy: &P,
event_handler: H,
) where
P: OwnedProxy,
H: EventHandler + 'scope,
{
let proxy = get_owned(proxy);
assert_eq!(proxy.queue(), &scope.data.queue);
unsafe {
proxy.set_event_handler3(
event_handler,
event_handler_func_scoped::<P, H>,
Some(scope),
);
}
}
impl ScopeData {
pub(super) unsafe fn handle_destruction(&self, destruction: ProxyDataDestruction) {
let _lock = self.queue.lock_dispatch();
match unsafe { self.defer_destruction.get() } {
true => {
let destructions = unsafe { &mut *self.destructions.get() };
destructions.push(destruction);
}
false => {
unsafe { destruction.run() }
}
}
}
}
unsafe extern "C" fn event_handler_func_scoped<P, T>(
event_handler_data: *const c_void,
target: *mut c_void,
opcode: u32,
msg: *const wl_message,
args: *mut wl_argument,
) -> c_int
where
P: OwnedProxy,
T: EventHandler,
{
let proxy_data = unsafe { &*(event_handler_data as *const UntypedOwnedProxyData) };
let scope_data = unsafe { &*proxy_data.scope_data.load(Relaxed) };
if unsafe { scope_data.may_dispatch.get() } {
return unsafe { event_handler_func::<T>(event_handler_data, target, opcode, msg, args) };
}
let target = unsafe { NonNull::new_unchecked(target.cast()) };
let target =
unsafe { UntypedBorrowedProxy::new_immutable(proxy_data.proxy.libwayland, target) };
let data = unsafe { proxy_data.queue.data() };
unsafe {
P::NO_OP_EVENT_HANDLER.handle_event(&proxy_data.queue, data, &target, opcode, args);
}
0
}