322 lines
10 KiB
Rust
322 lines
10 KiB
Rust
use super::*;
|
|
use crate::state::{ClientState, dispatch_test_client as dispatch, test_client_request as request};
|
|
use smithay::output::{Mode, PhysicalProperties, Subpixel};
|
|
use smithay::reexports::calloop::EventLoop;
|
|
use smithay::reexports::wayland_protocols_wlr::layer_shell::v1::server::zwlr_layer_shell_v1::ZwlrLayerShellV1;
|
|
use smithay::reexports::wayland_server::Resource;
|
|
use smithay::reexports::wayland_server::protocol::wl_compositor::WlCompositor;
|
|
use std::io::Read;
|
|
use std::os::unix::net::UnixStream;
|
|
use std::sync::Arc;
|
|
|
|
fn fixture() -> (Ferese, EventLoop<'static, Ferese>) {
|
|
let mut events = EventLoop::try_new().unwrap();
|
|
let state = crate::startup_tests::state(&mut events);
|
|
(state, events)
|
|
}
|
|
|
|
fn output(state: &mut Ferese, name: &str, x: i32) -> Output {
|
|
let output = Output::new(
|
|
name.into(),
|
|
PhysicalProperties {
|
|
serial_number: String::new(),
|
|
size: (0, 0).into(),
|
|
subpixel: Subpixel::Unknown,
|
|
make: "test".into(),
|
|
model: "test".into(),
|
|
},
|
|
);
|
|
output.change_current_state(
|
|
Some(Mode {
|
|
size: (800, 600).into(),
|
|
refresh: 60_000,
|
|
}),
|
|
None,
|
|
None,
|
|
None,
|
|
);
|
|
state.space.map_output(&output, (x, 0));
|
|
state.register_output(&output, name.into());
|
|
output
|
|
}
|
|
|
|
fn read(wire: &mut UnixStream) -> Vec<Vec<u32>> {
|
|
wire.set_nonblocking(true).unwrap();
|
|
let mut bytes = Vec::new();
|
|
assert_eq!(
|
|
wire.read_to_end(&mut bytes).unwrap_err().kind(),
|
|
std::io::ErrorKind::WouldBlock
|
|
);
|
|
wire.set_nonblocking(false).unwrap();
|
|
let mut messages = Vec::new();
|
|
let mut offset = 0;
|
|
while offset < bytes.len() {
|
|
let header = u32::from_ne_bytes(bytes[offset + 4..offset + 8].try_into().unwrap());
|
|
let len = (header >> 16) as usize;
|
|
messages.push(
|
|
bytes[offset..offset + len]
|
|
.chunks_exact(4)
|
|
.map(|word| u32::from_ne_bytes(word.try_into().unwrap()))
|
|
.collect(),
|
|
);
|
|
offset += len;
|
|
}
|
|
messages
|
|
}
|
|
|
|
struct Client {
|
|
client: smithay::reexports::wayland_server::Client,
|
|
wire: UnixStream,
|
|
compositor: WlCompositor,
|
|
shell: ZwlrLayerShellV1,
|
|
}
|
|
|
|
impl Client {
|
|
fn new(state: &mut Ferese) -> Self {
|
|
let (server, mut wire) = UnixStream::pair().unwrap();
|
|
let client = state
|
|
.display_handle
|
|
.insert_client(server, Arc::new(ClientState::default()))
|
|
.unwrap();
|
|
let compositor = client
|
|
.create_resource::<WlCompositor, smithay::wayland::GlobalData, Ferese>(
|
|
&state.display_handle,
|
|
6,
|
|
smithay::wayland::GlobalData,
|
|
)
|
|
.unwrap();
|
|
let shell = client
|
|
.create_resource::<ZwlrLayerShellV1, smithay::wayland::GlobalData, Ferese>(
|
|
&state.display_handle,
|
|
4,
|
|
smithay::wayland::GlobalData,
|
|
)
|
|
.unwrap();
|
|
request(&mut wire, 1, 1, &[2], None); // Registry.
|
|
request(&mut wire, compositor.id().protocol_id(), 0, &[3], None);
|
|
// Keep client IDs contiguous: 5 is the optional wl_output, 6 the role.
|
|
request(&mut wire, compositor.id().protocol_id(), 0, &[4], None);
|
|
Self {
|
|
client,
|
|
wire,
|
|
compositor,
|
|
shell,
|
|
}
|
|
}
|
|
|
|
fn bind_output(
|
|
&mut self,
|
|
state: &mut Ferese,
|
|
events: &mut EventLoop<'static, Ferese>,
|
|
output: &Output,
|
|
) -> smithay::reexports::wayland_server::backend::GlobalId {
|
|
let global = output.create_global::<Ferese>(&state.display_handle);
|
|
dispatch(events, state);
|
|
let names = read(&mut self.wire)
|
|
.into_iter()
|
|
.filter(|message| {
|
|
message[0] == 2
|
|
&& message[1] & 0xffff == 0
|
|
&& message[3] == 10
|
|
&& message.get(4) == Some(&u32::from_ne_bytes(*b"wl_o"))
|
|
})
|
|
.map(|message| message[2])
|
|
.collect::<Vec<_>>();
|
|
assert_eq!(names.len(), 1);
|
|
request(
|
|
&mut self.wire,
|
|
2,
|
|
0,
|
|
&[
|
|
names[0],
|
|
10,
|
|
u32::from_ne_bytes(*b"wl_o"),
|
|
u32::from_ne_bytes(*b"utpu"),
|
|
u32::from_ne_bytes([b't', 0, 0, 0]),
|
|
4,
|
|
5,
|
|
],
|
|
None,
|
|
);
|
|
dispatch(events, state);
|
|
read(&mut self.wire);
|
|
global
|
|
}
|
|
|
|
fn create_layer(
|
|
&mut self,
|
|
state: &mut Ferese,
|
|
events: &mut EventLoop<'static, Ferese>,
|
|
output: u32,
|
|
) -> Vec<Vec<u32>> {
|
|
if output == 0 {
|
|
// Reserve the output slot with a spare surface for unspecified requests.
|
|
request(
|
|
&mut self.wire,
|
|
self.compositor.id().protocol_id(),
|
|
0,
|
|
&[5],
|
|
None,
|
|
);
|
|
}
|
|
request(
|
|
&mut self.wire,
|
|
self.shell.id().protocol_id(),
|
|
0,
|
|
&[6, 3, output, 2, 4, u32::from_ne_bytes(*b"bar\0")],
|
|
None,
|
|
);
|
|
request(&mut self.wire, 6, 0, &[64, 48], None);
|
|
request(&mut self.wire, 3, 6, &[], None);
|
|
dispatch(events, state);
|
|
read(&mut self.wire)
|
|
}
|
|
|
|
fn mapped_on(&self, output: &Output) -> bool {
|
|
layer_map_for_output(output).layers().any(|layer| {
|
|
layer
|
|
.wl_surface()
|
|
.client()
|
|
.is_some_and(|client| client.id() == self.client.id())
|
|
})
|
|
}
|
|
}
|
|
|
|
fn assert_closed_without_configure(messages: &[Vec<u32>]) {
|
|
assert!(
|
|
messages
|
|
.iter()
|
|
.any(|message| message[0] == 6 && message[1] & 0xffff == 1),
|
|
"layer must receive closed"
|
|
);
|
|
assert!(
|
|
!messages
|
|
.iter()
|
|
.any(|message| message[0] == 6 && message[1] & 0xffff == 0),
|
|
"closed layers must not wait for configure"
|
|
);
|
|
assert!(
|
|
!messages
|
|
.iter()
|
|
.any(|message| message[0] == 1 && message[1] & 0xffff == 0),
|
|
"no client protocol error"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn zero_outputs_close_new_layer_roles() {
|
|
if !crate::startup_tests::private_runtime(
|
|
"handlers::layer_shell::output_tests::zero_outputs_close_new_layer_roles",
|
|
) {
|
|
return;
|
|
}
|
|
let (mut state, mut events) = fixture();
|
|
let mut client = Client::new(&mut state);
|
|
assert_closed_without_configure(&client.create_layer(&mut state, &mut events, 0));
|
|
}
|
|
|
|
#[test]
|
|
fn stale_explicit_output_closes_instead_of_retargeting() {
|
|
if !crate::startup_tests::private_runtime(
|
|
"handlers::layer_shell::output_tests::stale_explicit_output_closes_instead_of_retargeting",
|
|
) {
|
|
return;
|
|
}
|
|
let (mut state, mut events) = fixture();
|
|
let stale = output(&mut state, "stale", 0);
|
|
let survivor = output(&mut state, "survivor", 800);
|
|
let mut client = Client::new(&mut state);
|
|
let global = client.bind_output(&mut state, &mut events, &stale);
|
|
state.unregister_output(&stale);
|
|
state.display_handle.remove_global::<Ferese>(global);
|
|
dispatch(&mut events, &mut state);
|
|
read(&mut client.wire);
|
|
let messages = client.create_layer(&mut state, &mut events, 5);
|
|
assert_closed_without_configure(&messages);
|
|
assert!(!client.mapped_on(&stale));
|
|
assert!(
|
|
!client.mapped_on(&survivor),
|
|
"explicit requests must not silently move to another output"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn unspecified_output_uses_focused_registered_output() {
|
|
if !crate::startup_tests::private_runtime(
|
|
"handlers::layer_shell::output_tests::unspecified_output_uses_focused_registered_output",
|
|
) {
|
|
return;
|
|
}
|
|
let (mut state, mut events) = fixture();
|
|
let first = output(&mut state, "first", 0);
|
|
let focused = output(&mut state, "focused", 800);
|
|
state.focus_output_at((820., 20.).into());
|
|
assert_eq!(state.focused_output(), Some(&focused));
|
|
let mut client = Client::new(&mut state);
|
|
let messages = client.create_layer(&mut state, &mut events, 0);
|
|
assert!(client.mapped_on(&focused));
|
|
assert!(!client.mapped_on(&first));
|
|
assert!(
|
|
messages
|
|
.iter()
|
|
.any(|message| message[0] == 6 && message[1] & 0xffff == 0),
|
|
"valid layer receives configure"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn valid_explicit_output_is_preserved() {
|
|
if !crate::startup_tests::private_runtime(
|
|
"handlers::layer_shell::output_tests::valid_explicit_output_is_preserved",
|
|
) {
|
|
return;
|
|
}
|
|
let (mut state, mut events) = fixture();
|
|
let explicit = output(&mut state, "explicit", 0);
|
|
let focused = output(&mut state, "focused", 800);
|
|
state.focus_output_at((820., 20.).into());
|
|
let mut client = Client::new(&mut state);
|
|
client.bind_output(&mut state, &mut events, &explicit);
|
|
let messages = client.create_layer(&mut state, &mut events, 5);
|
|
assert!(client.mapped_on(&explicit));
|
|
assert!(!client.mapped_on(&focused));
|
|
assert!(
|
|
messages
|
|
.iter()
|
|
.any(|message| message[0] == 6 && message[1] & 0xffff == 0)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn unregistered_output_is_not_a_creation_fallback() {
|
|
if !crate::startup_tests::private_runtime(
|
|
"handlers::layer_shell::output_tests::unregistered_output_is_not_a_creation_fallback",
|
|
) {
|
|
return;
|
|
}
|
|
let (mut state, mut events) = fixture();
|
|
let ghost = Output::new(
|
|
"ghost".into(),
|
|
PhysicalProperties {
|
|
serial_number: String::new(),
|
|
size: (0, 0).into(),
|
|
subpixel: Subpixel::Unknown,
|
|
make: "test".into(),
|
|
model: "test".into(),
|
|
},
|
|
);
|
|
ghost.change_current_state(
|
|
Some(Mode {
|
|
size: (800, 600).into(),
|
|
refresh: 60_000,
|
|
}),
|
|
None,
|
|
None,
|
|
None,
|
|
);
|
|
state.space.map_output(&ghost, (0, 0));
|
|
let mut client = Client::new(&mut state);
|
|
assert_closed_without_configure(&client.create_layer(&mut state, &mut events, 0));
|
|
assert!(!client.mapped_on(&ghost));
|
|
}
|