feat(wayland): popups, more compatibility, more stability
get_parent grab popups fix head thingy popup list feat: remove set_active feat(wayland): commit_popup feat(wayland): cleanup moar changess actually fix the problem with everything oh my god proper popup state fix: multi thread event loop fix: match popup surface ID make wayland input system go over surfaces instead of toplevels feat: massive refactor of all wayland things
This commit is contained in:
@@ -1,29 +1,34 @@
|
||||
use crate::nodes::Node;
|
||||
|
||||
use super::{
|
||||
panel_item::{PanelItem, RecommendedState, ToplevelState},
|
||||
panel_item::{PanelItem, RecommendedState, SurfaceID},
|
||||
state::WaylandState,
|
||||
surface::SurfaceGeometry,
|
||||
surface::CoreSurface,
|
||||
SERIAL_COUNTER,
|
||||
};
|
||||
use mint::Vector2;
|
||||
use nanoid::nanoid;
|
||||
use parking_lot::Mutex;
|
||||
use serde::Serialize;
|
||||
use smithay::{
|
||||
reexports::{
|
||||
wayland_protocols::xdg::shell::server::{
|
||||
xdg_popup::{self, XdgPopup},
|
||||
xdg_positioner::{self, Anchor, ConstraintAdjustment, Gravity, XdgPositioner},
|
||||
xdg_surface::{self, XdgSurface},
|
||||
xdg_toplevel::{self, XdgToplevel, EVT_WM_CAPABILITIES_SINCE},
|
||||
xdg_wm_base::{self, XdgWmBase},
|
||||
},
|
||||
wayland_server::{
|
||||
protocol::wl_surface::WlSurface, Client, DataInit, Dispatch, DisplayHandle,
|
||||
GlobalDispatch, New, Resource, WEnum, Weak,
|
||||
},
|
||||
use serde::{ser::SerializeSeq, Serialize, Serializer};
|
||||
use smithay::reexports::{
|
||||
wayland_protocols::xdg::shell::server::{
|
||||
xdg_popup::{self, XdgPopup},
|
||||
xdg_positioner::{self, Anchor, ConstraintAdjustment, Gravity, XdgPositioner},
|
||||
xdg_surface::{self, XdgSurface},
|
||||
xdg_toplevel::{self, XdgToplevel, EVT_WM_CAPABILITIES_SINCE},
|
||||
xdg_wm_base::{self, XdgWmBase},
|
||||
},
|
||||
wayland_server::{
|
||||
backend::{ClientId, ObjectId},
|
||||
protocol::wl_surface::WlSurface,
|
||||
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, WEnum,
|
||||
Weak as WlWeak,
|
||||
},
|
||||
wayland::compositor,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
sync::{Arc, Weak},
|
||||
};
|
||||
use tracing::{debug, warn};
|
||||
|
||||
impl GlobalDispatch<XdgWmBase, (), WaylandState> for WaylandState {
|
||||
@@ -38,11 +43,6 @@ impl GlobalDispatch<XdgWmBase, (), WaylandState> for WaylandState {
|
||||
data_init.init(resource, ());
|
||||
}
|
||||
}
|
||||
#[derive(Debug)]
|
||||
pub struct WaylandSurface {
|
||||
wl_surface: Weak<WlSurface>,
|
||||
geometry: Arc<Mutex<Option<SurfaceGeometry>>>,
|
||||
}
|
||||
|
||||
impl Dispatch<XdgWmBase, (), WaylandState> for WaylandState {
|
||||
fn request(
|
||||
@@ -56,18 +56,11 @@ impl Dispatch<XdgWmBase, (), WaylandState> for WaylandState {
|
||||
) {
|
||||
match request {
|
||||
xdg_wm_base::Request::CreatePositioner { id } => {
|
||||
let positioner =
|
||||
data_init.init(id, Arc::new(Mutex::new(PositionerData::default())));
|
||||
let positioner = data_init.init(id, Mutex::new(PositionerData::default()));
|
||||
debug!(?positioner, "Create XDG positioner");
|
||||
}
|
||||
xdg_wm_base::Request::GetXdgSurface { id, surface } => {
|
||||
let xdg_surface = data_init.init(
|
||||
id,
|
||||
WaylandSurface {
|
||||
wl_surface: surface.downgrade(),
|
||||
geometry: Arc::new(Mutex::new(None)),
|
||||
},
|
||||
);
|
||||
let xdg_surface = data_init.init(id, Mutex::new(XdgSurfaceData::new(&surface)));
|
||||
debug!(?xdg_surface, "Create XDG surface");
|
||||
}
|
||||
xdg_wm_base::Request::Pong { serial } => {
|
||||
@@ -81,7 +74,7 @@ impl Dispatch<XdgWmBase, (), WaylandState> for WaylandState {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[derive(Debug, Serialize, Clone, Copy)]
|
||||
pub struct PositionerData {
|
||||
size: Vector2<u32>,
|
||||
anchor_rect_pos: Vector2<i32>,
|
||||
@@ -107,13 +100,13 @@ impl Default for PositionerData {
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<XdgPositioner, Arc<Mutex<PositionerData>>, WaylandState> for WaylandState {
|
||||
impl Dispatch<XdgPositioner, Mutex<PositionerData>, WaylandState> for WaylandState {
|
||||
fn request(
|
||||
_state: &mut WaylandState,
|
||||
_client: &Client,
|
||||
positioner: &XdgPositioner,
|
||||
request: xdg_positioner::Request,
|
||||
data: &Arc<Mutex<PositionerData>>,
|
||||
data: &Mutex<PositionerData>,
|
||||
_dhandle: &DisplayHandle,
|
||||
_data_init: &mut DataInit<'_, WaylandState>,
|
||||
) {
|
||||
@@ -195,39 +188,69 @@ impl Dispatch<XdgPositioner, Arc<Mutex<PositionerData>>, WaylandState> for Wayla
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct XdgSurfaceData {
|
||||
pub wl_surface: Weak<WlSurface>,
|
||||
pub xdg_surface: Weak<XdgSurface>,
|
||||
pub geometry: Arc<Mutex<Option<SurfaceGeometry>>>,
|
||||
#[derive(Debug, Serialize, Clone, Copy)]
|
||||
pub struct Geometry {
|
||||
pub origin: Vector2<i32>,
|
||||
pub size: Vector2<u32>,
|
||||
}
|
||||
impl Dispatch<XdgSurface, WaylandSurface, WaylandState> for WaylandState {
|
||||
impl Default for Geometry {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
origin: Vector2::from([0; 2]),
|
||||
size: Vector2::from([0; 2]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct XdgSurfaceData {
|
||||
wl_surface: WlWeak<WlSurface>,
|
||||
surface_id: SurfaceID,
|
||||
panel_item: Weak<PanelItem>,
|
||||
geometry: Option<Geometry>,
|
||||
}
|
||||
impl XdgSurfaceData {
|
||||
pub fn new(wl_surface: &WlSurface) -> Self {
|
||||
XdgSurfaceData {
|
||||
wl_surface: wl_surface.downgrade(),
|
||||
surface_id: SurfaceID::Toplevel,
|
||||
panel_item: Weak::new(),
|
||||
geometry: None,
|
||||
}
|
||||
}
|
||||
pub fn get(xdg_surface: &XdgSurface) -> &Mutex<Self> {
|
||||
xdg_surface.data::<Mutex<Self>>().unwrap()
|
||||
}
|
||||
pub fn wl_surface(&self) -> WlSurface {
|
||||
self.wl_surface.upgrade().unwrap()
|
||||
}
|
||||
pub fn panel_item(&self) -> Option<Arc<PanelItem>> {
|
||||
self.panel_item.upgrade()
|
||||
}
|
||||
}
|
||||
// impl Clone for XdgSurfaceData {
|
||||
// fn clone(&self) -> Self {
|
||||
// Self {
|
||||
// wl_surface: self.wl_surface.clone(),
|
||||
// geometry: self.geometry.clone(),
|
||||
// surface_type: Mutex::new(self.surface_type.lock().clone()),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
impl Dispatch<XdgSurface, Mutex<XdgSurfaceData>, WaylandState> for WaylandState {
|
||||
fn request(
|
||||
state: &mut WaylandState,
|
||||
client: &Client,
|
||||
xdg_surface: &XdgSurface,
|
||||
request: xdg_surface::Request,
|
||||
data: &WaylandSurface,
|
||||
xdg_surface_data: &Mutex<XdgSurfaceData>,
|
||||
_dhandle: &DisplayHandle,
|
||||
data_init: &mut DataInit<'_, WaylandState>,
|
||||
) {
|
||||
match request {
|
||||
xdg_surface::Request::GetToplevel { id } => {
|
||||
let toplevel_state = Arc::new(Mutex::new(ToplevelState {
|
||||
queued_state: Some(Box::new(ToplevelState::default())),
|
||||
..Default::default()
|
||||
}));
|
||||
let toplevel = data_init.init(
|
||||
id,
|
||||
XdgToplevelData {
|
||||
state: toplevel_state,
|
||||
xdg_surface_data: XdgSurfaceData {
|
||||
wl_surface: data.wl_surface.clone(),
|
||||
xdg_surface: xdg_surface.downgrade(),
|
||||
geometry: data.geometry.clone(),
|
||||
},
|
||||
},
|
||||
);
|
||||
let toplevel_state = Mutex::new(ToplevelData::new(xdg_surface));
|
||||
let toplevel = data_init.init(id, toplevel_state);
|
||||
debug!(?toplevel, ?xdg_surface, "Create XDG toplevel");
|
||||
|
||||
if toplevel.version() >= EVT_WM_CAPABILITIES_SINCE {
|
||||
@@ -236,25 +259,99 @@ impl Dispatch<XdgSurface, WaylandSurface, WaylandState> for WaylandState {
|
||||
toplevel.configure(0, 0, vec![]);
|
||||
xdg_surface.configure(SERIAL_COUNTER.inc());
|
||||
|
||||
let (node, item) = PanelItem::create(
|
||||
toplevel,
|
||||
data.wl_surface.upgrade().unwrap(),
|
||||
client.get_credentials(&state.display_handle).ok(),
|
||||
state.seats.get(&client.id()).unwrap().clone(),
|
||||
let client_credentials = client.get_credentials(&state.display_handle).ok();
|
||||
let seat_data = state.seats.get(&client.id()).unwrap().clone();
|
||||
let toplevel_weak = toplevel.downgrade();
|
||||
CoreSurface::add_to(
|
||||
&state.display,
|
||||
state.display_handle.clone(),
|
||||
&xdg_surface_data.lock().wl_surface(),
|
||||
move |c| match c {
|
||||
0 => {
|
||||
let toplevel = toplevel_weak.upgrade().unwrap();
|
||||
let toplevel_data = ToplevelData::get(&toplevel);
|
||||
let xdg_surface = toplevel_data.lock().xdg_surface();
|
||||
let xdg_surface_data = XdgSurfaceData::get(&xdg_surface);
|
||||
let wl_surface = xdg_surface_data.lock().wl_surface();
|
||||
|
||||
xdg_surface_data.lock().surface_id = SurfaceID::Toplevel;
|
||||
let (node, panel_item) = PanelItem::create(
|
||||
toplevel.clone(),
|
||||
wl_surface.clone(),
|
||||
client_credentials,
|
||||
seat_data.clone(),
|
||||
);
|
||||
toplevel_data.lock().panel_item_node.replace(node);
|
||||
xdg_surface_data.lock().panel_item = Arc::downgrade(&panel_item);
|
||||
}
|
||||
_ => {
|
||||
let toplevel = toplevel_weak.upgrade().unwrap();
|
||||
let toplevel_data = ToplevelData::get(&toplevel);
|
||||
let panel_item = toplevel_data.lock().panel_item().unwrap();
|
||||
panel_item.commit_toplevel();
|
||||
}
|
||||
},
|
||||
);
|
||||
compositor::with_states(&data.wl_surface.upgrade().unwrap(), |surface_data| {
|
||||
surface_data.data_map.insert_if_missing_threadsafe(|| node);
|
||||
surface_data.data_map.insert_if_missing_threadsafe(|| item);
|
||||
});
|
||||
}
|
||||
xdg_surface::Request::GetPopup {
|
||||
id,
|
||||
parent: _,
|
||||
positioner: _,
|
||||
parent,
|
||||
positioner,
|
||||
} => {
|
||||
let popup = data_init.init(id, ());
|
||||
debug!(?popup, ?xdg_surface, "Create XDG popup");
|
||||
popup.popup_done(); // temporary hack to avoid apps locking up before popups are implemented
|
||||
let parent_clone = parent.clone().unwrap();
|
||||
let parent_data = parent_clone.data::<Mutex<XdgSurfaceData>>().unwrap().lock();
|
||||
// let positioner_data = positioner
|
||||
// .data::<Mutex<PositionerData>>()
|
||||
// .unwrap()
|
||||
// .lock()
|
||||
// .clone();
|
||||
// let parent = match &*parent_data {
|
||||
// XdgSurfaceType::Toplevel(_) => SurfaceID::Toplevel,
|
||||
// XdgSurfaceType::Popup(p) => {
|
||||
// SurfaceID::Popup(p.upgrade().unwrap().uid.clone())
|
||||
// }
|
||||
// XdgSurfaceType::Unknown => return,
|
||||
// };
|
||||
let uid = nanoid!();
|
||||
let popup_data = Mutex::new(PopupData::new(
|
||||
uid.clone(),
|
||||
xdg_surface,
|
||||
parent_data.surface_id.clone(),
|
||||
positioner,
|
||||
));
|
||||
let xdg_popup = data_init.init(id, popup_data);
|
||||
xdg_surface_data.lock().surface_id = SurfaceID::Popup(uid);
|
||||
let panel_item = parent_data.panel_item().unwrap();
|
||||
xdg_surface_data.lock().panel_item = Arc::downgrade(&panel_item);
|
||||
|
||||
panel_item.seat_data.new_surface(
|
||||
&xdg_surface_data.lock().wl_surface(),
|
||||
Arc::downgrade(&panel_item),
|
||||
);
|
||||
debug!(?xdg_popup, ?xdg_surface, "Create XDG popup");
|
||||
|
||||
let xdg_surface = xdg_surface.downgrade();
|
||||
let xdg_popup = xdg_popup.downgrade();
|
||||
CoreSurface::add_to(
|
||||
&state.display,
|
||||
state.display_handle.clone(),
|
||||
&xdg_surface_data.lock().wl_surface.upgrade().unwrap(),
|
||||
move |commit_count| match commit_count {
|
||||
0 => xdg_surface
|
||||
.upgrade()
|
||||
.unwrap()
|
||||
.configure(SERIAL_COUNTER.inc()),
|
||||
c => {
|
||||
let xdg_popup = xdg_popup.upgrade().unwrap();
|
||||
let popup_data = PopupData::get(&xdg_popup);
|
||||
let popup_data = popup_data.lock();
|
||||
// panel_item.commit_popup(popup_data);
|
||||
if c == 1 {
|
||||
panel_item.new_popup(&xdg_popup, &*popup_data);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
xdg_surface::Request::SetWindowGeometry {
|
||||
x,
|
||||
@@ -266,18 +363,11 @@ impl Dispatch<XdgSurface, WaylandSurface, WaylandState> for WaylandState {
|
||||
?xdg_surface,
|
||||
x, y, width, height, "Set XDG surface geometry"
|
||||
);
|
||||
let geometry = SurfaceGeometry {
|
||||
origin: [x as u32, y as u32].into(),
|
||||
let geometry = Geometry {
|
||||
origin: [x, y].into(),
|
||||
size: [width as u32, height as u32].into(),
|
||||
};
|
||||
*data.geometry.lock() = Some(geometry);
|
||||
let Ok(wl_surface) = data.wl_surface.upgrade() else { return; };
|
||||
compositor::with_states(&wl_surface, |data| {
|
||||
// if let Some(core_surface) = data.data_map.get::<Arc<CoreSurface>>() {
|
||||
// core_surface.set_geometry(geometry);
|
||||
// }
|
||||
data.data_map.insert_if_missing_threadsafe(|| geometry);
|
||||
});
|
||||
xdg_surface_data.lock().geometry.replace(geometry);
|
||||
}
|
||||
xdg_surface::Request::AckConfigure { serial } => {
|
||||
debug!(?xdg_surface, serial, "Acknowledge XDG surface configure");
|
||||
@@ -290,47 +380,104 @@ impl Dispatch<XdgSurface, WaylandSurface, WaylandState> for WaylandState {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct XdgToplevelData {
|
||||
pub state: Arc<Mutex<ToplevelState>>,
|
||||
pub xdg_surface_data: XdgSurfaceData,
|
||||
fn serde_error<S: Serializer>(msg: &str) -> Result<S::Ok, S::Error> {
|
||||
Err(serde::ser::Error::custom(msg))
|
||||
}
|
||||
impl XdgToplevelData {
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ToplevelData {
|
||||
panel_item_node: Option<Arc<Node>>,
|
||||
xdg_surface: WlWeak<XdgSurface>,
|
||||
parent: Option<WlWeak<XdgToplevel>>,
|
||||
title: Option<String>,
|
||||
app_id: Option<String>,
|
||||
max_size: Option<Vector2<u32>>,
|
||||
min_size: Option<Vector2<u32>>,
|
||||
states: Vec<u32>,
|
||||
}
|
||||
impl ToplevelData {
|
||||
fn new(xdg_surface: &XdgSurface) -> Self {
|
||||
ToplevelData {
|
||||
panel_item_node: None,
|
||||
xdg_surface: xdg_surface.downgrade(),
|
||||
parent: None,
|
||||
title: None,
|
||||
app_id: None,
|
||||
max_size: None,
|
||||
min_size: None,
|
||||
states: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(toplevel: &XdgToplevel) -> &Mutex<ToplevelData> {
|
||||
toplevel.data::<Mutex<ToplevelData>>().unwrap()
|
||||
}
|
||||
|
||||
pub fn xdg_surface(&self) -> XdgSurface {
|
||||
self.xdg_surface.upgrade().unwrap()
|
||||
}
|
||||
fn panel_item(&self) -> Option<Arc<PanelItem>> {
|
||||
let wl_surface = self.xdg_surface_data.wl_surface.upgrade().ok()?;
|
||||
compositor::with_states(&wl_surface, |data| {
|
||||
data.data_map.get::<Arc<PanelItem>>().cloned()
|
||||
})
|
||||
let xdg_surface = self.xdg_surface();
|
||||
let xdg_surface_data = XdgSurfaceData::get(&xdg_surface).lock();
|
||||
xdg_surface_data.panel_item()
|
||||
}
|
||||
}
|
||||
impl Dispatch<XdgToplevel, XdgToplevelData, WaylandState> for WaylandState {
|
||||
impl Serialize for ToplevelData {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let xdg_surface = self.xdg_surface();
|
||||
let xdg_surface_data = XdgSurfaceData::get(&xdg_surface).lock();
|
||||
let geometry = xdg_surface_data.geometry.clone();
|
||||
let wl_surface = xdg_surface_data.wl_surface();
|
||||
let Some(core_surface) = CoreSurface::from_wl_surface(&wl_surface) else {return serde_error::<S>("Core surface not found")};
|
||||
let Some(size) = core_surface.size() else {return serializer.serialize_none()};
|
||||
let geometry = geometry.unwrap_or_else(|| Geometry {
|
||||
origin: [0; 2].into(),
|
||||
size,
|
||||
});
|
||||
|
||||
let mut seq = serializer.serialize_seq(None)?;
|
||||
// Parent UID
|
||||
seq.serialize_element(&self.parent.as_ref().and_then(|p| {
|
||||
Some(
|
||||
ToplevelData::get(&p.upgrade().ok()?)
|
||||
.lock()
|
||||
.panel_item()?
|
||||
.uid
|
||||
.clone(),
|
||||
)
|
||||
}))?;
|
||||
seq.serialize_element(&self.title)?;
|
||||
seq.serialize_element(&self.app_id)?;
|
||||
seq.serialize_element(&size)?;
|
||||
seq.serialize_element(&self.min_size)?;
|
||||
seq.serialize_element(&self.max_size)?;
|
||||
seq.serialize_element(&geometry)?;
|
||||
seq.serialize_element(&self.states)?;
|
||||
seq.end()
|
||||
}
|
||||
}
|
||||
impl Dispatch<XdgToplevel, Mutex<ToplevelData>, WaylandState> for WaylandState {
|
||||
fn request(
|
||||
_state: &mut WaylandState,
|
||||
_client: &Client,
|
||||
xdg_toplevel: &XdgToplevel,
|
||||
request: xdg_toplevel::Request,
|
||||
data: &XdgToplevelData,
|
||||
data: &Mutex<ToplevelData>,
|
||||
_dhandle: &DisplayHandle,
|
||||
_data_init: &mut DataInit<'_, WaylandState>,
|
||||
) {
|
||||
match request {
|
||||
xdg_toplevel::Request::SetParent { parent } => {
|
||||
debug!(?xdg_toplevel, ?parent, "Set XDG Toplevel parent");
|
||||
let mut state = data.state.lock();
|
||||
let queued_state = state.queued_state.as_mut().unwrap();
|
||||
queued_state.parent = parent.map(|toplevel| toplevel.downgrade());
|
||||
data.lock().parent = parent.map(|toplevel| toplevel.downgrade());
|
||||
}
|
||||
xdg_toplevel::Request::SetTitle { title } => {
|
||||
debug!(?xdg_toplevel, ?title, "Set XDG Toplevel title");
|
||||
let mut state = data.state.lock();
|
||||
let queued_state = state.queued_state.as_mut().unwrap();
|
||||
queued_state.title = (!title.is_empty()).then_some(title);
|
||||
data.lock().title = (!title.is_empty()).then_some(title);
|
||||
}
|
||||
xdg_toplevel::Request::SetAppId { app_id } => {
|
||||
debug!(?xdg_toplevel, ?app_id, "Set XDG Toplevel app ID");
|
||||
let mut state = data.state.lock();
|
||||
let queued_state = state.queued_state.as_mut().unwrap();
|
||||
queued_state.app_id = (!app_id.is_empty()).then_some(app_id);
|
||||
data.lock().app_id = (!app_id.is_empty()).then_some(app_id);
|
||||
}
|
||||
xdg_toplevel::Request::ShowWindowMenu { seat, serial, x, y } => {
|
||||
debug!(
|
||||
@@ -344,7 +491,7 @@ impl Dispatch<XdgToplevel, XdgToplevelData, WaylandState> for WaylandState {
|
||||
}
|
||||
xdg_toplevel::Request::Move { seat, serial } => {
|
||||
debug!(?xdg_toplevel, ?seat, serial, "XDG Toplevel move request");
|
||||
let Some(panel_item) = data.panel_item() else { return };
|
||||
let Some(panel_item) = data.lock().panel_item() else { return };
|
||||
panel_item.recommend_toplevel_state(RecommendedState::Move);
|
||||
}
|
||||
xdg_toplevel::Request::Resize {
|
||||
@@ -360,46 +507,42 @@ impl Dispatch<XdgToplevel, XdgToplevelData, WaylandState> for WaylandState {
|
||||
?edges,
|
||||
"XDG Toplevel resize request"
|
||||
);
|
||||
let Some(panel_item) = data.panel_item() else { return };
|
||||
let Some(panel_item) = data.lock().panel_item() else { return };
|
||||
panel_item.recommend_toplevel_state(RecommendedState::Resize(edges as u32));
|
||||
}
|
||||
xdg_toplevel::Request::SetMaxSize { width, height } => {
|
||||
debug!(?xdg_toplevel, width, height, "Set XDG Toplevel max size");
|
||||
let mut state = data.state.lock();
|
||||
let queued_state = state.queued_state.as_mut().unwrap();
|
||||
queued_state.max_size = (width > 1 || height > 1)
|
||||
data.lock().max_size = (width > 1 || height > 1)
|
||||
.then_some(Vector2::from([width as u32, height as u32]));
|
||||
}
|
||||
xdg_toplevel::Request::SetMinSize { width, height } => {
|
||||
debug!(?xdg_toplevel, width, height, "Set XDG Toplevel min size");
|
||||
let mut state = data.state.lock();
|
||||
let queued_state = state.queued_state.as_mut().unwrap();
|
||||
queued_state.min_size = (width > 1 || height > 1)
|
||||
data.lock().min_size = (width > 1 || height > 1)
|
||||
.then_some(Vector2::from([width as u32, height as u32]));
|
||||
}
|
||||
xdg_toplevel::Request::SetMaximized => {
|
||||
let Some(panel_item) = data.panel_item() else { return };
|
||||
let Some(panel_item) = data.lock().panel_item() else { return };
|
||||
panel_item.recommend_toplevel_state(RecommendedState::Maximize(true));
|
||||
}
|
||||
xdg_toplevel::Request::UnsetMaximized => {
|
||||
let Some(panel_item) = data.panel_item() else { return };
|
||||
let Some(panel_item) = data.lock().panel_item() else { return };
|
||||
panel_item.recommend_toplevel_state(RecommendedState::Maximize(false));
|
||||
}
|
||||
xdg_toplevel::Request::SetFullscreen { output: _ } => {
|
||||
let Some(panel_item) = data.panel_item() else { return };
|
||||
let Some(panel_item) = data.lock().panel_item() else { return };
|
||||
panel_item.recommend_toplevel_state(RecommendedState::Fullscreen(true));
|
||||
}
|
||||
xdg_toplevel::Request::UnsetFullscreen => {
|
||||
let Some(panel_item) = data.panel_item() else { return };
|
||||
let Some(panel_item) = data.lock().panel_item() else { return };
|
||||
panel_item.recommend_toplevel_state(RecommendedState::Fullscreen(true));
|
||||
}
|
||||
xdg_toplevel::Request::SetMinimized => {
|
||||
let Some(panel_item) = data.panel_item() else { return };
|
||||
let Some(panel_item) = data.lock().panel_item() else { return };
|
||||
panel_item.recommend_toplevel_state(RecommendedState::Minimize);
|
||||
}
|
||||
xdg_toplevel::Request::Destroy => {
|
||||
debug!(?xdg_toplevel, "Destroy XDG Toplevel");
|
||||
let Some(panel_item) = data.panel_item() else { return };
|
||||
let Some(panel_item) = data.lock().panel_item() else { return };
|
||||
panel_item.on_drop();
|
||||
}
|
||||
_ => unreachable!(),
|
||||
@@ -407,29 +550,121 @@ impl Dispatch<XdgToplevel, XdgToplevelData, WaylandState> for WaylandState {
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<XdgPopup, (), WaylandState> for WaylandState {
|
||||
#[derive(Clone)]
|
||||
pub struct PopupData {
|
||||
pub uid: String,
|
||||
grabbed: bool,
|
||||
parent_id: SurfaceID,
|
||||
positioner: XdgPositioner,
|
||||
xdg_surface: WlWeak<XdgSurface>,
|
||||
}
|
||||
impl PopupData {
|
||||
fn new(
|
||||
uid: impl ToString,
|
||||
xdg_surface: &XdgSurface,
|
||||
parent_id: SurfaceID,
|
||||
positioner: XdgPositioner,
|
||||
) -> Self {
|
||||
PopupData {
|
||||
uid: uid.to_string(),
|
||||
grabbed: false,
|
||||
parent_id,
|
||||
positioner,
|
||||
xdg_surface: xdg_surface.downgrade(),
|
||||
}
|
||||
}
|
||||
pub fn get(popup: &XdgPopup) -> &Mutex<Self> {
|
||||
popup.data::<Mutex<Self>>().unwrap()
|
||||
}
|
||||
pub fn xdg_surface(&self) -> XdgSurface {
|
||||
self.xdg_surface.upgrade().unwrap()
|
||||
}
|
||||
|
||||
fn panel_item(&self) -> Option<Arc<PanelItem>> {
|
||||
XdgSurfaceData::get(&self.xdg_surface()).lock().panel_item()
|
||||
}
|
||||
// fn get_parent(&self) -> Option<XdgSurface> {
|
||||
// self.parent.as_ref()?.upgrade().ok()
|
||||
// }
|
||||
pub fn wl_surface(&self) -> WlSurface {
|
||||
XdgSurfaceData::get(&self.xdg_surface()).lock().wl_surface()
|
||||
}
|
||||
|
||||
pub fn positioner_data(&self) -> Option<PositionerData> {
|
||||
Some(
|
||||
self.positioner
|
||||
.data::<Mutex<PositionerData>>()?
|
||||
.lock()
|
||||
.clone(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for PopupData {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let Some(positioner_data) = self.positioner_data() else {return serde_error::<S>("Positioner not found")};
|
||||
let mut seq = serializer.serialize_seq(None)?;
|
||||
seq.serialize_element(&self.uid)?;
|
||||
seq.serialize_element(&self.parent_id)?;
|
||||
seq.serialize_element(&positioner_data)?;
|
||||
seq.end()
|
||||
}
|
||||
}
|
||||
impl Debug for PopupData {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("XdgPopupData")
|
||||
.field("uid", &self.uid)
|
||||
.field("positioner", &self.positioner)
|
||||
.field("xdg_surface", &self.xdg_surface)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
impl Dispatch<XdgPopup, Mutex<PopupData>, WaylandState> for WaylandState {
|
||||
fn request(
|
||||
_state: &mut WaylandState,
|
||||
_client: &Client,
|
||||
xdg_popup: &XdgPopup,
|
||||
request: xdg_popup::Request,
|
||||
_data: &(),
|
||||
data: &Mutex<PopupData>,
|
||||
_dhandle: &DisplayHandle,
|
||||
_data_init: &mut DataInit<'_, WaylandState>,
|
||||
) {
|
||||
match request {
|
||||
xdg_popup::Request::Grab { seat, serial } => {
|
||||
let mut data = data.lock();
|
||||
data.grabbed = true;
|
||||
debug!(?xdg_popup, ?seat, serial, "XDG popup grab");
|
||||
xdg_popup.popup_done(); // temporary hack to avoid apps locking up before popups are implemented
|
||||
let Some(panel_item) = data.panel_item() else {return};
|
||||
panel_item.grab_keyboard(Some(SurfaceID::Popup(data.uid.clone())));
|
||||
}
|
||||
xdg_popup::Request::Reposition { positioner, token } => {
|
||||
let mut data = data.lock();
|
||||
debug!(?xdg_popup, ?positioner, token, "XDG popup reposition");
|
||||
xdg_popup.popup_done(); // temporary hack to avoid apps locking up before popups are implemented
|
||||
data.positioner = positioner;
|
||||
let Some(panel_item) = data.panel_item() else {return};
|
||||
panel_item.reposition_popup(&*data);
|
||||
// xdg_popup.popup_done(); // temporary hack to avoid apps locking up before popups are implemented
|
||||
}
|
||||
xdg_popup::Request::Destroy => {
|
||||
let data = data.lock();
|
||||
debug!(?xdg_popup, "Destroy XDG popup");
|
||||
if data.grabbed {
|
||||
let Some(panel_item) = data.panel_item() else {return};
|
||||
panel_item.grab_keyboard(None);
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn destroyed(
|
||||
_state: &mut WaylandState,
|
||||
_client: ClientId,
|
||||
_resource: ObjectId,
|
||||
data: &Mutex<PopupData>,
|
||||
) {
|
||||
let data = data.lock();
|
||||
let Some(panel_item) = data.panel_item() else {return};
|
||||
panel_item.drop_popup(&data.uid);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user