Switch to using xwayland shell protocol over WL_SURFACE_ID

Seems to fix #13. This means a hard requirement on Xwayland 23.1.
This commit is contained in:
Shawn Wallace 2024-05-20 18:31:16 -04:00
parent da82e6907c
commit 3b2698f1de
7 changed files with 225 additions and 112 deletions

View file

@ -3,6 +3,7 @@ xwayland-satellite grants rootless Xwayland integration to any Wayland composito
This is particularly useful for compositors that (understandably) do not want to go through implementing support for rootless Xwayland themselves. This is particularly useful for compositors that (understandably) do not want to go through implementing support for rootless Xwayland themselves.
## Dependencies ## Dependencies
- Xwayland >=23.1
- xcb - xcb
- xcb-util-cursor - xcb-util-cursor
- clang (building only) - clang (building only)

View file

@ -1,5 +1,5 @@
use super::*; use super::*;
use log::{debug, trace, warn}; use log::{debug, error, trace, warn};
use std::sync::{Arc, OnceLock}; use std::sync::{Arc, OnceLock};
use wayland_protocols::{ use wayland_protocols::{
wp::{ wp::{
@ -34,6 +34,10 @@ use wayland_protocols::{
zxdg_output_v1::{self as s_xdgo, ZxdgOutputV1 as XdgOutputServer}, zxdg_output_v1::{self as s_xdgo, ZxdgOutputV1 as XdgOutputServer},
}, },
}, },
xwayland::shell::v1::server::{
xwayland_shell_v1::{self, XwaylandShellV1},
xwayland_surface_v1::{self, XwaylandSurfaceV1},
},
}; };
use wayland_server::{ use wayland_server::{
protocol::{ protocol::{
@ -128,9 +132,15 @@ impl<C: XConnection> Dispatch<WlSurface, ObjectKey> for ServerState<C> {
Request::<WlSurface>::Destroy => { Request::<WlSurface>::Destroy => {
let mut object = state.objects.remove(*key).unwrap(); let mut object = state.objects.remove(*key).unwrap();
let surface: &mut SurfaceData = object.as_mut(); let surface: &mut SurfaceData = object.as_mut();
if let Some(window_data) = surface.window.and_then(|w| state.windows.get_mut(&w)) {
window_data.surface_key.take();
}
surface.destroy_role(); surface.destroy_role();
surface.client.destroy(); surface.client.destroy();
debug!("deleting key: {:?}", key); debug!(
"deleting key: {key:?} (surface {:?})",
surface.server.id().protocol_id()
);
} }
Request::<WlSurface>::SetBufferScale { scale } => { Request::<WlSurface>::SetBufferScale { scale } => {
surface.client.set_buffer_scale(scale); surface.client.set_buffer_scale(scale);
@ -181,37 +191,25 @@ impl<C: XConnection>
Request::<WlCompositor>::CreateSurface { id } => { Request::<WlCompositor>::CreateSurface { id } => {
let mut surface_id = None; let mut surface_id = None;
let key = state.objects.insert_with_key(|key| { state.objects.insert_with_key(|key| {
debug!("new surface with key {key:?}");
let client = client.create_surface(&state.qh, key); let client = client.create_surface(&state.qh, key);
let server = data_init.init(id, key); let server = data_init.init(id, key);
surface_id = Some(server.id().protocol_id()); surface_id = Some(server.id().protocol_id());
debug!("new surface with key {key:?} ({surface_id:?})");
SurfaceData { SurfaceData {
client, client,
server, server,
key, key,
serial: Default::default(),
attach: None, attach: None,
frame_callback: None, frame_callback: None,
role: None, role: None,
xwl: None,
window: None,
} }
.into() .into()
}); });
let surface_id = surface_id.unwrap();
if let Some((win, window_data)) =
state.windows.iter_mut().find_map(|(win, data)| {
Some(*win).zip((data.surface_id == surface_id).then_some(data))
})
{
window_data.surface_key = Some(key);
state.associated_windows.insert(key, win);
debug!("associate surface {surface_id} with window {win:?}");
if window_data.mapped {
state.create_role_window(win, key);
}
}
} }
Request::<WlCompositor>::CreateRegion { id } => { Request::<WlCompositor>::CreateRegion { id } => {
let c_region = client.create_region(&state.qh, ()); let c_region = client.create_region(&state.qh, ());
@ -1075,3 +1073,95 @@ global_dispatch_no_events!(PointerConstraintsServer, PointerConstraintsClient);
global_dispatch_with_events!(WlSeat, client::wl_seat::WlSeat); global_dispatch_with_events!(WlSeat, client::wl_seat::WlSeat);
global_dispatch_with_events!(WlOutput, client::wl_output::WlOutput); global_dispatch_with_events!(WlOutput, client::wl_output::WlOutput);
global_dispatch_with_events!(WlDrmServer, WlDrmClient); global_dispatch_with_events!(WlDrmServer, WlDrmClient);
impl<C: XConnection> GlobalDispatch<XwaylandShellV1, ()> for ServerState<C> {
fn bind(
_: &mut Self,
_: &DisplayHandle,
_: &wayland_server::Client,
resource: wayland_server::New<XwaylandShellV1>,
_: &(),
data_init: &mut wayland_server::DataInit<'_, Self>,
) {
data_init.init(resource, ());
}
}
impl<C: XConnection> Dispatch<XwaylandShellV1, ()> for ServerState<C> {
fn request(
state: &mut Self,
client: &wayland_server::Client,
_: &XwaylandShellV1,
request: <XwaylandShellV1 as Resource>::Request,
_: &(),
dhandle: &DisplayHandle,
data_init: &mut wayland_server::DataInit<'_, Self>,
) {
use xwayland_shell_v1::Request;
match request {
Request::GetXwaylandSurface { id, surface } => {
let key: ObjectKey = surface.data().copied().unwrap();
let data: &mut SurfaceData = state.objects[key].as_mut();
if data.xwl.is_some() {
error!("Surface {surface:?} already has the xwayland surface role!");
client.kill(
dhandle,
wayland_client::backend::protocol::ProtocolError {
code: 0,
object_id: surface.id().protocol_id(),
object_interface: "wl_surface".to_string(),
message: "Surface already has role".to_string(),
},
);
return;
}
let xwl = data_init.init(id, key);
data.xwl = Some(xwl);
}
Request::Destroy => {}
_ => unreachable!(),
}
}
}
impl<C: XConnection> Dispatch<XwaylandSurfaceV1, ObjectKey> for ServerState<C> {
fn request(
state: &mut Self,
_: &wayland_server::Client,
_: &XwaylandSurfaceV1,
request: <XwaylandSurfaceV1 as Resource>::Request,
key: &ObjectKey,
_: &DisplayHandle,
_: &mut wayland_server::DataInit<'_, Self>,
) {
use xwayland_surface_v1::Request;
let data: &mut SurfaceData = state.objects[*key].as_mut();
match request {
Request::SetSerial {
serial_lo,
serial_hi,
} => {
let serial = [serial_lo, serial_hi];
data.serial = Some(serial);
if let Some((win, window_data)) =
state.windows.iter_mut().find_map(|(win, data)| {
Some(*win)
.zip((data.surface_serial.is_some_and(|s| s == serial)).then_some(data))
})
{
debug!("associate surface {} with {:?}", data.server.id().protocol_id(), win);
window_data.surface_key = Some(*key);
state.associated_windows.insert(*key, win);
if window_data.mapped {
state.create_role_window(win, *key);
}
}
}
Request::Destroy => {
data.xwl.take();
}
_ => unreachable!(),
}
}
}

View file

@ -395,6 +395,9 @@ impl HandleEvent for Pointer {
} }
} }
client::wl_pointer::Event::Leave { serial, surface } => { client::wl_pointer::Event::Leave { serial, surface } => {
if !surface.is_alive() {
return;
}
debug!("leaving surface ({serial})"); debug!("leaving surface ({serial})");
self.pending_enter.0.take(); self.pending_enter.0.take();
self.server self.server
@ -504,7 +507,12 @@ impl HandleEvent for Keyboard {
}, },
Leave { Leave {
serial, serial,
|surface| state.get_server_surface_from_client(surface) |surface| {
if !surface.is_alive() {
return;
}
state.get_server_surface_from_client(surface)
}
}, },
Key { Key {
serial, serial,

View file

@ -33,6 +33,9 @@ use wayland_protocols::{
}, },
xdg_output::zv1::server::zxdg_output_manager_v1::ZxdgOutputManagerV1, xdg_output::zv1::server::zxdg_output_manager_v1::ZxdgOutputManagerV1,
}, },
xwayland::shell::v1::server::{
xwayland_shell_v1::XwaylandShellV1, xwayland_surface_v1::XwaylandSurfaceV1,
},
}; };
use wayland_server::{ use wayland_server::{
protocol::{ protocol::{
@ -81,7 +84,7 @@ pub struct WindowAttributes {
#[derive(Debug)] #[derive(Debug)]
struct WindowData { struct WindowData {
window: x::Window, window: x::Window,
surface_id: u32, surface_serial: Option<[u32; 2]>,
surface_key: Option<ObjectKey>, surface_key: Option<ObjectKey>,
mapped: bool, mapped: bool,
attrs: WindowAttributes, attrs: WindowAttributes,
@ -96,8 +99,8 @@ impl WindowData {
) -> Self { ) -> Self {
Self { Self {
window, window,
surface_id: 0,
surface_key: None, surface_key: None,
surface_serial: None,
mapped: false, mapped: false,
attrs: WindowAttributes { attrs: WindowAttributes {
override_redirect, override_redirect,
@ -119,9 +122,12 @@ pub struct SurfaceData {
client: client::wl_surface::WlSurface, client: client::wl_surface::WlSurface,
server: WlSurface, server: WlSurface,
key: ObjectKey, key: ObjectKey,
serial: Option<[u32; 2]>,
frame_callback: Option<WlCallback>, frame_callback: Option<WlCallback>,
attach: Option<SurfaceAttach>, attach: Option<SurfaceAttach>,
role: Option<SurfaceRole>, role: Option<SurfaceRole>,
xwl: Option<XwaylandSurfaceV1>,
window: Option<x::Window>,
} }
impl SurfaceData { impl SurfaceData {
@ -410,6 +416,8 @@ impl<C: XConnection> ServerState<C> {
.registry .registry
.bind::<XdgWmBase, _, _>(data.name, XDG_WM_BASE_VERSION, &qh, ()); .bind::<XdgWmBase, _, _>(data.name, XDG_WM_BASE_VERSION, &qh, ());
dh.create_global::<Self, XwaylandShellV1, _>(1, ());
let mut ret = Self { let mut ret = Self {
windows: HashMap::new(), windows: HashMap::new(),
clientside, clientside,
@ -558,27 +566,9 @@ impl<C: XConnection> ServerState<C> {
} }
} }
pub fn associate_window(&mut self, window: x::Window, surface_id: u32) { pub fn set_window_serial(&mut self, window: x::Window, serial: [u32; 2]) {
let win = self.windows.get_mut(&window).unwrap(); let win = self.windows.get_mut(&window).unwrap();
win.surface_id = surface_id; win.surface_serial = Some(serial);
if let Some(key) = self
.objects
.iter_mut()
.filter_map(|(key, obj)| {
Some(key).zip(<&mut SurfaceData>::try_from(obj.0.as_mut().unwrap()).ok())
})
.find_map(|(key, surface)| {
(surface_id == surface.server.id().protocol_id()).then_some(key)
})
{
win.surface_key = Some(key);
self.associated_windows.insert(key, window);
debug!("associate {:?} with surface {surface_id}", window);
if win.mapped {
self.create_role_window(window, key);
}
}
} }
pub fn reconfigure_window(&mut self, event: x::ConfigureNotifyEvent) { pub fn reconfigure_window(&mut self, event: x::ConfigureNotifyEvent) {
@ -689,7 +679,8 @@ impl<C: XConnection> ServerState<C> {
} }
fn create_role_window(&mut self, window: x::Window, surface_key: ObjectKey) { fn create_role_window(&mut self, window: x::Window, surface_key: ObjectKey) {
let surface: &SurfaceData = self.objects[surface_key].as_ref(); let surface: &mut SurfaceData = self.objects[surface_key].as_mut();
surface.window = Some(window);
let client = &surface.client; let client = &surface.client;
client.attach(None, 0, 0); client.attach(None, 0, 0);
client.commit(); client.commit();
@ -714,7 +705,7 @@ impl<C: XConnection> ServerState<C> {
window.window, window.window,
parent, parent,
window.attrs.dims, window.attrs.dims,
surface.client.id() client.id()
); );
let parent_window = self.windows.get(&parent).unwrap(); let parent_window = self.windows.get(&parent).unwrap();

View file

@ -31,6 +31,9 @@ use wayland_protocols::{
shell::server::{xdg_positioner, xdg_toplevel}, shell::server::{xdg_positioner, xdg_toplevel},
xdg_output::zv1::client::zxdg_output_manager_v1::ZxdgOutputManagerV1, xdg_output::zv1::client::zxdg_output_manager_v1::ZxdgOutputManagerV1,
}, },
xwayland::shell::v1::client::{
xwayland_shell_v1::XwaylandShellV1, xwayland_surface_v1::XwaylandSurfaceV1,
},
}; };
use wayland_server::{protocol as s_proto, Display, Resource}; use wayland_server::{protocol as s_proto, Display, Resource};
use wl_drm::client::wl_drm::WlDrm; use wl_drm::client::wl_drm::WlDrm;
@ -82,6 +85,7 @@ with_optional! {
struct Compositor { struct Compositor {
compositor: TestObject<WlCompositor>, compositor: TestObject<WlCompositor>,
shm: TestObject<WlShm>, shm: TestObject<WlShm>,
shell: TestObject<XwaylandShellV1>
} }
} }
@ -202,6 +206,7 @@ struct TestFixture {
exwl_connection: Arc<Connection>, exwl_connection: Arc<Connection>,
/// Exwayland's display - must dispatch this for our server state to advance /// Exwayland's display - must dispatch this for our server state to advance
exwl_display: Display<FakeServerState>, exwl_display: Display<FakeServerState>,
surface_serial: u64,
} }
static INIT: std::sync::Once = std::sync::Once::new(); static INIT: std::sync::Once = std::sync::Once::new();
@ -240,6 +245,7 @@ impl TestFixture {
exwayland, exwayland,
exwl_connection: Connection::from_socket(fake_client).unwrap().into(), exwl_connection: Connection::from_socket(fake_client).unwrap().into(),
exwl_display: display, exwl_display: display,
surface_serial: 1,
}; };
f.run(); f.run();
f f
@ -291,6 +297,12 @@ impl TestFixture {
bind_req(name, WlShm::interface(), version), bind_req(name, WlShm::interface(), version),
)); ));
} }
x if x == XwaylandShellV1::interface().name => {
ret.shell = Some(TestObject::from_request(
&registry.obj,
bind_req(name, XwaylandShellV1::interface(), version),
));
}
_ => {} _ => {}
} }
} }
@ -339,44 +351,68 @@ impl TestFixture {
.insert(window, data); .insert(window, data);
} }
fn create_and_map_window( fn new_window(
&mut self,
window: Window,
override_redirect: bool,
data: WindowData,
parent: Option<Window>,
) {
let dims = data.dims;
self.register_window(window, data);
self.exwayland
.new_window(window, override_redirect, dims, parent);
}
fn map_window(
&mut self, &mut self,
comp: &Compositor, comp: &Compositor,
window: Window, window: Window,
override_redirect: bool, surface: &WlSurface,
) -> (TestObject<WlSurface>, testwl::SurfaceId) { buffer: &TestObject<WlBuffer>,
let (_buffer, surface) = comp.create_surface(); ) {
self.exwayland.map_window(window);
let data = WindowData { self.associate_window(comp, window, surface);
mapped: true, self.run();
dims: WindowDims { surface
.send_request(Req::<WlSurface>::Attach {
buffer: Some(buffer.obj.clone()),
x: 0, x: 0,
y: 0, y: 0,
width: 50, })
height: 50, .unwrap();
}
fn associate_window(&mut self, comp: &Compositor, window: Window, surface: &WlSurface) {
let xwl = TestObject::<XwaylandSurfaceV1>::from_request(
&comp.shell.obj,
Req::<XwaylandShellV1>::GetXwaylandSurface {
surface: surface.clone(),
}, },
fullscreen: false, );
};
let dims = data.dims; let serial = self.surface_serial;
let serial_lo = (serial & 0xFF) as u32;
let serial_hi = (serial >> 8 & 0xFF) as u32;
self.surface_serial += 1;
self.register_window(window, data); xwl.send_request(Req::<XwaylandSurfaceV1>::SetSerial {
serial_lo,
serial_hi,
})
.unwrap();
self.exwayland self.exwayland
.new_window(window, override_redirect, dims, None); .set_window_serial(window, [serial_lo, serial_hi]);
self.exwayland.map_window(window); }
self.exwayland
.associate_window(window, surface.id().protocol_id());
self.run(); #[track_caller]
fn check_new_surface(&mut self) -> testwl::SurfaceId {
let testwl_id = self let id = self
.testwl .testwl
.last_created_surface_id() .last_created_surface_id()
.expect("Surface not created"); .expect("Surface not created");
assert!(self.testwl.get_surface_data(id).is_some());
assert!(self.testwl.get_surface_data(testwl_id).is_some()); id
(surface, testwl_id)
} }
fn create_toplevel( fn create_toplevel(
@ -384,7 +420,7 @@ impl TestFixture {
comp: &Compositor, comp: &Compositor,
window: Window, window: Window,
) -> (TestObject<WlSurface>, testwl::SurfaceId) { ) -> (TestObject<WlSurface>, testwl::SurfaceId) {
let (_buffer, surface) = comp.create_surface(); let (buffer, surface) = comp.create_surface();
let data = WindowData { let data = WindowData {
mapped: true, mapped: true,
@ -397,27 +433,18 @@ impl TestFixture {
fullscreen: false, fullscreen: false,
}; };
let dims = data.dims; self.new_window(window, false, data, None);
self.map_window(comp, window, &surface.obj, &buffer);
self.register_window(window, data);
self.exwayland.new_window(window, false, dims, None);
self.exwayland.map_window(window);
self.exwayland
.associate_window(window, surface.id().protocol_id());
self.run(); self.run();
let id = self.check_new_surface();
let testwl_id = self
.testwl
.last_created_surface_id()
.expect("Toplevel surface not created");
{ {
let surface_data = self.testwl.get_surface_data(testwl_id).unwrap(); let surface_data = self.testwl.get_surface_data(id).unwrap();
assert!( assert!(
surface_data.surface surface_data.surface
== self == self
.testwl .testwl
.get_object::<s_proto::wl_surface::WlSurface>(testwl_id) .get_object::<s_proto::wl_surface::WlSurface>(id)
.unwrap() .unwrap()
); );
assert!(surface_data.buffer.is_none()); assert!(surface_data.buffer.is_none());
@ -429,11 +456,11 @@ impl TestFixture {
} }
self.testwl self.testwl
.configure_toplevel(testwl_id, 100, 100, vec![xdg_toplevel::State::Activated]); .configure_toplevel(id, 100, 100, vec![xdg_toplevel::State::Activated]);
self.run(); self.run();
{ {
let surface_data = self.testwl.get_surface_data(testwl_id).unwrap(); let surface_data = self.testwl.get_surface_data(id).unwrap();
assert!(surface_data.buffer.is_some()); assert!(surface_data.buffer.is_some());
} }
@ -451,7 +478,7 @@ impl TestFixture {
"Incorrect window geometry: {win_data:?}" "Incorrect window geometry: {win_data:?}"
); );
(surface, testwl_id) (surface, id)
} }
fn create_popup( fn create_popup(
@ -460,7 +487,7 @@ impl TestFixture {
window: Window, window: Window,
parent_id: testwl::SurfaceId, parent_id: testwl::SurfaceId,
) -> (TestObject<WlSurface>, testwl::SurfaceId) { ) -> (TestObject<WlSurface>, testwl::SurfaceId) {
let (_, popup_surface) = comp.create_surface(); let (buffer, surface) = comp.create_surface();
let data = WindowData { let data = WindowData {
mapped: true, mapped: true,
dims: WindowDims { dims: WindowDims {
@ -472,13 +499,11 @@ impl TestFixture {
fullscreen: false, fullscreen: false,
}; };
let dims = data.dims; let dims = data.dims;
self.register_window(window, data); self.new_window(window, true, data, None);
self.exwayland.new_window(window, true, dims, None); self.map_window(&comp, window, &surface.obj, &buffer);
self.exwayland.map_window(window);
self.exwayland
.associate_window(window, popup_surface.id().protocol_id());
self.run(); self.run();
let popup_id = self.testwl.last_created_surface_id().unwrap();
let popup_id = self.check_new_surface();
assert_ne!(popup_id, parent_id); assert_ne!(popup_id, parent_id);
{ {
@ -533,7 +558,7 @@ impl TestFixture {
assert!(surface_data.buffer.is_some()); assert!(surface_data.buffer.is_some());
} }
(popup_surface, popup_id) (surface, popup_id)
} }
} }
@ -597,8 +622,6 @@ type Ev<T> = <T as Proxy>::Event;
// TODO: tests to add // TODO: tests to add
// - destroy window before surface // - destroy window before surface
// - destroy surface before window
// - destroy popup and reassociate with new surface
// - reconfigure window (popup) before mapping // - reconfigure window (popup) before mapping
// - associate window after surface is already created // - associate window after surface is already created
@ -701,7 +724,8 @@ fn pass_through_globals() {
ZxdgOutputManagerV1, ZxdgOutputManagerV1,
WpViewporter, WpViewporter,
WlDrm, WlDrm,
ZwpPointerConstraintsV1 ZwpPointerConstraintsV1,
XwaylandShellV1
} }
let mut globals = SupportedGlobals::default(); let mut globals = SupportedGlobals::default();
@ -777,8 +801,7 @@ fn popup_window_changes_surface() {
assert!(f.testwl.get_surface_data(id).is_some()); assert!(f.testwl.get_surface_data(id).is_some());
f.exwayland.map_window(win); f.exwayland.map_window(win);
f.exwayland f.associate_window(&comp, win, &surface.obj);
.associate_window(win, surface.id().protocol_id());
f.run(); f.run();
let data = f.testwl.get_surface_data(id).unwrap(); let data = f.testwl.get_surface_data(id).unwrap();
@ -807,7 +830,10 @@ fn override_redirect_window_after_toplevel_close() {
assert!(f.testwl.get_surface_data(first).is_none()); assert!(f.testwl.get_surface_data(first).is_none());
let win2 = unsafe { Window::new(2) }; let win2 = unsafe { Window::new(2) };
let (_, second) = f.create_and_map_window(&comp, win2, true); let (buffer, surface) = comp.create_surface();
f.new_window(win2, true, WindowData::default(), None);
f.map_window(&comp, win2, &surface.obj, &buffer);
let second = f.check_new_surface();
let data = f.testwl.get_surface_data(second).unwrap(); let data = f.testwl.get_surface_data(second).unwrap();
assert!( assert!(
matches!(data.role, Some(testwl::SurfaceRole::Toplevel(_))), matches!(data.role, Some(testwl::SurfaceRole::Toplevel(_))),
@ -934,8 +960,7 @@ fn window_group_properties() {
}, },
); );
f.exwayland.map_window(win); f.exwayland.map_window(win);
f.exwayland f.associate_window(&comp, win, &surface.obj);
.associate_window(win, surface.id().protocol_id());
f.run(); f.run();
let id = f.testwl.last_created_surface_id().unwrap(); let id = f.testwl.last_created_surface_id().unwrap();

View file

@ -263,11 +263,13 @@ impl XState {
} }
xcb::Event::X(x::Event::ClientMessage(e)) => match e.r#type() { xcb::Event::X(x::Event::ClientMessage(e)) => match e.r#type() {
x if x == self.atoms.wl_surface_id => { x if x == self.atoms.wl_surface_id => {
panic!("Xserver should be using WL_SURFACE_SERIAL, not WL_SURFACE_ID");
}
x if x == self.atoms.wl_surface_serial => {
let x::ClientMessageData::Data32(data) = e.data() else { let x::ClientMessageData::Data32(data) = e.data() else {
unreachable!(); unreachable!();
}; };
let id: u32 = (data[0] as u64 | ((data[1] as u64) << 32)) as u32; server_state.set_window_serial(e.window(), [data[0], data[1]]);
server_state.associate_window(e.window(), id);
} }
x if x == self.atoms.net_wm_state => { x if x == self.atoms.net_wm_state => {
let x::ClientMessageData::Data32(data) = e.data() else { let x::ClientMessageData::Data32(data) = e.data() else {
@ -544,6 +546,7 @@ xcb::atoms_struct! {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Atoms { pub struct Atoms {
pub wl_surface_id => b"WL_SURFACE_ID" only_if_exists = false, pub wl_surface_id => b"WL_SURFACE_ID" only_if_exists = false,
pub wl_surface_serial => b"WL_SURFACE_SERIAL" only_if_exists = false,
pub wm_protocols => b"WM_PROTOCOLS" only_if_exists = false, pub wm_protocols => b"WM_PROTOCOLS" only_if_exists = false,
pub wm_delete_window => b"WM_DELETE_WINDOW" only_if_exists = false, pub wm_delete_window => b"WM_DELETE_WINDOW" only_if_exists = false,
pub wm_transient_for => b"WM_TRANSIENT_FOR" only_if_exists = false, pub wm_transient_for => b"WM_TRANSIENT_FOR" only_if_exists = false,

View file

@ -300,11 +300,6 @@ impl Connection {
.unwrap(); .unwrap();
} }
fn unmap_window(&self, window: x::Window) {
self.send_and_check_request(&x::UnmapWindow { window })
.unwrap();
}
fn set_property<P: x::PropEl>( fn set_property<P: x::PropEl>(
&self, &self,
window: x::Window, window: x::Window,
@ -404,6 +399,7 @@ fn toplevel_flow() {
); );
f.wait_and_dispatch(); f.wait_and_dispatch();
let data = f.testwl.get_surface_data(surface).unwrap(); let data = f.testwl.get_surface_data(surface).unwrap();
let toplevel = data.toplevel().toplevel.clone();
assert_eq!(data.toplevel().title, Some("bindow".into())); assert_eq!(data.toplevel().title, Some("bindow".into()));
assert_eq!(data.toplevel().app_id, Some("ssalc".into())); assert_eq!(data.toplevel().app_id, Some("ssalc".into()));
assert_eq!( assert_eq!(
@ -421,8 +417,7 @@ fn toplevel_flow() {
drop(connection); drop(connection);
f.wait_and_dispatch(); f.wait_and_dispatch();
let data = f.testwl.get_surface_data(surface).expect("No surface data"); assert!(!toplevel.is_alive());
assert!(!data.toplevel().toplevel.is_alive());
} }
#[test] #[test]