refactor(items): codegen prototocol

This commit is contained in:
Nova
2024-05-30 00:34:29 -04:00
parent eda50b7d51
commit 5f9d9d4714
12 changed files with 432 additions and 591 deletions

8
Cargo.lock generated
View File

@@ -880,9 +880,9 @@ checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
[[package]] [[package]]
name = "flatbuffers" name = "flatbuffers"
version = "23.5.26" version = "24.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dac53e22462d78c16d64a1cd22371b54cc3fe94aa15e7886a2fa6e5d1ab8640" checksum = "8add37afff2d4ffa83bc748a70b4b1370984f6980768554182424ef71447c35f"
dependencies = [ dependencies = [
"bitflags 1.3.2", "bitflags 1.3.2",
"rustc_version", "rustc_version",
@@ -2483,7 +2483,7 @@ checksum = "2f2b15926089e5526bb2dd738a2eb0e59034356e06eb71e1cd912358c0e62c4d"
[[package]] [[package]]
name = "stardust-xr" name = "stardust-xr"
version = "0.45.0" version = "0.45.0"
source = "git+https://github.com/StardustXR/core.git#6ee323df1a1e76666609c1ebfd79f3390ce43c29" source = "git+https://github.com/StardustXR/core.git?branch=dev#47208187ffdb00627fabbb3af2bd0a12a6208194"
dependencies = [ dependencies = [
"cluFlock", "cluFlock",
"color-rs", "color-rs",
@@ -2503,7 +2503,7 @@ dependencies = [
[[package]] [[package]]
name = "stardust-xr-schemas" name = "stardust-xr-schemas"
version = "1.5.3" version = "1.5.3"
source = "git+https://github.com/StardustXR/core.git#6ee323df1a1e76666609c1ebfd79f3390ce43c29" source = "git+https://github.com/StardustXR/core.git?branch=dev#47208187ffdb00627fabbb3af2bd0a12a6208194"
dependencies = [ dependencies = [
"flatbuffers", "flatbuffers",
"flexbuffers", "flexbuffers",

View File

@@ -99,11 +99,7 @@ optional = true
[dependencies.stardust-xr] [dependencies.stardust-xr]
git = "https://github.com/StardustXR/core.git" git = "https://github.com/StardustXR/core.git"
branch = "dev"
[dependencies.stardust-xr-server-codegen] [dependencies.stardust-xr-server-codegen]
path = "codegen" path = "codegen"
# [patch.crates-io.stereokit]
# path = "../stereokit-rs"
# [patch.crates-io.stereokit-sys]
# path = "../stereokit-sys"

View File

@@ -15,3 +15,4 @@ split-iter = "0.1.0"
[dependencies.stardust-xr-schemas] [dependencies.stardust-xr-schemas]
git = "https://github.com/StardustXR/core.git" git = "https://github.com/StardustXR/core.git"
branch = "dev"

View File

@@ -40,6 +40,18 @@ pub fn codegen_drawable_protocol(_input: proc_macro::TokenStream) -> proc_macro:
pub fn codegen_input_protocol(_input: proc_macro::TokenStream) -> proc_macro::TokenStream { pub fn codegen_input_protocol(_input: proc_macro::TokenStream) -> proc_macro::TokenStream {
codegen_protocol(INPUT_PROTOCOL) codegen_protocol(INPUT_PROTOCOL)
} }
#[proc_macro]
pub fn codegen_item_protocol(_input: proc_macro::TokenStream) -> proc_macro::TokenStream {
codegen_protocol(ITEM_PROTOCOL)
}
#[proc_macro]
pub fn codegen_item_camera_protocol(_input: proc_macro::TokenStream) -> proc_macro::TokenStream {
codegen_protocol(ITEM_CAMERA_PROTOCOL)
}
#[proc_macro]
pub fn codegen_item_panel_protocol(_input: proc_macro::TokenStream) -> proc_macro::TokenStream {
codegen_protocol(ITEM_PANEL_PROTOCOL)
}
fn codegen_protocol(protocol: &'static str) -> proc_macro::TokenStream { fn codegen_protocol(protocol: &'static str) -> proc_macro::TokenStream {
let protocol = Protocol::parse(protocol).unwrap(); let protocol = Protocol::parse(protocol).unwrap();
@@ -457,13 +469,15 @@ fn generate_argument_decl(argument: &Argument, owned_values: bool) -> TokenStrea
} }
fn argument_type_option_name(argument_type: &ArgumentType) -> String { fn argument_type_option_name(argument_type: &ArgumentType) -> String {
match argument_type { match argument_type {
ArgumentType::Empty => "Empty".to_string(),
ArgumentType::Bool => "Bool".to_string(), ArgumentType::Bool => "Bool".to_string(),
ArgumentType::Int => "Int".to_string(), ArgumentType::Int => "Int".to_string(),
ArgumentType::UInt => "UInt".to_string(), ArgumentType::UInt => "UInt".to_string(),
ArgumentType::Float => "Float".to_string(), ArgumentType::Float => "Float".to_string(),
ArgumentType::Vec2 => "Vec2".to_string(), ArgumentType::Vec2(_) => "Vec2".to_string(),
ArgumentType::Vec3 => "Vec3".to_string(), ArgumentType::Vec3(_) => "Vec3".to_string(),
ArgumentType::Quat => "Quat".to_string(), ArgumentType::Quat => "Quat".to_string(),
ArgumentType::Mat4 => "Mat4".to_string(),
ArgumentType::Color => "Color".to_string(), ArgumentType::Color => "Color".to_string(),
ArgumentType::String => "String".to_string(), ArgumentType::String => "String".to_string(),
ArgumentType::Bytes => "Bytes".to_string(), ArgumentType::Bytes => "Bytes".to_string(),
@@ -483,13 +497,21 @@ fn generate_argument_type(
owned: bool, owned: bool,
) -> TokenStream { ) -> TokenStream {
let _type = match argument_type { let _type = match argument_type {
ArgumentType::Empty => quote!(()),
ArgumentType::Bool => quote!(bool), ArgumentType::Bool => quote!(bool),
ArgumentType::Int => quote!(i32), ArgumentType::Int => quote!(i32),
ArgumentType::UInt => quote!(u32), ArgumentType::UInt => quote!(u32),
ArgumentType::Float => quote!(f32), ArgumentType::Float => quote!(f32),
ArgumentType::Vec2 => quote!(mint::Vector2<f32>), ArgumentType::Vec2(t) => {
ArgumentType::Vec3 => quote!(mint::Vector3<f32>), let t = generate_argument_type(&t, false, true);
ArgumentType::Quat => quote!(mint::Quaternion<f32>), quote!(stardust_xr::values::Vector2<#t>)
}
ArgumentType::Vec3(t) => {
let t = generate_argument_type(&t, false, true);
quote!(stardust_xr::values::Vector3<#t>)
}
ArgumentType::Quat => quote!(stardust_xr::values::Quaternion),
ArgumentType::Mat4 => quote!(stardust_xr::values::Mat4),
ArgumentType::Color => quote!(stardust_xr::values::Color), ArgumentType::Color => quote!(stardust_xr::values::Color),
ArgumentType::Bytes => { ArgumentType::Bytes => {
if !owned { if !owned {
@@ -517,9 +539,9 @@ fn generate_argument_type(
let t = generate_argument_type(&t, false, true); let t = generate_argument_type(&t, false, true);
if !owned { if !owned {
quote!(&rustc_hash::FxHashMap<String, #t>) quote!(&stardust_xr::values::Map<String, #t>)
} else { } else {
quote!(rustc_hash::FxHashMap<String, #t>) quote!(stardust_xr::values::Map<String, #t>)
} }
} }
ArgumentType::Datamap => { ArgumentType::Datamap => {

View File

@@ -109,8 +109,9 @@ impl Client {
drawable::create_interface(&client)?; drawable::create_interface(&client)?;
audio::create_interface(&client)?; audio::create_interface(&client)?;
data::create_interface(&client)?; data::create_interface(&client)?;
items::create_interface(&client)?;
input::create_interface(&client)?; input::create_interface(&client)?;
items::camera::create_interface(&client)?;
items::panel::create_interface(&client)?;
client client
.root .root

View File

@@ -297,7 +297,3 @@ impl FieldInterfaceAspect for FieldInterface {
Ok(()) Ok(())
} }
} }
pub fn find_field(client: &Client, path: &str) -> Result<Arc<Field>> {
client.get_node("Field", path)?.get_aspect::<Field>()
}

View File

@@ -1,26 +1,26 @@
use super::{Item, ItemType}; use super::{create_item_acceptor_flex, register_item_ui_flex, Item, ItemInterface, ItemType};
use crate::{ use crate::{
core::{ core::{
client::{Client, INTERNAL_CLIENT}, client::{Client, INTERNAL_CLIENT},
registry::Registry, registry::Registry,
scenegraph::MethodResponseSender, scenegraph::MethodResponseSender,
}, },
create_interface,
nodes::{ nodes::{
drawable::{model::ModelPart, shaders::UNLIT_SHADER_BYTES}, drawable::{model::ModelPart, shaders::UNLIT_SHADER_BYTES},
items::TypeInfo, items::TypeInfo,
spatial::{parse_transform, Spatial, Transform}, spatial::{Spatial, Transform},
Message, Node, Message, Node,
}, },
}; };
use color_eyre::eyre::{bail, eyre, Result}; use color_eyre::eyre::{bail, eyre, Result};
use glam::Mat4; use glam::Mat4;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use mint::{RowMatrix4, Vector2}; use mint::{ColumnMatrix4, Vector2};
use nanoid::nanoid; use nanoid::nanoid;
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use parking_lot::Mutex; use parking_lot::Mutex;
use send_wrapper::SendWrapper; use send_wrapper::SendWrapper;
use serde::Deserialize;
use stardust_xr::schemas::flex::{deserialize, serialize}; use stardust_xr::schemas::flex::{deserialize, serialize};
use std::sync::Arc; use std::sync::Arc;
use stereokit_rust::{ use stereokit_rust::{
@@ -33,6 +33,7 @@ use stereokit_rust::{
}; };
use tracing::error; use tracing::error;
stardust_xr_server_codegen::codegen_item_camera_protocol!();
lazy_static! { lazy_static! {
pub(super) static ref ITEM_TYPE_INFO_CAMERA: TypeInfo = TypeInfo { pub(super) static ref ITEM_TYPE_INFO_CAMERA: TypeInfo = TypeInfo {
type_name: "camera", type_name: "camera",
@@ -42,6 +43,9 @@ lazy_static! {
ui: Default::default(), ui: Default::default(),
items: Registry::new(), items: Registry::new(),
acceptors: Registry::new(), acceptors: Registry::new(),
new_acceptor_fn: |node, uid, acceptor, acceptor_field| {
let _ = camera_item_ui_client::create_acceptor(node, uid, acceptor, acceptor_field);
}
}; };
} }
@@ -114,8 +118,11 @@ impl CameraItem {
Ok(()) Ok(())
} }
pub fn serialize_start_data(&self, id: &str) -> Result<Message> { pub fn send_ui_item_created(&self, node: &Node, uid: &str, item: &Arc<Node>) {
Ok(serialize(id)?.into()) let _ = camera_item_ui_client::create_item(node, uid, item);
}
pub fn send_acceptor_item_created(&self, node: &Node, uid: &str, item: &Arc<Node>) {
let _ = camera_item_acceptor_client::capture_item(node, uid, item);
} }
pub fn update(&self, token: &MainThreadToken) { pub fn update(&self, token: &MainThreadToken) {
@@ -169,34 +176,55 @@ pub fn update(token: &MainThreadToken) {
} }
} }
pub(super) fn create_camera_item_flex( create_interface!(ItemInterface, ItemCameraInterfaceAspect, "/item/camera");
_node: Arc<Node>, impl ItemCameraInterfaceAspect for ItemInterface {
calling_client: Arc<Client>, #[doc = "Create a camera item at a specific location"]
message: Message, fn create_camera_item(
) -> Result<()> { _node: Arc<Node>,
#[derive(Deserialize)] calling_client: Arc<Client>,
struct CreateCameraItemInfo<'a> { name: String,
name: &'a str, parent: Arc<Node>,
parent_path: &'a str,
transform: Transform, transform: Transform,
proj_matrix: RowMatrix4<f32>, proj_matrix: ColumnMatrix4<f32>,
px_size: Vector2<u32>, px_size: Vector2<u32>,
} ) -> Result<()> {
let info: CreateCameraItemInfo = deserialize(message.as_ref())?; let parent_name = format!("/item/{}/item", ITEM_TYPE_INFO_CAMERA.type_name);
let parent_name = format!("/item/{}/item", ITEM_TYPE_INFO_CAMERA.type_name); let space = parent.get_aspect::<Spatial>()?;
let space = calling_client let transform = transform.to_mat4(true, true, false);
.get_node("Spatial parent", info.parent_path)?
.get_aspect::<Spatial>()?;
let transform = parse_transform(info.transform, true, true, false);
let node = Node::create_parent_name(&INTERNAL_CLIENT, &parent_name, info.name, false) let node = Node::create_parent_name(&INTERNAL_CLIENT, &parent_name, &name, false)
.add_to_scenegraph()?; .add_to_scenegraph()?;
Spatial::add_to(&node, None, transform * space.global_transform(), false); Spatial::add_to(&node, None, transform * space.global_transform(), false);
CameraItem::add_to(&node, info.proj_matrix.into(), info.px_size); CameraItem::add_to(&node, proj_matrix.into(), px_size);
node.get_aspect::<Item>().unwrap().make_alias_named( node.get_aspect::<Item>().unwrap().make_alias_named(
&calling_client, &calling_client,
&parent_name, &parent_name,
info.name, &name,
)?; )?;
Ok(()) Ok(())
}
#[doc = "Register this client to manage camera items and create default 3D UI for them."]
fn register_camera_item_ui(_node: Arc<Node>, calling_client: Arc<Client>) -> Result<()> {
register_item_ui_flex(calling_client, &ITEM_TYPE_INFO_CAMERA)
}
#[doc = "Create an item acceptor to allow temporary ownership of a given type of item. Creates a node at `/item/camera/acceptor/<name>`."]
fn create_camera_item_acceptor(
_node: Arc<Node>,
calling_client: Arc<Client>,
name: String,
parent: Arc<Node>,
transform: Transform,
field: Arc<Node>,
) -> Result<()> {
create_item_acceptor_flex(
calling_client,
name,
parent,
transform,
&ITEM_TYPE_INFO_CAMERA,
field,
)
}
} }

View File

@@ -1,97 +0,0 @@
use super::{Item, ItemType};
use crate::{
core::{
client::{Client, INTERNAL_CLIENT},
registry::Registry,
scenegraph::MethodResponseSender,
},
nodes::{
items::TypeInfo,
spatial::{parse_transform, Spatial, Transform},
Message, Node,
},
};
use color_eyre::eyre::{eyre, Result};
use lazy_static::lazy_static;
use nanoid::nanoid;
use serde::Deserialize;
use stardust_xr::schemas::flex::{deserialize, serialize};
use std::sync::Arc;
lazy_static! {
pub(super) static ref ITEM_TYPE_INFO_ENVIRONMENT: TypeInfo = TypeInfo {
type_name: "environment",
aliased_local_signals: vec!["apply_sky_tex", "apply_sky_light"],
aliased_local_methods: vec![],
aliased_remote_signals: vec![],
ui: Default::default(),
items: Registry::new(),
acceptors: Registry::new(),
};
}
pub struct EnvironmentItem {
path: String,
}
impl EnvironmentItem {
pub fn add_to(node: &Arc<Node>, path: String) {
Item::add_to(
node,
nanoid!(),
&ITEM_TYPE_INFO_ENVIRONMENT,
ItemType::Environment(EnvironmentItem { path }),
);
node.add_local_method("get_path", EnvironmentItem::get_path_flex);
}
fn get_path_flex(
node: Arc<Node>,
_calling_client: Arc<Client>,
_message: Message,
response: MethodResponseSender,
) {
response.wrap_sync(move || {
let ItemType::Environment(environment_item) =
&node.get_aspect::<Item>().unwrap().specialization
else {
return Err(eyre!("Wrong item type?"));
};
Ok(serialize(environment_item.path.as_str())?.into())
});
}
pub fn serialize_start_data(&self, id: &str) -> Result<Message> {
Ok(serialize((id, self.path.as_str()))?.into())
}
}
pub(super) fn create_environment_item_flex(
_node: Arc<Node>,
calling_client: Arc<Client>,
message: Message,
) -> Result<()> {
#[derive(Deserialize)]
struct CreateEnvironmentItemInfo<'a> {
name: &'a str,
parent_path: &'a str,
transform: Transform,
item_data: String,
}
let info: CreateEnvironmentItemInfo = deserialize(message.as_ref())?;
let parent_name = format!("/item/{}/item", ITEM_TYPE_INFO_ENVIRONMENT.type_name);
let space = calling_client
.get_node("Spatial parent", info.parent_path)?
.get_aspect::<Spatial>()?;
let transform = parse_transform(info.transform, true, true, false);
let node = Node::create_parent_name(&INTERNAL_CLIENT, &parent_name, info.name, false)
.add_to_scenegraph()?;
Spatial::add_to(&node, None, transform * space.global_transform(), false);
EnvironmentItem::add_to(&node, info.item_data);
node.get_aspect::<Item>().unwrap().make_alias_named(
&calling_client,
&parent_name,
info.name,
)?;
Ok(())
}

View File

@@ -1,30 +1,28 @@
pub mod camera; pub mod camera;
mod environment;
pub mod panel; pub mod panel;
use self::camera::CameraItem; use self::camera::CameraItem;
use self::environment::{EnvironmentItem, ITEM_TYPE_INFO_ENVIRONMENT}; use self::panel::PanelItemTrait;
use self::panel::{PanelItemTrait, ITEM_TYPE_INFO_PANEL};
use super::fields::Field; use super::fields::Field;
use super::spatial::{parse_transform, Spatial}; use super::spatial::Spatial;
use super::{Alias, Aspect, Message, Node}; use super::{Alias, Aspect, Message, Node};
use crate::core::client::Client; use crate::core::client::Client;
use crate::core::node_collections::LifeLinkedNodeMap; 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::spatial::Transform; use crate::nodes::spatial::Transform;
use color_eyre::eyre::{ensure, eyre, Result}; use color_eyre::eyre::{ensure, Result};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use nanoid::nanoid; use nanoid::nanoid;
use parking_lot::Mutex; use parking_lot::Mutex;
use portable_atomic::Ordering; use portable_atomic::Ordering;
use serde::Deserialize; use stardust_xr::schemas::flex::deserialize;
use stardust_xr::schemas::flex::{deserialize, serialize};
use std::hash::Hash; use std::hash::Hash;
use std::sync::{Arc, Weak}; use std::sync::{Arc, Weak};
stardust_xr_server_codegen::codegen_item_protocol!();
lazy_static! { lazy_static! {
static ref ITEM_ALIAS_LOCAL_SIGNALS: Vec<&'static str> = vec![ static ref ITEM_ALIAS_LOCAL_SIGNALS: Vec<&'static str> = vec![
"get_bounds", "get_bounds",
@@ -68,6 +66,8 @@ pub struct TypeInfo {
pub ui: Mutex<Weak<ItemUI>>, pub ui: Mutex<Weak<ItemUI>>,
pub items: Registry<Item>, pub items: Registry<Item>,
pub acceptors: Registry<ItemAcceptor>, pub acceptors: Registry<ItemAcceptor>,
pub new_acceptor_fn:
fn(node: &Node, uid: &str, acceptor: &Arc<Node>, acceptor_field: &Arc<Node>),
} }
impl Hash for TypeInfo { impl Hash for TypeInfo {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
@@ -182,15 +182,19 @@ impl Drop for Item {
pub enum ItemType { pub enum ItemType {
Camera(CameraItem), Camera(CameraItem),
Environment(EnvironmentItem),
Panel(Arc<dyn PanelItemTrait>), Panel(Arc<dyn PanelItemTrait>),
} }
impl ItemType { impl ItemType {
fn serialize_start_data(&self, id: &str) -> Result<Message> { fn send_ui_item_created(&self, node: &Node, uid: &str, item: &Arc<Node>) {
match self { match self {
ItemType::Camera(c) => c.serialize_start_data(id), ItemType::Camera(c) => c.send_ui_item_created(node, uid, item),
ItemType::Environment(e) => e.serialize_start_data(id), ItemType::Panel(p) => p.send_ui_item_created(node, uid, item),
ItemType::Panel(p) => p.serialize_start_data(id), }
}
fn send_acceptor_item_created(&self, node: &Node, uid: &str, item: &Arc<Node>) {
match self {
ItemType::Camera(c) => c.send_acceptor_item_created(node, uid, item),
ItemType::Panel(p) => p.send_acceptor_item_created(node, uid, item),
} }
} }
} }
@@ -237,16 +241,6 @@ impl ItemUI {
} }
Ok(()) Ok(())
} }
fn send_state(&self, state: &str, name: &str) {
let Ok(serialized_data) = serialize(name) else {
return;
};
let _ = self
.node
.upgrade()
.unwrap()
.send_remote_signal(state, serialized_data);
}
fn handle_create_item(&self, item: &Item) { fn handle_create_item(&self, item: &Item) {
let Some(node) = self.node.upgrade() else { let Some(node) = self.node.upgrade() else {
@@ -256,38 +250,26 @@ impl ItemUI {
return; return;
}; };
if let Ok(alias_node) = item.make_alias(&client, &(node.get_path().to_string() + "/item")) { let Ok(item_alias) = item.make_alias(&client, &(node.get_path().to_string() + "/item"))
self.item_aliases.add(item.uid.clone(), &alias_node); else {
}
let Ok(serialized_data) = item.specialization.serialize_start_data(&item.uid) else {
return; return;
}; };
let _ = node.send_remote_signal("create_item", serialized_data); self.item_aliases.add(item.uid.clone(), &item_alias);
}
fn handle_destroy_item(&self, item: &Item) { item.specialization
self.item_aliases.remove(&item.uid); .send_ui_item_created(&node, &item.uid, &item_alias);
self.send_state("destroy_item", item.uid.as_str());
} }
fn handle_capture_item(&self, item: &Item, acceptor: &ItemAcceptor) { fn handle_capture_item(&self, item: &Item, acceptor: &ItemAcceptor) {
let Some(node) = self.node.upgrade() else { let _ =
return; item_ui_client::capture_item(&self.node.upgrade().unwrap(), &item.uid, &acceptor.uid);
};
let Ok(message) = serialize((item.uid.as_str(), acceptor.uid.as_str())) else {
return;
};
let _ = node.send_remote_signal("capture_item", message);
} }
fn handle_release_item(&self, item: &Item, acceptor: &ItemAcceptor) { fn handle_release_item(&self, item: &Item, acceptor: &ItemAcceptor) {
let Some(node) = self.node.upgrade() else { let _ =
return; item_ui_client::release_item(&self.node.upgrade().unwrap(), &item.uid, &acceptor.uid);
}; }
fn handle_destroy_item(&self, item: &Item) {
let Ok(message) = serialize((item.uid.as_str(), acceptor.uid.as_str())) else { let _ = item_ui_client::destroy_item(&self.node.upgrade().unwrap(), &item.uid);
return; self.item_aliases.remove(&item.uid);
};
let _ = node.send_remote_signal("release_item", message);
} }
fn handle_create_acceptor(&self, acceptor: &ItemAcceptor) { fn handle_create_acceptor(&self, acceptor: &ItemAcceptor) {
let Some(node) = self.node.upgrade() else { let Some(node) = self.node.upgrade() else {
@@ -297,22 +279,26 @@ impl ItemUI {
return; return;
}; };
let Ok((alias, field_alias)) = acceptor.make_aliases( let Ok((acceptor_alias, acceptor_field_alias)) = acceptor.make_aliases(
&client, &client,
&format!("/item/{}/acceptor", self.type_info.type_name), &format!("/item/{}/acceptor", self.type_info.type_name),
) else { ) else {
return; return;
}; };
self.acceptor_aliases.add(acceptor.uid.clone(), &alias); self.acceptor_aliases
.add(acceptor.uid.clone(), &acceptor_alias);
self.acceptor_field_aliases self.acceptor_field_aliases
.add(acceptor.uid.clone(), &field_alias); .add(acceptor.uid.clone(), &acceptor_field_alias);
let Ok(message) = serialize(&acceptor.uid) else {
return; (acceptor.type_info.new_acceptor_fn)(
}; &node,
let _ = node.send_remote_signal("create_acceptor", message); &acceptor.uid,
&acceptor_alias,
&acceptor_field_alias,
);
} }
fn handle_destroy_acceptor(&self, acceptor: &ItemAcceptor) { fn handle_destroy_acceptor(&self, acceptor: &ItemAcceptor) {
self.send_state("destroy_acceptor", acceptor.uid.as_str()); let _ = item_ui_client::destroy_acceptor(&self.node.upgrade().unwrap(), &acceptor.uid);
self.acceptor_aliases.remove(&acceptor.uid); self.acceptor_aliases.remove(&acceptor.uid);
self.acceptor_field_aliases.remove(&acceptor.uid); self.acceptor_field_aliases.remove(&acceptor.uid);
} }
@@ -397,31 +383,27 @@ impl ItemAcceptor {
}; };
self.accepted_registry.add_raw(item); self.accepted_registry.add_raw(item);
if let Ok(alias_node) = item.make_alias(&client, &node.path) { let Ok(alias_node) = item.make_alias(&client, &node.path) else {
self.accepted_aliases.add(item.uid.clone(), &alias_node);
}
let Ok(serialized_data) = item.specialization.serialize_start_data(&item.uid) else {
return; return;
}; };
let _ = node.send_remote_signal("capture", serialized_data); self.accepted_aliases.add(item.uid.clone(), &alias_node);
item.specialization
.send_acceptor_item_created(&node, &item.uid, &alias_node);
} }
fn handle_release(&self, item: &Item) { fn handle_release(&self, item: &Item) {
let Some(node) = self.node.upgrade() else { if let Some(node) = self.node.upgrade() {
return; let _ = item_acceptor_client::release_item(&node, &item.uid);
}; }
self.accepted_registry.remove(item); self.accepted_registry.remove(item);
self.accepted_aliases.remove(&item.uid); self.accepted_aliases.remove(&item.uid);
let Ok(message) = serialize(&item.uid) else {
return;
};
let _ = node.send_remote_signal("release", message);
} }
} }
impl Aspect for ItemAcceptor { impl Aspect for ItemAcceptor {
const NAME: &'static str = "ItemAcceptor"; const NAME: &'static str = "ItemAcceptor";
} }
impl ItemAcceptorAspect for ItemAcceptor {}
impl Drop for ItemAcceptor { impl Drop for ItemAcceptor {
fn drop(&mut self) { fn drop(&mut self) {
self.type_info.acceptors.remove(self); self.type_info.acceptors.remove(self);
@@ -434,69 +416,31 @@ impl Drop for ItemAcceptor {
} }
} }
pub fn create_interface(client: &Arc<Client>) -> Result<()> {
let node = Node::create_parent_name(client, "", "item", false);
node.add_local_signal("create_camera_item", camera::create_camera_item_flex);
node.add_local_signal(
"create_environment_item",
environment::create_environment_item_flex,
);
node.add_local_signal("register_item_ui", register_item_ui_flex);
node.add_local_signal("create_item_acceptor", create_item_acceptor_flex);
node.add_to_scenegraph().map(|_| ())
}
fn type_info(name: &str) -> Result<&'static TypeInfo> {
match name {
"environment" => Ok(&ITEM_TYPE_INFO_ENVIRONMENT),
#[cfg(feature = "wayland")]
"panel" => Ok(&ITEM_TYPE_INFO_PANEL),
_ => Err(eyre!("Invalid item type")),
}
}
pub fn register_item_ui_flex( pub fn register_item_ui_flex(
_node: Arc<Node>,
calling_client: Arc<Client>, calling_client: Arc<Client>,
message: Message, type_info: &'static TypeInfo,
) -> Result<()> { ) -> Result<()> {
#[derive(Deserialize)]
struct RegisterItemUIInfo<'a> {
item_type: &'a str,
}
let info: RegisterItemUIInfo = deserialize(message.as_ref())?;
let type_info = type_info(info.item_type)?;
let ui = Node::create_parent_name(&calling_client, "/item", type_info.type_name, true) let ui = Node::create_parent_name(&calling_client, "/item", type_info.type_name, true)
.add_to_scenegraph()?; .add_to_scenegraph()?;
ItemUI::add_to(&ui, type_info)?; ItemUI::add_to(&ui, type_info)?;
Ok(()) Ok(())
} }
fn create_item_acceptor_flex( fn create_item_acceptor_flex(
_node: Arc<Node>,
calling_client: Arc<Client>, calling_client: Arc<Client>,
message: Message, name: String,
parent: Arc<Node>,
transform: Transform,
type_info: &'static TypeInfo,
field: Arc<Node>,
) -> Result<()> { ) -> Result<()> {
#[derive(Deserialize)] let space = parent.get_aspect::<Spatial>()?;
struct CreateItemAcceptorInfo<'a> { let field = field.get_aspect::<Field>()?;
name: &'a str, let transform = transform.to_mat4(true, true, false);
parent_path: &'a str,
transform: Transform,
field_path: &'a str,
item_type: &'a str,
}
let info: CreateItemAcceptorInfo = deserialize(message.as_ref())?;
let space = calling_client
.get_node("Reference space", info.parent_path)?
.get_aspect::<Spatial>()?;
let transform = parse_transform(info.transform, true, true, false);
let field = find_field(&calling_client, info.field_path)?;
let type_info = type_info(info.item_type)?;
let node = Node::create_parent_name( let node = Node::create_parent_name(
&calling_client, &calling_client,
&format!("/item/{}/acceptor", type_info.type_name), &format!("/item/{}/acceptor", type_info.type_name),
info.name, &name,
true, true,
) )
.add_to_scenegraph()?; .add_to_scenegraph()?;
@@ -504,3 +448,6 @@ fn create_item_acceptor_flex(
ItemAcceptor::add_to(&node, type_info, field); ItemAcceptor::add_to(&node, type_info, field);
Ok(()) Ok(())
} }
struct ItemInterface;
// create_interface!(ItemInterface, ItemInterfaceAspect, "/item");

View File

@@ -3,28 +3,25 @@ use crate::{
client::{get_env, state, Client, INTERNAL_CLIENT}, client::{get_env, state, Client, INTERNAL_CLIENT},
registry::Registry, registry::Registry,
}, },
create_interface,
nodes::{ nodes::{
drawable::model::ModelPart, drawable::model::ModelPart,
items::{Item, ItemType, TypeInfo}, items::{Item, ItemType, TypeInfo},
spatial::Spatial, spatial::{Spatial, Transform},
Message, Node, Node,
}, },
}; };
use color_eyre::eyre::{eyre, Result}; use color_eyre::eyre::Result;
use glam::Mat4; use glam::Mat4;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use mint::Vector2; use mint::Vector2;
use nanoid::nanoid; use nanoid::nanoid;
use rustc_hash::FxHashMap;
use serde::{
de::{Deserializer, Error, SeqAccess, Visitor},
ser::Serializer,
Deserialize, Serialize,
};
use stardust_xr::schemas::flex::{deserialize, serialize};
use std::sync::{Arc, Weak}; use std::sync::{Arc, Weak};
use tracing::{debug, info}; use tracing::{debug, info};
use super::{create_item_acceptor_flex, register_item_ui_flex, ItemInterface};
stardust_xr_server_codegen::codegen_item_panel_protocol!();
lazy_static! { lazy_static! {
pub static ref ITEM_TYPE_INFO_PANEL: TypeInfo = TypeInfo { pub static ref ITEM_TYPE_INFO_PANEL: TypeInfo = TypeInfo {
type_name: "panel", type_name: "panel",
@@ -61,146 +58,39 @@ lazy_static! {
ui: Default::default(), ui: Default::default(),
items: Registry::new(), items: Registry::new(),
acceptors: Registry::new(), acceptors: Registry::new(),
new_acceptor_fn: |node, uid, acceptor, acceptor_field| {
let _ = panel_item_ui_client::create_acceptor(node, uid, acceptor, acceptor_field);
}
}; };
} }
/// An ID for a surface inside this panel item
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub enum SurfaceID {
Cursor,
Toplevel,
Child(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),
"Child" => {
let Some(text) = seq.next_element()? else {
return Err(A::Error::missing_field("child_text"));
};
Ok(SurfaceID::Child(text))
}
_ => Err(A::Error::unknown_variant(
discrim,
&["Cursor", "Toplevel", "Child"],
)),
}
}
}
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::Child(text) => ["Child", text].serialize(serializer),
}
}
}
/// The origin and size of the surface's "solid" part.
#[derive(Debug, Serialize, Clone, Copy)]
pub struct Geometry {
pub origin: Vector2<i32>,
pub size: Vector2<u32>,
}
/// The state of the panel item's toplevel.
#[derive(Debug, Clone, Serialize)]
pub struct ToplevelInfo {
/// The UID of the panel item of the parent of this toplevel, if it exists
pub parent: Option<String>,
/// Equivalent to the window title
pub title: Option<String>,
/// Application identifier, see <https://standards.freedesktop.org/desktop-entry-spec/>
pub app_id: Option<String>,
/// Current size in pixels
pub size: Vector2<u32>,
/// Recommended minimum size in pixels
pub min_size: Option<Vector2<u32>>,
/// Recommended maximum size in pixels
pub max_size: Option<Vector2<u32>>,
/// Surface geometry
pub logical_rectangle: Geometry,
}
/// Data on positioning a child
#[derive(Debug, Clone, Serialize)]
pub struct ChildInfo {
pub parent: SurfaceID,
pub geometry: Geometry,
}
/// The init data for the panel item.
#[derive(Debug, Clone, Serialize)]
pub struct PanelItemInitData {
/// The cursor, if applicable.
pub cursor: Option<Geometry>,
/// Size of the toplevel surface in pixels.
pub toplevel: ToplevelInfo,
/// Vector of childs that already exist
pub children: FxHashMap<String, ChildInfo>,
/// The surface, if any, that has exclusive input to the pointer.
pub pointer_grab: Option<SurfaceID>,
/// The surface, if any, that has exclusive input to the keyboard.
pub keyboard_grab: Option<SurfaceID>,
}
pub trait Backend: Send + Sync + 'static { pub trait Backend: Send + Sync + 'static {
fn start_data(&self) -> Result<PanelItemInitData>; fn start_data(&self) -> Result<PanelItemInitData>;
fn surface_alive(&self, surface: &SurfaceID) -> bool; fn surface_alive(&self, surface: &SurfaceId) -> bool;
fn apply_surface_material(&self, surface: SurfaceID, model_part: &Arc<ModelPart>); fn apply_cursor_material(&self, model_part: &Arc<ModelPart>);
fn apply_surface_material(&self, surface: SurfaceId, model_part: &Arc<ModelPart>);
fn close_toplevel(&self); fn close_toplevel(&self);
fn auto_size_toplevel(&self); fn auto_size_toplevel(&self);
fn set_toplevel_size(&self, size: Vector2<u32>); fn set_toplevel_size(&self, size: Vector2<u32>);
fn set_toplevel_focused_visuals(&self, focused: bool); fn set_toplevel_focused_visuals(&self, focused: bool);
fn pointer_motion(&self, surface: &SurfaceID, position: Vector2<f32>); fn pointer_motion(&self, surface: &SurfaceId, position: Vector2<f32>);
fn pointer_button(&self, surface: &SurfaceID, button: u32, pressed: bool); fn pointer_button(&self, surface: &SurfaceId, button: u32, pressed: bool);
fn pointer_scroll( fn pointer_scroll(
&self, &self,
surface: &SurfaceID, surface: &SurfaceId,
scroll_distance: Option<Vector2<f32>>, scroll_distance: Option<Vector2<f32>>,
scroll_steps: Option<Vector2<f32>>, scroll_steps: Option<Vector2<f32>>,
); );
fn keyboard_keys(&self, surface: &SurfaceID, keymap_id: &str, keys: Vec<i32>); fn keyboard_keys(&self, surface: &SurfaceId, keymap_id: &str, keys: Vec<i32>);
fn touch_down(&self, surface: &SurfaceID, id: u32, position: Vector2<f32>); fn touch_down(&self, surface: &SurfaceId, id: u32, position: Vector2<f32>);
fn touch_move(&self, id: u32, position: Vector2<f32>); fn touch_move(&self, id: u32, position: Vector2<f32>);
fn touch_up(&self, id: u32); fn touch_up(&self, id: u32);
fn reset_touches(&self); fn reset_input(&self);
} }
pub fn panel_item_from_node(node: &Node) -> Option<Arc<dyn PanelItemTrait>> { pub fn panel_item_from_node(node: &Node) -> Option<Arc<dyn PanelItemTrait>> {
@@ -211,7 +101,8 @@ pub fn panel_item_from_node(node: &Node) -> Option<Arc<dyn PanelItemTrait>> {
} }
pub trait PanelItemTrait: Backend + Send + Sync + 'static { pub trait PanelItemTrait: Backend + Send + Sync + 'static {
fn serialize_start_data(&self, id: &str) -> Result<Message>; fn send_ui_item_created(&self, node: &Node, uid: &str, item: &Arc<Node>);
fn send_acceptor_item_created(&self, node: &Node, uid: &str, item: &Arc<Node>);
} }
pub struct PanelItem<B: Backend + ?Sized> { pub struct PanelItem<B: Backend + ?Sized> {
@@ -252,22 +143,7 @@ impl<B: Backend + ?Sized> PanelItem<B> {
&ITEM_TYPE_INFO_PANEL, &ITEM_TYPE_INFO_PANEL,
ItemType::Panel(generic_panel_item), ItemType::Panel(generic_panel_item),
); );
<Self as PanelItemAspect>::add_node_members(&node);
node.add_local_signal("apply_surface_material", Self::apply_surface_material_flex);
node.add_local_signal("close_toplevel", Self::close_toplevel_flex);
node.add_local_signal("auto_size_toplevel", Self::auto_size_toplevel_flex);
node.add_local_signal("set_toplevel_size", Self::set_toplevel_size_flex);
node.add_local_signal("pointer_motion", Self::pointer_motion_flex);
node.add_local_signal("pointer_button", Self::pointer_button_flex);
node.add_local_signal("pointer_scroll", Self::pointer_scroll_flex);
node.add_local_signal("keyboard_key", Self::keyboard_keys_flex);
node.add_local_signal("touch_down", Self::touch_down_flex);
node.add_local_signal("touch_move", Self::touch_move_flex);
node.add_local_signal("touch_up", Self::touch_up_flex);
node.add_local_signal("reset_touches", Self::reset_touches_flex);
(node, panel_item) (node, panel_item)
} }
@@ -286,265 +162,300 @@ impl<B: Backend + ?Sized> PanelItem<B> {
let Some(node) = self.node.upgrade() else { let Some(node) = self.node.upgrade() else {
return; return;
}; };
let _ = node.send_remote_signal("toplevel_parent_changed", serialize(parent).unwrap()); panel_item_client::toplevel_parent_changed(&node, parent);
} }
pub fn toplevel_title_changed(&self, title: &str) { pub fn toplevel_title_changed(&self, title: &str) {
let Some(node) = self.node.upgrade() else { let Some(node) = self.node.upgrade() else {
return; return;
}; };
let _ = node.send_remote_signal("toplevel_title_changed", serialize(title).unwrap()); panel_item_client::toplevel_title_changed(&node, title);
} }
pub fn toplevel_app_id_changed(&self, app_id: &str) { pub fn toplevel_app_id_changed(&self, app_id: &str) {
let Some(node) = self.node.upgrade() else { let Some(node) = self.node.upgrade() else {
return; return;
}; };
let _ = node.send_remote_signal("toplevel_app_id_changed", serialize(app_id).unwrap()); panel_item_client::toplevel_app_id_changed(&node, app_id);
} }
pub fn toplevel_fullscreen_active(&self, active: bool) { pub fn toplevel_fullscreen_active(&self, active: bool) {
let Some(node) = self.node.upgrade() else { let Some(node) = self.node.upgrade() else {
return; return;
}; };
let _ = node.send_remote_signal("toplevel_fullscreen_active", serialize(active).unwrap()); panel_item_client::toplevel_fullscreen_active(&node, active);
} }
pub fn toplevel_move_request(&self) { pub fn toplevel_move_request(&self) {
let Some(node) = self.node.upgrade() else { let Some(node) = self.node.upgrade() else {
return; return;
}; };
let _ = node.send_remote_signal("toplevel_move_request", Vec::<u8>::new()); panel_item_client::toplevel_move_request(&node);
} }
pub fn toplevel_resize_request(&self, up: bool, down: bool, left: bool, right: bool) { pub fn toplevel_resize_request(&self, up: bool, down: bool, left: bool, right: bool) {
let Some(node) = self.node.upgrade() else { let Some(node) = self.node.upgrade() else {
return; return;
}; };
let _ = node.send_remote_signal( panel_item_client::toplevel_resize_request(&node, up, down, left, right);
"toplevel_resize_request",
serialize((up, down, left, right)).unwrap(),
);
} }
pub fn toplevel_size_changed(&self, size: Vector2<u32>) { pub fn toplevel_size_changed(&self, size: Vector2<u32>) {
let Some(node) = self.node.upgrade() else { let Some(node) = self.node.upgrade() else {
return; return;
}; };
let _ = node.send_remote_signal("toplevel_size_changed", serialize(size).unwrap()); panel_item_client::toplevel_size_changed(&node, size);
} }
pub fn set_cursor(&self, geometry: Option<Geometry>) { pub fn set_cursor(&self, geometry: Option<Geometry>) {
let Some(node) = self.node.upgrade() else { let Some(node) = self.node.upgrade() else {
return; return;
}; };
let _ = node.send_remote_signal("set_cursor", serialize(geometry).unwrap()); if let Some(geometry) = geometry {
panel_item_client::set_cursor(&node, &geometry);
} else {
panel_item_client::hide_cursor(&node);
}
} }
pub fn new_child(&self, uid: &str, info: ChildInfo) { pub fn create_child(&self, uid: &str, info: ChildInfo) {
let Some(node) = self.node.upgrade() else { let Some(node) = self.node.upgrade() else {
return; return;
}; };
let _ = node.send_remote_signal("new_child", serialize((uid, info)).unwrap()); panel_item_client::create_child(&node, uid, &info);
} }
pub fn reposition_child(&self, uid: &str, geometry: Geometry) { pub fn reposition_child(&self, uid: &str, geometry: Geometry) {
let Some(node) = self.node.upgrade() else { let Some(node) = self.node.upgrade() else {
return; return;
}; };
let _ = node.send_remote_signal("reposition_child", serialize((uid, geometry)).unwrap()); panel_item_client::reposition_child(&node, uid, &geometry);
} }
pub fn drop_child(&self, uid: &str) { pub fn destroy_child(&self, uid: &str) {
let Some(node) = self.node.upgrade() else { let Some(node) = self.node.upgrade() else {
return; return;
}; };
let _ = node.send_remote_signal("drop_child", serialize(uid).unwrap()); panel_item_client::destroy_child(&node, uid);
} }
} }
// Local signals
macro_rules! flex_no_args { // make these stupid vectors u32 in the protocol somehow!!!!!!!1
($fn_name: ident, $trait_fn: ident) => {
fn $fn_name(
node: Arc<Node>,
_calling_client: Arc<Client>,
_message: Message,
) -> Result<()> {
let Some(panel_item) = panel_item_from_node(&node) else {
return Ok(());
};
panel_item.$trait_fn();
Ok(())
}
};
}
macro_rules! flex_deserialize {
($fn_name: ident, $trait_fn: ident) => {
fn $fn_name(node: Arc<Node>, _calling_client: Arc<Client>, message: Message) -> Result<()> {
let Some(panel_item) = panel_item_from_node(&node) else {
return Ok(());
};
panel_item.$trait_fn(deserialize(message.as_ref())?);
Ok(())
}
};
}
#[allow(unused)] #[allow(unused)]
impl<B: Backend + ?Sized> PanelItem<B> { impl<B: Backend + ?Sized> PanelItemAspect for PanelItem<B> {
fn apply_surface_material_flex( #[doc = "Apply the cursor as a material to a model."]
fn apply_cursor_material(
node: Arc<Node>,
_calling_client: Arc<Client>,
model_part: Arc<Node>,
) -> Result<()> {
let Some(panel_item) = panel_item_from_node(&node) else {
return Ok(());
};
let model_part = model_part.get_aspect::<ModelPart>()?;
panel_item.apply_cursor_material(&model_part);
Ok(())
}
#[doc = "Apply a surface's visuals as a material to a model."]
fn apply_surface_material(
node: Arc<Node>, node: Arc<Node>,
calling_client: Arc<Client>, calling_client: Arc<Client>,
message: Message, surface: SurfaceId,
model_part: Arc<Node>,
) -> Result<()> { ) -> Result<()> {
let Some(panel_item) = panel_item_from_node(&node) else { let Some(panel_item) = panel_item_from_node(&node) else {
return Ok(()); return Ok(());
}; };
let model_part = model_part.get_aspect::<ModelPart>()?;
#[derive(Debug, Deserialize)] panel_item.apply_surface_material(surface, &model_part);
struct SurfaceMaterialInfo<'a> {
surface: SurfaceID,
model_node_path: &'a str,
}
let info: SurfaceMaterialInfo = deserialize(message.as_ref())?;
let model_node = calling_client
.scenegraph
.get_node(info.model_node_path)
.ok_or_else(|| eyre!("Model node not found"))?;
let model_part = model_node.get_aspect::<ModelPart>()?;
debug!(?info, "Apply surface material");
panel_item.apply_surface_material(info.surface, &model_part);
Ok(()) Ok(())
} }
flex_no_args!(close_toplevel_flex, close_toplevel); #[doc = "Try to close the toplevel.\n \n The panel item UI handler or panel item acceptor will drop the panel item if this succeeds."]
flex_no_args!(auto_size_toplevel_flex, auto_size_toplevel); fn close_toplevel(node: Arc<Node>, _calling_client: Arc<Client>) -> Result<()> {
flex_deserialize!(set_toplevel_size_flex, set_toplevel_size); let Some(panel_item) = panel_item_from_node(&node) else {
return Ok(());
};
panel_item.close_toplevel();
Ok(())
}
fn pointer_motion_flex( #[doc = "Request a resize of the surface to whatever size the 2D app wants."]
fn auto_size_toplevel(node: Arc<Node>, _calling_client: Arc<Client>) -> Result<()> {
let Some(panel_item) = panel_item_from_node(&node) else {
return Ok(());
};
panel_item.auto_size_toplevel();
Ok(())
}
#[doc = "Request a resize of the surface (in pixels)."]
fn set_toplevel_size(
node: Arc<Node>, node: Arc<Node>,
_calling_client: Arc<Client>, _calling_client: Arc<Client>,
message: Message, size: mint::Vector2<u32>,
) -> Result<()> { ) -> Result<()> {
let Some(panel_item) = panel_item_from_node(&node) else { let Some(panel_item) = panel_item_from_node(&node) else {
return Ok(()); return Ok(());
}; };
panel_item.set_toplevel_size(size);
let (surface_id, position): (SurfaceID, Vector2<f32>) = deserialize(message.as_ref())?;
debug!(?surface_id, ?position, "Pointer deactivate");
panel_item.pointer_motion(&surface_id, position);
Ok(()) Ok(())
} }
fn pointer_button_flex(
#[doc = "Tell the toplevel to appear focused visually if true, or unfocused if false."]
fn set_toplevel_focused_visuals(
node: Arc<Node>, node: Arc<Node>,
_calling_client: Arc<Client>, _calling_client: Arc<Client>,
message: Message, focused: bool,
) -> Result<()> { ) -> Result<()> {
let Some(panel_item) = panel_item_from_node(&node) else { let Some(panel_item) = panel_item_from_node(&node) else {
return Ok(()); return Ok(());
}; };
panel_item.set_toplevel_focused_visuals(focused);
let (surface_id, button, state): (SurfaceID, u32, u32) = deserialize(message.as_ref())?;
debug!(?surface_id, button, state, "Pointer button");
panel_item.pointer_button(&surface_id, button, state != 0);
Ok(()) Ok(())
} }
fn pointer_scroll_flex(
#[doc = "Send an event to set the pointer's position (in pixels, relative to top-left of surface). This will activate the pointer."]
fn pointer_motion(
node: Arc<Node>, node: Arc<Node>,
_calling_client: Arc<Client>, _calling_client: Arc<Client>,
message: Message, surface: SurfaceId,
position: mint::Vector2<f32>,
) -> Result<()> { ) -> Result<()> {
let Some(panel_item) = panel_item_from_node(&node) else { let Some(panel_item) = panel_item_from_node(&node) else {
return Ok(()); return Ok(());
}; };
panel_item.pointer_motion(&surface, position);
#[derive(Debug, Deserialize)]
struct PointerScrollInfo {
surface_id: SurfaceID,
axis_continuous: Option<Vector2<f32>>,
axis_discrete: Option<Vector2<f32>>,
}
let info: PointerScrollInfo = deserialize(message.as_ref())?;
debug!(?info, "Pointer scroll");
panel_item.pointer_scroll(&info.surface_id, info.axis_continuous, info.axis_discrete);
Ok(()) Ok(())
} }
fn keyboard_keys_flex( #[doc = "Send an event to set a pointer button's state if the pointer's active. The `button` is from the `input_event_codes` crate (e.g. BTN_LEFT for left click)."]
fn pointer_button(
node: Arc<Node>, node: Arc<Node>,
_calling_client: Arc<Client>, _calling_client: Arc<Client>,
message: Message, surface: SurfaceId,
button: u32,
pressed: bool,
) -> Result<()> { ) -> Result<()> {
let Some(panel_item) = panel_item_from_node(&node) else { let Some(panel_item) = panel_item_from_node(&node) else {
return Ok(()); return Ok(());
}; };
let (surface_id, keymap_id, keys): (SurfaceID, &str, Vec<i32>) = panel_item.pointer_button(&surface, button, pressed);
deserialize(message.as_ref())?;
debug!(?keys, "Set keyboard key state");
panel_item.keyboard_keys(&surface_id, keymap_id, keys);
Ok(()) Ok(())
} }
pub fn grab_keyboard(&self, sid: Option<SurfaceID>) {
let Some(node) = self.node.upgrade() else {
return;
};
let Ok(message) = serialize(sid) else { return }; #[doc = "Send an event to scroll the pointer if it's active.\nScroll distance is a value in pixels corresponding to the `distance` the surface should be scrolled.\nScroll steps is a value in columns/rows corresponding to the wheel clicks of a mouse or such. This also supports fractions of a wheel click."]
let _ = node.send_remote_signal("grab_keyboard", message); fn pointer_scroll(
}
fn touch_down_flex(
node: Arc<Node>, node: Arc<Node>,
_calling_client: Arc<Client>, _calling_client: Arc<Client>,
message: Message, surface: SurfaceId,
scroll_distance: mint::Vector2<f32>,
scroll_steps: mint::Vector2<f32>,
) -> Result<()> { ) -> Result<()> {
let Some(panel_item) = panel_item_from_node(&node) else { let Some(panel_item) = panel_item_from_node(&node) else {
return Ok(()); return Ok(());
}; };
panel_item.pointer_scroll(&surface, Some(scroll_distance), Some(scroll_steps));
let (surface_id, id, position): (SurfaceID, u32, Vector2<f32>) =
deserialize(message.as_ref())?;
debug!(?surface_id, id, ?position, "Touch down");
panel_item.touch_down(&surface_id, id, position);
Ok(()) Ok(())
} }
fn touch_move_flex(
#[doc = "Send an event to stop scrolling the pointer."]
fn pointer_stop_scroll(
node: Arc<Node>, node: Arc<Node>,
_calling_client: Arc<Client>, _calling_client: Arc<Client>,
message: Message, surface: SurfaceId,
) -> Result<()> { ) -> Result<()> {
let Some(panel_item) = panel_item_from_node(&node) else { let Some(panel_item) = panel_item_from_node(&node) else {
return Ok(()); return Ok(());
}; };
panel_item.pointer_scroll(&surface, None, None);
let (id, position): (u32, Vector2<f32>) = deserialize(message.as_ref())?; Ok(())
debug!(?position, "Touch move"); }
panel_item.touch_move(id, position); #[doc = "Send a series of key presses and releases (positive keycode for pressed, negative for released)."]
fn keyboard_keys(
node: Arc<Node>,
_calling_client: Arc<Client>,
surface: SurfaceId,
keymap_id: String,
keys: Vec<i32>,
) -> Result<()> {
let Some(panel_item) = panel_item_from_node(&node) else {
return Ok(());
};
panel_item.keyboard_keys(&surface, &keymap_id, keys);
Ok(())
}
#[doc = "Put a touch down on this surface with the unique ID `uid` at `position` (in pixels) from top left corner of the surface."]
fn touch_down(
node: Arc<Node>,
_calling_client: Arc<Client>,
surface: SurfaceId,
uid: u32,
position: mint::Vector2<f32>,
) -> Result<()> {
let Some(panel_item) = panel_item_from_node(&node) else {
return Ok(());
};
panel_item.touch_down(&surface, uid, position);
Ok(())
}
#[doc = "Move an existing touch point."]
fn touch_move(
node: Arc<Node>,
_calling_client: Arc<Client>,
uid: u32,
position: mint::Vector2<f32>,
) -> Result<()> {
let Some(panel_item) = panel_item_from_node(&node) else {
return Ok(());
};
panel_item.touch_move(uid, position);
Ok(())
}
#[doc = "Release a touch from its surface."]
fn touch_up(node: Arc<Node>, _calling_client: Arc<Client>, uid: u32) -> Result<()> {
let Some(panel_item) = panel_item_from_node(&node) else {
return Ok(());
};
panel_item.touch_up(uid);
Ok(())
}
#[doc = "Reset all input, such as pressed keys and pointer clicks and touches. Useful for when it's newly captured into an item acceptor to make sure no input gets stuck."]
fn reset_input(node: Arc<Node>, _calling_client: Arc<Client>) -> Result<()> {
let Some(panel_item) = panel_item_from_node(&node) else {
return Ok(());
};
panel_item.reset_input();
Ok(()) Ok(())
} }
flex_deserialize!(touch_up_flex, touch_up);
flex_no_args!(reset_touches_flex, reset_touches);
} }
impl<B: Backend + ?Sized> PanelItemTrait for PanelItem<B> { impl<B: Backend + ?Sized> PanelItemTrait for PanelItem<B> {
fn serialize_start_data(&self, id: &str) -> Result<Message> { fn send_ui_item_created(&self, node: &Node, uid: &str, item: &Arc<Node>) {
Ok(serialize((id, self.start_data()?))?.into()) let Ok(init_data) = self.backend.start_data() else {
return;
};
let _ = panel_item_ui_client::create_item(node, uid, item, init_data);
}
fn send_acceptor_item_created(&self, node: &Node, uid: &str, item: &Arc<Node>) {
let Ok(init_data) = self.backend.start_data() else {
return;
};
let _ = panel_item_acceptor_client::capture_item(node, uid, item, init_data);
} }
} }
impl<B: Backend + ?Sized> Backend for PanelItem<B> { impl<B: Backend + ?Sized> Backend for PanelItem<B> {
fn start_data(&self) -> Result<PanelItemInitData> { fn start_data(&self) -> Result<PanelItemInitData> {
self.backend.start_data() self.backend.start_data()
} }
fn surface_alive(&self, surface: &SurfaceID) -> bool { fn surface_alive(&self, surface: &SurfaceId) -> bool {
self.backend.surface_alive(surface) self.backend.surface_alive(surface)
} }
fn apply_surface_material(&self, surface: SurfaceID, model_part: &Arc<ModelPart>) { fn apply_cursor_material(&self, model_part: &Arc<ModelPart>) {
self.backend.apply_cursor_material(model_part)
}
fn apply_surface_material(&self, surface: SurfaceId, model_part: &Arc<ModelPart>) {
self.backend.apply_surface_material(surface, model_part) self.backend.apply_surface_material(surface, model_part)
} }
@@ -561,15 +472,15 @@ impl<B: Backend + ?Sized> Backend for PanelItem<B> {
self.backend.set_toplevel_focused_visuals(focused) self.backend.set_toplevel_focused_visuals(focused)
} }
fn pointer_motion(&self, surface: &SurfaceID, position: Vector2<f32>) { fn pointer_motion(&self, surface: &SurfaceId, position: Vector2<f32>) {
self.backend.pointer_motion(surface, position) self.backend.pointer_motion(surface, position)
} }
fn pointer_button(&self, surface: &SurfaceID, button: u32, pressed: bool) { fn pointer_button(&self, surface: &SurfaceId, button: u32, pressed: bool) {
self.backend.pointer_button(surface, button, pressed) self.backend.pointer_button(surface, button, pressed)
} }
fn pointer_scroll( fn pointer_scroll(
&self, &self,
surface: &SurfaceID, surface: &SurfaceId,
scroll_distance: Option<Vector2<f32>>, scroll_distance: Option<Vector2<f32>>,
scroll_steps: Option<Vector2<f32>>, scroll_steps: Option<Vector2<f32>>,
) { ) {
@@ -577,11 +488,11 @@ impl<B: Backend + ?Sized> Backend for PanelItem<B> {
.pointer_scroll(surface, scroll_distance, scroll_steps) .pointer_scroll(surface, scroll_distance, scroll_steps)
} }
fn keyboard_keys(&self, surface: &SurfaceID, keymap_id: &str, keys: Vec<i32>) { fn keyboard_keys(&self, surface: &SurfaceId, keymap_id: &str, keys: Vec<i32>) {
self.backend.keyboard_keys(surface, keymap_id, keys) self.backend.keyboard_keys(surface, keymap_id, keys)
} }
fn touch_down(&self, surface: &SurfaceID, id: u32, position: Vector2<f32>) { fn touch_down(&self, surface: &SurfaceId, id: u32, position: Vector2<f32>) {
self.backend.touch_down(surface, id, position) self.backend.touch_down(surface, id, position)
} }
fn touch_move(&self, id: u32, position: Vector2<f32>) { fn touch_move(&self, id: u32, position: Vector2<f32>) {
@@ -590,8 +501,8 @@ impl<B: Backend + ?Sized> Backend for PanelItem<B> {
fn touch_up(&self, id: u32) { fn touch_up(&self, id: u32) {
self.backend.touch_up(id) self.backend.touch_up(id)
} }
fn reset_touches(&self) { fn reset_input(&self) {
self.backend.reset_touches() self.backend.reset_input()
} }
} }
impl<B: Backend + ?Sized> Drop for PanelItem<B> { impl<B: Backend + ?Sized> Drop for PanelItem<B> {
@@ -600,3 +511,30 @@ impl<B: Backend + ?Sized> Drop for PanelItem<B> {
info!("Dropped panel item {}", self.uid); info!("Dropped panel item {}", self.uid);
} }
} }
create_interface!(ItemInterface, ItemPanelInterfaceAspect, "/item/panel");
impl ItemPanelInterfaceAspect for ItemInterface {
#[doc = "Register this client to manage the items of a certain type and create default 3D UI for them."]
fn register_panel_item_ui(_node: Arc<Node>, calling_client: Arc<Client>) -> Result<()> {
register_item_ui_flex(calling_client, &ITEM_TYPE_INFO_PANEL)
}
#[doc = "Create an item acceptor to allow temporary ownership of a given type of item. Creates a node at `/item/<item_type>/acceptor/<name>`."]
fn create_panel_item_acceptor(
_node: Arc<Node>,
calling_client: Arc<Client>,
name: String,
parent: Arc<Node>,
transform: Transform,
field: Arc<Node>,
) -> Result<()> {
create_item_acceptor_flex(
calling_client,
name,
parent,
transform,
&ITEM_TYPE_INFO_PANEL,
field,
)
}
}

View File

@@ -307,7 +307,7 @@ impl SeatWrapper {
}, },
); );
} }
pub fn reset_touches(&self) { pub fn reset_input(&self) {
for id in self.touches.lock().keys() { for id in self.touches.lock().keys() {
self.touch_up(*id) self.touch_up(*id)
} }

View File

@@ -7,7 +7,7 @@ use super::{
use crate::nodes::{ use crate::nodes::{
drawable::model::ModelPart, drawable::model::ModelPart,
items::panel::{ items::panel::{
Backend, ChildInfo, Geometry, PanelItem, PanelItemInitData, SurfaceID, ToplevelInfo, Backend, ChildInfo, Geometry, PanelItem, PanelItemInitData, SurfaceId, ToplevelInfo,
}, },
}; };
use color_eyre::eyre::Result; use color_eyre::eyre::Result;
@@ -85,7 +85,7 @@ impl XdgShellHandler for WaylandState {
s.states.unset(State::Fullscreen); s.states.unset(State::Fullscreen);
}); });
toplevel.send_configure(); toplevel.send_configure();
utils::insert_data(toplevel.wl_surface(), SurfaceID::Toplevel); utils::insert_data(toplevel.wl_surface(), SurfaceId::Toplevel);
CoreSurface::add_to( CoreSurface::add_to(
self.display_handle.clone(), self.display_handle.clone(),
toplevel.wl_surface(), toplevel.wl_surface(),
@@ -194,7 +194,7 @@ impl XdgShellHandler for WaylandState {
if popup.send_configure().is_err() { if popup.send_configure().is_err() {
return; return;
} }
utils::insert_data(popup.wl_surface(), SurfaceID::Child(uid.clone())); utils::insert_data(popup.wl_surface(), SurfaceId::Child(uid.clone()));
utils::insert_data(popup.wl_surface(), uid.clone()); utils::insert_data(popup.wl_surface(), uid.clone());
utils::insert_data(popup.wl_surface(), Arc::downgrade(&panel_item)); utils::insert_data(popup.wl_surface(), Arc::downgrade(&panel_item));
CoreSurface::add_to( CoreSurface::add_to(
@@ -341,18 +341,10 @@ impl XdgBackend {
seat, seat,
} }
} }
fn wl_surface_from_id(&self, id: &SurfaceID) -> Option<WlSurface> { fn wl_surface_from_id(&self, id: &SurfaceId) -> Option<WlSurface> {
match id { match id {
SurfaceID::Cursor => self SurfaceId::Toplevel(_) => Some(self.toplevel.lock().clone()?.wl_surface().clone()),
.seat SurfaceId::Child(popup) => {
.cursor_info_rx
.borrow()
.surface
.clone()?
.upgrade()
.ok(),
SurfaceID::Toplevel => Some(self.toplevel.lock().clone()?.wl_surface().clone()),
SurfaceID::Child(popup) => {
let popups = self.popups.lock(); let popups = self.popups.lock();
Some(popups.get(popup)?.0.wl_surface().clone()) Some(popups.get(popup)?.0.wl_surface().clone())
} }
@@ -371,12 +363,12 @@ impl XdgBackend {
.lock() .lock()
.insert(uid.to_string(), (popup, positioner)); .insert(uid.to_string(), (popup, positioner));
panel_item.new_child(uid, self.child_data(uid).unwrap()); panel_item.create_child(uid, self.child_data(uid).unwrap());
} }
pub fn reposition_popup(&self, uid: &str, _popup: PopupSurface, positioner: PositionerState) { pub fn reposition_popup(&self, uid: &str, _popup: PopupSurface, positioner: PositionerState) {
let mut popups = self.popups.lock(); let mut popups = self.popups.lock();
let Some((_, old_positioner)) = popups.get_mut(uid) else { let Some((_, old_positioner)) = popups.get_mut(uid) else {
return return;
}; };
let Some(panel_item) = self.panel_item() else { let Some(panel_item) = self.panel_item() else {
return; return;
@@ -390,13 +382,13 @@ impl XdgBackend {
let Some(panel_item) = self.panel_item() else { let Some(panel_item) = self.panel_item() else {
return; return;
}; };
panel_item.drop_child(uid); panel_item.destroy_child(uid);
} }
fn child_data(&self, uid: &str) -> Option<ChildInfo> { fn child_data(&self, uid: &str) -> Option<ChildInfo> {
let (popup, positioner) = self.popups.lock().get(uid)?.clone(); let (popup, positioner) = self.popups.lock().get(uid)?.clone();
Some(ChildInfo { Some(ChildInfo {
parent: (*utils::get_data::<SurfaceID>(&popup.get_parent_surface()?)?).clone(), parent: (*utils::get_data::<SurfaceId>(&popup.get_parent_surface()?)?).clone(),
geometry: positioner.get_geometry().into(), geometry: positioner.get_geometry().into(),
}) })
} }
@@ -448,9 +440,9 @@ impl Backend for XdgBackend {
.current_state() .current_state()
.size .size
.clone() .clone()
.map(|s| [s.w as u32, s.h as u32].into()) .map(|s| Vector2::from([s.w as u32, s.h as u32]))
.or_else(|| toplevel_core_surface.size()) .or_else(|| toplevel_core_surface.size())
.unwrap_or([0; 2].into()); .unwrap_or(Vector2::from([0; 2]));
let toplevel = ToplevelInfo { let toplevel = ToplevelInfo {
parent: toplevel parent: toplevel
.parent() .parent()
@@ -465,8 +457,8 @@ impl Backend for XdgBackend {
{ {
Some( Some(
[ [
toplevel_cached_state.min_size.w as u32, toplevel_cached_state.min_size.w as f32,
toplevel_cached_state.min_size.h as u32, toplevel_cached_state.min_size.h as f32,
] ]
.into(), .into(),
) )
@@ -478,8 +470,8 @@ impl Backend for XdgBackend {
{ {
Some( Some(
[ [
toplevel_cached_state.max_size.w as u32, toplevel_cached_state.max_size.w as f32,
toplevel_cached_state.max_size.h as u32, toplevel_cached_state.max_size.h as f32,
] ]
.into(), .into(),
) )
@@ -489,7 +481,7 @@ impl Backend for XdgBackend {
logical_rectangle: toplevel_cached_state logical_rectangle: toplevel_cached_state
.geometry .geometry
.map(Into::into) .map(Into::into)
.unwrap_or(Geometry { .unwrap_or_else(|| Geometry {
origin: [0; 2].into(), origin: [0; 2].into(),
size, size,
}), }),
@@ -511,14 +503,31 @@ impl Backend for XdgBackend {
}) })
} }
fn surface_alive(&self, surface: &SurfaceID) -> bool { fn surface_alive(&self, surface: &SurfaceId) -> bool {
let Some(surface) = self.wl_surface_from_id(surface) else { let Some(surface) = self.wl_surface_from_id(surface) else {
return false; return false;
}; };
surface.is_alive() surface.is_alive()
} }
fn apply_surface_material(&self, surface: SurfaceID, model_part: &Arc<ModelPart>) { fn apply_cursor_material(&self, model_part: &Arc<ModelPart>) {
let Some(surface) = self
.seat
.cursor_info_rx
.borrow()
.surface
.clone()
.and_then(|s| s.upgrade().ok())
else {
return;
};
let Some(core_surface) = CoreSurface::from_wl_surface(&surface) else {
return;
};
core_surface.apply_material(model_part);
}
fn apply_surface_material(&self, surface: SurfaceId, model_part: &Arc<ModelPart>) {
let Some(surface) = self.wl_surface_from_id(&surface) else { let Some(surface) = self.wl_surface_from_id(&surface) else {
return; return;
}; };
@@ -561,32 +570,32 @@ impl Backend for XdgBackend {
}) })
} }
fn pointer_motion(&self, surface: &SurfaceID, position: Vector2<f32>) { fn pointer_motion(&self, surface: &SurfaceId, position: Vector2<f32>) {
let Some(surface) = self.wl_surface_from_id(&surface) else { let Some(surface) = self.wl_surface_from_id(&surface) else {
return; return;
}; };
self.seat.pointer_motion(surface, position) self.seat.pointer_motion(surface, position)
} }
fn pointer_button(&self, _surface: &SurfaceID, button: u32, pressed: bool) { fn pointer_button(&self, _surface: &SurfaceId, button: u32, pressed: bool) {
self.seat.pointer_button(button, pressed) self.seat.pointer_button(button, pressed)
} }
fn pointer_scroll( fn pointer_scroll(
&self, &self,
_surface: &SurfaceID, _surface: &SurfaceId,
scroll_distance: Option<Vector2<f32>>, scroll_distance: Option<Vector2<f32>>,
scroll_steps: Option<Vector2<f32>>, scroll_steps: Option<Vector2<f32>>,
) { ) {
self.seat.pointer_scroll(scroll_distance, scroll_steps) self.seat.pointer_scroll(scroll_distance, scroll_steps)
} }
fn keyboard_keys(&self, surface: &SurfaceID, keymap_id: &str, keys: Vec<i32>) { fn keyboard_keys(&self, surface: &SurfaceId, keymap_id: &str, keys: Vec<i32>) {
let Some(surface) = self.wl_surface_from_id(&surface) else { let Some(surface) = self.wl_surface_from_id(&surface) else {
return; return;
}; };
self.seat.keyboard_keys(surface, keymap_id, keys) self.seat.keyboard_keys(surface, keymap_id, keys)
} }
fn touch_down(&self, surface: &SurfaceID, id: u32, position: Vector2<f32>) { fn touch_down(&self, surface: &SurfaceId, id: u32, position: Vector2<f32>) {
let Some(surface) = self.wl_surface_from_id(&surface) else { let Some(surface) = self.wl_surface_from_id(&surface) else {
return; return;
}; };
@@ -598,7 +607,7 @@ impl Backend for XdgBackend {
fn touch_up(&self, id: u32) { fn touch_up(&self, id: u32) {
self.seat.touch_up(id) self.seat.touch_up(id)
} }
fn reset_touches(&self) { fn reset_input(&self) {
self.seat.reset_touches() self.seat.reset_input()
} }
} }