206 lines
6.6 KiB
Rust
206 lines
6.6 KiB
Rust
use std::sync::{Arc, Mutex, mpsc};
|
|
|
|
use ferese_protocols::effects::v1::server::{
|
|
ferese_effects_manager_v1 as manager, ferese_surface_effects_v1 as effects,
|
|
};
|
|
use wayland_server::protocol::{wl_compositor, wl_surface};
|
|
use wayland_server::{Client, DataInit, Dispatch, Display, DisplayHandle, GlobalDispatch, New};
|
|
|
|
#[derive(Default)]
|
|
struct Counts {
|
|
managers: usize,
|
|
manager_destroys: usize,
|
|
surfaces: usize,
|
|
effect_destroys: usize,
|
|
}
|
|
|
|
struct Server(Arc<Mutex<Counts>>);
|
|
|
|
impl GlobalDispatch<wl_compositor::WlCompositor, ()> for Server {
|
|
fn bind(
|
|
_: &mut Self,
|
|
_: &DisplayHandle,
|
|
_: &Client,
|
|
resource: New<wl_compositor::WlCompositor>,
|
|
_: &(),
|
|
init: &mut DataInit<'_, Self>,
|
|
) {
|
|
init.init(resource, ());
|
|
}
|
|
}
|
|
|
|
impl Dispatch<wl_compositor::WlCompositor, ()> for Server {
|
|
fn request(
|
|
_: &mut Self,
|
|
_: &Client,
|
|
_: &wl_compositor::WlCompositor,
|
|
request: wl_compositor::Request,
|
|
_: &(),
|
|
_: &DisplayHandle,
|
|
init: &mut DataInit<'_, Self>,
|
|
) {
|
|
if let wl_compositor::Request::CreateSurface { id } = request {
|
|
init.init(id, ());
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Dispatch<wl_surface::WlSurface, ()> for Server {
|
|
fn request(
|
|
_: &mut Self,
|
|
_: &Client,
|
|
_: &wl_surface::WlSurface,
|
|
_: wl_surface::Request,
|
|
_: &(),
|
|
_: &DisplayHandle,
|
|
_: &mut DataInit<'_, Self>,
|
|
) {
|
|
}
|
|
}
|
|
|
|
impl GlobalDispatch<manager::FereseEffectsManagerV1, ()> for Server {
|
|
fn bind(
|
|
state: &mut Self,
|
|
_: &DisplayHandle,
|
|
_: &Client,
|
|
resource: New<manager::FereseEffectsManagerV1>,
|
|
_: &(),
|
|
init: &mut DataInit<'_, Self>,
|
|
) {
|
|
state.0.lock().unwrap().managers += 1;
|
|
init.init(resource, ());
|
|
}
|
|
}
|
|
|
|
impl Dispatch<manager::FereseEffectsManagerV1, ()> for Server {
|
|
fn request(
|
|
state: &mut Self,
|
|
_: &Client,
|
|
_: &manager::FereseEffectsManagerV1,
|
|
request: manager::Request,
|
|
_: &(),
|
|
_: &DisplayHandle,
|
|
init: &mut DataInit<'_, Self>,
|
|
) {
|
|
match request {
|
|
manager::Request::GetSurfaceEffects { id, .. } => {
|
|
state.0.lock().unwrap().surfaces += 1;
|
|
init.init(id, ());
|
|
}
|
|
manager::Request::Destroy => state.0.lock().unwrap().manager_destroys += 1,
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Dispatch<effects::FereseSurfaceEffectsV1, ()> for Server {
|
|
fn request(
|
|
state: &mut Self,
|
|
_: &Client,
|
|
_: &effects::FereseSurfaceEffectsV1,
|
|
request: effects::Request,
|
|
_: &(),
|
|
_: &DisplayHandle,
|
|
_: &mut DataInit<'_, Self>,
|
|
) {
|
|
if matches!(request, effects::Request::Destroy) {
|
|
state.0.lock().unwrap().effect_destroys += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
wayland_client::delegate_noop!(super::EffectsState: ignore wayland_client::protocol::wl_compositor::WlCompositor);
|
|
wayland_client::delegate_noop!(super::EffectsState: ignore wayland_client::protocol::wl_surface::WlSurface);
|
|
|
|
#[test]
|
|
fn popup_effects_share_the_registry_and_destroy_every_protocol_object() {
|
|
effects_lifecycle(true);
|
|
}
|
|
|
|
#[test]
|
|
fn missing_effects_extension_reuses_the_registry_across_failed_attachments() {
|
|
effects_lifecycle(false);
|
|
}
|
|
|
|
fn effects_lifecycle(advertise: bool) {
|
|
use wayland_client::protocol::wl_compositor::WlCompositor;
|
|
let counts = Arc::new(Mutex::new(Counts::default()));
|
|
let worker_counts = counts.clone();
|
|
let (client, server) = std::os::unix::net::UnixStream::pair().unwrap();
|
|
let (stop, stopped) = mpsc::channel::<()>();
|
|
let worker = std::thread::spawn(move || {
|
|
let mut display = Display::<Server>::new().unwrap();
|
|
let mut handle = display.handle();
|
|
handle.create_global::<Server, wl_compositor::WlCompositor, _>(1, ());
|
|
if advertise {
|
|
handle.create_global::<Server, manager::FereseEffectsManagerV1, _>(6, ());
|
|
}
|
|
handle.insert_client(server, Arc::new(())).unwrap();
|
|
let mut state = Server(worker_counts);
|
|
loop {
|
|
display.dispatch_clients(&mut state).unwrap();
|
|
display.flush_clients().unwrap();
|
|
match stopped.recv_timeout(std::time::Duration::from_millis(1)) {
|
|
Err(mpsc::RecvTimeoutError::Timeout) => {}
|
|
_ => break,
|
|
}
|
|
}
|
|
});
|
|
let connection = wayland_client::Connection::from_socket(client).unwrap();
|
|
let (globals, queue) =
|
|
wayland_client::globals::registry_queue_init::<super::EffectsState>(&connection).unwrap();
|
|
let compositor = globals
|
|
.bind::<WlCompositor, _, _>(&queue.handle(), 1..=1, ())
|
|
.unwrap();
|
|
let mut context = None;
|
|
let mut first = None;
|
|
for _ in 0..20 {
|
|
let surface = compositor.create_surface(&queue.handle(), ());
|
|
use wayland_client::Proxy;
|
|
// Match window::run: each native callback lends the same display through
|
|
// a fresh borrowed backend. Neither wrapper owns the actual display.
|
|
let borrowed = wayland_client::Connection::from_backend(unsafe {
|
|
wayland_client::backend::Backend::from_foreign_display(
|
|
connection.backend().display_ptr(),
|
|
)
|
|
});
|
|
assert_ne!(borrowed, connection);
|
|
let id = unsafe {
|
|
wayland_client::backend::ObjectId::from_ptr(
|
|
wayland_client::protocol::wl_surface::WlSurface::interface(),
|
|
surface.id().as_ptr(),
|
|
)
|
|
}
|
|
.unwrap();
|
|
let borrowed_surface =
|
|
wayland_client::protocol::wl_surface::WlSurface::from_id(&borrowed, id).unwrap();
|
|
let binding = super::EffectsBinding::attach_role(&mut context, &borrowed_surface, None, 1.);
|
|
assert_eq!(binding.is_ok(), advertise);
|
|
if let Some(first) = &first {
|
|
assert!(std::rc::Rc::ptr_eq(first, context.as_ref().unwrap()));
|
|
} else {
|
|
first = context.clone();
|
|
}
|
|
// Parent surface lifetime does not release the effects protocol object.
|
|
surface.destroy();
|
|
drop(binding);
|
|
}
|
|
connection.roundtrip().unwrap();
|
|
{
|
|
let counts = counts.lock().unwrap();
|
|
assert_eq!(counts.managers, usize::from(advertise));
|
|
assert_eq!(counts.surfaces, if advertise { 20 } else { 0 });
|
|
assert_eq!(counts.effect_destroys, counts.surfaces);
|
|
assert_eq!(counts.manager_destroys, 0);
|
|
}
|
|
drop(first);
|
|
drop(context);
|
|
connection.roundtrip().unwrap();
|
|
assert_eq!(
|
|
counts.lock().unwrap().manager_destroys,
|
|
usize::from(advertise)
|
|
);
|
|
drop(stop);
|
|
worker.join().unwrap();
|
|
}
|