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:
Nova
2023-04-24 06:18:21 -04:00
parent 648451b47e
commit 4737149c85
14 changed files with 1091 additions and 773 deletions

523
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -10,7 +10,7 @@ homepage = "https://stardustxr.org"
[dependencies]
color-eyre = { version = "0.6.2", default-features = false }
clap = { version = "4.1.6", features = ["derive"] }
clap = { version = "4.2.4", features = ["derive"] }
dashmap = "5.4.0"
glam = {version = "0.23.0", features = ["mint"]}
lazy_static = "1.4.0"
@@ -18,19 +18,17 @@ mint = "0.5.9"
nanoid = "0.4.0"
once_cell = "1.17.1"
parking_lot = "0.12.1"
portable-atomic = {version = "1.0.1", features = ["float", "std"]}
portable-atomic = {version = "1.2.0", features = ["float", "std"]}
rustc-hash = "1.1.0"
tokio = { version = "1.25.0", features = ["rt-multi-thread", "signal"] }
tokio = { version = "1.27.0", features = ["rt-multi-thread", "signal"] }
send_wrapper = "0.6.0"
prisma = "0.1.1"
slog = "2.7.0"
xkbcommon = { version = "0.5.0", default-features = false, optional = true }
stardust-xr = "0.11.0"
stardust-xr = "0.11.1"
directories = "5.0.0"
serde = { version = "1.0.152", features = ["derive"] }
serde = { version = "1.0.160", features = ["derive"] }
tracing = "0.1.37"
tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
tracing-slog = "0.2.0"
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
global_counter = "0.2.2"
rand = "0.8.5"
@@ -40,7 +38,8 @@ features = ["linux-egl", "color_named", "prisma"]
version = "0.15.3"
[dependencies.smithay]
git = "https://github.com/technobaboo/smithay.git" # Until we get stereokit to understand OES samplers and external textures
# git = "https://github.com/technobaboo/smithay.git" # Until we get stereokit to understand OES samplers and external textures
git = "https://github.com/smithay/smithay.git" # Until we get stereokit to understand OES samplers and external textures
default-features = false
features = ["desktop", "renderer_gl", "wayland_frontend"]
version = "*"

View File

@@ -74,6 +74,7 @@ fn main() -> Result<()> {
let log_layer = fmt::Layer::new()
.with_thread_names(true)
.with_ansi(true)
.with_line_number(true)
.with_filter(EnvFilter::from_default_env());
registry.with(log_layer).init();
@@ -250,8 +251,8 @@ fn main() -> Result<()> {
Ok(())
}
// #[tokio::main]
#[tokio::main]
// #[tokio::main(flavor = "current_thread")]
async fn event_loop(
info_sender: oneshot::Sender<EventLoopInfo>,
stop_rx: oneshot::Receiver<()>,

View File

@@ -107,7 +107,7 @@ impl MaterialParameter {
let Some(texture_path) = resource.get_file(
&client.base_resource_prefixes.lock().clone(),
&[OsStr::new("png"), OsStr::new("jpg")],
) else { return; };
) else {return};
if let Some(tex) = Texture::from_file(sk, texture_path, true, 0) {
material.set_parameter(sk, parameter_name, &tex);
}

View File

@@ -12,6 +12,7 @@ use crate::{
};
use color_eyre::eyre::{eyre, Result};
use lazy_static::lazy_static;
use nanoid::nanoid;
use serde::Deserialize;
use stardust_xr::{
schemas::flex::{deserialize, flexbuffers, serialize},
@@ -38,6 +39,7 @@ impl EnvironmentItem {
pub fn add_to(node: &Arc<Node>, path: String) {
Item::add_to(
node,
nanoid!(),
&ITEM_TYPE_INFO_ENVIRONMENT,
ItemType::Environment(EnvironmentItem { path }),
);

View File

@@ -88,12 +88,13 @@ pub struct Item {
impl Item {
pub fn add_to(
node: &Arc<Node>,
uid: String,
type_info: &'static TypeInfo,
specialization: ItemType,
) -> Arc<Self> {
let item = Item {
node: Arc::downgrade(node),
uid: node.uid.clone(),
uid,
type_info,
captured_acceptor: Default::default(),
specialization,

View File

@@ -272,3 +272,8 @@ impl Debug for Node {
.finish()
}
}
impl Drop for Node {
fn drop(&mut self) {
// Debug breakpoint
}
}

View File

@@ -1,4 +1,7 @@
use super::{panel_item::PanelItem, state::WaylandState, surface::CoreSurface};
use crate::wayland::surface::CoreSurface;
use super::state::WaylandState;
use portable_atomic::{AtomicU32, Ordering};
use smithay::{
delegate_compositor,
reexports::wayland_server::protocol::wl_surface::WlSurface,
@@ -14,12 +17,24 @@ impl CompositorHandler for WaylandState {
fn commit(&mut self, surface: &WlSurface) {
debug!(?surface, "Surface commit");
CoreSurface::add_to(&self.display, self.display_handle.clone(), surface);
if let Some(panel_item) = compositor::with_states(surface, |data| {
data.data_map.get::<Arc<PanelItem>>().cloned()
}) {
panel_item.commit_toplevel();
};
let mut count = 0;
let core_surface = compositor::with_states(surface, |data| {
let count_new = data
.data_map
.insert_if_missing_threadsafe(|| AtomicU32::new(0));
if !count_new {
count = data
.data_map
.get::<AtomicU32>()
.unwrap()
.fetch_add(1, Ordering::Relaxed);
}
data.data_map.get::<Arc<CoreSurface>>().cloned()
});
if let Some(core_surface) = core_surface {
core_surface.commit(count);
}
}
}

View File

@@ -16,11 +16,9 @@ use global_counter::primitive::exact::CounterU32;
use once_cell::sync::OnceCell;
use parking_lot::Mutex;
use sk::lifecycle::StereoKitDraw;
use slog::Drain;
use smithay::{
backend::{egl::EGLContext, renderer::gles2::Gles2Renderer},
reexports::wayland_server::{backend::GlobalId, Display, ListeningSocket},
};
use smithay::backend::egl::EGLContext;
use smithay::backend::renderer::gles::GlesRenderer;
use smithay::reexports::wayland_server::{backend::GlobalId, Display, ListeningSocket};
use std::os::unix::prelude::AsRawFd;
use std::{
ffi::c_void,
@@ -59,36 +57,28 @@ fn get_sk_egl() -> Result<EGLRawHandles> {
static GLOBAL_DESTROY_QUEUE: OnceCell<mpsc::Sender<GlobalId>> = OnceCell::new();
pub struct Wayland {
log: slog::Logger,
display: Arc<Mutex<Display<WaylandState>>>,
pub socket_name: String,
join_handle: JoinHandle<Result<()>>,
renderer: Gles2Renderer,
renderer: GlesRenderer,
state: Arc<Mutex<WaylandState>>,
}
impl Wayland {
pub fn new() -> Result<Self> {
let log = ::slog::Logger::root(::tracing_slog::TracingSlogDrain.fuse(), slog::o!());
let egl_raw_handles = get_sk_egl()?;
let renderer = unsafe {
Gles2Renderer::new(
EGLContext::from_raw(
egl_raw_handles.display,
egl_raw_handles.config,
egl_raw_handles.context,
log.clone(),
)?,
log.clone(),
)?
GlesRenderer::new(EGLContext::from_raw(
egl_raw_handles.display,
egl_raw_handles.config,
egl_raw_handles.context,
)?)?
};
let display: Display<WaylandState> = Display::new()?;
let display_handle = display.handle();
let display = Arc::new(Mutex::new(display));
let state = WaylandState::new(log.clone(), display.clone(), display_handle, &renderer);
let state = WaylandState::new(display.clone(), display_handle, &renderer);
let (global_destroy_queue_in, global_destroy_queue) = mpsc::channel(8);
GLOBAL_DESTROY_QUEUE.set(global_destroy_queue_in).unwrap();
@@ -101,7 +91,6 @@ impl Wayland {
Wayland::start_loop(display.clone(), socket, state.clone(), global_destroy_queue)?;
Ok(Wayland {
log,
display,
socket_name,
join_handle,
@@ -157,7 +146,7 @@ impl Wayland {
#[instrument(level = "debug", name = "Wayland frame", skip(self, sk))]
pub fn update(&mut self, sk: &StereoKitDraw) {
for core_surface in CORE_SURFACES.get_valid_contents() {
core_surface.process(sk, &mut self.renderer, &self.log);
core_surface.process(sk, &mut self.renderer);
}
self.display.lock().flush_clients().unwrap();

View File

@@ -1,7 +1,7 @@
use super::{
seat::{Cursor, SeatData},
surface::CoreSurface,
xdg_shell::{XdgSurfaceData, XdgToplevelData},
xdg_shell::{PopupData, ToplevelData, XdgSurfaceData},
SERIAL_COUNTER,
};
use crate::{
@@ -23,11 +23,18 @@ use lazy_static::lazy_static;
use mint::Vector2;
use nanoid::nanoid;
use parking_lot::Mutex;
use serde::{Deserialize, Serialize};
use rustc_hash::FxHashMap;
use serde::{
de::{Deserializer, Error, SeqAccess, Visitor},
ser::Serializer,
Deserialize, Serialize,
};
use smithay::{
reexports::{
wayland_protocols::xdg::shell::server::xdg_toplevel::{
XdgToplevel, EVT_CONFIGURE_BOUNDS_SINCE, EVT_WM_CAPABILITIES_SINCE,
wayland_protocols::xdg::shell::server::{
xdg_popup::XdgPopup,
xdg_surface::XdgSurface,
xdg_toplevel::{XdgToplevel, EVT_CONFIGURE_BOUNDS_SINCE, EVT_WM_CAPABILITIES_SINCE},
},
wayland_server::{
backend::Credentials, protocol::wl_surface::WlSurface, Resource, Weak as WlWeak,
@@ -44,55 +51,92 @@ lazy_static! {
pub static ref ITEM_TYPE_INFO_PANEL: TypeInfo = TypeInfo {
type_name: "panel",
aliased_local_signals: vec![
"apply_cursor_material",
"apply_toplevel_material",
"apply_surface_material",
"configure_toplevel",
"set_toplevel_capabilities",
"pointer_set_active",
"pointer_scroll",
"pointer_button",
"pointer_motion",
"keyboard_set_active",
"keyboard_key",
"keyboard_set_keymap_names",
"keyboard_set_keymap_string",
"close",
],
aliased_local_methods: vec![],
aliased_remote_signals: vec!["commit_toplevel", "recommend_toplevel_state", "set_cursor"],
aliased_remote_signals: vec![
"commit_toplevel",
"recommend_toplevel_state",
"set_cursor",
"new_popup",
"reposition_popup",
"drop_popup",
],
ui: Default::default(),
items: Registry::new(),
acceptors: Registry::new(),
};
}
#[derive(Debug, Clone, Serialize)]
pub struct ToplevelState {
#[serde(skip_serializing)]
pub mapped: bool,
#[serde(skip_serializing)]
pub parent: Option<WlWeak<XdgToplevel>>,
pub title: Option<String>,
pub app_id: Option<String>,
pub size: Vector2<u32>,
pub max_size: Option<Vector2<u32>>,
pub min_size: Option<Vector2<u32>>,
pub states: Vec<u32>,
#[serde(skip_serializing)]
pub queued_state: Option<Box<ToplevelState>>,
/// An ID for a surface inside this panel item
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub enum SurfaceID {
Cursor,
Toplevel,
Popup(String),
}
impl Default for ToplevelState {
impl Default for SurfaceID {
fn default() -> Self {
Self {
mapped: false,
parent: None,
title: None,
app_id: None,
size: Vector2::from([0; 2]),
max_size: None,
min_size: None,
states: Vec::new(),
queued_state: None,
Self::Toplevel
}
}
impl<'de> serde::Deserialize<'de> for SurfaceID {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_seq(SurfaceIDVisitor)
}
}
struct SurfaceIDVisitor;
impl<'de> Visitor<'de> for SurfaceIDVisitor {
type Value = SurfaceID;
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.write_str("idk")
}
fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
let Some(discrim) = seq.next_element()? else {
return Err(A::Error::missing_field("discrim"));
};
// idk if you wanna check for extraneous elements
// I didn't bother
match discrim {
"Cursor" => Ok(SurfaceID::Cursor),
"Toplevel" => Ok(SurfaceID::Toplevel),
"Popup" => {
let Some(text) = seq.next_element()? else {
return Err(A::Error::missing_field("popup_text"));
};
Ok(SurfaceID::Popup(text))
}
_ => Err(A::Error::unknown_variant(
discrim,
&["Cursor", "Toplevel", "Popup"],
)),
}
}
}
impl serde::Serialize for SurfaceID {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self {
Self::Cursor => ["Cursor"].serialize(serializer),
Self::Toplevel => ["Toplevel"].serialize(serializer),
Self::Popup(text) => ["Popup", text].serialize(serializer),
}
}
}
@@ -108,11 +152,15 @@ pub enum RecommendedState {
}
pub struct PanelItem {
pub uid: String,
node: Weak<Node>,
client_credentials: Option<Credentials>,
cursor: Mutex<Option<WlWeak<WlSurface>>>,
pub seat_data: Arc<SeatData>,
toplevel: WlWeak<XdgToplevel>,
pub cursor: Mutex<Option<WlWeak<WlSurface>>>,
seat_data: Arc<SeatData>,
popups: Mutex<FxHashMap<String, WlWeak<XdgPopup>>>,
pointer_grab: Mutex<Option<SurfaceID>>,
keyboard_grab: Mutex<Option<SurfaceID>>,
}
impl PanelItem {
pub fn create(
@@ -122,51 +170,49 @@ impl PanelItem {
seat_data: Arc<SeatData>,
) -> (Arc<Node>, Arc<PanelItem>) {
debug!(?toplevel, ?client_credentials, "Create panel item");
let uid = nanoid!();
let node = Arc::new(Node::create(
&INTERNAL_CLIENT,
"/item/panel/item",
&nanoid!(),
&uid,
true,
));
let spatial = Spatial::add_to(&node, None, Mat4::IDENTITY, false).unwrap();
let panel_item = Arc::new(PanelItem {
uid: uid.clone(),
node: Arc::downgrade(&node),
client_credentials,
toplevel: toplevel.downgrade(),
cursor: Mutex::new(None),
seat_data,
toplevel: toplevel.downgrade(),
popups: Mutex::new(FxHashMap::default()),
pointer_grab: Mutex::new(None),
keyboard_grab: Mutex::new(None),
});
panel_item
.seat_data
.new_panel_item(&panel_item, &toplevel, &wl_surface);
.new_surface(&wl_surface, Arc::downgrade(&panel_item));
let item = Item::add_to(
&node,
uid,
&ITEM_TYPE_INFO_PANEL,
ItemType::Panel(panel_item.clone()),
);
node.add_local_signal(
"apply_toplevel_material",
PanelItem::apply_toplevel_material_flex,
"apply_surface_material",
PanelItem::apply_surface_material_flex,
);
node.add_local_signal("configure_toplevel", PanelItem::configure_toplevel_flex);
if toplevel.version() >= EVT_WM_CAPABILITIES_SINCE {
node.add_local_signal(
"set_toplevel_capabilities",
PanelItem::set_toplevel_capabilities_flex,
);
}
node.add_local_signal(
"apply_cursor_material",
PanelItem::apply_cursor_material_flex,
"set_toplevel_capabilities",
PanelItem::set_toplevel_capabilities_flex,
);
node.add_local_signal("pointer_set_active", PanelItem::pointer_set_active_flex);
node.add_local_signal("pointer_scroll", PanelItem::pointer_scroll_flex);
node.add_local_signal("pointer_button", PanelItem::pointer_button_flex);
node.add_local_signal("pointer_motion", PanelItem::pointer_motion_flex);
node.add_local_signal("keyboard_set_active", PanelItem::keyboard_set_active_flex);
node.add_local_signal(
"keyboard_set_keymap_string",
PanelItem::keyboard_set_keymap_string_flex,
@@ -195,38 +241,26 @@ impl PanelItem {
(node, panel_item)
}
pub fn from_node(node: &Node) -> Option<&PanelItem> {
node.item.get().and_then(|item| match &item.specialization {
ItemType::Panel(panel_item) => Some(&**panel_item),
_ => None,
})
pub fn from_node(node: &Node) -> Option<Arc<PanelItem>> {
let ItemType::Panel(panel_item) = &node.item.get()?.specialization else {return None};
Some(panel_item.clone())
}
fn toplevel_surface_data(&self) -> Option<XdgSurfaceData> {
Some(
self.toplevel
.upgrade()
.ok()?
.data::<XdgToplevelData>()?
.xdg_surface_data
.clone(),
)
fn toplevel(&self) -> XdgToplevel {
self.toplevel.upgrade().unwrap()
}
fn toplevel_state(&self) -> Option<Arc<Mutex<ToplevelState>>> {
Some(
self.toplevel
.upgrade()
.ok()?
.data::<XdgToplevelData>()?
.state
.clone(),
)
fn toplevel_xdg_surface(&self) -> XdgSurface {
let toplevel = self.toplevel();
let data = ToplevelData::get(&toplevel).lock();
data.xdg_surface()
}
pub fn toplevel_wl_surface(&self) -> Option<WlSurface> {
self.toplevel_surface_data()?.wl_surface.upgrade().ok()
fn toplevel_wl_surface(&self) -> WlSurface {
XdgSurfaceData::get(&self.toplevel_xdg_surface())
.lock()
.wl_surface()
}
fn core_surface(&self) -> Option<Arc<CoreSurface>> {
compositor::with_states(&self.toplevel_wl_surface()?, |data| {
compositor::with_states(&self.toplevel_wl_surface(), |data| {
data.data_map.get::<Arc<CoreSurface>>().cloned()
})
}
@@ -235,55 +269,49 @@ impl PanelItem {
core_surface.flush_clients();
}
}
fn apply_toplevel_material_flex(
node: &Node,
calling_client: Arc<Client>,
data: &[u8],
) -> Result<()> {
#[derive(Debug, Deserialize)]
struct SurfaceMaterialInfo<'a> {
model_path: &'a str,
idx: u32,
}
let info: SurfaceMaterialInfo = deserialize(data)?;
let model_node = calling_client
.scenegraph
.get_node(info.model_path)
.ok_or_else(|| eyre!("Model node not found"))?;
let Some(Drawable::Model(model)) = model_node.drawable.get() else {bail!("Node is not a model")};
debug!(?info, "Apply toplevel material");
if let ItemType::Panel(panel_item) = &node.item.get().unwrap().specialization {
if let Some(core_surface) = panel_item.core_surface() {
core_surface.apply_material(model.clone(), info.idx);
fn wl_surface_from_id(&self, id: &SurfaceID) -> Option<WlSurface> {
match id {
SurfaceID::Cursor => self.cursor.lock().clone()?.upgrade().ok(),
SurfaceID::Toplevel => Some(self.toplevel_wl_surface()),
SurfaceID::Popup(popup) => {
let popups = self.popups.lock();
let popup = popups.get(popup)?.upgrade().ok()?;
let surf = PopupData::get(&popup).lock().wl_surface();
Some(surf)
}
}
Ok(())
}
fn wl_surface_from_id_result(&self, id: &SurfaceID) -> Result<WlSurface> {
self.wl_surface_from_id(id)
.ok_or(eyre!("Surface with ID not found"))
}
fn apply_cursor_material_flex(
fn apply_surface_material_flex(
node: &Node,
calling_client: Arc<Client>,
data: &[u8],
) -> Result<()> {
let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) };
let Some(cursor) = panel_item.cursor.lock().as_ref().and_then(|c| c.upgrade().ok()) else { return Ok(())};
let Some(core_surface) = CoreSurface::from_wl_surface(&cursor) else { return Ok(()) };
#[derive(Debug, Deserialize)]
struct SurfaceMaterialInfo<'a> {
surface: SurfaceID,
model_path: &'a str,
idx: u32,
}
let info: SurfaceMaterialInfo = deserialize(data)?;
debug!(?cursor, ?info, "Apply cursor material");
let Some(wl_surface) = panel_item.wl_surface_from_id(&info.surface) else { return Ok(()) };
let Some(core_surface) = CoreSurface::from_wl_surface(&wl_surface) else { return Ok(()) };
let model_node = calling_client
.scenegraph
.get_node(info.model_path)
.ok_or_else(|| eyre!("Model node not found"))?;
let Some(Drawable::Model(model)) = model_node.drawable.get() else {bail!("Node is not a model")};
debug!(?info, "Apply surface material");
core_surface.apply_material(model.clone(), info.idx);
Ok(())
@@ -291,69 +319,57 @@ impl PanelItem {
fn pointer_motion_flex(node: &Node, _calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) };
let Ok(toplevel) = panel_item.toplevel.upgrade() else { return Ok(()) };
debug!(?toplevel, "Pointer deactivate");
let (surface_id, position): (SurfaceID, Vector2<f64>) = deserialize(data)?;
let wl_surface = panel_item.wl_surface_from_id_result(&surface_id)?;
debug!(?surface_id, ?position, "Pointer deactivate");
panel_item
.seat_data
.pointer_event(&toplevel, PointerEvent::Motion(deserialize(data)?));
.pointer_event(&wl_surface, PointerEvent::Motion(position));
panel_item.flush_clients();
Ok(())
}
fn pointer_button_flex(node: &Node, _calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) };
let Ok(toplevel) = panel_item.toplevel.upgrade() else { return Ok(()) };
let (button, state): (u32, u32) = deserialize(data)?;
debug!(button, state, "Pointer button");
let (surface_id, button, state): (SurfaceID, u32, u32) = deserialize(data)?;
let wl_surface = panel_item.wl_surface_from_id_result(&surface_id)?;
debug!(?surface_id, button, state, "Pointer button");
panel_item
.seat_data
.pointer_event(&toplevel, PointerEvent::Button { button, state });
.pointer_event(&wl_surface, PointerEvent::Button { button, state });
panel_item.flush_clients();
Ok(())
}
fn pointer_scroll_flex(node: &Node, _calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) };
let Ok(toplevel) = panel_item.toplevel.upgrade() else { return Ok(()) };
#[derive(Debug, Deserialize)]
struct PointerScrollArgs {
struct PointerScrollInfo {
surface_id: SurfaceID,
axis_continuous: Option<Vector2<f32>>,
axis_discrete: Option<Vector2<f32>>,
}
let args: PointerScrollArgs = deserialize(data)?;
let info: PointerScrollInfo = deserialize(data)?;
let wl_surface = panel_item.wl_surface_from_id_result(&info.surface_id)?;
debug!(?args, "Pointer scroll");
debug!(?info, "Pointer scroll");
panel_item.seat_data.pointer_event(
&toplevel,
&wl_surface,
PointerEvent::Scroll {
axis_continuous: args.axis_continuous,
axis_discrete: args.axis_discrete,
axis_continuous: info.axis_continuous,
axis_discrete: info.axis_discrete,
},
);
panel_item.flush_clients();
Ok(())
}
fn pointer_set_active_flex(
node: &Node,
_calling_client: Arc<Client>,
data: &[u8],
) -> Result<()> {
let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) };
let Ok(toplevel) = panel_item.toplevel.upgrade() else { return Ok(()) };
let active: bool = deserialize(data)?;
debug!(?toplevel, active, "Pointer set active");
panel_item.seat_data.set_pointer_active(&toplevel, active);
panel_item.flush_clients();
Ok(())
}
fn keyboard_set_keymap_string_flex(
node: &Node,
@@ -397,38 +413,32 @@ impl PanelItem {
}
fn keyboard_set_keymap_flex(node: &Node, keymap: &Keymap) -> Result<()> {
let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) };
let Ok(toplevel) = panel_item.toplevel.upgrade() else { return Ok(()) };
let toplevel = panel_item.toplevel_wl_surface();
debug!(?toplevel, "Keyboard set keymap");
panel_item.seat_data.set_keymap(&toplevel, keymap);
let mut surfaces = vec![toplevel];
surfaces.extend(panel_item.popups.lock().values().filter_map(|p| {
let popup = p.upgrade().ok()?;
let popup_data = PopupData::get(&popup).lock();
let xdg_surface = popup_data.xdg_surface();
let xdg_surface_data = XdgSurfaceData::get(&xdg_surface).lock();
Some(xdg_surface_data.wl_surface())
}));
Ok(())
}
fn keyboard_set_active_flex(
node: &Node,
_calling_client: Arc<Client>,
data: &[u8],
) -> Result<()> {
let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) };
let Ok(toplevel) = panel_item.toplevel.upgrade() else { return Ok(()) };
let active: bool = deserialize(data)?;
debug!(?toplevel, active, "Keyboard set active");
panel_item.seat_data.set_keyboard_active(&toplevel, active);
panel_item.seat_data.set_keymap(keymap, surfaces);
Ok(())
}
fn keyboard_key_flex(node: &Node, _calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) };
let Ok(toplevel) = panel_item.toplevel.upgrade() else { return Ok(()) };
let (key, state): (u32, u32) = deserialize(data)?;
let (surface_id, key, state): (SurfaceID, u32, u32) = deserialize(data)?;
let wl_surface = panel_item.wl_surface_from_id_result(&surface_id)?;
debug!(key, state, "Set keyboard key state");
panel_item
.seat_data
.keyboard_event(&toplevel, KeyboardEvent::Key { key, state });
.keyboard_event(&wl_surface, KeyboardEvent::Key { key, state });
Ok(())
}
@@ -441,7 +451,7 @@ impl PanelItem {
let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) };
let Some(core_surface) = panel_item.core_surface() else { return Ok(()) };
let Ok(xdg_toplevel) = panel_item.toplevel.upgrade() else { return Ok(()) };
let Some(xdg_surface) = panel_item.toplevel_surface_data().and_then(|d| d.xdg_surface.upgrade().ok()) else { return Ok(()) };
let xdg_surface = panel_item.toplevel_xdg_surface();
#[derive(Debug, Deserialize)]
struct ConfigureToplevelInfo {
@@ -452,9 +462,6 @@ impl PanelItem {
let info: ConfigureToplevelInfo = deserialize(data)?;
debug!(info = ?&info, "Configure toplevel info");
if let Some(xdg_state) = panel_item.toplevel_state() {
xdg_state.lock().queued_state.as_mut().unwrap().states = info.states.clone();
}
if let Some(bounds) = info.bounds {
if xdg_toplevel.version() > EVT_CONFIGURE_BOUNDS_SINCE {
xdg_toplevel.configure_bounds(bounds.x as i32, bounds.y as i32);
@@ -483,7 +490,10 @@ impl PanelItem {
let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) };
let Some(core_surface) = panel_item.core_surface() else { return Ok(()) };
let Ok(xdg_toplevel) = panel_item.toplevel.upgrade() else { return Ok(()) };
let Some(xdg_surface) = panel_item.toplevel_surface_data().and_then(|d| d.xdg_surface.upgrade().ok()) else { return Ok(()) };
if xdg_toplevel.version() < EVT_WM_CAPABILITIES_SINCE {
return Ok(());
}
let xdg_surface = panel_item.toplevel_xdg_surface();
let capabilities: Vec<u8> = deserialize(data)?;
debug!("Set toplevel capabilities");
@@ -495,23 +505,22 @@ impl PanelItem {
}
pub fn commit_toplevel(&self) {
let mapped_size = self.core_surface().and_then(|c| c.size());
let Some(state) = self.toplevel_state() else { return };
let mut state = state.lock();
let mut queued_state = state.queued_state.take().unwrap();
queued_state.mapped = mapped_size.is_some();
if let Some(size) = mapped_size {
queued_state.size = size;
}
*state = (*queued_state).clone();
state.queued_state = Some(queued_state);
// let mapped_size = self.core_surface().and_then(|c| c.size());
let toplevel = self.toplevel();
let state = ToplevelData::get(&toplevel);
let state = state.lock();
// let mut queued_state = state.queued_state.take().unwrap();
// queued_state.mapped = mapped_size.is_some();
// if let Some(size) = mapped_size {
// queued_state.size = size;
// queued_state.geometry.update_to_surface_size(size);
// }
// *state = (*queued_state).clone();
// state.queued_state = Some(queued_state);
debug!(state = ?&state.mapped.then_some(&*state), "Commit toplevel");
debug!(state = ?&*state, "Commit toplevel");
let Some(node) = self.node.upgrade() else { return };
let _ = node.send_remote_signal(
"commit_toplevel",
&serialize(&state.mapped.then_some(&*state)).unwrap(),
);
let _ = node.send_remote_signal("commit_toplevel", &serialize(&*state).unwrap());
}
pub fn recommend_toplevel_state(&self, state: RecommendedState) {
@@ -522,6 +531,56 @@ impl PanelItem {
let _ = node.send_remote_signal("recommend_toplevel_state", &data);
}
pub fn new_popup(&self, popup: &XdgPopup, data: &PopupData) {
let uid = data.uid.clone();
self.popups.lock().insert(uid.clone(), popup.downgrade());
let Some(node) = self.node.upgrade() else { return };
let _ = node.send_remote_signal("new_popup", &serialize(&(&uid, data)).unwrap());
}
// pub fn commit_popup(&self, data: &PopupData) {
// let xdg_surf = data.xdg_surface.upgrade().unwrap();
// let surf = xdg_surf
// .data::<XdgSurfaceData>()
// .unwrap()
// .wl_surface
// .upgrade()
// .unwrap();
// let core_surface =
// compositor::with_states(&surf, |s| s.data_map.get::<Arc<CoreSurface>>().cloned())
// .unwrap();
// let mut popup_state = data.state.lock();
// popup_state.mapped = core_surface.size().is_some();
// }
pub fn reposition_popup(&self, popup_state: &PopupData) {
let Some(node) = self.node.upgrade() else { return };
let _ = node.send_remote_signal(
"reposition_popup",
&serialize(popup_state.positioner_data().unwrap()).unwrap(),
);
}
pub fn drop_popup(&self, uid: &str) {
if let Some(popup) = self
.popups
.lock()
.remove(uid)
.and_then(|popup| popup.upgrade().ok()?.data::<Arc<PopupData>>().cloned())
{
self.seat_data.drop_surface(&popup.wl_surface());
}
let Some(node) = self.node.upgrade() else { return };
let _ = node.send_remote_signal("drop_popup", &serialize(uid).unwrap());
}
pub fn grab_keyboard(&self, sid: Option<SurfaceID>) {
let Some(node) = self.node.upgrade() else { return };
let _ = node.send_remote_signal("grab_keyboard", &serialize(sid).unwrap());
}
pub fn set_cursor(&self, surface: Option<&WlSurface>, hotspot_x: i32, hotspot_y: i32) {
let Some(node) = self.node.upgrade() else { return };
debug!(?surface, hotspot_x, hotspot_y, "Set cursor size");
@@ -540,8 +599,8 @@ impl PanelItem {
}
pub fn on_drop(&self) {
let Ok(toplevel) = self.toplevel.upgrade() else { return; };
self.seat_data.drop_panel_item(&toplevel);
let toplevel = self.toplevel_wl_surface();
self.seat_data.drop_surface(&toplevel);
debug!("Drop panel item");
}
@@ -559,12 +618,35 @@ impl ItemSpecialization for PanelItem {
})
.map(|cursor| cursor.hotspot);
let toplevel_state = self.toplevel_state();
let toplevel_state = toplevel_state.as_ref().map(|state| state.lock());
let toplevel_state = toplevel_state
.as_ref()
.and_then(|state| state.mapped.then_some(&**state));
let toplevel = self.toplevel();
let toplevel_state = ToplevelData::get(&toplevel);
let toplevel_state = toplevel_state.lock().clone();
serialize((id, (toplevel_state, cursor_size.zip(cursor_hotspot)))).unwrap()
let popups = self
.popups
.lock()
.values()
.filter_map(|v| Some(v.upgrade().ok()?.data::<Mutex<PopupData>>()?.lock().clone()))
.collect::<Vec<_>>();
let pointer_grab = self.pointer_grab.lock().clone();
let keyboard_grab = self.keyboard_grab.lock().clone();
serialize((
id,
(
cursor_size.zip(cursor_hotspot),
toplevel_state,
popups,
pointer_grab,
keyboard_grab,
),
))
.unwrap()
}
}
impl Drop for PanelItem {
fn drop(&mut self) {
// Dropped panel item, basically just a debug breakpoint place
}
}

View File

@@ -1,9 +1,8 @@
use crate::core::task;
use super::{
panel_item::PanelItem, state::WaylandState, surface::CoreSurface, GLOBAL_DESTROY_QUEUE,
SERIAL_COUNTER,
};
use crate::core::task;
use color_eyre::eyre::Result;
use mint::Vector2;
use nanoid::nanoid;
@@ -13,20 +12,16 @@ use rand::{seq::IteratorRandom, thread_rng};
use rustc_hash::{FxHashMap, FxHashSet};
use smithay::{
input::keyboard::{KeymapFile, ModifiersState},
reexports::{
wayland_protocols::xdg::shell::server::xdg_toplevel::XdgToplevel,
wayland_server::{
backend::{ClientId, GlobalId, ObjectId},
protocol::{
wl_keyboard::{self, KeyState, WlKeyboard},
wl_pointer::{self, Axis, ButtonState, WlPointer},
wl_seat::{self, Capability, WlSeat, EVT_NAME_SINCE},
wl_surface::WlSurface,
wl_touch::{self, WlTouch},
},
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource,
Weak as WlWeak,
reexports::wayland_server::{
backend::{ClientId, GlobalId, ObjectId},
protocol::{
wl_keyboard::{self, KeyState, WlKeyboard},
wl_pointer::{self, Axis, ButtonState, WlPointer},
wl_seat::{self, Capability, WlSeat, EVT_NAME_SINCE},
wl_surface::WlSurface,
wl_touch::{self, WlTouch},
},
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, Weak as WlWeak,
},
wayland::compositor,
};
@@ -48,7 +43,7 @@ impl KeyboardInfo {
pub fn new(keymap: &Keymap) -> Self {
KeyboardInfo {
state: xkb::State::new(keymap),
keymap: KeymapFile::new(keymap, None),
keymap: KeymapFile::new(keymap),
mods: ModifiersState::default(),
keys: FxHashSet::default(),
}
@@ -109,51 +104,34 @@ pub enum KeyboardEvent {
}
const POINTER_EVENT_TIMEOUT: Duration = Duration::from_secs(1);
struct PanelInfo {
struct SurfaceInfo {
wl_surface: WlWeak<WlSurface>,
panel_item: Weak<PanelItem>,
toplevel: WlWeak<XdgToplevel>,
focus: WlWeak<WlSurface>,
pointer_queue: Option<VecDeque<PointerEvent>>,
pointer_queue: VecDeque<PointerEvent>,
pointer_latest_event: Instant,
keyboard_queue: Option<VecDeque<KeyboardEvent>>,
keyboard_queue: VecDeque<KeyboardEvent>,
keyboard_info: Option<KeyboardInfo>,
}
impl PanelInfo {
fn new(panel_item: &Arc<PanelItem>, toplevel: &XdgToplevel, focus: &WlSurface) -> Self {
PanelInfo {
toplevel: toplevel.downgrade(),
panel_item: Arc::downgrade(panel_item),
focus: focus.downgrade(),
pointer_queue: None,
impl SurfaceInfo {
fn new(wl_surface: &WlSurface, panel_item: Weak<PanelItem>) -> Self {
SurfaceInfo {
wl_surface: wl_surface.downgrade(),
panel_item,
pointer_queue: VecDeque::new(),
pointer_latest_event: Instant::now(),
keyboard_queue: None,
keyboard_queue: VecDeque::new(),
keyboard_info: None,
}
}
pub fn set_pointer_active(&mut self, seat_data: &SeatData, active: bool) {
if active && self.pointer_queue.is_none() {
self.pointer_queue.replace(Default::default());
}
if !active && self.pointer_queue.is_some() {
self.pointer_queue.take();
let Ok(focus) = self.focus.upgrade() else {return};
let Some((pointer, pointer_focus)) = seat_data.pointer.get() else {return};
if &*pointer_focus.lock() == &Some(self.toplevel.id()) {
pointer.leave(SERIAL_COUNTER.inc(), &focus);
}
}
}
fn handle_pointer_events(&mut self, pointer: &WlPointer, mut locked: bool) -> bool {
let Ok(focus) = self.focus.upgrade() else { return false; };
let Some(pointer_queue) = self.pointer_queue.as_mut() else { return false; };
let Ok(focus) = self.wl_surface.upgrade() else { return false; };
let Some(core_surface) = CoreSurface::from_wl_surface(&focus) else { return false; };
let Some(focus_size) = core_surface.size() else { return false; };
if !pointer_queue.is_empty() {
if !self.pointer_queue.is_empty() {
self.pointer_latest_event = Instant::now();
}
while let Some(event) = pointer_queue.pop_front() {
while let Some(event) = self.pointer_queue.pop_front() {
match (locked, event) {
(false, PointerEvent::Motion(pos)) => {
pointer.enter(
@@ -218,22 +196,8 @@ impl PanelInfo {
locked
}
pub fn set_keyboard_active(&mut self, seat_data: &SeatData, active: bool) {
if active && self.keyboard_queue.is_none() {
self.keyboard_queue.replace(Default::default());
}
if !active && self.keyboard_queue.is_some() {
self.keyboard_queue.take();
let Ok(focus) = self.focus.upgrade() else {return};
let Some((keyboard, keyboard_focus)) = seat_data.keyboard.get() else {return};
if &*keyboard_focus.lock() == &Some(self.toplevel.id()) {
keyboard.leave(SERIAL_COUNTER.inc(), &focus);
}
}
}
fn handle_keyboard_events(&mut self, keyboard: &WlKeyboard, mut locked: bool) -> bool {
let Ok(focus) = self.focus.upgrade() else { return false; };
let Some(keyboard_queue) = self.keyboard_queue.as_mut() else { return false; };
let Ok(focus) = self.wl_surface.upgrade() else { return false; };
let Some(info) = self.keyboard_info.as_mut() else { return true; };
if !locked {
@@ -241,7 +205,7 @@ impl PanelInfo {
keyboard.repeat_info(0, 0);
locked = info.keymap.send(keyboard).is_ok();
}
while let Some(event) = keyboard_queue.pop_front() {
while let Some(event) = self.keyboard_queue.pop_front() {
debug!(locked, ?event, "Process keyboard event");
match (locked, event) {
(true, KeyboardEvent::Keymap) => {
@@ -267,9 +231,9 @@ impl PanelInfo {
pub struct SeatData {
client: ClientId,
global_id: OnceCell<GlobalId>,
panels: Mutex<FxHashMap<ObjectId, PanelInfo>>,
pointer: OnceCell<(WlPointer, Mutex<Option<ObjectId>>)>,
keyboard: OnceCell<(WlKeyboard, Mutex<Option<ObjectId>>)>,
surfaces: Mutex<FxHashMap<ObjectId, SurfaceInfo>>,
pointer: OnceCell<(WlPointer, Mutex<ObjectId>)>,
keyboard: OnceCell<(WlKeyboard, Mutex<ObjectId>)>,
touch: OnceCell<WlTouch>,
}
impl SeatData {
@@ -277,7 +241,7 @@ impl SeatData {
let seat_data = Arc::new(SeatData {
client,
global_id: OnceCell::new(),
panels: Mutex::new(FxHashMap::default()),
surfaces: Mutex::new(FxHashMap::default()),
pointer: OnceCell::new(),
keyboard: OnceCell::new(),
touch: OnceCell::new(),
@@ -291,144 +255,111 @@ impl SeatData {
seat_data
}
// pub fn set_focus(&self, toplevel: &WlSurface, focus: &WlSurface) {
// if let Some(panel_info) = self.panels.lock().get_mut(&toplevel.id()) {
// panel_info.focus = focus.downgrade();
// match panel_info.pointer_queue.back() {
// None => (),
// Some(&PointerEvent::Leave) => (),
// _ => panel_info.pointer_queue.push_back(PointerEvent::Leave),
// };
// match panel_info.keyboard_queue.back() {
// None => (),
// Some(&KeyboardEvent::Leave) => (),
// _ => panel_info.keyboard_queue.push_back(KeyboardEvent::Leave),
// };
// }
// }
pub fn set_pointer_active(&self, toplevel: &XdgToplevel, active: bool) {
let mut panels = self.panels.lock();
let Some(panel_info) = panels.get_mut(&toplevel.id()) else {return};
panel_info.set_pointer_active(self, active);
}
pub fn set_keyboard_active(&self, toplevel: &XdgToplevel, active: bool) {
let mut panels = self.panels.lock();
let Some(panel_info) = panels.get_mut(&toplevel.id()) else {return};
panel_info.set_keyboard_active(self, active);
}
pub fn set_keymap(&self, toplevel: &XdgToplevel, keymap: &Keymap) {
let mut panels = self.panels.lock();
let Some(panel_info) = panels.get_mut(&toplevel.id()) else {return};
panel_info.keyboard_info.replace(KeyboardInfo::new(keymap));
let Some(keyboard_queue) = panel_info.keyboard_queue.as_mut() else {return};
pub fn set_keymap(&self, keymap: &Keymap, surfaces: Vec<WlSurface>) {
let mut panels = self.surfaces.lock();
let Some((_, focus)) = self.keyboard.get() else {return};
let Some(id) = &*focus.lock() else {return};
if id == &toplevel.id() {
keyboard_queue.push_back(KeyboardEvent::Keymap);
for surface in surfaces {
let Some(surface_info) = panels.get_mut(&surface.id()) else {continue};
surface_info
.keyboard_info
.replace(KeyboardInfo::new(keymap));
if *focus.lock() == surface.id() {
surface_info.keyboard_queue.push_back(KeyboardEvent::Keymap);
}
}
}
pub fn pointer_event(&self, toplevel: &XdgToplevel, event: PointerEvent) {
let mut panels = self.panels.lock();
let Some(panel_info) = panels.get_mut(&toplevel.id()) else {return};
let Some(pointer_queue) = panel_info.pointer_queue.as_mut() else {return};
pointer_queue.push_back(event);
drop(panels);
pub fn pointer_event(&self, surface: &WlSurface, event: PointerEvent) {
let mut surfaces = self.surfaces.lock();
let Some(surface_info) = surfaces.get_mut(&surface.id()) else {return};
surface_info.pointer_queue.push_back(event);
drop(surfaces);
self.handle_pointer_events();
}
pub fn keyboard_event(&self, toplevel: &XdgToplevel, event: KeyboardEvent) {
let mut panels = self.panels.lock();
let Some(panel_info) = panels.get_mut(&toplevel.id()) else {return};
let Some(keyboard_queue) = panel_info.keyboard_queue.as_mut() else {return};
keyboard_queue.push_back(event);
drop(panels);
pub fn keyboard_event(&self, surface: &WlSurface, event: KeyboardEvent) {
let mut surfaces = self.surfaces.lock();
let Some(surface_info) = surfaces.get_mut(&surface.id()) else {return};
surface_info.keyboard_queue.push_back(event);
drop(surfaces);
self.handle_keyboard_events();
}
fn handle_pointer_events(&self) {
let mut panels = self.panels.lock();
let mut surfaces = self.surfaces.lock();
let Some((pointer, pointer_focus)) = self.pointer.get() else {return};
let mut pointer_focus = pointer_focus.lock();
loop {
let locked = pointer_focus.is_some();
let locked = !pointer_focus.is_null();
// Pick a pointer to focus on if there is none
if pointer_focus.is_none() {
*pointer_focus = panels
if pointer_focus.is_null() {
*pointer_focus = surfaces
.iter()
.filter(|(_k, v)| v.pointer_queue.is_some())
.filter(|(_k, v)| !v.pointer_queue.as_ref().unwrap().is_empty())
.filter(|(_k, v)| !v.pointer_queue.is_empty())
.map(|(k, _v)| k)
.choose(&mut thread_rng())
.cloned();
.cloned()
.unwrap_or(ObjectId::null());
}
if pointer_focus.is_none() {
if pointer_focus.is_null() {
// If there's still none, guess we're done with pointer events for the time being
break;
}
let Some(panel_info) = panels.get_mut(pointer_focus.as_ref().unwrap()) else {break};
if panel_info.handle_pointer_events(pointer, locked) {
let Some(surface_info) = surfaces.get_mut(&pointer_focus) else {break};
if surface_info.handle_pointer_events(pointer, locked) {
// We haven't gotten to a point where we can switch the focus
break;
} else {
pointer_focus.take();
*pointer_focus = ObjectId::null();
}
}
}
fn handle_keyboard_events(&self) {
let mut panels = self.panels.lock();
let mut surfaces = self.surfaces.lock();
let Some((keyboard, keyboard_focus)) = self.keyboard.get() else {return};
let mut keyboard_focus = keyboard_focus.lock();
loop {
let locked = keyboard_focus.is_some();
let locked = !keyboard_focus.is_null();
// Pick a keyboard to focus on if there is none
if keyboard_focus.is_none() {
*keyboard_focus = panels
if keyboard_focus.is_null() {
*keyboard_focus = surfaces
.iter()
.filter(|(_k, v)| v.keyboard_info.is_some())
.filter(|(_k, v)| v.keyboard_queue.is_some())
.filter(|(_k, v)| !v.keyboard_queue.as_ref().unwrap().is_empty())
.filter(|(_k, v)| !v.keyboard_queue.is_empty())
.map(|(k, _v)| k)
.choose(&mut thread_rng())
.cloned();
.cloned()
.unwrap_or(ObjectId::null());
}
if keyboard_focus.is_none() {
// If there's still none, guess we're done with keyboard events for the time being
break;
}
let Some(panel_info) = panels.get_mut(keyboard_focus.as_ref().unwrap()) else {break};
if panel_info.handle_keyboard_events(keyboard, locked) {
// If there's still none, guess we're done with keyboard events for the time being
let Some(surface_info) = surfaces.get_mut(&keyboard_focus) else {break};
if surface_info.handle_keyboard_events(keyboard, locked) {
// We haven't gotten to a point where we can switch the focus
break;
} else {
keyboard_focus.take();
*keyboard_focus = ObjectId::null();
}
}
}
pub fn new_panel_item(
&self,
panel_item: &Arc<PanelItem>,
toplevel: &XdgToplevel,
focus: &WlSurface,
) {
self.panels
pub fn new_surface(&self, surface: &WlSurface, panel_item: Weak<PanelItem>) {
self.surfaces
.lock()
.insert(toplevel.id(), PanelInfo::new(panel_item, toplevel, focus));
.insert(surface.id(), SurfaceInfo::new(surface, panel_item));
}
pub fn drop_panel_item(&self, toplevel: &XdgToplevel) {
self.panels.lock().remove(&toplevel.id());
pub fn drop_surface(&self, surface: &WlSurface) {
self.surfaces.lock().remove(&surface.id());
if let Some((_, pointer_focus)) = self.pointer.get() {
let mut pointer_focus = pointer_focus.lock();
if *pointer_focus == Some(toplevel.id()) {
pointer_focus.take();
if *pointer_focus == surface.id() {
*pointer_focus = ObjectId::null();
}
}
if let Some((_, keyboard_focus)) = self.keyboard.get() {
let mut keyboard_focus = keyboard_focus.lock();
if *keyboard_focus == Some(toplevel.id()) {
keyboard_focus.take();
if *keyboard_focus == surface.id() {
*keyboard_focus = ObjectId::null();
}
}
}
@@ -478,12 +409,12 @@ impl Dispatch<WlSeat, Arc<SeatData>, WaylandState> for WaylandState {
match request {
wl_seat::Request::GetPointer { id } => {
let pointer = data_init.init(id, data.clone());
let _ = data.pointer.set((pointer, Mutex::new(None)));
let _ = data.pointer.set((pointer, Mutex::new(ObjectId::null())));
}
wl_seat::Request::GetKeyboard { id } => {
let keyboard = data_init.init(id, data.clone());
keyboard.repeat_info(0, 0);
let _ = data.keyboard.set((keyboard, Mutex::new(None)));
let _ = data.keyboard.set((keyboard, Mutex::new(ObjectId::null())));
}
wl_seat::Request::GetTouch { id } => {
let _ = data.touch.set(data_init.init(id, data.clone()));
@@ -515,7 +446,7 @@ impl Dispatch<WlPointer, Arc<SeatData>, WaylandState> for WaylandState {
hotspot_y,
} => {
if let Some(surface) = surface.as_ref() {
CoreSurface::add_to(&state.display, dh.clone(), surface);
CoreSurface::add_to(&state.display, dh.clone(), surface, |_| ());
compositor::with_states(surface, |data| {
data.data_map.insert_if_missing_threadsafe(|| {
Arc::new(Mutex::new(Cursor {
@@ -533,10 +464,9 @@ impl Dispatch<WlPointer, Arc<SeatData>, WaylandState> for WaylandState {
let Some((_, focus)) = seat_data.pointer.get() else {return};
let focus = focus.lock();
let Some(id) = &*focus else {return};
let panels = seat_data.panels.lock();
let Some(panel_info) = panels.get(&id) else {return};
let Some(panel_item) = panel_info.panel_item.upgrade() else {return};
let surfaces = seat_data.surfaces.lock();
let Some(surface_info) = surfaces.get(&focus) else {return};
let Some(panel_item) = surface_info.panel_item.upgrade() else {return};
panel_item.set_cursor(surface.as_ref(), hotspot_x, hotspot_y);
}
wl_pointer::Request::Release => (),

View File

@@ -1,11 +1,10 @@
use crate::wayland::seat::SeatData;
use parking_lot::Mutex;
use rustc_hash::FxHashMap;
use slog::Logger;
use smithay::{
backend::{
allocator::dmabuf::Dmabuf,
renderer::{gles2::Gles2Renderer, ImportDma},
renderer::{gles::GlesRenderer, ImportDma},
},
delegate_dmabuf, delegate_output, delegate_shm,
output::{Mode, Output, Scale, Subpixel},
@@ -49,7 +48,6 @@ impl ClientData for ClientState {
pub struct WaylandState {
pub weak_ref: Weak<Mutex<WaylandState>>,
pub log: Logger,
pub display: Arc<Mutex<Display<WaylandState>>>,
pub display_handle: DisplayHandle,
@@ -66,24 +64,19 @@ pub struct WaylandState {
impl WaylandState {
pub fn new(
log: Logger,
display: Arc<Mutex<Display<WaylandState>>>,
display_handle: DisplayHandle,
renderer: &Gles2Renderer,
renderer: &GlesRenderer,
) -> Arc<Mutex<Self>> {
let compositor_state = CompositorState::new::<Self, _>(&display_handle, log.clone());
// let xdg_activation_state = XdgActivationState::new::<Self, _>(&display_handle, log.clone());
let kde_decoration_state = KdeDecorationState::new::<Self, _>(
&display_handle,
DecorationMode::Server,
log.clone(),
);
let shm_state = ShmState::new::<Self, _>(&display_handle, vec![], log.clone());
let compositor_state = CompositorState::new::<Self>(&display_handle);
// let xdg_activation_state = XdgActivationState::new::<Self, _>(&display_handle);
let kde_decoration_state =
KdeDecorationState::new::<Self>(&display_handle, DecorationMode::Server);
let shm_state = ShmState::new::<Self>(&display_handle, vec![]);
let mut dmabuf_state = DmabufState::new();
let dmabuf_global = dmabuf_state.create_global::<Self, _>(
let dmabuf_global = dmabuf_state.create_global::<Self>(
&display_handle,
renderer.dmabuf_formats().cloned().collect::<Vec<_>>(),
log.clone(),
renderer.dmabuf_formats().collect::<Vec<_>>(),
);
let output = Output::new(
"1x".to_owned(),
@@ -93,18 +86,19 @@ impl WaylandState {
make: "Virtual XR Display".to_owned(),
model: "Your Headset Name Here".to_owned(),
},
log.clone(),
);
let _output_global = output.create_global::<Self>(&display_handle);
let mode = Mode {
size: (4096, 4096).into(),
refresh: 60000,
};
output.change_current_state(
Some(Mode {
size: (4096, 4096).into(),
refresh: 60000,
}),
Some(mode),
Some(Transform::Normal),
Some(Scale::Integer(2)),
None,
);
output.set_preferred(mode);
display_handle.create_global::<Self, WlDataDeviceManager, _>(3, ());
display_handle.create_global::<Self, XdgWmBase, _>(5, ());
display_handle.create_global::<Self, ZxdgDecorationManagerV1, _>(1, ());
@@ -114,7 +108,6 @@ impl WaylandState {
Arc::new_cyclic(|weak| {
Mutex::new(WaylandState {
weak_ref: weak.clone(),
log,
display,
display_handle,

View File

@@ -7,10 +7,9 @@ use mint::Vector2;
use once_cell::sync::OnceCell;
use parking_lot::Mutex;
use send_wrapper::SendWrapper;
use slog::Logger;
use smithay::{
backend::renderer::{
gles2::{Gles2Renderer, Gles2Texture},
gles::{GlesRenderer, GlesTexture},
utils::{import_surface_tree, on_commit_buffer_handler, RendererSurfaceStateUserData},
Renderer, Texture,
},
@@ -35,22 +34,8 @@ use stereokit::{
pub static CORE_SURFACES: Registry<CoreSurface> = Registry::new();
#[derive(Debug, Clone, Copy)]
pub struct SurfaceGeometry {
pub origin: Vector2<u32>,
pub size: Vector2<u32>,
}
impl Default for SurfaceGeometry {
fn default() -> Self {
Self {
origin: [0; 2].into(),
size: [0; 2].into(),
}
}
}
pub struct CoreSurfaceData {
wl_tex: Option<SendWrapper<Gles2Texture>>,
wl_tex: Option<SendWrapper<GlesTexture>>,
pub size: Vector2<u32>,
}
impl Drop for CoreSurfaceData {
@@ -67,7 +52,7 @@ pub struct CoreSurface {
sk_tex: OnceCell<SendWrapper<SKTexture>>,
sk_mat: OnceCell<Arc<SendWrapper<Material>>>,
material_offset: Mutex<Delta<u32>>,
// geometry: Mutex<Delta<Option<SurfaceGeometry>>>,
on_commit: Box<dyn Fn(u32) + Send + Sync>,
pub pending_material_applications: Mutex<Vec<(Arc<Model>, u32)>>,
}
@@ -76,11 +61,9 @@ impl CoreSurface {
display: &Arc<Mutex<Display<WaylandState>>>,
dh: DisplayHandle,
surface: &WlSurface,
on_commit: impl Fn(u32) + Send + Sync + 'static,
) {
compositor::with_states(surface, |data| {
// let mut geometry: Delta<Option<SurfaceGeometry>> =
// Delta::new(data.data_map.get::<SurfaceGeometry>().cloned());
// geometry.mark_changed();
data.data_map.insert_if_missing_threadsafe(|| {
CORE_SURFACES.add(CoreSurface {
display: Arc::downgrade(display),
@@ -90,20 +73,24 @@ impl CoreSurface {
sk_tex: OnceCell::new(),
sk_mat: OnceCell::new(),
material_offset: Mutex::new(Delta::new(0)),
// geometry: Mutex::new(geometry),
on_commit: Box::new(on_commit) as Box<dyn Fn(u32) + Send + Sync>,
pending_material_applications: Mutex::new(Vec::new()),
})
});
});
}
pub fn commit(&self, count: u32) {
(self.on_commit)(count);
}
pub fn from_wl_surface(surf: &WlSurface) -> Option<Arc<CoreSurface>> {
compositor::with_states(surf, |data| {
data.data_map.get::<Arc<CoreSurface>>().cloned()
})
}
pub fn process(&self, sk: &StereoKitDraw, renderer: &mut Gles2Renderer, log: &Logger) {
pub fn process(&self, sk: &StereoKitDraw, renderer: &mut GlesRenderer) {
let Some(wl_surface) = self.wl_surface() else { return };
let sk_tex = self.sk_tex.get_or_init(|| {
@@ -122,14 +109,14 @@ impl CoreSurface {
// Let smithay handle buffer management (has to be done here as RendererSurfaceStates is not thread safe)
on_commit_buffer_handler(&wl_surface);
// Import all surface buffers into textures
if import_surface_tree(renderer, &wl_surface, log).is_err() {
if import_surface_tree(renderer, &wl_surface).is_err() {
return;
}
let mapped = compositor::with_states(&wl_surface, |data| {
data.data_map
.get::<RendererSurfaceStateUserData>()
.map(|surface_states| surface_states.borrow().wl_buffer().is_some())
.map(|surface_states| surface_states.borrow().buffer().is_some())
.unwrap_or(false)
});
@@ -147,7 +134,7 @@ impl CoreSurface {
.unwrap()
.borrow();
let smithay_tex = renderer_surface_state
.texture::<Gles2Renderer>(renderer.id())
.texture::<GlesRenderer>(renderer.id())
.unwrap()
.clone();
@@ -156,7 +143,7 @@ impl CoreSurface {
unsafe {
sk_tex.set_native(
smithay_tex.tex_id() as usize,
smithay::backend::renderer::gles2::ffi::RGBA8.into(),
smithay::backend::renderer::gles::ffi::RGBA8.into(),
TextureType::ImageNoMips,
smithay_tex.width(),
smithay_tex.height(),
@@ -195,10 +182,6 @@ impl CoreSurface {
*self.material_offset.lock().value_mut() = material_offset;
}
// pub fn set_geometry(&self, geometry: SurfaceGeometry) {
// *self.geometry.lock().value_mut() = Some(geometry);
// }
pub fn apply_material(&self, model: Arc<Model>, material_idx: u32) {
self.pending_material_applications
.lock()

View File

@@ -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);
}
}