refactor: trait away panel item backends
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
use super::{Item, ItemSpecialization, ItemType};
|
use super::{Item, ItemType};
|
||||||
use crate::{
|
use crate::{
|
||||||
core::{
|
core::{
|
||||||
client::{Client, INTERNAL_CLIENT},
|
client::{Client, INTERNAL_CLIENT},
|
||||||
@@ -52,10 +52,9 @@ impl EnvironmentItem {
|
|||||||
};
|
};
|
||||||
Ok(flexbuffers::singleton(environment_item.path.as_str()))
|
Ok(flexbuffers::singleton(environment_item.path.as_str()))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
impl ItemSpecialization for EnvironmentItem {
|
pub fn serialize_start_data(&self, id: &str) -> Result<Vec<u8>> {
|
||||||
fn serialize_start_data(&self, id: &str) -> Option<Vec<u8>> {
|
serialize((id, self.path.as_str())).map_err(|e| e.into())
|
||||||
serialize((id, self.path.as_str())).ok()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
mod environment;
|
mod environment;
|
||||||
|
pub mod panel;
|
||||||
|
|
||||||
use self::environment::{EnvironmentItem, ITEM_TYPE_INFO_ENVIRONMENT};
|
use self::environment::{EnvironmentItem, ITEM_TYPE_INFO_ENVIRONMENT};
|
||||||
|
use self::panel::{PanelItemTrait, ITEM_TYPE_INFO_PANEL};
|
||||||
use super::fields::Field;
|
use super::fields::Field;
|
||||||
use super::spatial::{find_spatial_parent, parse_transform, Spatial};
|
use super::spatial::{find_spatial_parent, parse_transform, Spatial};
|
||||||
use super::{Alias, Node};
|
use super::{Alias, Node};
|
||||||
@@ -9,8 +11,6 @@ use crate::core::node_collections::LifeLinkedNodeMap;
|
|||||||
use crate::core::registry::Registry;
|
use crate::core::registry::Registry;
|
||||||
use crate::nodes::alias::AliasInfo;
|
use crate::nodes::alias::AliasInfo;
|
||||||
use crate::nodes::fields::find_field;
|
use crate::nodes::fields::find_field;
|
||||||
#[cfg(feature = "wayland")]
|
|
||||||
use crate::wayland::panel_item::{PanelItem, ITEM_TYPE_INFO_PANEL};
|
|
||||||
use color_eyre::eyre::{ensure, eyre, Result};
|
use color_eyre::eyre::{ensure, eyre, Result};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use nanoid::nanoid;
|
use nanoid::nanoid;
|
||||||
@@ -20,7 +20,6 @@ use serde::Deserialize;
|
|||||||
use stardust_xr::schemas::flex::{deserialize, flexbuffers, serialize};
|
use stardust_xr::schemas::flex::{deserialize, flexbuffers, serialize};
|
||||||
use stardust_xr::values::Transform;
|
use stardust_xr::values::Transform;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::ops::Deref;
|
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
@@ -171,26 +170,28 @@ impl Drop for Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ItemSpecialization {
|
|
||||||
fn serialize_start_data(&self, id: &str) -> Option<Vec<u8>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum ItemType {
|
pub enum ItemType {
|
||||||
Environment(EnvironmentItem),
|
Environment(EnvironmentItem),
|
||||||
#[cfg(feature = "wayland")]
|
Panel(Arc<dyn PanelItemTrait>),
|
||||||
Panel(Arc<PanelItem>),
|
|
||||||
}
|
}
|
||||||
impl Deref for ItemType {
|
impl ItemType {
|
||||||
type Target = dyn ItemSpecialization;
|
fn serialize_start_data(&self, id: &str) -> Result<Vec<u8>> {
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
match self {
|
match self {
|
||||||
ItemType::Environment(item) => item,
|
ItemType::Environment(e) => e.serialize_start_data(id),
|
||||||
#[cfg(feature = "wayland")]
|
ItemType::Panel(p) => p.serialize_start_data(id),
|
||||||
ItemType::Panel(item) => item.as_ref(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// impl Deref for ItemType {
|
||||||
|
// type Target = dyn ItemSpecialization;
|
||||||
|
|
||||||
|
// fn deref(&self) -> &Self::Target {
|
||||||
|
// match self {
|
||||||
|
// ItemType::Environment(item) => item,
|
||||||
|
// ItemType::Panel(item) => item.as_ref(),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
pub struct ItemUI {
|
pub struct ItemUI {
|
||||||
node: Weak<Node>,
|
node: Weak<Node>,
|
||||||
@@ -240,7 +241,7 @@ impl ItemUI {
|
|||||||
self.item_aliases.add(item.uid.clone(), &alias_node);
|
self.item_aliases.add(item.uid.clone(), &alias_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(serialized_data) = item.specialization.serialize_start_data(&item.uid) else {return};
|
let Ok(serialized_data) = item.specialization.serialize_start_data(&item.uid) else {return};
|
||||||
let _ = node.send_remote_signal("create_item", &serialized_data);
|
let _ = node.send_remote_signal("create_item", &serialized_data);
|
||||||
}
|
}
|
||||||
fn handle_destroy_item(&self, item: &Item) {
|
fn handle_destroy_item(&self, item: &Item) {
|
||||||
@@ -359,7 +360,7 @@ impl ItemAcceptor {
|
|||||||
self.accepted_aliases.add(item.uid.clone(), &alias_node);
|
self.accepted_aliases.add(item.uid.clone(), &alias_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(serialized_data) = item.specialization.serialize_start_data(&item.uid) else {return};
|
let Ok(serialized_data) = item.specialization.serialize_start_data(&item.uid) else {return};
|
||||||
let _ = node.send_remote_signal("capture", &serialized_data);
|
let _ = node.send_remote_signal("capture", &serialized_data);
|
||||||
}
|
}
|
||||||
fn handle_release(&self, item: &Item) {
|
fn handle_release(&self, item: &Item) {
|
||||||
|
|||||||
490
src/nodes/items/panel.rs
Normal file
490
src/nodes/items/panel.rs
Normal file
@@ -0,0 +1,490 @@
|
|||||||
|
use crate::{
|
||||||
|
core::{
|
||||||
|
client::{get_env, startup_settings, Client, INTERNAL_CLIENT},
|
||||||
|
registry::Registry,
|
||||||
|
},
|
||||||
|
nodes::{
|
||||||
|
drawable::{model::ModelPart, Drawable},
|
||||||
|
items::{self, Item, ItemType, TypeInfo},
|
||||||
|
spatial::Spatial,
|
||||||
|
Node,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use color_eyre::eyre::{bail, eyre, Result};
|
||||||
|
use glam::Mat4;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use mint::Vector2;
|
||||||
|
use nanoid::nanoid;
|
||||||
|
use serde::{
|
||||||
|
de::{Deserializer, Error, SeqAccess, Visitor},
|
||||||
|
ser::Serializer,
|
||||||
|
Deserialize, Serialize,
|
||||||
|
};
|
||||||
|
use stardust_xr::schemas::flex::{deserialize, serialize};
|
||||||
|
use std::sync::{Arc, Weak};
|
||||||
|
use tracing::debug;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref ITEM_TYPE_INFO_PANEL: TypeInfo = TypeInfo {
|
||||||
|
type_name: "panel",
|
||||||
|
aliased_local_signals: vec![
|
||||||
|
"apply_surface_material",
|
||||||
|
"configure_toplevel",
|
||||||
|
"set_toplevel_capabilities",
|
||||||
|
"pointer_scroll",
|
||||||
|
"pointer_button",
|
||||||
|
"pointer_motion",
|
||||||
|
"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",
|
||||||
|
"new_popup",
|
||||||
|
"reposition_popup",
|
||||||
|
"drop_popup",
|
||||||
|
],
|
||||||
|
ui: Default::default(),
|
||||||
|
items: Registry::new(),
|
||||||
|
acceptors: Registry::new(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 SurfaceID {
|
||||||
|
fn default() -> Self {
|
||||||
|
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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Serialize)]
|
||||||
|
#[serde(tag = "type", content = "content")]
|
||||||
|
pub enum RecommendedState {
|
||||||
|
Maximize(bool),
|
||||||
|
Fullscreen(bool),
|
||||||
|
Minimize,
|
||||||
|
Move,
|
||||||
|
Resize(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Backend: Send + Sync + 'static {
|
||||||
|
fn serialize_start_data(&self, id: &str) -> Result<Vec<u8>>;
|
||||||
|
fn serialize_toplevel(&self) -> Result<Vec<u8>>;
|
||||||
|
fn set_toplevel_capabilities(&self, capabilities: Vec<u8>);
|
||||||
|
fn configure_toplevel(
|
||||||
|
&self,
|
||||||
|
size: Option<Vector2<u32>>,
|
||||||
|
states: Vec<u32>,
|
||||||
|
bounds: Option<Vector2<u32>>,
|
||||||
|
);
|
||||||
|
fn apply_surface_material(&self, surface: SurfaceID, model_part: &Arc<ModelPart>);
|
||||||
|
|
||||||
|
fn pointer_motion(&self, surface: &SurfaceID, position: Vector2<f32>);
|
||||||
|
fn pointer_button(&self, surface: &SurfaceID, button: u32, pressed: bool);
|
||||||
|
fn pointer_scroll(
|
||||||
|
&self,
|
||||||
|
surface: &SurfaceID,
|
||||||
|
scroll_distance: Option<Vector2<f32>>,
|
||||||
|
scroll_steps: Option<Vector2<f32>>,
|
||||||
|
);
|
||||||
|
|
||||||
|
fn keyboard_set_keymap(&self, keymap: &str) -> Result<()>;
|
||||||
|
fn keyboard_key(&self, surface: &SurfaceID, key: u32, state: bool);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn panel_item_from_node(node: &Node) -> Option<Arc<dyn PanelItemTrait>> {
|
||||||
|
let ItemType::Panel(panel_item) = &node.item.get()?.specialization else {return None};
|
||||||
|
Some(panel_item.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait PanelItemTrait: Backend + Send + Sync + 'static {
|
||||||
|
fn uid(&self) -> &str;
|
||||||
|
// fn node(&self) -> Option<Arc<Node>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PanelItem<B: Backend + ?Sized> {
|
||||||
|
pub uid: String,
|
||||||
|
node: Weak<Node>,
|
||||||
|
pub backend: Box<B>,
|
||||||
|
}
|
||||||
|
impl<B: Backend + ?Sized> PanelItem<B> {
|
||||||
|
pub fn create(backend: Box<B>, pid: Option<i32>) -> (Arc<Node>, Arc<PanelItem<B>>) {
|
||||||
|
debug!(?pid, "Create panel item");
|
||||||
|
|
||||||
|
let startup_settings = pid
|
||||||
|
.and_then(|pid| get_env(pid).ok())
|
||||||
|
.and_then(|env| startup_settings(&env));
|
||||||
|
|
||||||
|
let uid = nanoid!();
|
||||||
|
let node = Node::create(&INTERNAL_CLIENT, "/item/panel/item", &uid, true)
|
||||||
|
.add_to_scenegraph()
|
||||||
|
.unwrap();
|
||||||
|
let spatial = Spatial::add_to(&node, None, Mat4::IDENTITY, false).unwrap();
|
||||||
|
if let Some(startup_settings) = &startup_settings {
|
||||||
|
spatial.set_local_transform(
|
||||||
|
spatial.global_transform().inverse() * startup_settings.transform,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let panel_item = Arc::new(PanelItem {
|
||||||
|
uid: uid.clone(),
|
||||||
|
node: Arc::downgrade(&node),
|
||||||
|
backend,
|
||||||
|
});
|
||||||
|
|
||||||
|
let generic_panel_item: Arc<dyn PanelItemTrait> = panel_item.clone();
|
||||||
|
let item = Item::add_to(
|
||||||
|
&node,
|
||||||
|
uid,
|
||||||
|
&ITEM_TYPE_INFO_PANEL,
|
||||||
|
ItemType::Panel(generic_panel_item),
|
||||||
|
);
|
||||||
|
|
||||||
|
// panel_item
|
||||||
|
// .seat_data
|
||||||
|
// .new_surface(&wl_surface, Arc::downgrade(&panel_item));
|
||||||
|
|
||||||
|
if let Some(startup_settings) = &startup_settings {
|
||||||
|
if let Some(acceptor) = startup_settings
|
||||||
|
.acceptors
|
||||||
|
.get(&*ITEM_TYPE_INFO_PANEL)
|
||||||
|
.and_then(|acc| acc.upgrade())
|
||||||
|
{
|
||||||
|
items::capture(&item, &acceptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node.add_local_signal("apply_surface_material", Self::apply_surface_material_flex);
|
||||||
|
node.add_local_signal("configure_toplevel", Self::configure_toplevel_flex);
|
||||||
|
node.add_local_signal(
|
||||||
|
"set_toplevel_capabilities",
|
||||||
|
Self::set_toplevel_capabilities_flex,
|
||||||
|
);
|
||||||
|
node.add_local_signal("pointer_scroll", Self::pointer_scroll_flex);
|
||||||
|
node.add_local_signal("pointer_button", Self::pointer_button_flex);
|
||||||
|
node.add_local_signal("pointer_motion", Self::pointer_motion_flex);
|
||||||
|
|
||||||
|
node.add_local_signal(
|
||||||
|
"keyboard_set_keymap_string",
|
||||||
|
Self::keyboard_set_keymap_string_flex,
|
||||||
|
);
|
||||||
|
// node.add_local_signal(
|
||||||
|
// "keyboard_set_keymap_names",
|
||||||
|
// Self::keyboard_set_keymap_names_flex,
|
||||||
|
// );
|
||||||
|
node.add_local_signal("keyboard_key", Self::keyboard_key_flex);
|
||||||
|
|
||||||
|
(node, panel_item)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn node(&self) -> Option<Arc<Node>> {
|
||||||
|
self.node.upgrade()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_surface_material_flex(
|
||||||
|
node: &Node,
|
||||||
|
calling_client: Arc<Client>,
|
||||||
|
data: &[u8],
|
||||||
|
) -> Result<()> {
|
||||||
|
let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) };
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct SurfaceMaterialInfo<'a> {
|
||||||
|
surface: SurfaceID,
|
||||||
|
model_node_path: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
let info: SurfaceMaterialInfo = deserialize(data)?;
|
||||||
|
|
||||||
|
let model_node = calling_client
|
||||||
|
.scenegraph
|
||||||
|
.get_node(info.model_node_path)
|
||||||
|
.ok_or_else(|| eyre!("Model node not found"))?;
|
||||||
|
let Some(Drawable::ModelPart(model_part)) = model_node.drawable.get() else {bail!("Node is not a model")};
|
||||||
|
debug!(?info, "Apply surface material");
|
||||||
|
|
||||||
|
panel_item.apply_surface_material(info.surface, model_part);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pointer_motion_flex(node: &Node, _calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
|
||||||
|
let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) };
|
||||||
|
|
||||||
|
let (surface_id, position): (SurfaceID, Vector2<f32>) = deserialize(data)?;
|
||||||
|
debug!(?surface_id, ?position, "Pointer deactivate");
|
||||||
|
|
||||||
|
panel_item.pointer_motion(&surface_id, position);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn pointer_button_flex(node: &Node, _calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
|
||||||
|
let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) };
|
||||||
|
|
||||||
|
let (surface_id, button, state): (SurfaceID, u32, u32) = deserialize(data)?;
|
||||||
|
debug!(?surface_id, button, state, "Pointer button");
|
||||||
|
|
||||||
|
panel_item.pointer_button(&surface_id, button, state == 0);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn pointer_scroll_flex(node: &Node, _calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
|
||||||
|
let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) };
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct PointerScrollInfo {
|
||||||
|
surface_id: SurfaceID,
|
||||||
|
axis_continuous: Option<Vector2<f32>>,
|
||||||
|
axis_discrete: Option<Vector2<f32>>,
|
||||||
|
}
|
||||||
|
let info: PointerScrollInfo = deserialize(data)?;
|
||||||
|
debug!(?info, "Pointer scroll");
|
||||||
|
|
||||||
|
panel_item.pointer_scroll(&info.surface_id, info.axis_continuous, info.axis_discrete);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn keyboard_set_keymap_string_flex(
|
||||||
|
node: &Node,
|
||||||
|
_calling_client: Arc<Client>,
|
||||||
|
data: &[u8],
|
||||||
|
) -> Result<()> {
|
||||||
|
let keymap_string: &str = deserialize(data)?;
|
||||||
|
|
||||||
|
let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) };
|
||||||
|
debug!("Keyboard set keymap");
|
||||||
|
panel_item.keyboard_set_keymap(keymap_string)
|
||||||
|
|
||||||
|
// PanelItem::keyboard_set_keymap_flex(node, &keymap)
|
||||||
|
}
|
||||||
|
// fn keyboard_set_keymap_names_flex(
|
||||||
|
// node: &Node,
|
||||||
|
// _calling_client: Arc<Client>,
|
||||||
|
// data: &[u8],
|
||||||
|
// ) -> Result<()> {
|
||||||
|
// #[derive(Debug, Deserialize)]
|
||||||
|
// struct Names<'a> {
|
||||||
|
// rules: &'a str,
|
||||||
|
// model: &'a str,
|
||||||
|
// layout: &'a str,
|
||||||
|
// variant: &'a str,
|
||||||
|
// options: Option<String>,
|
||||||
|
// }
|
||||||
|
// let names: Names = deserialize(data)?;
|
||||||
|
// let context = xkb::Context::new(0);
|
||||||
|
// let keymap = Keymap::new_from_names(
|
||||||
|
// &context,
|
||||||
|
// names.rules,
|
||||||
|
// names.model,
|
||||||
|
// names.layout,
|
||||||
|
// names.variant,
|
||||||
|
// names.options,
|
||||||
|
// XKB_KEYMAP_FORMAT_TEXT_V1,
|
||||||
|
// )
|
||||||
|
// .ok_or_else(|| eyre!("Keymap is not valid"))?;
|
||||||
|
|
||||||
|
// PanelItem::keyboard_set_keymap_flex(node, &keymap)
|
||||||
|
// }
|
||||||
|
// fn keyboard_set_keymap_flex(node: &Node, keymap: &str) -> Result<()> {
|
||||||
|
// let Some(panel_item): Option<Arc<PanelItem<dyn WaylandBackend>>> = panel_item_from_node(node) else { return Ok(()) };
|
||||||
|
// debug!("Keyboard set keymap");
|
||||||
|
|
||||||
|
// panel_item.seat_data.set_keymap(
|
||||||
|
// keymap,
|
||||||
|
// match &panel_item {
|
||||||
|
// Backend::Wayland(w) => w.input_surfaces(),
|
||||||
|
// #[cfg(feature = "xwayland")]
|
||||||
|
// Backend::X11(_) => panel_item
|
||||||
|
// .toplevel_wl_surface()
|
||||||
|
// .map(|s| vec![s])
|
||||||
|
// .unwrap_or_default(),
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
|
||||||
|
// Ok(())
|
||||||
|
// }
|
||||||
|
fn keyboard_key_flex(node: &Node, _calling_client: Arc<Client>, data: &[u8]) -> Result<()> {
|
||||||
|
let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) };
|
||||||
|
let (surface_id, key, state): (SurfaceID, u32, u32) = deserialize(data)?;
|
||||||
|
debug!(key, state, "Set keyboard key state");
|
||||||
|
|
||||||
|
panel_item.keyboard_key(&surface_id, key, state == 0);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn configure_toplevel_flex(
|
||||||
|
node: &Node,
|
||||||
|
_calling_client: Arc<Client>,
|
||||||
|
data: &[u8],
|
||||||
|
) -> Result<()> {
|
||||||
|
let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) };
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct ConfigureToplevelInfo {
|
||||||
|
size: Option<Vector2<u32>>,
|
||||||
|
states: Vec<u32>,
|
||||||
|
bounds: Option<Vector2<u32>>,
|
||||||
|
}
|
||||||
|
let info: ConfigureToplevelInfo = deserialize(data)?;
|
||||||
|
|
||||||
|
panel_item.configure_toplevel(info.size, info.states, info.bounds);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_toplevel_capabilities_flex(
|
||||||
|
node: &Node,
|
||||||
|
_calling_client: Arc<Client>,
|
||||||
|
data: &[u8],
|
||||||
|
) -> Result<()> {
|
||||||
|
let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) };
|
||||||
|
|
||||||
|
let capabilities: Vec<u8> = deserialize(data)?;
|
||||||
|
debug!("Set toplevel capabilities");
|
||||||
|
panel_item.set_toplevel_capabilities(capabilities);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn commit_toplevel(&self) {
|
||||||
|
debug!("Commit toplevel");
|
||||||
|
let Some(node) = self.node.upgrade() else { return };
|
||||||
|
let Ok(data) = self.backend.serialize_toplevel() else {return};
|
||||||
|
let _ = node.send_remote_signal("commit_toplevel", &data);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn recommend_toplevel_state(&self, state: RecommendedState) {
|
||||||
|
let Some(node) = self.node.upgrade() else { return };
|
||||||
|
let data = serialize(state).unwrap();
|
||||||
|
debug!(?state, "Recommend toplevel state");
|
||||||
|
|
||||||
|
let _ = node.send_remote_signal("recommend_toplevel_state", &data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<B: Backend + ?Sized> PanelItemTrait for PanelItem<B> {
|
||||||
|
fn uid(&self) -> &str {
|
||||||
|
&self.uid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<B: Backend + ?Sized> Backend for PanelItem<B> {
|
||||||
|
fn serialize_start_data(&self, id: &str) -> Result<Vec<u8>> {
|
||||||
|
self.backend.serialize_start_data(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_toplevel(&self) -> Result<Vec<u8>> {
|
||||||
|
self.backend.serialize_toplevel()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_toplevel_capabilities(&self, capabilities: Vec<u8>) {
|
||||||
|
self.backend.set_toplevel_capabilities(capabilities)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn configure_toplevel(
|
||||||
|
&self,
|
||||||
|
size: Option<Vector2<u32>>,
|
||||||
|
states: Vec<u32>,
|
||||||
|
bounds: Option<Vector2<u32>>,
|
||||||
|
) {
|
||||||
|
self.backend.configure_toplevel(size, states, bounds)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_surface_material(&self, surface: SurfaceID, model_part: &Arc<ModelPart>) {
|
||||||
|
self.backend.apply_surface_material(surface, model_part)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pointer_motion(&self, surface: &SurfaceID, position: Vector2<f32>) {
|
||||||
|
self.backend.pointer_motion(surface, position)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pointer_button(&self, surface: &SurfaceID, button: u32, pressed: bool) {
|
||||||
|
self.backend.pointer_button(surface, button, pressed)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pointer_scroll(
|
||||||
|
&self,
|
||||||
|
surface: &SurfaceID,
|
||||||
|
scroll_distance: Option<Vector2<f32>>,
|
||||||
|
scroll_steps: Option<Vector2<f32>>,
|
||||||
|
) {
|
||||||
|
self.backend
|
||||||
|
.pointer_scroll(surface, scroll_distance, scroll_steps)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn keyboard_set_keymap(&self, keymap: &str) -> Result<()> {
|
||||||
|
self.backend.keyboard_set_keymap(keymap)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn keyboard_key(&self, surface: &SurfaceID, key: u32, state: bool) {
|
||||||
|
self.backend.keyboard_key(surface, key, state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<B: Backend + ?Sized> Drop for PanelItem<B> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// Dropped panel item, basically just a debug breakpoint place
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
mod compositor;
|
mod compositor;
|
||||||
mod data_device;
|
mod data_device;
|
||||||
mod decoration;
|
mod decoration;
|
||||||
pub mod panel_item;
|
|
||||||
mod seat;
|
mod seat;
|
||||||
mod shaders;
|
mod shaders;
|
||||||
mod state;
|
mod state;
|
||||||
@@ -37,7 +36,6 @@ use tokio::{
|
|||||||
use tracing::{debug, debug_span, info, instrument};
|
use tracing::{debug, debug_span, info, instrument};
|
||||||
|
|
||||||
pub static WAYLAND_DISPLAY: OnceCell<String> = OnceCell::new();
|
pub static WAYLAND_DISPLAY: OnceCell<String> = OnceCell::new();
|
||||||
|
|
||||||
pub static SERIAL_COUNTER: CounterU32 = CounterU32::new(0);
|
pub static SERIAL_COUNTER: CounterU32 = CounterU32::new(0);
|
||||||
|
|
||||||
struct EGLRawHandles {
|
struct EGLRawHandles {
|
||||||
@@ -90,7 +88,7 @@ impl Wayland {
|
|||||||
let (dmabuf_tx, dmabuf_rx) = mpsc::unbounded_channel();
|
let (dmabuf_tx, dmabuf_rx) = mpsc::unbounded_channel();
|
||||||
let display = Arc::new(Mutex::new(display));
|
let display = Arc::new(Mutex::new(display));
|
||||||
#[cfg(feature = "xwayland")]
|
#[cfg(feature = "xwayland")]
|
||||||
let xwayland_state = xwayland::XWaylandState::create(display.clone(), &display_handle).unwrap();
|
let xwayland_state = xwayland::XWaylandState::create(&display_handle).unwrap();
|
||||||
let wayland_state =
|
let wayland_state =
|
||||||
WaylandState::new(display.clone(), display_handle, &renderer, dmabuf_tx);
|
WaylandState::new(display.clone(), display_handle, &renderer, dmabuf_tx);
|
||||||
|
|
||||||
|
|||||||
@@ -1,805 +0,0 @@
|
|||||||
use super::{
|
|
||||||
seat::{Cursor, SeatData},
|
|
||||||
surface::CoreSurface,
|
|
||||||
xdg_shell::{PopupData, ToplevelData, XdgSurfaceData},
|
|
||||||
SERIAL_COUNTER,
|
|
||||||
};
|
|
||||||
use crate::{
|
|
||||||
core::{
|
|
||||||
client::{get_env, startup_settings, Client, INTERNAL_CLIENT},
|
|
||||||
registry::Registry,
|
|
||||||
},
|
|
||||||
nodes::{
|
|
||||||
drawable::Drawable,
|
|
||||||
items::{self, Item, ItemSpecialization, ItemType, TypeInfo},
|
|
||||||
spatial::Spatial,
|
|
||||||
Node,
|
|
||||||
},
|
|
||||||
wayland::seat::{KeyboardEvent, PointerEvent},
|
|
||||||
};
|
|
||||||
use color_eyre::eyre::{bail, eyre, Result};
|
|
||||||
use glam::Mat4;
|
|
||||||
use lazy_static::lazy_static;
|
|
||||||
use mint::Vector2;
|
|
||||||
use nanoid::nanoid;
|
|
||||||
use parking_lot::Mutex;
|
|
||||||
use rustc_hash::FxHashMap;
|
|
||||||
use serde::{
|
|
||||||
de::{Deserializer, Error, SeqAccess, Visitor},
|
|
||||||
ser::Serializer,
|
|
||||||
Deserialize, Serialize,
|
|
||||||
};
|
|
||||||
use smithay::{
|
|
||||||
reexports::{
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
wayland::compositor,
|
|
||||||
};
|
|
||||||
#[cfg(feature = "xwayland")]
|
|
||||||
use smithay::{
|
|
||||||
utils::Rectangle,
|
|
||||||
xwayland::{xwm::X11SurfaceError, X11Surface},
|
|
||||||
};
|
|
||||||
use stardust_xr::schemas::flex::{deserialize, serialize};
|
|
||||||
use std::sync::{Arc, Weak};
|
|
||||||
use tracing::debug;
|
|
||||||
use xkbcommon::xkb::{self, ffi::XKB_KEYMAP_FORMAT_TEXT_V1, Keymap};
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
pub static ref ITEM_TYPE_INFO_PANEL: TypeInfo = TypeInfo {
|
|
||||||
type_name: "panel",
|
|
||||||
aliased_local_signals: vec![
|
|
||||||
"apply_surface_material",
|
|
||||||
"configure_toplevel",
|
|
||||||
"set_toplevel_capabilities",
|
|
||||||
"pointer_scroll",
|
|
||||||
"pointer_button",
|
|
||||||
"pointer_motion",
|
|
||||||
"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",
|
|
||||||
"new_popup",
|
|
||||||
"reposition_popup",
|
|
||||||
"drop_popup",
|
|
||||||
],
|
|
||||||
ui: Default::default(),
|
|
||||||
items: Registry::new(),
|
|
||||||
acceptors: Registry::new(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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 SurfaceID {
|
|
||||||
fn default() -> Self {
|
|
||||||
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),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Serialize)]
|
|
||||||
#[serde(tag = "type", content = "content")]
|
|
||||||
pub enum RecommendedState {
|
|
||||||
Maximize(bool),
|
|
||||||
Fullscreen(bool),
|
|
||||||
Minimize,
|
|
||||||
Move,
|
|
||||||
Resize(u32),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct WaylandBackend {
|
|
||||||
toplevel: WlWeak<XdgToplevel>,
|
|
||||||
toplevel_wl_surface: WlWeak<WlSurface>,
|
|
||||||
popups: Mutex<FxHashMap<String, WlWeak<XdgPopup>>>,
|
|
||||||
cursor: Mutex<Option<WlWeak<WlSurface>>>,
|
|
||||||
}
|
|
||||||
impl WaylandBackend {
|
|
||||||
pub fn create(toplevel: XdgToplevel) -> Option<Self> {
|
|
||||||
let toplevel_wl_surface =
|
|
||||||
XdgSurfaceData::get(&ToplevelData::get(&toplevel).lock().xdg_surface()?)?
|
|
||||||
.lock()
|
|
||||||
.wl_surface()?
|
|
||||||
.downgrade();
|
|
||||||
Some(WaylandBackend {
|
|
||||||
toplevel: toplevel.downgrade(),
|
|
||||||
toplevel_wl_surface,
|
|
||||||
popups: Mutex::new(FxHashMap::default()),
|
|
||||||
cursor: Mutex::new(None),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
fn toplevel(&self) -> Option<XdgToplevel> {
|
|
||||||
self.toplevel.upgrade().ok()
|
|
||||||
}
|
|
||||||
fn toplevel_xdg_surface(&self) -> Option<XdgSurface> {
|
|
||||||
let toplevel = self.toplevel()?;
|
|
||||||
let data = ToplevelData::get(&toplevel).lock();
|
|
||||||
data.xdg_surface()
|
|
||||||
}
|
|
||||||
fn toplevel_wl_surface(&self) -> Option<WlSurface> {
|
|
||||||
self.toplevel_wl_surface.upgrade().ok()
|
|
||||||
}
|
|
||||||
fn wl_surface_from_id(&self, id: &SurfaceID) -> Option<WlSurface> {
|
|
||||||
match id {
|
|
||||||
SurfaceID::Cursor => self.cursor.lock().clone()?.upgrade().ok(),
|
|
||||||
SurfaceID::Toplevel => self.toplevel_wl_surface(),
|
|
||||||
SurfaceID::Popup(popup) => {
|
|
||||||
let popups = self.popups.lock();
|
|
||||||
let popup = popups.get(popup)?.upgrade().ok()?;
|
|
||||||
let wl_surface = PopupData::get(&popup)?.lock().wl_surface();
|
|
||||||
wl_surface
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn input_surfaces(&self) -> Vec<WlSurface> {
|
|
||||||
let mut surfaces = self
|
|
||||||
.toplevel_wl_surface()
|
|
||||||
.map(|s| vec![s])
|
|
||||||
.unwrap_or_default();
|
|
||||||
surfaces.extend(self.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();
|
|
||||||
xdg_surface_data.wl_surface()
|
|
||||||
}));
|
|
||||||
surfaces
|
|
||||||
}
|
|
||||||
|
|
||||||
fn configure_toplevel(
|
|
||||||
&self,
|
|
||||||
size: Option<Vector2<u32>>,
|
|
||||||
states: Vec<u32>,
|
|
||||||
bounds: Option<Vector2<u32>>,
|
|
||||||
) {
|
|
||||||
let Ok(xdg_toplevel) = self.toplevel.upgrade() else {return};
|
|
||||||
let Some(xdg_surface) = self.toplevel_xdg_surface() else {return};
|
|
||||||
debug!(?size, ?states, ?bounds, "Configure toplevel info");
|
|
||||||
if let Some(bounds) = bounds {
|
|
||||||
if xdg_toplevel.version() > EVT_CONFIGURE_BOUNDS_SINCE {
|
|
||||||
xdg_toplevel.configure_bounds(bounds.x as i32, bounds.y as i32);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let size = size.unwrap_or(Vector2::from([0; 2]));
|
|
||||||
xdg_toplevel.configure(
|
|
||||||
size.x as i32,
|
|
||||||
size.y as i32,
|
|
||||||
states
|
|
||||||
.into_iter()
|
|
||||||
.flat_map(|state| state.to_ne_bytes())
|
|
||||||
.collect(),
|
|
||||||
);
|
|
||||||
xdg_surface.configure(SERIAL_COUNTER.inc());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_toplevel(&self) -> Result<Vec<u8>> {
|
|
||||||
let toplevel = self
|
|
||||||
.toplevel()
|
|
||||||
.ok_or_else(|| eyre!("Toplevel does not exist"))?;
|
|
||||||
let state = ToplevelData::get(&toplevel);
|
|
||||||
let data = serialize(&state.lock().clone())?;
|
|
||||||
Ok(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_toplevel_capabilities(&self, capabilities: Vec<u8>) {
|
|
||||||
let Ok(xdg_toplevel) = self.toplevel.upgrade() else {return};
|
|
||||||
xdg_toplevel.wm_capabilities(capabilities);
|
|
||||||
let Some(xdg_surface) = self.toplevel_xdg_surface() else {return};
|
|
||||||
xdg_surface.configure(SERIAL_COUNTER.inc());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_popup(&self, panel_item: &PanelItem, popup: &XdgPopup, data: &PopupData) {
|
|
||||||
let uid = data.uid.clone();
|
|
||||||
|
|
||||||
self.popups.lock().insert(uid.clone(), popup.downgrade());
|
|
||||||
|
|
||||||
let Some(node) = panel_item.node.upgrade() else { return };
|
|
||||||
let _ = node.send_remote_signal("new_popup", &serialize(&(&uid, data)).unwrap());
|
|
||||||
}
|
|
||||||
pub fn reposition_popup(&self, panel_item: &PanelItem, popup_state: &PopupData) {
|
|
||||||
let Some(node) = panel_item.node.upgrade() else { return };
|
|
||||||
|
|
||||||
let _ = node.send_remote_signal(
|
|
||||||
"reposition_popup",
|
|
||||||
&serialize(popup_state.positioner_data().unwrap()).unwrap(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
pub fn drop_popup(&self, panel_item: &PanelItem, uid: &str) {
|
|
||||||
'seat_drop: {
|
|
||||||
let Some(popup) = self
|
|
||||||
.popups
|
|
||||||
.lock()
|
|
||||||
.remove(uid) else {break 'seat_drop};
|
|
||||||
let Some(popup) = popup.upgrade().ok() else {break 'seat_drop};
|
|
||||||
let Some(popup) = popup.data::<Arc<PopupData>>().cloned() else {break 'seat_drop};
|
|
||||||
let Some(wl_surface) = popup.wl_surface() else {break 'seat_drop};
|
|
||||||
panel_item.seat_data.drop_surface(&wl_surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
let Some(node) = panel_item.node.upgrade() else { return };
|
|
||||||
let _ = node.send_remote_signal("drop_popup", &serialize(uid).unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn popups_data(&self) -> Vec<PopupData> {
|
|
||||||
self.popups
|
|
||||||
.lock()
|
|
||||||
.values()
|
|
||||||
.filter_map(|v| Some(v.upgrade().ok()?.data::<Mutex<PopupData>>()?.lock().clone()))
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
}
|
|
||||||
fn cursor_data(&self) -> Option<(Vector2<u32>, Vector2<i32>)> {
|
|
||||||
let cursor = self.cursor.lock().as_ref().and_then(|c| c.upgrade().ok());
|
|
||||||
let cursor_size = cursor
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|c| CoreSurface::from_wl_surface(&c))
|
|
||||||
.and_then(|c| c.size());
|
|
||||||
let cursor_hotspot = cursor
|
|
||||||
.and_then(|c| {
|
|
||||||
compositor::with_states(&c, |data| data.data_map.get::<Arc<Cursor>>().cloned())
|
|
||||||
})
|
|
||||||
.map(|cursor| cursor.hotspot);
|
|
||||||
|
|
||||||
cursor_size.zip(cursor_hotspot)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_cursor(
|
|
||||||
&self,
|
|
||||||
panel_item: &PanelItem,
|
|
||||||
surface: Option<&WlSurface>,
|
|
||||||
hotspot_x: i32,
|
|
||||||
hotspot_y: i32,
|
|
||||||
) {
|
|
||||||
let Some(node) = panel_item.node.upgrade() else { return };
|
|
||||||
debug!(?surface, hotspot_x, hotspot_y, "Set cursor size");
|
|
||||||
let mut data = serialize(()).unwrap();
|
|
||||||
|
|
||||||
let cursor_size = surface
|
|
||||||
.and_then(|c| CoreSurface::from_wl_surface(c))
|
|
||||||
.and_then(|c| c.size());
|
|
||||||
|
|
||||||
if let Some(size) = cursor_size {
|
|
||||||
data = serialize((size, (hotspot_x, hotspot_y))).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
let _ = node.send_remote_signal("set_cursor", &data);
|
|
||||||
*self.cursor.lock() = surface.map(|surf| surf.downgrade());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "xwayland")]
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct X11Backend {
|
|
||||||
pub toplevel_parent: Option<X11Surface>,
|
|
||||||
pub toplevel: X11Surface,
|
|
||||||
}
|
|
||||||
#[cfg(feature = "xwayland")]
|
|
||||||
impl X11Backend {
|
|
||||||
fn configure_toplevel(
|
|
||||||
&self,
|
|
||||||
size: Option<Vector2<u32>>,
|
|
||||||
states: Vec<u32>,
|
|
||||||
) -> Result<(), X11SurfaceError> {
|
|
||||||
self.toplevel.configure(
|
|
||||||
size.map(|s| Rectangle::from_loc_and_size((0, 0), (s.x as i32, s.y as i32))),
|
|
||||||
)?;
|
|
||||||
self.toplevel.set_maximized(states.contains(&1))?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_toplevel(&self) -> Result<Vec<u8>> {
|
|
||||||
let toplevel_state = (
|
|
||||||
None::<String>,
|
|
||||||
self.toplevel.title(),
|
|
||||||
None::<String>,
|
|
||||||
(
|
|
||||||
self.toplevel.geometry().size.w,
|
|
||||||
self.toplevel.geometry().size.h,
|
|
||||||
),
|
|
||||||
self.toplevel.min_size().map(|s| (s.w, s.h)),
|
|
||||||
self.toplevel.max_size().map(|s| (s.w, s.w)),
|
|
||||||
);
|
|
||||||
let data = serialize(&toplevel_state)?;
|
|
||||||
Ok(data)
|
|
||||||
}
|
|
||||||
fn wl_surface_from_id(&self, id: &SurfaceID) -> Option<WlSurface> {
|
|
||||||
match id {
|
|
||||||
SurfaceID::Cursor => None,
|
|
||||||
SurfaceID::Toplevel => self.toplevel.wl_surface(),
|
|
||||||
SurfaceID::Popup(_) => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: abstract this away into a trait so other platforms e.g. arcan/android/windows/xrdesktop can also be 2D backends
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Backend {
|
|
||||||
Wayland(WaylandBackend),
|
|
||||||
#[cfg(feature = "xwayland")]
|
|
||||||
X11(X11Backend),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PanelItem {
|
|
||||||
pub uid: String,
|
|
||||||
node: Weak<Node>,
|
|
||||||
pub seat_data: Arc<SeatData>,
|
|
||||||
pub backend: Backend,
|
|
||||||
pointer_grab: Mutex<Option<SurfaceID>>,
|
|
||||||
keyboard_grab: Mutex<Option<SurfaceID>>,
|
|
||||||
}
|
|
||||||
impl PanelItem {
|
|
||||||
pub fn create(
|
|
||||||
wl_surface: WlSurface,
|
|
||||||
backend: Backend,
|
|
||||||
client_credentials: Option<Credentials>,
|
|
||||||
seat_data: Arc<SeatData>,
|
|
||||||
) -> (Arc<Node>, Arc<PanelItem>) {
|
|
||||||
debug!(?backend, ?client_credentials, "Create panel item");
|
|
||||||
|
|
||||||
let startup_settings = client_credentials
|
|
||||||
.and_then(|cred| get_env(cred.pid).ok())
|
|
||||||
.and_then(|env| startup_settings(&env));
|
|
||||||
|
|
||||||
let uid = nanoid!();
|
|
||||||
let node = Node::create(&INTERNAL_CLIENT, "/item/panel/item", &uid, true)
|
|
||||||
.add_to_scenegraph()
|
|
||||||
.unwrap();
|
|
||||||
let spatial = Spatial::add_to(&node, None, Mat4::IDENTITY, false).unwrap();
|
|
||||||
let panel_item = Arc::new(PanelItem {
|
|
||||||
uid: uid.clone(),
|
|
||||||
node: Arc::downgrade(&node),
|
|
||||||
seat_data,
|
|
||||||
backend,
|
|
||||||
pointer_grab: Mutex::new(None),
|
|
||||||
keyboard_grab: Mutex::new(None),
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(startup_settings) = &startup_settings {
|
|
||||||
spatial.set_local_transform(
|
|
||||||
spatial.global_transform().inverse() * startup_settings.transform,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
panel_item
|
|
||||||
.seat_data
|
|
||||||
.new_surface(&wl_surface, Arc::downgrade(&panel_item));
|
|
||||||
|
|
||||||
let item = Item::add_to(
|
|
||||||
&node,
|
|
||||||
uid,
|
|
||||||
&ITEM_TYPE_INFO_PANEL,
|
|
||||||
ItemType::Panel(panel_item.clone()),
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(startup_settings) = &startup_settings {
|
|
||||||
if let Some(acceptor) = startup_settings
|
|
||||||
.acceptors
|
|
||||||
.get(&*ITEM_TYPE_INFO_PANEL)
|
|
||||||
.and_then(|acc| acc.upgrade())
|
|
||||||
{
|
|
||||||
items::capture(&item, &acceptor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
node.add_local_signal(
|
|
||||||
"apply_surface_material",
|
|
||||||
PanelItem::apply_surface_material_flex,
|
|
||||||
);
|
|
||||||
node.add_local_signal("configure_toplevel", PanelItem::configure_toplevel_flex);
|
|
||||||
node.add_local_signal(
|
|
||||||
"set_toplevel_capabilities",
|
|
||||||
PanelItem::set_toplevel_capabilities_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_keymap_string",
|
|
||||||
PanelItem::keyboard_set_keymap_string_flex,
|
|
||||||
);
|
|
||||||
node.add_local_signal(
|
|
||||||
"keyboard_set_keymap_names",
|
|
||||||
PanelItem::keyboard_set_keymap_names_flex,
|
|
||||||
);
|
|
||||||
node.add_local_signal("keyboard_key", PanelItem::keyboard_key_flex);
|
|
||||||
|
|
||||||
(node, panel_item)
|
|
||||||
}
|
|
||||||
|
|
||||||
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_wl_surface(&self) -> Option<WlSurface> {
|
|
||||||
match &self.backend {
|
|
||||||
Backend::Wayland(w) => w.toplevel_wl_surface(),
|
|
||||||
#[cfg(feature = "xwayland")]
|
|
||||||
Backend::X11(x) => x.toplevel.wl_surface(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn core_surface(&self) -> Option<Arc<CoreSurface>> {
|
|
||||||
compositor::with_states(&self.toplevel_wl_surface()?, |data| {
|
|
||||||
data.data_map.get::<Arc<CoreSurface>>().cloned()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
fn flush_clients(&self) {
|
|
||||||
if let Some(core_surface) = self.core_surface() {
|
|
||||||
core_surface.flush_clients();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn wl_surface_from_id(&self, id: &SurfaceID) -> Option<WlSurface> {
|
|
||||||
match &self.backend {
|
|
||||||
Backend::Wayland(w) => w.wl_surface_from_id(id),
|
|
||||||
#[cfg(feature = "xwayland")]
|
|
||||||
Backend::X11(x) => x.wl_surface_from_id(id),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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_surface_material_flex(
|
|
||||||
node: &Node,
|
|
||||||
calling_client: Arc<Client>,
|
|
||||||
data: &[u8],
|
|
||||||
) -> Result<()> {
|
|
||||||
let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) };
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
struct SurfaceMaterialInfo<'a> {
|
|
||||||
surface: SurfaceID,
|
|
||||||
model_node_path: &'a str,
|
|
||||||
}
|
|
||||||
|
|
||||||
let info: SurfaceMaterialInfo = deserialize(data)?;
|
|
||||||
|
|
||||||
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_node_path)
|
|
||||||
.ok_or_else(|| eyre!("Model node not found"))?;
|
|
||||||
let Some(Drawable::ModelPart(model_node)) = model_node.drawable.get() else {bail!("Node is not a model")};
|
|
||||||
debug!(?info, "Apply surface material");
|
|
||||||
|
|
||||||
core_surface.apply_material(model_node);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (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(&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 (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(&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(()) };
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
struct PointerScrollInfo {
|
|
||||||
surface_id: SurfaceID,
|
|
||||||
axis_continuous: Option<Vector2<f32>>,
|
|
||||||
axis_discrete: Option<Vector2<f32>>,
|
|
||||||
}
|
|
||||||
let info: PointerScrollInfo = deserialize(data)?;
|
|
||||||
let wl_surface = panel_item.wl_surface_from_id_result(&info.surface_id)?;
|
|
||||||
|
|
||||||
debug!(?info, "Pointer scroll");
|
|
||||||
|
|
||||||
panel_item.seat_data.pointer_event(
|
|
||||||
&wl_surface,
|
|
||||||
PointerEvent::Scroll {
|
|
||||||
axis_continuous: info.axis_continuous,
|
|
||||||
axis_discrete: info.axis_discrete,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
panel_item.flush_clients();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn keyboard_set_keymap_string_flex(
|
|
||||||
node: &Node,
|
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
data: &[u8],
|
|
||||||
) -> Result<()> {
|
|
||||||
let context = xkb::Context::new(0);
|
|
||||||
let keymap =
|
|
||||||
Keymap::new_from_string(&context, deserialize(data)?, XKB_KEYMAP_FORMAT_TEXT_V1, 0)
|
|
||||||
.ok_or_else(|| eyre!("Keymap is not valid"))?;
|
|
||||||
|
|
||||||
PanelItem::keyboard_set_keymap_flex(node, &keymap)
|
|
||||||
}
|
|
||||||
fn keyboard_set_keymap_names_flex(
|
|
||||||
node: &Node,
|
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
data: &[u8],
|
|
||||||
) -> Result<()> {
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
struct Names<'a> {
|
|
||||||
rules: &'a str,
|
|
||||||
model: &'a str,
|
|
||||||
layout: &'a str,
|
|
||||||
variant: &'a str,
|
|
||||||
options: Option<String>,
|
|
||||||
}
|
|
||||||
let names: Names = deserialize(data)?;
|
|
||||||
let context = xkb::Context::new(0);
|
|
||||||
let keymap = Keymap::new_from_names(
|
|
||||||
&context,
|
|
||||||
names.rules,
|
|
||||||
names.model,
|
|
||||||
names.layout,
|
|
||||||
names.variant,
|
|
||||||
names.options,
|
|
||||||
XKB_KEYMAP_FORMAT_TEXT_V1,
|
|
||||||
)
|
|
||||||
.ok_or_else(|| eyre!("Keymap is not valid"))?;
|
|
||||||
|
|
||||||
PanelItem::keyboard_set_keymap_flex(node, &keymap)
|
|
||||||
}
|
|
||||||
fn keyboard_set_keymap_flex(node: &Node, keymap: &Keymap) -> Result<()> {
|
|
||||||
let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) };
|
|
||||||
debug!("Keyboard set keymap");
|
|
||||||
|
|
||||||
panel_item.seat_data.set_keymap(
|
|
||||||
keymap,
|
|
||||||
match &panel_item.backend {
|
|
||||||
Backend::Wayland(w) => w.input_surfaces(),
|
|
||||||
#[cfg(feature = "xwayland")]
|
|
||||||
Backend::X11(_) => panel_item
|
|
||||||
.toplevel_wl_surface()
|
|
||||||
.map(|s| vec![s])
|
|
||||||
.unwrap_or_default(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
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 (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(&wl_surface, KeyboardEvent::Key { key, state });
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn configure_toplevel_flex(
|
|
||||||
node: &Node,
|
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
data: &[u8],
|
|
||||||
) -> Result<()> {
|
|
||||||
let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) };
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
struct ConfigureToplevelInfo {
|
|
||||||
size: Option<Vector2<u32>>,
|
|
||||||
states: Vec<u32>,
|
|
||||||
bounds: Option<Vector2<u32>>,
|
|
||||||
}
|
|
||||||
let info: ConfigureToplevelInfo = deserialize(data)?;
|
|
||||||
|
|
||||||
match &panel_item.backend {
|
|
||||||
Backend::Wayland(w) => w.configure_toplevel(info.size, info.states, info.bounds),
|
|
||||||
#[cfg(feature = "xwayland")]
|
|
||||||
Backend::X11(x) => x.configure_toplevel(info.size, info.states)?,
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_toplevel_capabilities_flex(
|
|
||||||
node: &Node,
|
|
||||||
_calling_client: Arc<Client>,
|
|
||||||
data: &[u8],
|
|
||||||
) -> Result<()> {
|
|
||||||
let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) };
|
|
||||||
let Some(core_surface) = panel_item.core_surface() else { return Ok(()) };
|
|
||||||
if let Backend::Wayland(w) = &panel_item.backend {
|
|
||||||
let Some(toplevel) = w.toplevel() else {return Ok(())};
|
|
||||||
if toplevel.version() < EVT_WM_CAPABILITIES_SINCE {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let capabilities: Vec<u8> = deserialize(data)?;
|
|
||||||
debug!("Set toplevel capabilities");
|
|
||||||
match &panel_item.backend {
|
|
||||||
Backend::Wayland(w) => w.set_toplevel_capabilities(capabilities),
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
core_surface.flush_clients();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn commit_toplevel(&self) {
|
|
||||||
debug!("Commit toplevel");
|
|
||||||
let Some(node) = self.node.upgrade() else { return };
|
|
||||||
let Ok(data) = (match &self.backend {
|
|
||||||
Backend::Wayland(w) => w.serialize_toplevel(),
|
|
||||||
#[cfg(feature = "xwayland")]
|
|
||||||
Backend::X11(x) => x.serialize_toplevel(),
|
|
||||||
}) else {return};
|
|
||||||
let _ = node.send_remote_signal("commit_toplevel", &data);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn recommend_toplevel_state(&self, state: RecommendedState) {
|
|
||||||
let Some(node) = self.node.upgrade() else { return };
|
|
||||||
let data = serialize(state).unwrap();
|
|
||||||
debug!(?state, "Recommend toplevel state");
|
|
||||||
|
|
||||||
let _ = node.send_remote_signal("recommend_toplevel_state", &data);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 on_drop(&self) {
|
|
||||||
let Some(toplevel) = self.toplevel_wl_surface() else {return};
|
|
||||||
self.seat_data.drop_surface(&toplevel);
|
|
||||||
|
|
||||||
debug!("Dropped panel item gracefully");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl ItemSpecialization for PanelItem {
|
|
||||||
fn serialize_start_data(&self, id: &str) -> Option<Vec<u8>> {
|
|
||||||
match &self.backend {
|
|
||||||
Backend::Wayland(w) => {
|
|
||||||
let toplevel = w.toplevel()?;
|
|
||||||
let toplevel_state = ToplevelData::get(&toplevel);
|
|
||||||
let toplevel_state = toplevel_state.lock().clone();
|
|
||||||
|
|
||||||
let pointer_grab = self.pointer_grab.lock().clone();
|
|
||||||
let keyboard_grab = self.keyboard_grab.lock().clone();
|
|
||||||
|
|
||||||
serialize((
|
|
||||||
id,
|
|
||||||
(
|
|
||||||
w.cursor_data(),
|
|
||||||
toplevel_state,
|
|
||||||
w.popups_data(),
|
|
||||||
pointer_grab,
|
|
||||||
keyboard_grab,
|
|
||||||
),
|
|
||||||
))
|
|
||||||
.ok()
|
|
||||||
}
|
|
||||||
#[cfg(feature = "xwayland")]
|
|
||||||
Backend::X11(x) => {
|
|
||||||
let size = (
|
|
||||||
x.toplevel.geometry().size.w as u32,
|
|
||||||
x.toplevel.geometry().size.h as u32,
|
|
||||||
);
|
|
||||||
let toplevel_state = (
|
|
||||||
None::<String>,
|
|
||||||
x.toplevel.title(),
|
|
||||||
None::<String>,
|
|
||||||
(
|
|
||||||
x.toplevel.geometry().size.w as u32,
|
|
||||||
x.toplevel.geometry().size.h as u32,
|
|
||||||
),
|
|
||||||
x.toplevel.min_size().map(|s| (s.w as u32, s.h as u32)),
|
|
||||||
x.toplevel.max_size().map(|s| (s.w as u32, s.w as u32)),
|
|
||||||
((0_i32, 0_i32), size),
|
|
||||||
vec![0_u32; 0],
|
|
||||||
);
|
|
||||||
let info = (
|
|
||||||
None::<(Vector2<u32>, Vector2<i32>)>,
|
|
||||||
toplevel_state,
|
|
||||||
Vec::<PopupData>::new(),
|
|
||||||
None::<SurfaceID>,
|
|
||||||
None::<SurfaceID>,
|
|
||||||
);
|
|
||||||
serialize((id, info)).ok()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Drop for PanelItem {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
// Dropped panel item, basically just a debug breakpoint place
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,10 @@
|
|||||||
use super::{
|
use super::{
|
||||||
panel_item::{Backend, PanelItem},
|
state::{ClientState, WaylandState},
|
||||||
state::WaylandState,
|
|
||||||
surface::CoreSurface,
|
surface::CoreSurface,
|
||||||
GLOBAL_DESTROY_QUEUE, SERIAL_COUNTER,
|
GLOBAL_DESTROY_QUEUE, SERIAL_COUNTER,
|
||||||
};
|
};
|
||||||
use crate::core::task;
|
use crate::core::task;
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::{eyre, Result};
|
||||||
use mint::Vector2;
|
use mint::Vector2;
|
||||||
use nanoid::nanoid;
|
use nanoid::nanoid;
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
@@ -29,11 +28,12 @@ use smithay::{
|
|||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
collections::VecDeque,
|
collections::VecDeque,
|
||||||
sync::{Arc, Weak},
|
sync::Arc,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
use tokio::sync::watch;
|
||||||
use tracing::{debug, warn};
|
use tracing::{debug, warn};
|
||||||
use xkbcommon::xkb::{self, Keymap};
|
use xkbcommon::xkb::{self, ffi::XKB_KEYMAP_FORMAT_TEXT_V1, Keymap};
|
||||||
|
|
||||||
pub struct KeyboardInfo {
|
pub struct KeyboardInfo {
|
||||||
keymap: KeymapFile,
|
keymap: KeymapFile,
|
||||||
@@ -89,7 +89,7 @@ unsafe impl Send for KeyboardInfo {}
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum PointerEvent {
|
pub enum PointerEvent {
|
||||||
Motion(Vector2<f64>),
|
Motion(Vector2<f32>),
|
||||||
Button {
|
Button {
|
||||||
button: u32,
|
button: u32,
|
||||||
state: u32,
|
state: u32,
|
||||||
@@ -108,23 +108,30 @@ pub enum KeyboardEvent {
|
|||||||
const POINTER_EVENT_TIMEOUT: Duration = Duration::from_secs(1);
|
const POINTER_EVENT_TIMEOUT: Duration = Duration::from_secs(1);
|
||||||
struct SurfaceInfo {
|
struct SurfaceInfo {
|
||||||
wl_surface: WlWeak<WlSurface>,
|
wl_surface: WlWeak<WlSurface>,
|
||||||
panel_item: Weak<PanelItem>,
|
cursor_sender: watch::Sender<Option<CursorInfo>>,
|
||||||
pointer_queue: VecDeque<PointerEvent>,
|
pointer_queue: VecDeque<PointerEvent>,
|
||||||
pointer_latest_event: Instant,
|
pointer_latest_event: Instant,
|
||||||
keyboard_queue: VecDeque<KeyboardEvent>,
|
keyboard_queue: VecDeque<KeyboardEvent>,
|
||||||
keyboard_info: Option<KeyboardInfo>,
|
keyboard_info: Option<KeyboardInfo>,
|
||||||
}
|
}
|
||||||
impl SurfaceInfo {
|
impl SurfaceInfo {
|
||||||
fn new(wl_surface: &WlSurface, panel_item: Weak<PanelItem>) -> Self {
|
fn new(wl_surface: &WlSurface, cursor_sender: watch::Sender<Option<CursorInfo>>) -> Self {
|
||||||
SurfaceInfo {
|
SurfaceInfo {
|
||||||
wl_surface: wl_surface.downgrade(),
|
wl_surface: wl_surface.downgrade(),
|
||||||
panel_item,
|
cursor_sender,
|
||||||
pointer_queue: VecDeque::new(),
|
pointer_queue: VecDeque::new(),
|
||||||
pointer_latest_event: Instant::now(),
|
pointer_latest_event: Instant::now(),
|
||||||
keyboard_queue: VecDeque::new(),
|
keyboard_queue: VecDeque::new(),
|
||||||
keyboard_info: None,
|
keyboard_info: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn flush(&self) {
|
||||||
|
if let Some(client) = self.wl_surface.upgrade().ok().and_then(|s| s.client()) {
|
||||||
|
if let Some(client_data) = client.get_data::<ClientState>() {
|
||||||
|
client_data.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
fn handle_pointer_events(&mut self, pointer: &WlPointer, mut locked: bool) -> bool {
|
fn handle_pointer_events(&mut self, pointer: &WlPointer, mut locked: bool) -> bool {
|
||||||
let Ok(focus) = self.wl_surface.upgrade() 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(core_surface) = CoreSurface::from_wl_surface(&focus) else { return false; };
|
||||||
@@ -139,16 +146,16 @@ impl SurfaceInfo {
|
|||||||
pointer.enter(
|
pointer.enter(
|
||||||
SERIAL_COUNTER.inc(),
|
SERIAL_COUNTER.inc(),
|
||||||
&focus,
|
&focus,
|
||||||
pos.x.clamp(0.0, focus_size.x as f64),
|
(pos.x as f64).clamp(0.0, focus_size.x as f64),
|
||||||
pos.y.clamp(0.0, focus_size.y as f64),
|
(pos.y as f64).clamp(0.0, focus_size.y as f64),
|
||||||
);
|
);
|
||||||
locked = true;
|
locked = true;
|
||||||
}
|
}
|
||||||
(true, PointerEvent::Motion(pos)) => {
|
(true, PointerEvent::Motion(pos)) => {
|
||||||
pointer.motion(
|
pointer.motion(
|
||||||
0,
|
0,
|
||||||
pos.x.clamp(0.0, focus_size.x as f64),
|
(pos.x as f64).clamp(0.0, focus_size.x as f64),
|
||||||
pos.y.clamp(0.0, focus_size.y as f64),
|
(pos.y as f64).clamp(0.0, focus_size.y as f64),
|
||||||
);
|
);
|
||||||
if pointer.version() >= wl_pointer::EVT_FRAME_SINCE {
|
if pointer.version() >= wl_pointer::EVT_FRAME_SINCE {
|
||||||
pointer.frame();
|
pointer.frame();
|
||||||
@@ -206,6 +213,7 @@ impl SurfaceInfo {
|
|||||||
pointer.leave(SERIAL_COUNTER.inc(), &focus);
|
pointer.leave(SERIAL_COUNTER.inc(), &focus);
|
||||||
locked = false;
|
locked = false;
|
||||||
}
|
}
|
||||||
|
self.flush();
|
||||||
|
|
||||||
locked
|
locked
|
||||||
}
|
}
|
||||||
@@ -239,6 +247,7 @@ impl SurfaceInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.flush();
|
||||||
locked
|
locked
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -270,6 +279,14 @@ impl SeatData {
|
|||||||
seat_data
|
seat_data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_keymap_str(&self, keymap: &str, surfaces: Vec<WlSurface>) -> Result<()> {
|
||||||
|
let context = xkb::Context::new(0);
|
||||||
|
let keymap =
|
||||||
|
Keymap::new_from_string(&context, keymap.to_string(), XKB_KEYMAP_FORMAT_TEXT_V1, 0)
|
||||||
|
.ok_or_else(|| eyre!("Keymap is not valid"))?;
|
||||||
|
self.set_keymap(&keymap, surfaces);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
pub fn set_keymap(&self, keymap: &Keymap, surfaces: Vec<WlSurface>) {
|
pub fn set_keymap(&self, keymap: &Keymap, surfaces: Vec<WlSurface>) {
|
||||||
let mut panels = self.surfaces.lock();
|
let mut panels = self.surfaces.lock();
|
||||||
let Some((_, focus)) = self.keyboard.get() else {return};
|
let Some((_, focus)) = self.keyboard.get() else {return};
|
||||||
@@ -358,10 +375,13 @@ impl SeatData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_surface(&self, surface: &WlSurface, panel_item: Weak<PanelItem>) {
|
pub fn new_surface(&self, surface: &WlSurface) -> watch::Receiver<Option<CursorInfo>> {
|
||||||
|
let (tx, rx) = watch::channel(None);
|
||||||
self.surfaces
|
self.surfaces
|
||||||
.lock()
|
.lock()
|
||||||
.insert(surface.id(), SurfaceInfo::new(surface, panel_item));
|
.insert(surface.id(), SurfaceInfo::new(surface, tx));
|
||||||
|
|
||||||
|
rx
|
||||||
}
|
}
|
||||||
pub fn drop_surface(&self, surface: &WlSurface) {
|
pub fn drop_surface(&self, surface: &WlSurface) {
|
||||||
self.surfaces.lock().remove(&surface.id());
|
self.surfaces.lock().remove(&surface.id());
|
||||||
@@ -388,6 +408,18 @@ impl Drop for SeatData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct CursorInfo {
|
||||||
|
pub surface: WlWeak<WlSurface>,
|
||||||
|
pub hotspot_x: i32,
|
||||||
|
pub hotspot_y: i32,
|
||||||
|
}
|
||||||
|
impl CursorInfo {
|
||||||
|
pub fn cursor_data(&self) -> Option<(Vector2<u32>, Vector2<i32>)> {
|
||||||
|
let cursor_size = CoreSurface::from_wl_surface(&self.surface.upgrade().ok()?)?.size()?;
|
||||||
|
Some((cursor_size, [self.hotspot_x, self.hotspot_y].into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl GlobalDispatch<WlSeat, Arc<SeatData>, WaylandState> for WaylandState {
|
impl GlobalDispatch<WlSeat, Arc<SeatData>, WaylandState> for WaylandState {
|
||||||
fn bind(
|
fn bind(
|
||||||
_state: &mut WaylandState,
|
_state: &mut WaylandState,
|
||||||
@@ -442,12 +474,9 @@ impl Dispatch<WlSeat, Arc<SeatData>, WaylandState> for WaylandState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Cursor {
|
|
||||||
pub hotspot: Vector2<i32>,
|
|
||||||
}
|
|
||||||
impl Dispatch<WlPointer, Arc<SeatData>, WaylandState> for WaylandState {
|
impl Dispatch<WlPointer, Arc<SeatData>, WaylandState> for WaylandState {
|
||||||
fn request(
|
fn request(
|
||||||
state: &mut WaylandState,
|
_state: &mut WaylandState,
|
||||||
_client: &Client,
|
_client: &Client,
|
||||||
_resource: &WlPointer,
|
_resource: &WlPointer,
|
||||||
request: wl_pointer::Request,
|
request: wl_pointer::Request,
|
||||||
@@ -463,16 +492,8 @@ impl Dispatch<WlPointer, Arc<SeatData>, WaylandState> for WaylandState {
|
|||||||
hotspot_y,
|
hotspot_y,
|
||||||
} => {
|
} => {
|
||||||
if let Some(surface) = surface.as_ref() {
|
if let Some(surface) = surface.as_ref() {
|
||||||
CoreSurface::add_to(&state.display, dh.clone(), surface, || (), |_| ());
|
CoreSurface::add_to(dh.clone(), surface, || (), |_| ());
|
||||||
compositor::with_states(surface, |data| {
|
compositor::with_states(surface, |data| {
|
||||||
data.data_map.insert_if_missing_threadsafe(|| {
|
|
||||||
Arc::new(Mutex::new(Cursor {
|
|
||||||
hotspot: Vector2::from([hotspot_x, hotspot_y]),
|
|
||||||
}))
|
|
||||||
});
|
|
||||||
let mut cursor = data.data_map.get::<Arc<Mutex<Cursor>>>().unwrap().lock();
|
|
||||||
cursor.hotspot = Vector2::from([hotspot_x, hotspot_y]);
|
|
||||||
|
|
||||||
if let Some(core_surface) = data.data_map.get::<Arc<CoreSurface>>() {
|
if let Some(core_surface) = data.data_map.get::<Arc<CoreSurface>>() {
|
||||||
core_surface.set_material_offset(1);
|
core_surface.set_material_offset(1);
|
||||||
}
|
}
|
||||||
@@ -483,10 +504,12 @@ impl Dispatch<WlPointer, Arc<SeatData>, WaylandState> for WaylandState {
|
|||||||
let focus = focus.lock();
|
let focus = focus.lock();
|
||||||
let surfaces = seat_data.surfaces.lock();
|
let surfaces = seat_data.surfaces.lock();
|
||||||
let Some(surface_info) = surfaces.get(&focus) else {return};
|
let Some(surface_info) = surfaces.get(&focus) else {return};
|
||||||
let Some(panel_item) = surface_info.panel_item.upgrade() else {return};
|
let cursor_info = surface.map(|surface| CursorInfo {
|
||||||
if let Backend::Wayland(w) = &panel_item.backend {
|
surface: surface.downgrade(),
|
||||||
w.set_cursor(&panel_item, surface.as_ref(), hotspot_x, hotspot_y);
|
hotspot_x,
|
||||||
}
|
hotspot_y,
|
||||||
|
});
|
||||||
|
let _ = surface_info.cursor_sender.send_replace(cursor_info);
|
||||||
}
|
}
|
||||||
wl_pointer::Request::Release => (),
|
wl_pointer::Request::Release => (),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
|
|||||||
@@ -40,6 +40,13 @@ use tracing::{info, warn};
|
|||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct ClientState {
|
pub struct ClientState {
|
||||||
pub compositor_state: CompositorClientState,
|
pub compositor_state: CompositorClientState,
|
||||||
|
pub display: Weak<Mutex<Display<WaylandState>>>,
|
||||||
|
}
|
||||||
|
impl ClientState {
|
||||||
|
pub fn flush(&self) {
|
||||||
|
let Some(display) = self.display.upgrade() else {return};
|
||||||
|
let _ = display.lock().flush_clients();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl ClientData for ClientState {
|
impl ClientData for ClientState {
|
||||||
fn initialized(&self, client_id: ClientId) {
|
fn initialized(&self, client_id: ClientId) {
|
||||||
|
|||||||
@@ -15,16 +15,10 @@ use smithay::{
|
|||||||
},
|
},
|
||||||
desktop::utils::send_frames_surface_tree,
|
desktop::utils::send_frames_surface_tree,
|
||||||
output::Output,
|
output::Output,
|
||||||
reexports::wayland_server::{
|
reexports::wayland_server::{self, protocol::wl_surface::WlSurface, DisplayHandle, Resource},
|
||||||
self, protocol::wl_surface::WlSurface, Display, DisplayHandle, Resource,
|
|
||||||
},
|
|
||||||
wayland::compositor::{self, SurfaceData},
|
wayland::compositor::{self, SurfaceData},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{ffi::c_void, sync::Arc, time::Duration};
|
||||||
ffi::c_void,
|
|
||||||
sync::{Arc, Weak},
|
|
||||||
time::Duration,
|
|
||||||
};
|
|
||||||
use stereokit::{
|
use stereokit::{
|
||||||
Material, StereoKitDraw, Tex, TextureAddress, TextureFormat, TextureSample, TextureType,
|
Material, StereoKitDraw, Tex, TextureAddress, TextureFormat, TextureSample, TextureType,
|
||||||
Transparency,
|
Transparency,
|
||||||
@@ -43,7 +37,6 @@ impl Drop for CoreSurfaceData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct CoreSurface {
|
pub struct CoreSurface {
|
||||||
display: Weak<Mutex<Display<WaylandState>>>,
|
|
||||||
pub dh: DisplayHandle,
|
pub dh: DisplayHandle,
|
||||||
pub weak_surface: wayland_server::Weak<WlSurface>,
|
pub weak_surface: wayland_server::Weak<WlSurface>,
|
||||||
mapped_data: Mutex<Option<CoreSurfaceData>>,
|
mapped_data: Mutex<Option<CoreSurfaceData>>,
|
||||||
@@ -57,7 +50,6 @@ pub struct CoreSurface {
|
|||||||
|
|
||||||
impl CoreSurface {
|
impl CoreSurface {
|
||||||
pub fn add_to(
|
pub fn add_to(
|
||||||
display: &Arc<Mutex<Display<WaylandState>>>,
|
|
||||||
dh: DisplayHandle,
|
dh: DisplayHandle,
|
||||||
surface: &WlSurface,
|
surface: &WlSurface,
|
||||||
on_mapped: impl Fn() + Send + Sync + 'static,
|
on_mapped: impl Fn() + Send + Sync + 'static,
|
||||||
@@ -66,7 +58,6 @@ impl CoreSurface {
|
|||||||
compositor::with_states(surface, |data| {
|
compositor::with_states(surface, |data| {
|
||||||
data.data_map.insert_if_missing_threadsafe(|| {
|
data.data_map.insert_if_missing_threadsafe(|| {
|
||||||
CORE_SURFACES.add(CoreSurface {
|
CORE_SURFACES.add(CoreSurface {
|
||||||
display: Arc::downgrade(display),
|
|
||||||
dh,
|
dh,
|
||||||
weak_surface: surface.downgrade(),
|
weak_surface: surface.downgrade(),
|
||||||
mapped_data: Mutex::new(None),
|
mapped_data: Mutex::new(None),
|
||||||
@@ -190,8 +181,8 @@ impl CoreSurface {
|
|||||||
*self.material_offset.lock().value_mut() = material_offset;
|
*self.material_offset.lock().value_mut() = material_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply_material(&self, model_node: &Arc<ModelPart>) {
|
pub fn apply_material(&self, model_part: &Arc<ModelPart>) {
|
||||||
self.pending_material_applications.add_raw(model_node)
|
self.pending_material_applications.add_raw(model_part)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_surface_materials(&self) {
|
fn apply_surface_materials(&self) {
|
||||||
@@ -216,15 +207,6 @@ impl CoreSurface {
|
|||||||
pub fn size(&self) -> Option<Vector2<u32>> {
|
pub fn size(&self) -> Option<Vector2<u32>> {
|
||||||
self.mapped_data.lock().as_ref().map(|d| d.size)
|
self.mapped_data.lock().as_ref().map(|d| d.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn flush_clients(&self) {
|
|
||||||
self.display
|
|
||||||
.upgrade()
|
|
||||||
.unwrap()
|
|
||||||
.lock()
|
|
||||||
.flush_clients()
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
impl Drop for CoreSurface {
|
impl Drop for CoreSurface {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
|
|||||||
@@ -1,24 +1,26 @@
|
|||||||
use crate::{
|
|
||||||
nodes::Node,
|
|
||||||
wayland::panel_item::{Backend, WaylandBackend},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
panel_item::{PanelItem, RecommendedState, SurfaceID},
|
seat::{CursorInfo, KeyboardEvent, PointerEvent, SeatData},
|
||||||
state::WaylandState,
|
state::{ClientState, WaylandState},
|
||||||
surface::CoreSurface,
|
surface::CoreSurface,
|
||||||
SERIAL_COUNTER,
|
SERIAL_COUNTER,
|
||||||
};
|
};
|
||||||
|
use crate::nodes::{
|
||||||
|
drawable::model::ModelPart,
|
||||||
|
items::panel::{Backend, PanelItem, RecommendedState, SurfaceID},
|
||||||
|
Node,
|
||||||
|
};
|
||||||
|
use color_eyre::eyre::{eyre, Result};
|
||||||
use mint::Vector2;
|
use mint::Vector2;
|
||||||
use nanoid::nanoid;
|
use nanoid::nanoid;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
use rustc_hash::FxHashMap;
|
||||||
use serde::{ser::SerializeSeq, Serialize, Serializer};
|
use serde::{ser::SerializeSeq, Serialize, Serializer};
|
||||||
use smithay::reexports::{
|
use smithay::reexports::{
|
||||||
wayland_protocols::xdg::shell::server::{
|
wayland_protocols::xdg::shell::server::{
|
||||||
xdg_popup::{self, XdgPopup},
|
xdg_popup::{self, XdgPopup},
|
||||||
xdg_positioner::{self, Anchor, ConstraintAdjustment, Gravity, XdgPositioner},
|
xdg_positioner::{self, Anchor, ConstraintAdjustment, Gravity, XdgPositioner},
|
||||||
xdg_surface::{self, XdgSurface},
|
xdg_surface::{self, XdgSurface},
|
||||||
xdg_toplevel::{self, XdgToplevel, EVT_WM_CAPABILITIES_SINCE},
|
xdg_toplevel::{self, XdgToplevel, EVT_CONFIGURE_BOUNDS_SINCE, EVT_WM_CAPABILITIES_SINCE},
|
||||||
xdg_wm_base::{self, XdgWmBase},
|
xdg_wm_base::{self, XdgWmBase},
|
||||||
},
|
},
|
||||||
wayland_server::{
|
wayland_server::{
|
||||||
@@ -28,11 +30,14 @@ use smithay::reexports::{
|
|||||||
Weak as WlWeak,
|
Weak as WlWeak,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
use stardust_xr::schemas::flex::serialize;
|
||||||
use std::{
|
use std::{
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
sync::{Arc, Weak},
|
sync::{Arc, Weak},
|
||||||
};
|
};
|
||||||
|
use tokio::sync::watch;
|
||||||
use tracing::{debug, warn};
|
use tracing::{debug, warn};
|
||||||
|
use xkbcommon::xkb::{self, ffi::XKB_KEYMAP_FORMAT_TEXT_V1, Keymap};
|
||||||
|
|
||||||
impl GlobalDispatch<XdgWmBase, (), WaylandState> for WaylandState {
|
impl GlobalDispatch<XdgWmBase, (), WaylandState> for WaylandState {
|
||||||
fn bind(
|
fn bind(
|
||||||
@@ -208,7 +213,7 @@ impl Default for Geometry {
|
|||||||
pub struct XdgSurfaceData {
|
pub struct XdgSurfaceData {
|
||||||
wl_surface: WlWeak<WlSurface>,
|
wl_surface: WlWeak<WlSurface>,
|
||||||
surface_id: SurfaceID,
|
surface_id: SurfaceID,
|
||||||
panel_item: Weak<PanelItem>,
|
panel_item: Weak<PanelItem<XDGBackend>>,
|
||||||
geometry: Option<Geometry>,
|
geometry: Option<Geometry>,
|
||||||
}
|
}
|
||||||
impl XdgSurfaceData {
|
impl XdgSurfaceData {
|
||||||
@@ -226,7 +231,7 @@ impl XdgSurfaceData {
|
|||||||
pub fn wl_surface(&self) -> Option<WlSurface> {
|
pub fn wl_surface(&self) -> Option<WlSurface> {
|
||||||
self.wl_surface.upgrade().ok()
|
self.wl_surface.upgrade().ok()
|
||||||
}
|
}
|
||||||
pub fn panel_item(&self) -> Option<Arc<PanelItem>> {
|
pub fn panel_item(&self) -> Option<Arc<PanelItem<XDGBackend>>> {
|
||||||
self.panel_item.upgrade()
|
self.panel_item.upgrade()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -276,7 +281,6 @@ impl Dispatch<XdgSurface, Mutex<XdgSurfaceData>, WaylandState> for WaylandState
|
|||||||
let seat_data = state.seats.get(&client.id()).unwrap().clone();
|
let seat_data = state.seats.get(&client.id()).unwrap().clone();
|
||||||
let Some(wl_surface) = xdg_surface_data.lock().wl_surface() else {return};
|
let Some(wl_surface) = xdg_surface_data.lock().wl_surface() else {return};
|
||||||
CoreSurface::add_to(
|
CoreSurface::add_to(
|
||||||
&state.display,
|
|
||||||
state.display_handle.clone(),
|
state.display_handle.clone(),
|
||||||
&wl_surface,
|
&wl_surface,
|
||||||
{
|
{
|
||||||
@@ -286,15 +290,12 @@ impl Dispatch<XdgSurface, Mutex<XdgSurfaceData>, WaylandState> for WaylandState
|
|||||||
let toplevel_data = ToplevelData::get(&toplevel);
|
let toplevel_data = ToplevelData::get(&toplevel);
|
||||||
let Some(xdg_surface) = toplevel_data.lock().xdg_surface() else {return};
|
let Some(xdg_surface) = toplevel_data.lock().xdg_surface() else {return};
|
||||||
let Some(xdg_surface_data) = XdgSurfaceData::get(&xdg_surface) else {return};
|
let Some(xdg_surface_data) = XdgSurfaceData::get(&xdg_surface) else {return};
|
||||||
let Some(wl_surface) = xdg_surface_data.lock().wl_surface() else {return};
|
|
||||||
|
|
||||||
xdg_surface_data.lock().surface_id = SurfaceID::Toplevel;
|
xdg_surface_data.lock().surface_id = SurfaceID::Toplevel;
|
||||||
let Some(backend) = WaylandBackend::create(toplevel.clone()) else {return};
|
let Some(backend) = XDGBackend::create(toplevel.clone(), seat_data.clone()) else {return};
|
||||||
let (node, panel_item) = PanelItem::create(
|
let (node, panel_item) = PanelItem::create(
|
||||||
wl_surface.clone(),
|
Box::new(backend),
|
||||||
Backend::Wayland(backend),
|
client_credentials.map(|c| c.pid),
|
||||||
client_credentials,
|
|
||||||
seat_data.clone(),
|
|
||||||
);
|
);
|
||||||
toplevel_data.lock().panel_item_node.replace(node);
|
toplevel_data.lock().panel_item_node.replace(node);
|
||||||
xdg_surface_data.lock().panel_item = Arc::downgrade(&panel_item);
|
xdg_surface_data.lock().panel_item = Arc::downgrade(&panel_item);
|
||||||
@@ -355,16 +356,11 @@ impl Dispatch<XdgSurface, Mutex<XdgSurfaceData>, WaylandState> for WaylandState
|
|||||||
xdg_surface_data.lock().surface_id = SurfaceID::Popup(uid);
|
xdg_surface_data.lock().surface_id = SurfaceID::Popup(uid);
|
||||||
let panel_item = parent_data.panel_item().unwrap();
|
let panel_item = parent_data.panel_item().unwrap();
|
||||||
xdg_surface_data.lock().panel_item = Arc::downgrade(&panel_item);
|
xdg_surface_data.lock().panel_item = Arc::downgrade(&panel_item);
|
||||||
let Some(wl_surface) = xdg_surface_data.lock().wl_surface() else {return};
|
|
||||||
panel_item
|
|
||||||
.seat_data
|
|
||||||
.new_surface(&wl_surface, Arc::downgrade(&panel_item));
|
|
||||||
debug!(?xdg_popup, ?xdg_surface, "Create XDG popup");
|
debug!(?xdg_popup, ?xdg_surface, "Create XDG popup");
|
||||||
|
|
||||||
let xdg_surface = xdg_surface.downgrade();
|
let xdg_surface = xdg_surface.downgrade();
|
||||||
let xdg_popup = xdg_popup.downgrade();
|
let xdg_popup = xdg_popup.downgrade();
|
||||||
CoreSurface::add_to(
|
CoreSurface::add_to(
|
||||||
&state.display,
|
|
||||||
state.display_handle.clone(),
|
state.display_handle.clone(),
|
||||||
&xdg_surface_data.lock().wl_surface.upgrade().unwrap(),
|
&xdg_surface_data.lock().wl_surface.upgrade().unwrap(),
|
||||||
move || {
|
move || {
|
||||||
@@ -372,8 +368,9 @@ impl Dispatch<XdgSurface, Mutex<XdgSurfaceData>, WaylandState> for WaylandState
|
|||||||
let Some(popup_data) = PopupData::get(&xdg_popup) else {return};
|
let Some(popup_data) = PopupData::get(&xdg_popup) else {return};
|
||||||
let popup_data = popup_data.lock();
|
let popup_data = popup_data.lock();
|
||||||
// panel_item.commit_popup(popup_data);
|
// panel_item.commit_popup(popup_data);
|
||||||
let Backend::Wayland(wayland_backend) = &panel_item.backend else {return};
|
panel_item
|
||||||
wayland_backend.new_popup(&panel_item, &xdg_popup, &*popup_data);
|
.backend
|
||||||
|
.new_popup(&panel_item, &xdg_popup, &*popup_data);
|
||||||
},
|
},
|
||||||
move |commit_count| {
|
move |commit_count| {
|
||||||
if commit_count == 0 {
|
if commit_count == 0 {
|
||||||
@@ -448,7 +445,7 @@ impl ToplevelData {
|
|||||||
pub fn xdg_surface(&self) -> Option<XdgSurface> {
|
pub fn xdg_surface(&self) -> Option<XdgSurface> {
|
||||||
self.xdg_surface.upgrade().ok()
|
self.xdg_surface.upgrade().ok()
|
||||||
}
|
}
|
||||||
fn panel_item(&self) -> Option<Arc<PanelItem>> {
|
fn panel_item(&self) -> Option<Arc<PanelItem<XDGBackend>>> {
|
||||||
let xdg_surface = self.xdg_surface()?;
|
let xdg_surface = self.xdg_surface()?;
|
||||||
let xdg_surface_data = XdgSurfaceData::get(&xdg_surface)?.lock();
|
let xdg_surface_data = XdgSurfaceData::get(&xdg_surface)?.lock();
|
||||||
xdg_surface_data.panel_item()
|
xdg_surface_data.panel_item()
|
||||||
@@ -576,7 +573,7 @@ impl Dispatch<XdgToplevel, Mutex<ToplevelData>, WaylandState> for WaylandState {
|
|||||||
xdg_toplevel::Request::Destroy => {
|
xdg_toplevel::Request::Destroy => {
|
||||||
debug!(?xdg_toplevel, "Destroy XDG Toplevel");
|
debug!(?xdg_toplevel, "Destroy XDG Toplevel");
|
||||||
let Some(panel_item) = data.lock().panel_item() else { return };
|
let Some(panel_item) = data.lock().panel_item() else { return };
|
||||||
panel_item.on_drop();
|
panel_item.backend.on_drop();
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
@@ -613,7 +610,7 @@ impl PopupData {
|
|||||||
self.xdg_surface.upgrade().ok()
|
self.xdg_surface.upgrade().ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn panel_item(&self) -> Option<Arc<PanelItem>> {
|
fn panel_item(&self) -> Option<Arc<PanelItem<XDGBackend>>> {
|
||||||
XdgSurfaceData::get(&self.xdg_surface()?)?
|
XdgSurfaceData::get(&self.xdg_surface()?)?
|
||||||
.lock()
|
.lock()
|
||||||
.panel_item()
|
.panel_item()
|
||||||
@@ -680,9 +677,7 @@ impl Dispatch<XdgPopup, Mutex<PopupData>, WaylandState> for WaylandState {
|
|||||||
data.positioner = positioner;
|
data.positioner = positioner;
|
||||||
let Some(panel_item) = data.panel_item() else {return};
|
let Some(panel_item) = data.panel_item() else {return};
|
||||||
|
|
||||||
if let Backend::Wayland(w) = &panel_item.backend {
|
panel_item.backend.reposition_popup(&panel_item, &*data)
|
||||||
w.reposition_popup(&panel_item, &*data)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
xdg_popup::Request::Destroy => {
|
xdg_popup::Request::Destroy => {
|
||||||
let data = data.lock();
|
let data = data.lock();
|
||||||
@@ -704,9 +699,258 @@ impl Dispatch<XdgPopup, Mutex<PopupData>, WaylandState> for WaylandState {
|
|||||||
) {
|
) {
|
||||||
let data = data.lock();
|
let data = data.lock();
|
||||||
let Some(panel_item) = data.panel_item() else {return};
|
let Some(panel_item) = data.panel_item() else {return};
|
||||||
|
panel_item.backend.drop_popup(&panel_item, &data.uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Backend::Wayland(w) = &panel_item.backend {
|
pub struct XDGBackend {
|
||||||
w.drop_popup(&panel_item, &data.uid);
|
toplevel: WlWeak<XdgToplevel>,
|
||||||
|
toplevel_wl_surface: WlWeak<WlSurface>,
|
||||||
|
popups: Mutex<FxHashMap<String, WlWeak<XdgPopup>>>,
|
||||||
|
cursor: watch::Receiver<Option<CursorInfo>>,
|
||||||
|
pub seat: Arc<SeatData>,
|
||||||
|
pointer_grab: Mutex<Option<SurfaceID>>,
|
||||||
|
keyboard_grab: Mutex<Option<SurfaceID>>,
|
||||||
|
}
|
||||||
|
impl XDGBackend {
|
||||||
|
pub fn create(toplevel: XdgToplevel, seat: Arc<SeatData>) -> Option<Self> {
|
||||||
|
let toplevel_wl_surface =
|
||||||
|
XdgSurfaceData::get(&ToplevelData::get(&toplevel).lock().xdg_surface()?)?
|
||||||
|
.lock()
|
||||||
|
.wl_surface()?
|
||||||
|
.downgrade();
|
||||||
|
|
||||||
|
let cursor = seat.new_surface(&toplevel_wl_surface.upgrade().ok()?);
|
||||||
|
Some(XDGBackend {
|
||||||
|
toplevel: toplevel.downgrade(),
|
||||||
|
toplevel_wl_surface,
|
||||||
|
popups: Mutex::new(FxHashMap::default()),
|
||||||
|
cursor,
|
||||||
|
seat,
|
||||||
|
pointer_grab: Mutex::new(None),
|
||||||
|
keyboard_grab: Mutex::new(None),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fn wl_surface_from_id(&self, id: &SurfaceID) -> Option<WlSurface> {
|
||||||
|
match id {
|
||||||
|
SurfaceID::Cursor => self.cursor.borrow().as_ref()?.surface.upgrade().ok(),
|
||||||
|
SurfaceID::Toplevel => self.toplevel_wl_surface(),
|
||||||
|
SurfaceID::Popup(popup) => {
|
||||||
|
let popups = self.popups.lock();
|
||||||
|
let popup = popups.get(popup)?.upgrade().ok()?;
|
||||||
|
let wl_surface = PopupData::get(&popup)?.lock().wl_surface();
|
||||||
|
wl_surface
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn toplevel(&self) -> Option<XdgToplevel> {
|
||||||
|
self.toplevel.upgrade().ok()
|
||||||
|
}
|
||||||
|
fn toplevel_xdg_surface(&self) -> Option<XdgSurface> {
|
||||||
|
let toplevel = self.toplevel()?;
|
||||||
|
let data = ToplevelData::get(&toplevel).lock();
|
||||||
|
data.xdg_surface()
|
||||||
|
}
|
||||||
|
fn toplevel_wl_surface(&self) -> Option<WlSurface> {
|
||||||
|
self.toplevel_wl_surface.upgrade().ok()
|
||||||
|
}
|
||||||
|
fn input_surfaces(&self) -> Vec<WlSurface> {
|
||||||
|
let mut surfaces = self
|
||||||
|
.toplevel_wl_surface()
|
||||||
|
.map(|s| vec![s])
|
||||||
|
.unwrap_or_default();
|
||||||
|
surfaces.extend(self.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();
|
||||||
|
xdg_surface_data.wl_surface()
|
||||||
|
}));
|
||||||
|
surfaces
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_popup(
|
||||||
|
&self,
|
||||||
|
panel_item: &PanelItem<XDGBackend>,
|
||||||
|
popup: &XdgPopup,
|
||||||
|
data: &PopupData,
|
||||||
|
) {
|
||||||
|
let uid = data.uid.clone();
|
||||||
|
|
||||||
|
self.popups.lock().insert(uid.clone(), popup.downgrade());
|
||||||
|
|
||||||
|
let Some(node) = panel_item.node() else { return };
|
||||||
|
let _ = node.send_remote_signal("new_popup", &serialize(&(&uid, data)).unwrap());
|
||||||
|
}
|
||||||
|
pub fn reposition_popup(&self, panel_item: &PanelItem<XDGBackend>, popup_state: &PopupData) {
|
||||||
|
let Some(node) = panel_item.node() else { return };
|
||||||
|
|
||||||
|
let _ = node.send_remote_signal(
|
||||||
|
"reposition_popup",
|
||||||
|
&serialize(popup_state.positioner_data().unwrap()).unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
pub fn drop_popup(&self, panel_item: &PanelItem<XDGBackend>, uid: &str) {
|
||||||
|
'seat_drop: {
|
||||||
|
let Some(popup) = self
|
||||||
|
.popups
|
||||||
|
.lock()
|
||||||
|
.remove(uid) else {break 'seat_drop};
|
||||||
|
let Some(popup) = popup.upgrade().ok() else {break 'seat_drop};
|
||||||
|
let Some(popup) = popup.data::<Arc<PopupData>>().cloned() else {break 'seat_drop};
|
||||||
|
let Some(wl_surface) = popup.wl_surface() else {break 'seat_drop};
|
||||||
|
self.seat.drop_surface(&wl_surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(node) = panel_item.node() else { return };
|
||||||
|
let _ = node.send_remote_signal("drop_popup", &serialize(uid).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn popups_data(&self) -> Vec<PopupData> {
|
||||||
|
self.popups
|
||||||
|
.lock()
|
||||||
|
.values()
|
||||||
|
.filter_map(|v| Some(v.upgrade().ok()?.data::<Mutex<PopupData>>()?.lock().clone()))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_drop(&self) {
|
||||||
|
let Some(toplevel) = self.toplevel_wl_surface() else {return};
|
||||||
|
self.seat.drop_surface(&toplevel);
|
||||||
|
|
||||||
|
debug!("Dropped panel item gracefully");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush_client(&self) {
|
||||||
|
let Some(client) = self.toplevel_wl_surface().and_then(|s| s.client()) else {return};
|
||||||
|
if let Some(client_state) = client.get_data::<ClientState>() {
|
||||||
|
client_state.flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl Backend for XDGBackend {
|
||||||
|
fn serialize_start_data(&self, id: &str) -> Result<Vec<u8>> {
|
||||||
|
let toplevel_state = self
|
||||||
|
.toplevel()
|
||||||
|
.map(|t| ToplevelData::get(&t).lock().clone());
|
||||||
|
|
||||||
|
let pointer_grab = self.pointer_grab.lock().clone();
|
||||||
|
let keyboard_grab = self.keyboard_grab.lock().clone();
|
||||||
|
|
||||||
|
serialize((
|
||||||
|
id,
|
||||||
|
(
|
||||||
|
self.cursor.borrow().as_ref().and_then(|c| c.cursor_data()),
|
||||||
|
toplevel_state,
|
||||||
|
self.popups_data(),
|
||||||
|
pointer_grab,
|
||||||
|
keyboard_grab,
|
||||||
|
),
|
||||||
|
))
|
||||||
|
.map_err(|e| e.into())
|
||||||
|
}
|
||||||
|
fn serialize_toplevel(&self) -> Result<Vec<u8>> {
|
||||||
|
let toplevel = self
|
||||||
|
.toplevel()
|
||||||
|
.ok_or_else(|| eyre!("Toplevel does not exist"))?;
|
||||||
|
let state = ToplevelData::get(&toplevel);
|
||||||
|
let data = serialize(&state.lock().clone())?;
|
||||||
|
Ok(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_toplevel_capabilities(&self, capabilities: Vec<u8>) {
|
||||||
|
let Ok(xdg_toplevel) = self.toplevel.upgrade() else {return};
|
||||||
|
let Some(xdg_surface) = self.toplevel_xdg_surface() else {return};
|
||||||
|
|
||||||
|
if xdg_toplevel.version() < EVT_WM_CAPABILITIES_SINCE {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
xdg_toplevel.wm_capabilities(capabilities);
|
||||||
|
xdg_surface.configure(SERIAL_COUNTER.inc());
|
||||||
|
self.flush_client();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn configure_toplevel(
|
||||||
|
&self,
|
||||||
|
size: Option<Vector2<u32>>,
|
||||||
|
states: Vec<u32>,
|
||||||
|
bounds: Option<Vector2<u32>>,
|
||||||
|
) {
|
||||||
|
let Ok(xdg_toplevel) = self.toplevel.upgrade() else {return};
|
||||||
|
let Some(xdg_surface) = self.toplevel_xdg_surface() else {return};
|
||||||
|
debug!(?size, ?states, ?bounds, "Configure toplevel info");
|
||||||
|
if let Some(bounds) = bounds {
|
||||||
|
if xdg_toplevel.version() > EVT_CONFIGURE_BOUNDS_SINCE {
|
||||||
|
xdg_toplevel.configure_bounds(bounds.x as i32, bounds.y as i32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let size = size.unwrap_or(Vector2::from([0; 2]));
|
||||||
|
xdg_toplevel.configure(
|
||||||
|
size.x as i32,
|
||||||
|
size.y as i32,
|
||||||
|
states
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|state| state.to_ne_bytes())
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
|
xdg_surface.configure(SERIAL_COUNTER.inc());
|
||||||
|
self.flush_client();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_surface_material(&self, surface: SurfaceID, model_part: &Arc<ModelPart>) {
|
||||||
|
let Some(wl_surface) = self.wl_surface_from_id(&surface) else {return};
|
||||||
|
let Some(core_surface) = CoreSurface::from_wl_surface(&wl_surface) else {return};
|
||||||
|
|
||||||
|
core_surface.apply_material(model_part);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pointer_motion(&self, surface: &SurfaceID, position: Vector2<f32>) {
|
||||||
|
let Some(surface) = self.wl_surface_from_id(surface) else {return};
|
||||||
|
self.seat
|
||||||
|
.pointer_event(&surface, PointerEvent::Motion(position));
|
||||||
|
}
|
||||||
|
fn pointer_button(&self, surface: &SurfaceID, button: u32, pressed: bool) {
|
||||||
|
let Some(surface) = self.wl_surface_from_id(surface) else {return};
|
||||||
|
self.seat.pointer_event(
|
||||||
|
&surface,
|
||||||
|
PointerEvent::Button {
|
||||||
|
button,
|
||||||
|
state: if pressed { 1 } else { 0 },
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
fn pointer_scroll(
|
||||||
|
&self,
|
||||||
|
surface: &SurfaceID,
|
||||||
|
scroll_distance: Option<Vector2<f32>>,
|
||||||
|
scroll_steps: Option<Vector2<f32>>,
|
||||||
|
) {
|
||||||
|
let Some(surface) = self.wl_surface_from_id(surface) else {return};
|
||||||
|
self.seat.pointer_event(
|
||||||
|
&surface,
|
||||||
|
PointerEvent::Scroll {
|
||||||
|
axis_continuous: scroll_distance,
|
||||||
|
axis_discrete: scroll_steps,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn keyboard_set_keymap(&self, keymap: &str) -> Result<()> {
|
||||||
|
let context = xkb::Context::new(0);
|
||||||
|
let keymap =
|
||||||
|
Keymap::new_from_string(&context, keymap.to_string(), XKB_KEYMAP_FORMAT_TEXT_V1, 0)
|
||||||
|
.ok_or_else(|| eyre!("Keymap is not valid"))?;
|
||||||
|
self.seat.set_keymap(&keymap, self.input_surfaces());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn keyboard_key(&self, surface: &SurfaceID, key: u32, state: bool) {
|
||||||
|
let Some(surface) = self.wl_surface_from_id(surface) else {return};
|
||||||
|
self.seat.keyboard_event(
|
||||||
|
&surface,
|
||||||
|
KeyboardEvent::Key {
|
||||||
|
key,
|
||||||
|
state: if state { 1 } else { 0 },
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,16 +1,23 @@
|
|||||||
use super::{panel_item::RecommendedState, seat::SeatData, state::WaylandState};
|
use super::{
|
||||||
use crate::wayland::{
|
seat::{KeyboardEvent, PointerEvent, SeatData},
|
||||||
panel_item::{Backend, PanelItem, X11Backend},
|
xdg_shell::PopupData,
|
||||||
surface::CoreSurface,
|
};
|
||||||
|
use crate::{
|
||||||
|
nodes::{
|
||||||
|
drawable::model::ModelPart,
|
||||||
|
items::panel::{Backend, PanelItem, RecommendedState, SurfaceID},
|
||||||
|
},
|
||||||
|
wayland::surface::CoreSurface,
|
||||||
};
|
};
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
|
use mint::Vector2;
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use smithay::{
|
use smithay::{
|
||||||
reexports::{
|
reexports::{
|
||||||
calloop::{EventLoop, LoopSignal},
|
calloop::{EventLoop, LoopSignal},
|
||||||
wayland_protocols::xdg::shell::server::xdg_toplevel,
|
wayland_protocols::xdg::shell::server::xdg_toplevel,
|
||||||
wayland_server::{Display, DisplayHandle, Resource, WEnum},
|
wayland_server::{protocol::wl_surface::WlSurface, DisplayHandle, Resource, WEnum},
|
||||||
x11rb::protocol::xproto::Window,
|
x11rb::protocol::xproto::Window,
|
||||||
},
|
},
|
||||||
utils::{Logical, Rectangle},
|
utils::{Logical, Rectangle},
|
||||||
@@ -20,6 +27,7 @@ use smithay::{
|
|||||||
X11Surface, X11Wm, XWayland, XWaylandEvent, XwmHandler,
|
X11Surface, X11Wm, XWayland, XWaylandEvent, XwmHandler,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
use stardust_xr::schemas::flex::serialize;
|
||||||
use std::{ffi::OsStr, iter::empty, sync::Arc, time::Duration};
|
use std::{ffi::OsStr, iter::empty, sync::Arc, time::Duration};
|
||||||
use tokio::sync::oneshot;
|
use tokio::sync::oneshot;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
@@ -31,10 +39,7 @@ pub struct XWaylandState {
|
|||||||
event_loop_signal: LoopSignal,
|
event_loop_signal: LoopSignal,
|
||||||
}
|
}
|
||||||
impl XWaylandState {
|
impl XWaylandState {
|
||||||
pub fn create(
|
pub fn create(dh: &DisplayHandle) -> Result<Self> {
|
||||||
wayland_display: Arc<Mutex<Display<WaylandState>>>,
|
|
||||||
dh: &DisplayHandle,
|
|
||||||
) -> Result<Self> {
|
|
||||||
let dh = dh.clone();
|
let dh = dh.clone();
|
||||||
|
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
@@ -45,18 +50,22 @@ impl XWaylandState {
|
|||||||
let handle = event_loop.handle();
|
let handle = event_loop.handle();
|
||||||
event_loop
|
event_loop
|
||||||
.handle()
|
.handle()
|
||||||
.insert_source(connection, move |event, _, handler| match event {
|
.insert_source(connection, {
|
||||||
XWaylandEvent::Ready {
|
let dh = dh.clone();
|
||||||
connection,
|
move |event, _, handler| match event {
|
||||||
client,
|
XWaylandEvent::Ready {
|
||||||
client_fd: _,
|
connection,
|
||||||
display: _,
|
client,
|
||||||
} => {
|
client_fd: _,
|
||||||
handler.seat = Some(SeatData::new(&dh, client.id()));
|
display: _,
|
||||||
handler.wm =
|
} => {
|
||||||
X11Wm::start_wm(handle.clone(), dh.clone(), connection, client).ok();
|
handler.seat = Some(SeatData::new(&dh, client.id()));
|
||||||
|
handler.wm =
|
||||||
|
X11Wm::start_wm(handle.clone(), dh.clone(), connection, client)
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
XWaylandEvent::Exited => (),
|
||||||
}
|
}
|
||||||
XWaylandEvent::Exited => (),
|
|
||||||
})
|
})
|
||||||
.map_err(|e| e.error)?;
|
.map_err(|e| e.error)?;
|
||||||
|
|
||||||
@@ -71,10 +80,8 @@ impl XWaylandState {
|
|||||||
display,
|
display,
|
||||||
event_loop_signal: event_loop.get_signal(),
|
event_loop_signal: event_loop.get_signal(),
|
||||||
});
|
});
|
||||||
let wayland_display_handle = wayland_display.lock().handle();
|
|
||||||
let mut handler = XWaylandHandler {
|
let mut handler = XWaylandHandler {
|
||||||
wayland_display,
|
wayland_display_handle: dh,
|
||||||
wayland_display_handle,
|
|
||||||
wm: None,
|
wm: None,
|
||||||
seat: None,
|
seat: None,
|
||||||
};
|
};
|
||||||
@@ -94,15 +101,14 @@ impl Drop for XWaylandState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct XWaylandHandler {
|
struct XWaylandHandler {
|
||||||
wayland_display: Arc<Mutex<Display<WaylandState>>>,
|
|
||||||
wayland_display_handle: DisplayHandle,
|
wayland_display_handle: DisplayHandle,
|
||||||
wm: Option<X11Wm>,
|
wm: Option<X11Wm>,
|
||||||
seat: Option<Arc<SeatData>>,
|
seat: Option<Arc<SeatData>>,
|
||||||
}
|
}
|
||||||
impl XWaylandHandler {
|
impl XWaylandHandler {
|
||||||
fn panel_item(&self, window: &X11Surface) -> Option<Arc<PanelItem>> {
|
fn panel_item(&self, window: &X11Surface) -> Option<Arc<PanelItem<X11Backend>>> {
|
||||||
compositor::with_states(&window.wl_surface()?, |s| {
|
compositor::with_states(&window.wl_surface()?, |s| {
|
||||||
s.data_map.get::<Arc<PanelItem>>().cloned()
|
s.data_map.get::<Arc<PanelItem<X11Backend>>>().cloned()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -130,8 +136,7 @@ impl XwmHandler for XWaylandHandler {
|
|||||||
let dh = self.wayland_display_handle.clone();
|
let dh = self.wayland_display_handle.clone();
|
||||||
let seat = self.seat.clone().unwrap();
|
let seat = self.seat.clone().unwrap();
|
||||||
CoreSurface::add_to(
|
CoreSurface::add_to(
|
||||||
&self.wayland_display,
|
self.wayland_display_handle.clone(),
|
||||||
self.wayland_display.lock().handle(),
|
|
||||||
&window.wl_surface().unwrap(),
|
&window.wl_surface().unwrap(),
|
||||||
{
|
{
|
||||||
let window = window.clone();
|
let window = window.clone();
|
||||||
@@ -140,22 +145,24 @@ impl XwmHandler for XWaylandHandler {
|
|||||||
let seat = seat.clone();
|
let seat = seat.clone();
|
||||||
window.user_data().insert_if_missing_threadsafe(|| {
|
window.user_data().insert_if_missing_threadsafe(|| {
|
||||||
let (_node, panel_item) = PanelItem::create(
|
let (_node, panel_item) = PanelItem::create(
|
||||||
wl_surface.clone(),
|
Box::new(X11Backend {
|
||||||
Backend::X11(X11Backend {
|
|
||||||
toplevel_parent: None,
|
toplevel_parent: None,
|
||||||
toplevel: window.clone(),
|
toplevel: window.clone(),
|
||||||
|
seat,
|
||||||
|
_pointer_grab: Mutex::new(None),
|
||||||
|
_keyboard_grab: Mutex::new(None),
|
||||||
}),
|
}),
|
||||||
wl_surface
|
wl_surface
|
||||||
.client()
|
.client()
|
||||||
.and_then(|c| c.get_credentials(&dh).ok()),
|
.and_then(|c| c.get_credentials(&dh).ok())
|
||||||
seat,
|
.map(|c| c.pid),
|
||||||
);
|
);
|
||||||
panel_item
|
panel_item
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
move |_| {
|
move |_| {
|
||||||
let Some(panel_item) = window.user_data().get::<Arc<PanelItem>>() else {return};
|
let Some(panel_item) = window.user_data().get::<Arc<PanelItem<X11Backend>>>() else {return};
|
||||||
panel_item.commit_toplevel();
|
panel_item.commit_toplevel();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -246,3 +253,144 @@ impl XwmHandler for XWaylandHandler {
|
|||||||
panel_item.recommend_toplevel_state(RecommendedState::Minimize);
|
panel_item.recommend_toplevel_state(RecommendedState::Minimize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct X11Backend {
|
||||||
|
pub toplevel_parent: Option<X11Surface>,
|
||||||
|
pub toplevel: X11Surface,
|
||||||
|
pub seat: Arc<SeatData>,
|
||||||
|
_pointer_grab: Mutex<Option<SurfaceID>>,
|
||||||
|
_keyboard_grab: Mutex<Option<SurfaceID>>,
|
||||||
|
}
|
||||||
|
impl X11Backend {
|
||||||
|
fn wl_surface_from_id(&self, id: &SurfaceID) -> Option<WlSurface> {
|
||||||
|
match id {
|
||||||
|
SurfaceID::Cursor => None,
|
||||||
|
SurfaceID::Toplevel => self.toplevel.wl_surface(),
|
||||||
|
SurfaceID::Popup(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn flush_client(&self) {
|
||||||
|
// let Some(client) = self.toplevel.wl_surface().and_then(|s| s.client()) else {return};
|
||||||
|
// if let Some(client_state) = client.get_data::<ClientState>() {
|
||||||
|
// client_state.flush();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
impl Backend for X11Backend {
|
||||||
|
fn serialize_start_data(&self, id: &str) -> Result<Vec<u8>> {
|
||||||
|
let size = (
|
||||||
|
self.toplevel.geometry().size.w as u32,
|
||||||
|
self.toplevel.geometry().size.h as u32,
|
||||||
|
);
|
||||||
|
let toplevel_state = (
|
||||||
|
None::<String>,
|
||||||
|
self.toplevel.title(),
|
||||||
|
None::<String>,
|
||||||
|
(
|
||||||
|
self.toplevel.geometry().size.w as u32,
|
||||||
|
self.toplevel.geometry().size.h as u32,
|
||||||
|
),
|
||||||
|
self.toplevel.min_size().map(|s| (s.w as u32, s.h as u32)),
|
||||||
|
self.toplevel.max_size().map(|s| (s.w as u32, s.w as u32)),
|
||||||
|
((0_i32, 0_i32), size),
|
||||||
|
vec![0_u32; 0],
|
||||||
|
);
|
||||||
|
let info = (
|
||||||
|
None::<(Vector2<u32>, Vector2<i32>)>,
|
||||||
|
toplevel_state,
|
||||||
|
Vec::<PopupData>::new(),
|
||||||
|
None::<SurfaceID>,
|
||||||
|
None::<SurfaceID>,
|
||||||
|
);
|
||||||
|
serialize((id, info)).map_err(|e| e.into())
|
||||||
|
}
|
||||||
|
fn serialize_toplevel(&self) -> Result<Vec<u8>> {
|
||||||
|
let toplevel_state = (
|
||||||
|
None::<String>,
|
||||||
|
self.toplevel.title(),
|
||||||
|
None::<String>,
|
||||||
|
(
|
||||||
|
self.toplevel.geometry().size.w,
|
||||||
|
self.toplevel.geometry().size.h,
|
||||||
|
),
|
||||||
|
self.toplevel.min_size().map(|s| (s.w, s.h)),
|
||||||
|
self.toplevel.max_size().map(|s| (s.w, s.w)),
|
||||||
|
);
|
||||||
|
let data = serialize(&toplevel_state)?;
|
||||||
|
Ok(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_toplevel_capabilities(&self, _capabilities: Vec<u8>) {}
|
||||||
|
|
||||||
|
fn configure_toplevel(
|
||||||
|
&self,
|
||||||
|
size: Option<Vector2<u32>>,
|
||||||
|
states: Vec<u32>,
|
||||||
|
_bounds: Option<Vector2<u32>>,
|
||||||
|
) {
|
||||||
|
let _ = self.toplevel.configure(
|
||||||
|
size.map(|s| Rectangle::from_loc_and_size((0, 0), (s.x as i32, s.y as i32))),
|
||||||
|
);
|
||||||
|
let _ = self.toplevel.set_maximized(states.contains(&1));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_surface_material(&self, surface: SurfaceID, model_part: &Arc<ModelPart>) {
|
||||||
|
let Some(wl_surface) = self.wl_surface_from_id(&surface) else {return};
|
||||||
|
let Some(core_surface) = CoreSurface::from_wl_surface(&wl_surface) else {return};
|
||||||
|
|
||||||
|
core_surface.apply_material(model_part);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pointer_motion(&self, surface: &SurfaceID, position: Vector2<f32>) {
|
||||||
|
let Some(surface) = self.wl_surface_from_id(surface) else {return};
|
||||||
|
self.seat
|
||||||
|
.pointer_event(&surface, PointerEvent::Motion(position));
|
||||||
|
}
|
||||||
|
fn pointer_button(&self, surface: &SurfaceID, button: u32, pressed: bool) {
|
||||||
|
let Some(surface) = self.wl_surface_from_id(surface) else {return};
|
||||||
|
self.seat.pointer_event(
|
||||||
|
&surface,
|
||||||
|
PointerEvent::Button {
|
||||||
|
button,
|
||||||
|
state: if pressed { 1 } else { 0 },
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
fn pointer_scroll(
|
||||||
|
&self,
|
||||||
|
surface: &SurfaceID,
|
||||||
|
scroll_distance: Option<Vector2<f32>>,
|
||||||
|
scroll_steps: Option<Vector2<f32>>,
|
||||||
|
) {
|
||||||
|
let Some(surface) = self.wl_surface_from_id(surface) else {return};
|
||||||
|
self.seat.pointer_event(
|
||||||
|
&surface,
|
||||||
|
PointerEvent::Scroll {
|
||||||
|
axis_continuous: scroll_distance,
|
||||||
|
axis_discrete: scroll_steps,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn keyboard_set_keymap(&self, keymap: &str) -> Result<()> {
|
||||||
|
self.seat.set_keymap_str(
|
||||||
|
&keymap,
|
||||||
|
if let Some(toplevel) = self.toplevel.wl_surface() {
|
||||||
|
vec![toplevel]
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
fn keyboard_key(&self, surface: &SurfaceID, key: u32, state: bool) {
|
||||||
|
let Some(surface) = self.wl_surface_from_id(surface) else {return};
|
||||||
|
self.seat.keyboard_event(
|
||||||
|
&surface,
|
||||||
|
KeyboardEvent::Key {
|
||||||
|
key,
|
||||||
|
state: if state { 1 } else { 0 },
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user