fix(wayland): popups don't crash anymore
This commit is contained in:
4
Cargo.lock
generated
4
Cargo.lock
generated
@@ -1339,7 +1339,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets 0.48.5",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2285,7 +2285,7 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||
[[package]]
|
||||
name = "smithay"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/smithay/smithay.git#f208cd758416e4495e9eb8b27a96c523e92817a6"
|
||||
source = "git+https://github.com/smithay/smithay.git#c6aab182a3c9f106d9c7a0ea34187f90403e59e7"
|
||||
dependencies = [
|
||||
"appendlist",
|
||||
"bitflags 2.6.0",
|
||||
|
||||
@@ -110,12 +110,6 @@ impl<B: Backend> PanelItem<B> {
|
||||
|
||||
(node, panel_item)
|
||||
}
|
||||
pub fn drop_toplevel(&self) {
|
||||
let Some(node) = self.node.upgrade() else {
|
||||
return;
|
||||
};
|
||||
node.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
// Remote signals
|
||||
|
||||
@@ -33,9 +33,6 @@ impl CompositorHandler for WaylandState {
|
||||
|
||||
data.data_map.get::<Arc<CoreSurface>>().cloned()
|
||||
});
|
||||
if let Some(core_surface) = CoreSurface::from_wl_surface(surface) {
|
||||
core_surface.commit(count);
|
||||
}
|
||||
}
|
||||
|
||||
fn client_compositor_state<'a>(&self, client: &'a Client) -> &'a CompositorClientState {
|
||||
|
||||
@@ -39,7 +39,7 @@ impl SeatHandler for WaylandState {
|
||||
self.seat.cursor_info_tx.send_modify(|c| match image {
|
||||
CursorImageStatus::Hidden => c.surface = None,
|
||||
CursorImageStatus::Surface(surface) => {
|
||||
CoreSurface::add_to(&surface, || (), |_| ());
|
||||
CoreSurface::add_to(&surface);
|
||||
compositor::with_states(&surface, |data| {
|
||||
if let Some(core_surface) = data.data_map.get::<Arc<CoreSurface>>() {
|
||||
core_surface.set_material_offset(1);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use super::utils;
|
||||
use super::utils::WlSurfaceExt;
|
||||
use crate::{
|
||||
core::{delta::Delta, destroy_queue, registry::Registry},
|
||||
nodes::{
|
||||
@@ -50,41 +50,24 @@ pub struct CoreSurface {
|
||||
sk_tex: OnceCell<Mutex<TexWrapper>>,
|
||||
sk_mat: OnceCell<Mutex<MaterialWrapper>>,
|
||||
material_offset: Mutex<Delta<u32>>,
|
||||
on_mapped: Mutex<Box<dyn Fn() + Send + Sync>>,
|
||||
on_commit: Mutex<Box<dyn Fn(u32) + Send + Sync>>,
|
||||
pub pending_material_applications: Registry<ModelPart>,
|
||||
}
|
||||
|
||||
impl CoreSurface {
|
||||
pub fn add_to(
|
||||
surface: &WlSurface,
|
||||
on_mapped: impl Fn() + Send + Sync + 'static,
|
||||
on_commit: impl Fn(u32) + Send + Sync + 'static,
|
||||
) {
|
||||
pub fn add_to(surface: &WlSurface) {
|
||||
let core_surface = CORE_SURFACES.add(CoreSurface {
|
||||
weak_surface: surface.downgrade(),
|
||||
mapped_data: Mutex::new(None),
|
||||
sk_tex: OnceCell::new(),
|
||||
sk_mat: OnceCell::new(),
|
||||
material_offset: Mutex::new(Delta::new(0)),
|
||||
on_mapped: Mutex::new(Box::new(on_mapped) as Box<dyn Fn() + Send + Sync>),
|
||||
on_commit: Mutex::new(Box::new(on_commit) as Box<dyn Fn(u32) + Send + Sync>),
|
||||
pending_material_applications: Registry::new(),
|
||||
});
|
||||
utils::insert_data_raw(surface, core_surface);
|
||||
}
|
||||
|
||||
pub fn commit(&self, count: u32) {
|
||||
(*self.on_commit.lock())(count);
|
||||
surface.insert_data(core_surface);
|
||||
}
|
||||
|
||||
pub fn from_wl_surface(surf: &WlSurface) -> Option<Arc<CoreSurface>> {
|
||||
utils::get_data(surf)
|
||||
}
|
||||
|
||||
pub fn decycle(&self) {
|
||||
*self.on_mapped.lock() = Box::new(|| {});
|
||||
*self.on_commit.lock() = Box::new(|_| {});
|
||||
surf.get_data()
|
||||
}
|
||||
|
||||
pub fn process(&self, renderer: &mut GlesRenderer) {
|
||||
@@ -128,14 +111,12 @@ impl CoreSurface {
|
||||
}
|
||||
|
||||
let mut mapped_data = self.mapped_data.lock();
|
||||
let just_mapped = mapped_data.is_none();
|
||||
self.with_states(|data| {
|
||||
let Some(renderer_surface_state) = data
|
||||
.data_map
|
||||
.get::<RendererSurfaceStateUserData>()
|
||||
.map(std::sync::Mutex::lock)
|
||||
.map(Result::ok)
|
||||
.flatten()
|
||||
.and_then(Result::ok)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
@@ -181,9 +162,6 @@ impl CoreSurface {
|
||||
*mapped_data = Some(new_mapped_data);
|
||||
});
|
||||
drop(mapped_data);
|
||||
if just_mapped {
|
||||
(*self.on_mapped.lock())();
|
||||
}
|
||||
self.apply_surface_materials();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
use smithay::{reexports::wayland_server::protocol::wl_surface::WlSurface, wayland::compositor};
|
||||
use std::sync::Arc;
|
||||
|
||||
pub fn insert_data<T: Send + Sync + 'static>(wl_surface: &WlSurface, data: T) {
|
||||
insert_data_raw(wl_surface, Arc::new(data))
|
||||
pub trait WlSurfaceExt {
|
||||
fn insert_data<T: Send + Sync + 'static>(&self, data: T) -> bool;
|
||||
fn get_data<T: Send + Sync + Clone + 'static>(&self) -> Option<T>;
|
||||
fn get_data_raw<T: Send + Sync + 'static, O, F: FnOnce(&T) -> O>(&self, f: F) -> Option<O>;
|
||||
}
|
||||
pub fn insert_data_raw<T: Send + Sync + 'static>(wl_surface: &WlSurface, data: Arc<T>) {
|
||||
compositor::with_states(wl_surface, |d| {
|
||||
impl WlSurfaceExt for WlSurface {
|
||||
fn insert_data<T: Send + Sync + 'static>(&self, data: T) -> bool {
|
||||
compositor::with_states(self, |d| {
|
||||
d.data_map.insert_if_missing_threadsafe(move || data)
|
||||
});
|
||||
})
|
||||
}
|
||||
fn get_data<T: Send + Sync + Clone + 'static>(&self) -> Option<T> {
|
||||
compositor::with_states(self, |d| d.data_map.get::<T>().cloned())
|
||||
}
|
||||
fn get_data_raw<T: Send + Sync + 'static, O, F: FnOnce(&T) -> O>(&self, f: F) -> Option<O> {
|
||||
compositor::with_states(self, |d| Some((f)(d.data_map.get::<T>()?)))
|
||||
}
|
||||
pub fn get_data<T: Send + Sync + 'static>(wl_surface: &WlSurface) -> Option<Arc<T>> {
|
||||
compositor::with_states(wl_surface, |d| d.data_map.get::<Arc<T>>().cloned())
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use super::{
|
||||
seat::{handle_cursor, SeatWrapper},
|
||||
state::{ClientState, WaylandState},
|
||||
surface::CoreSurface,
|
||||
utils,
|
||||
utils::WlSurfaceExt,
|
||||
};
|
||||
use crate::nodes::{
|
||||
drawable::model::ModelPart,
|
||||
@@ -29,7 +29,7 @@ use smithay::{
|
||||
},
|
||||
utils::{Logical, Rectangle, Serial},
|
||||
wayland::{
|
||||
compositor,
|
||||
compositor::{self, add_post_commit_hook},
|
||||
shell::xdg::{
|
||||
PopupSurface, PositionerState, ShellClient, SurfaceCachedState, ToplevelSurface,
|
||||
XdgShellHandler, XdgShellState, XdgToplevelSurfaceData,
|
||||
@@ -49,20 +49,12 @@ impl From<Rectangle<i32, Logical>> for Geometry {
|
||||
}
|
||||
|
||||
fn surface_panel_item(wl_surface: &WlSurface) -> Option<Arc<PanelItem<XdgBackend>>> {
|
||||
let panel_item = utils::get_data::<Weak<PanelItem<XdgBackend>>>(wl_surface)
|
||||
.as_deref()
|
||||
let panel_item = wl_surface
|
||||
.get_data::<Weak<PanelItem<XdgBackend>>>()
|
||||
.as_ref()
|
||||
.and_then(Weak::upgrade);
|
||||
if panel_item.is_none() {
|
||||
warn!("Couldn't get panel item");
|
||||
// println!("panel item not found at \n{}\n\n", {
|
||||
// let backtrace = Backtrace::force_capture().to_string();
|
||||
// let mut split_backtrace = backtrace
|
||||
// .split('\n')
|
||||
// .map(|s| s.to_string())
|
||||
// .collect::<Vec<_>>();
|
||||
// split_backtrace.resize(4, "".to_string());
|
||||
// split_backtrace.join("\n")
|
||||
// });
|
||||
}
|
||||
panel_item
|
||||
}
|
||||
@@ -76,6 +68,7 @@ impl XdgShellHandler for WaylandState {
|
||||
fn client_destroyed(&mut self, _client: ShellClient) {}
|
||||
|
||||
fn new_toplevel(&mut self, toplevel: ToplevelSurface) {
|
||||
toplevel.wl_surface().insert_data(SurfaceId::Toplevel(()));
|
||||
toplevel.with_pending_state(|s| {
|
||||
s.decoration_mode = Some(Mode::ServerSide);
|
||||
s.states.set(State::TiledTop);
|
||||
@@ -86,15 +79,27 @@ impl XdgShellHandler for WaylandState {
|
||||
s.states.unset(State::Fullscreen);
|
||||
});
|
||||
toplevel.send_configure();
|
||||
utils::insert_data(toplevel.wl_surface(), SurfaceId::Toplevel);
|
||||
utils::insert_data(toplevel.wl_surface(), Mutex::new(Vector2::from([0_u32; 2])));
|
||||
CoreSurface::add_to(
|
||||
toplevel
|
||||
.wl_surface()
|
||||
.insert_data(Mutex::new(Vector2::from([0_u32; 2])));
|
||||
|
||||
CoreSurface::add_to(toplevel.wl_surface());
|
||||
add_post_commit_hook(
|
||||
toplevel.wl_surface(),
|
||||
{
|
||||
let toplevel = toplevel.clone();
|
||||
move || {
|
||||
let wl_surface = toplevel.wl_surface().client().unwrap();
|
||||
let client_state = wl_surface.get_data::<ClientState>().unwrap();
|
||||
|state: &mut WaylandState, _dh, surf| {
|
||||
if surface_panel_item(surf).is_some() {
|
||||
return;
|
||||
}
|
||||
let client = surf.client().unwrap();
|
||||
let client_state = client.get_data::<ClientState>().unwrap();
|
||||
let Some(toplevel) = state
|
||||
.xdg_shell
|
||||
.toplevel_surfaces()
|
||||
.iter()
|
||||
.find(|s| s.wl_surface() == surf)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let (node, panel_item) = PanelItem::create(
|
||||
Box::new(XdgBackend::create(
|
||||
toplevel.clone(),
|
||||
@@ -103,27 +108,21 @@ impl XdgShellHandler for WaylandState {
|
||||
client_state.pid,
|
||||
);
|
||||
handle_cursor(&panel_item, panel_item.backend.seat.cursor_info_rx.clone());
|
||||
utils::insert_data(toplevel.wl_surface(), Arc::downgrade(&panel_item));
|
||||
utils::insert_data_raw(toplevel.wl_surface(), node);
|
||||
}
|
||||
surf.insert_data(Arc::downgrade(&panel_item));
|
||||
surf.insert_data(node);
|
||||
},
|
||||
{
|
||||
let toplevel = toplevel.clone();
|
||||
move |_c| {
|
||||
let Some(panel_item) = surface_panel_item(toplevel.wl_surface()) else {
|
||||
// if the wayland toplevel isn't mapped, hammer it again with a configure until it cooperates
|
||||
toplevel.send_configure();
|
||||
);
|
||||
|
||||
add_post_commit_hook(
|
||||
toplevel.wl_surface(),
|
||||
|_state: &mut WaylandState, _dh, surf| {
|
||||
let Some(panel_item) = surface_panel_item(surf) else {
|
||||
return;
|
||||
};
|
||||
let Some(core_surface) = CoreSurface::from_wl_surface(toplevel.wl_surface())
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let Some(old_size) =
|
||||
utils::get_data::<Mutex<Vector2<u32>>>(toplevel.wl_surface())
|
||||
else {
|
||||
let Some(core_surface) = CoreSurface::from_wl_surface(surf) else {
|
||||
return;
|
||||
};
|
||||
surf.get_data_raw::<Mutex<Vector2<u32>>, _, _>(|old_size| {
|
||||
let mut old_size = old_size.lock();
|
||||
let Some(size) = core_surface.size() else {
|
||||
return;
|
||||
@@ -132,94 +131,79 @@ impl XdgShellHandler for WaylandState {
|
||||
panel_item.toplevel_size_changed(size);
|
||||
*old_size = size;
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
fn toplevel_destroyed(&mut self, toplevel: ToplevelSurface) {
|
||||
if let Some(core_surface) = CoreSurface::from_wl_surface(toplevel.wl_surface()) {
|
||||
core_surface.decycle();
|
||||
}
|
||||
let Some(panel_item) = surface_panel_item(toplevel.wl_surface()) else {
|
||||
return;
|
||||
};
|
||||
panel_item.backend.seat.unfocus(toplevel.wl_surface(), self);
|
||||
panel_item.backend.toplevel.lock().take();
|
||||
panel_item.backend.popups.lock().clear();
|
||||
panel_item.drop_toplevel();
|
||||
// println!(
|
||||
// "Dropping toplevel resulted in {} references",
|
||||
// Arc::strong_count(&panel_item)
|
||||
// );
|
||||
}
|
||||
fn app_id_changed(&mut self, toplevel: ToplevelSurface) {
|
||||
let Some(panel_item) = surface_panel_item(toplevel.wl_surface()) else {
|
||||
return;
|
||||
};
|
||||
|
||||
panel_item.toplevel_app_id_changed(&compositor::with_states(
|
||||
toplevel.wl_surface(),
|
||||
|states| {
|
||||
states
|
||||
.data_map
|
||||
.get::<XdgToplevelSurfaceData>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.app_id
|
||||
.clone()
|
||||
.unwrap()
|
||||
},
|
||||
))
|
||||
panel_item.toplevel_app_id_changed(
|
||||
&toplevel
|
||||
.wl_surface()
|
||||
.get_data_raw::<XdgToplevelSurfaceData, _, _>(|d| {
|
||||
d.lock().unwrap().app_id.clone().unwrap()
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
}
|
||||
fn title_changed(&mut self, toplevel: ToplevelSurface) {
|
||||
let Some(panel_item) = surface_panel_item(toplevel.wl_surface()) else {
|
||||
return;
|
||||
};
|
||||
|
||||
panel_item.toplevel_title_changed(&compositor::with_states(
|
||||
toplevel.wl_surface(),
|
||||
|states| {
|
||||
states
|
||||
.data_map
|
||||
.get::<XdgToplevelSurfaceData>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.title
|
||||
.clone()
|
||||
.unwrap()
|
||||
},
|
||||
))
|
||||
panel_item.toplevel_title_changed(
|
||||
&toplevel
|
||||
.wl_surface()
|
||||
.get_data_raw::<XdgToplevelSurfaceData, _, _>(|d| {
|
||||
d.lock().unwrap().title.clone().unwrap()
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
}
|
||||
|
||||
fn new_popup(&mut self, popup: PopupSurface, positioner: PositionerState) {
|
||||
let uid = rand::thread_rng().gen_range(0..u64::MAX);
|
||||
popup.wl_surface().insert_data(SurfaceId::Child(uid));
|
||||
let Some(parent) = popup.get_parent_surface() else {
|
||||
return;
|
||||
};
|
||||
let _ = popup.send_configure();
|
||||
CoreSurface::add_to(popup.wl_surface());
|
||||
|
||||
let Some(panel_item) = surface_panel_item(&parent) else {
|
||||
return;
|
||||
};
|
||||
let _ = popup.send_configure();
|
||||
utils::insert_data(popup.wl_surface(), SurfaceId::Child(uid));
|
||||
utils::insert_data(popup.wl_surface(), Arc::downgrade(&panel_item));
|
||||
CoreSurface::add_to(
|
||||
let panel_item_weak = Arc::downgrade(&panel_item);
|
||||
add_post_commit_hook(
|
||||
popup.wl_surface(),
|
||||
{
|
||||
let popup = popup.clone();
|
||||
move || {
|
||||
panel_item.backend.new_popup(uid, popup.clone(), positioner);
|
||||
move |state: &mut WaylandState, _dh, surf| {
|
||||
if surface_panel_item(surf).is_some() {
|
||||
return;
|
||||
}
|
||||
},
|
||||
{
|
||||
let popup = popup.clone();
|
||||
move |_c| {
|
||||
if surface_panel_item(popup.wl_surface()).is_none() {
|
||||
// if the popup toplevel isn't mapped, hammer it again with a configure until it cooperates
|
||||
let _ = popup.send_configure();
|
||||
surf.insert_data(panel_item_weak.clone());
|
||||
let Some(panel) = surface_panel_item(surf) else {
|
||||
return;
|
||||
};
|
||||
}
|
||||
let Some(popup) = state
|
||||
.xdg_shell
|
||||
.popup_surfaces()
|
||||
.iter()
|
||||
.find(|p| p.wl_surface() == surf)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
panel.backend.new_popup(uid, popup.clone(), positioner);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -232,26 +216,17 @@ impl XdgShellHandler for WaylandState {
|
||||
let Some(panel_item) = surface_panel_item(popup.wl_surface()) else {
|
||||
return;
|
||||
};
|
||||
let Some(SurfaceId::Child(uid)) = utils::get_data::<SurfaceId>(popup.wl_surface())
|
||||
.as_deref()
|
||||
.cloned()
|
||||
else {
|
||||
let Some(SurfaceId::Child(uid)) = popup.wl_surface().get_data::<SurfaceId>() else {
|
||||
return;
|
||||
};
|
||||
|
||||
panel_item.backend.reposition_popup(uid, popup, positioner)
|
||||
}
|
||||
fn popup_destroyed(&mut self, popup: PopupSurface) {
|
||||
if let Some(core_surface) = CoreSurface::from_wl_surface(popup.wl_surface()) {
|
||||
core_surface.decycle();
|
||||
}
|
||||
let Some(panel_item) = surface_panel_item(popup.wl_surface()) else {
|
||||
return;
|
||||
};
|
||||
let Some(SurfaceId::Child(uid)) = utils::get_data::<SurfaceId>(popup.wl_surface())
|
||||
.as_deref()
|
||||
.cloned()
|
||||
else {
|
||||
let Some(SurfaceId::Child(uid)) = popup.wl_surface().get_data::<SurfaceId>() else {
|
||||
return;
|
||||
};
|
||||
panel_item.backend.seat.unfocus(popup.wl_surface(), self);
|
||||
@@ -375,11 +350,9 @@ impl XdgBackend {
|
||||
}
|
||||
|
||||
fn child_data(&self, id: u64) -> Option<ChildInfo> {
|
||||
let (popup, positioner) = self.popups.lock().get(&id)?.clone();
|
||||
let parent_surface = popup.get_parent_surface()?;
|
||||
let parent = utils::get_data::<SurfaceId>(&parent_surface)?
|
||||
.as_ref()
|
||||
.clone();
|
||||
let (popup, positioner) = self.popups.lock().get(&id).unwrap().clone();
|
||||
let parent = popup.get_parent_surface().unwrap();
|
||||
let parent = parent.get_data::<SurfaceId>().unwrap();
|
||||
Some(ChildInfo {
|
||||
id,
|
||||
parent,
|
||||
@@ -426,11 +399,7 @@ impl Backend for XdgBackend {
|
||||
.clone()
|
||||
});
|
||||
let toplevel_cached_state = compositor::with_states(toplevel.wl_surface(), |states| {
|
||||
states
|
||||
.cached_state
|
||||
.get::<SurfaceCachedState>()
|
||||
.current()
|
||||
.clone()
|
||||
*states.cached_state.get::<SurfaceCachedState>().current()
|
||||
});
|
||||
let toplevel_core_surface = CoreSurface::from_wl_surface(toplevel.wl_surface()).unwrap();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user