feat: client state (save/restore)
This commit is contained in:
@@ -47,7 +47,7 @@ once_cell = "1.17.1"
|
||||
parking_lot = "0.12.1"
|
||||
portable-atomic = { version = "1.2.0", features = ["float", "std"] }
|
||||
rustc-hash = "1.1.0"
|
||||
tokio = { version = "1.27.0", features = ["rt-multi-thread", "signal"] }
|
||||
tokio = { version = "1.27.0", features = ["rt-multi-thread", "signal", "time"] }
|
||||
send_wrapper = "0.6.0"
|
||||
prisma = "0.1.1"
|
||||
stardust-xr = "0.14.1"
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
use super::scenegraph::Scenegraph;
|
||||
use super::{
|
||||
client_state::{ClientState, CLIENT_STATES},
|
||||
scenegraph::Scenegraph,
|
||||
};
|
||||
use crate::{
|
||||
core::{registry::OwnedRegistry, task},
|
||||
nodes::{
|
||||
audio, data, drawable, fields, hmd, input, items,
|
||||
root::Root,
|
||||
spatial,
|
||||
startup::{self, StartupSettings, STARTUP_SETTINGS},
|
||||
Node,
|
||||
},
|
||||
nodes::{audio, data, drawable, fields, hmd, input, items, root::Root, spatial, Node},
|
||||
};
|
||||
use color_eyre::eyre::{eyre, Result};
|
||||
use lazy_static::lazy_static;
|
||||
use once_cell::sync::OnceCell;
|
||||
use parking_lot::Mutex;
|
||||
use rustc_hash::FxHashMap;
|
||||
use stardust_xr::messenger::{self, MessageSenderHandle};
|
||||
use stardust_xr::{
|
||||
messenger::{self, MessageSenderHandle},
|
||||
schemas::flex::serialize,
|
||||
};
|
||||
use std::{fs, iter::FromIterator, path::PathBuf, sync::Arc};
|
||||
use tokio::{net::UnixStream, task::JoinHandle};
|
||||
use tracing::info;
|
||||
@@ -34,7 +34,7 @@ lazy_static! {
|
||||
scenegraph: Default::default(),
|
||||
root: OnceCell::new(),
|
||||
base_resource_prefixes: Default::default(),
|
||||
startup_settings: None,
|
||||
state: Arc::new(ClientState::default()),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -46,9 +46,9 @@ pub fn get_env(pid: i32) -> Result<FxHashMap<String, String>, std::io::Error> {
|
||||
.map(|(k, v)| (k.to_string(), v.to_string())),
|
||||
))
|
||||
}
|
||||
pub fn startup_settings(env: &FxHashMap<String, String>) -> Option<StartupSettings> {
|
||||
pub fn state(env: &FxHashMap<String, String>) -> Option<Arc<ClientState>> {
|
||||
let token = env.get("STARDUST_STARTUP_TOKEN")?;
|
||||
STARTUP_SETTINGS.lock().get(token).cloned()
|
||||
CLIENT_STATES.lock().get(token).cloned()
|
||||
}
|
||||
|
||||
pub struct Client {
|
||||
@@ -63,7 +63,7 @@ pub struct Client {
|
||||
pub scenegraph: Arc<Scenegraph>,
|
||||
pub root: OnceCell<Arc<Root>>,
|
||||
pub base_resource_prefixes: Mutex<Vec<PathBuf>>,
|
||||
pub startup_settings: Option<StartupSettings>,
|
||||
pub state: Arc<ClientState>,
|
||||
}
|
||||
impl Client {
|
||||
pub fn from_connection(connection: UnixStream) -> Result<Arc<Self>> {
|
||||
@@ -80,7 +80,10 @@ impl Client {
|
||||
|
||||
let (mut messenger_tx, mut messenger_rx) = messenger::create(connection);
|
||||
let scenegraph = Arc::new(Scenegraph::default());
|
||||
let startup_settings = env.as_ref().and_then(startup_settings);
|
||||
let state = env
|
||||
.as_ref()
|
||||
.and_then(state)
|
||||
.unwrap_or_else(|| Arc::new(ClientState::default()));
|
||||
|
||||
let client = CLIENTS.add(Client {
|
||||
pid,
|
||||
@@ -95,7 +98,7 @@ impl Client {
|
||||
scenegraph: scenegraph.clone(),
|
||||
root: OnceCell::new(),
|
||||
base_resource_prefixes: Default::default(),
|
||||
startup_settings,
|
||||
state,
|
||||
});
|
||||
let _ = client.scenegraph.client.set(Arc::downgrade(&client));
|
||||
let _ = client.root.set(Root::create(&client)?);
|
||||
@@ -107,7 +110,13 @@ impl Client {
|
||||
data::create_interface(&client)?;
|
||||
items::create_interface(&client)?;
|
||||
input::create_interface(&client)?;
|
||||
startup::create_interface(&client)?;
|
||||
|
||||
client
|
||||
.root
|
||||
.get()
|
||||
.unwrap()
|
||||
.node
|
||||
.send_remote_signal("restore_state", serialize(client.state.apply_to(&client))?)?;
|
||||
|
||||
let pid_printable = pid
|
||||
.map(|pid| pid.to_string())
|
||||
@@ -164,6 +173,27 @@ impl Client {
|
||||
Ok(client)
|
||||
}
|
||||
|
||||
pub fn get_cmdline(&self) -> Option<Vec<String>> {
|
||||
let pid = self.pid?;
|
||||
let exe_proc_path = format!("/proc/{pid}/exe");
|
||||
let cmdline_proc_path = format!("/proc/{pid}/cmdline");
|
||||
let exe = std::fs::read_link(exe_proc_path).ok()?;
|
||||
let cmdline = std::fs::read_to_string(cmdline_proc_path).ok()?;
|
||||
let mut cmdline_split: Vec<_> = cmdline.split('\0').map(ToString::to_string).collect();
|
||||
cmdline_split.pop();
|
||||
*cmdline_split.get_mut(0).unwrap() = exe.to_str()?.to_string();
|
||||
Some(cmdline_split)
|
||||
}
|
||||
pub fn get_cwd(&self) -> Option<PathBuf> {
|
||||
let pid = self.pid?;
|
||||
let cwd_proc_path = format!("/proc/{pid}/cwd");
|
||||
std::fs::read_link(cwd_proc_path).ok()
|
||||
}
|
||||
pub async fn save_state(&self) -> Option<ClientState> {
|
||||
let internal = self.root.get()?.save_state().await.ok()?;
|
||||
Some(dbg!(ClientState::from_deserialized(self, internal)))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_node(&self, name: &'static str, path: &str) -> Result<Arc<Node>> {
|
||||
self.scenegraph
|
||||
|
||||
94
src/core/client_state.rs
Normal file
94
src/core/client_state.rs
Normal file
@@ -0,0 +1,94 @@
|
||||
use super::client::Client;
|
||||
use crate::nodes::{spatial::Spatial, Node};
|
||||
use glam::Mat4;
|
||||
use parking_lot::Mutex;
|
||||
use rustc_hash::FxHashMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref CLIENT_STATES: Mutex<FxHashMap<String, Arc<ClientState>>> = Default::default();
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct ClientState {
|
||||
pub cmdline: Option<Vec<String>>,
|
||||
pub cwd: Option<PathBuf>,
|
||||
pub data: Option<Vec<u8>>,
|
||||
pub root: Mat4,
|
||||
pub spatial_anchors: FxHashMap<String, Mat4>,
|
||||
}
|
||||
impl ClientState {
|
||||
pub fn from_deserialized(client: &Client, state: ClientStateInternal) -> Self {
|
||||
ClientState {
|
||||
cmdline: client.get_cmdline(),
|
||||
cwd: client.get_cwd(),
|
||||
data: state.data,
|
||||
root: Self::spatial_transform(client, &state.root.unwrap_or_default())
|
||||
.unwrap_or_default(),
|
||||
spatial_anchors: state
|
||||
.spatial_anchors
|
||||
.into_iter()
|
||||
.filter_map(|(k, v)| Some((k, Self::spatial_transform(client, &v)?)))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
fn spatial_transform(client: &Client, path: &str) -> Option<Mat4> {
|
||||
let node = client.scenegraph.get_node(path)?;
|
||||
let spatial = node.spatial.get()?;
|
||||
Some(spatial.global_transform())
|
||||
}
|
||||
|
||||
pub fn token(self) -> String {
|
||||
let token = nanoid::nanoid!();
|
||||
CLIENT_STATES.lock().insert(token.clone(), Arc::new(self));
|
||||
token
|
||||
}
|
||||
pub fn to_file(self) {
|
||||
let state_dir = directories::ProjectDirs::from("", "", "stardust")
|
||||
.unwrap()
|
||||
.state_dir()
|
||||
.unwrap();
|
||||
// std::fs::File::create(state_dir.join(""))
|
||||
}
|
||||
pub fn apply_to(&self, client: &Arc<Client>) -> ClientStateInternal {
|
||||
if let Some(root) = client.root.get() {
|
||||
root.set_transform(self.root)
|
||||
}
|
||||
ClientStateInternal {
|
||||
data: self.data.clone(),
|
||||
root: Some("/".to_string()),
|
||||
spatial_anchors: self
|
||||
.spatial_anchors
|
||||
.iter()
|
||||
.map(|(k, v)| {
|
||||
(k.clone(), {
|
||||
let node = Node::create(client, "/spatial/anchor", k, true)
|
||||
.add_to_scenegraph()
|
||||
.unwrap();
|
||||
Spatial::add_to(&node, None, *v, false).unwrap();
|
||||
k.clone()
|
||||
})
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Default for ClientState {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
cmdline: None,
|
||||
cwd: None,
|
||||
data: None,
|
||||
root: Mat4::IDENTITY,
|
||||
spatial_anchors: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize, Deserialize)]
|
||||
pub struct ClientStateInternal {
|
||||
data: Option<Vec<u8>>,
|
||||
root: Option<String>,
|
||||
spatial_anchors: FxHashMap<String, String>,
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod client;
|
||||
pub mod client_state;
|
||||
pub mod delta;
|
||||
pub mod destroy_queue;
|
||||
pub mod eventloop;
|
||||
|
||||
18
src/main.rs
18
src/main.rs
@@ -4,6 +4,8 @@ mod objects;
|
||||
#[cfg(feature = "wayland")]
|
||||
mod wayland;
|
||||
|
||||
use crate::core::client::CLIENTS;
|
||||
use crate::core::client_state::ClientState;
|
||||
use crate::core::destroy_queue;
|
||||
use crate::nodes::items::camera;
|
||||
use crate::nodes::{audio, drawable, hmd, input};
|
||||
@@ -27,6 +29,7 @@ use stereokit::{
|
||||
TextureFormat, TextureType,
|
||||
};
|
||||
use stereokit::{DisplayBlend, Sk};
|
||||
use tokio::task::LocalSet;
|
||||
use tokio::{runtime::Handle, sync::oneshot};
|
||||
use tracing::metadata::LevelFilter;
|
||||
use tracing::{debug_span, error, info};
|
||||
@@ -353,6 +356,7 @@ async fn event_loop(
|
||||
_ = stop_rx => (),
|
||||
};
|
||||
}
|
||||
save_clients().await;
|
||||
|
||||
info!("Cleanly shut down event loop");
|
||||
|
||||
@@ -362,3 +366,17 @@ async fn event_loop(
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn save_clients() {
|
||||
let local_set = LocalSet::new();
|
||||
for client in CLIENTS.get_vec() {
|
||||
local_set.spawn_local(async move {
|
||||
tokio::select! {
|
||||
biased;
|
||||
s = client.save_state() => {s.map(ClientState::to_file);},
|
||||
_ = tokio::time::sleep(Duration::from_millis(100)) => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
local_set.await;
|
||||
}
|
||||
|
||||
@@ -109,15 +109,15 @@ impl Item {
|
||||
}
|
||||
let _ = node.item.set(item.clone());
|
||||
|
||||
if let Some(auto_acceptor) = node.get_client().and_then(|client| {
|
||||
client
|
||||
.startup_settings
|
||||
.as_ref()
|
||||
.and_then(|settings| settings.acceptors.get(type_info))
|
||||
.and_then(|acceptor| acceptor.upgrade())
|
||||
}) {
|
||||
capture(&item, &auto_acceptor);
|
||||
}
|
||||
// if let Some(auto_acceptor) = node.get_client().and_then(|client| {
|
||||
// client
|
||||
// .state
|
||||
// .as_ref()
|
||||
// .and_then(|settings| settings.acceptors.get(type_info))
|
||||
// .and_then(|acceptor| acceptor.upgrade())
|
||||
// }) {
|
||||
// capture(&item, &auto_acceptor);
|
||||
// }
|
||||
|
||||
item
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use crate::{
|
||||
core::{
|
||||
client::{get_env, startup_settings, Client, INTERNAL_CLIENT},
|
||||
client::{get_env, state, Client, INTERNAL_CLIENT},
|
||||
registry::Registry,
|
||||
},
|
||||
nodes::{
|
||||
drawable::{model::ModelPart, Drawable},
|
||||
items::{self, Item, ItemType, TypeInfo},
|
||||
items::{Item, ItemType, TypeInfo},
|
||||
spatial::Spatial,
|
||||
Message, Node,
|
||||
},
|
||||
@@ -223,7 +223,7 @@ impl<B: Backend + ?Sized> PanelItem<B> {
|
||||
|
||||
let startup_settings = pid
|
||||
.and_then(|pid| get_env(pid).ok())
|
||||
.and_then(|env| startup_settings(&env));
|
||||
.and_then(|env| state(&env));
|
||||
|
||||
let uid = nanoid!();
|
||||
let node = Node::create(&INTERNAL_CLIENT, "/item/panel/item", &uid, true)
|
||||
@@ -231,7 +231,7 @@ impl<B: Backend + ?Sized> PanelItem<B> {
|
||||
.unwrap();
|
||||
let spatial = Spatial::add_to(&node, None, Mat4::IDENTITY, false).unwrap();
|
||||
if let Some(startup_settings) = &startup_settings {
|
||||
spatial.set_local_transform(startup_settings.transform);
|
||||
spatial.set_local_transform(startup_settings.root);
|
||||
}
|
||||
|
||||
let panel_item = Arc::new(PanelItem {
|
||||
@@ -241,23 +241,13 @@ impl<B: Backend + ?Sized> PanelItem<B> {
|
||||
});
|
||||
|
||||
let generic_panel_item: Arc<dyn PanelItemTrait> = panel_item.clone();
|
||||
let item = Item::add_to(
|
||||
Item::add_to(
|
||||
&node,
|
||||
uid,
|
||||
&ITEM_TYPE_INFO_PANEL,
|
||||
ItemType::Panel(generic_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("close_toplevel", Self::close_toplevel_flex);
|
||||
node.add_local_signal("auto_size_toplevel", Self::auto_size_toplevel_flex);
|
||||
|
||||
@@ -8,7 +8,6 @@ pub mod input;
|
||||
pub mod items;
|
||||
pub mod root;
|
||||
pub mod spatial;
|
||||
pub mod startup;
|
||||
|
||||
use color_eyre::eyre::{eyre, Result};
|
||||
use nanoid::nanoid;
|
||||
@@ -20,6 +19,7 @@ use stardust_xr::messenger::MessageSenderHandle;
|
||||
use stardust_xr::scenegraph::ScenegraphError;
|
||||
use stardust_xr::schemas::flex::deserialize;
|
||||
use std::fmt::Debug;
|
||||
use std::future::Future;
|
||||
use std::os::fd::OwnedFd;
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::vec::Vec;
|
||||
@@ -38,8 +38,8 @@ use self::input::{InputHandler, InputMethod};
|
||||
use self::items::{Item, ItemAcceptor, ItemUI};
|
||||
use self::spatial::zone::Zone;
|
||||
use self::spatial::Spatial;
|
||||
use self::startup::StartupSettings;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Message {
|
||||
pub data: Vec<u8>,
|
||||
pub fds: Vec<OwnedFd>,
|
||||
@@ -97,9 +97,6 @@ pub struct Node {
|
||||
|
||||
// Sound
|
||||
pub sound: OnceCell<Arc<Sound>>,
|
||||
|
||||
// Startup
|
||||
pub startup_settings: OnceCell<Mutex<StartupSettings>>,
|
||||
}
|
||||
|
||||
impl Node {
|
||||
@@ -143,7 +140,6 @@ impl Node {
|
||||
item_acceptor: OnceCell::new(),
|
||||
item_ui: OnceCell::new(),
|
||||
sound: OnceCell::new(),
|
||||
startup_settings: OnceCell::new(),
|
||||
};
|
||||
node.add_local_signal("set_enabled", Node::set_enabled_flex);
|
||||
node.add_local_signal("destroy", Node::destroy_flex);
|
||||
@@ -302,20 +298,30 @@ impl Node {
|
||||
Ok(())
|
||||
}
|
||||
// #[instrument(level = "debug", skip_all)]
|
||||
// pub fn execute_remote_method(
|
||||
// &self,
|
||||
// method: &str,
|
||||
// data: Vec<u8>,
|
||||
// ) -> Result<impl Future<Output = Result<Message>>> {
|
||||
// let message_sender_handle = self
|
||||
// .message_sender_handle
|
||||
// .as_ref()
|
||||
// .ok_or(eyre!("Messenger does not exist for this node"))?;
|
||||
pub fn execute_remote_method(
|
||||
&self,
|
||||
method: &str,
|
||||
message: impl Into<Message>,
|
||||
) -> Result<impl Future<Output = Result<Message>>> {
|
||||
let message = message.into();
|
||||
let message_sender_handle = self
|
||||
.message_sender_handle
|
||||
.as_ref()
|
||||
.ok_or(eyre!("Messenger does not exist for this node"))?;
|
||||
|
||||
// let future = message_sender_handle.method(self.path.as_str(), method, &data)?;
|
||||
let future =
|
||||
message_sender_handle.method(self.path.as_str(), method, &message.data, message.fds)?;
|
||||
|
||||
// Ok(async { future.await.map_err(|e| eyre!(e)) })
|
||||
// }
|
||||
Ok(async {
|
||||
match future.await {
|
||||
Ok(m) => {
|
||||
let (data, fds) = m.into_components();
|
||||
Ok(Message { data, fds })
|
||||
}
|
||||
Err(e) => Err(eyre!(e)),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
impl Debug for Node {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
|
||||
@@ -1,19 +1,25 @@
|
||||
use super::spatial::Spatial;
|
||||
use super::{Message, Node};
|
||||
use crate::core::client::Client;
|
||||
use crate::core::client_state::{ClientState, ClientStateInternal};
|
||||
use crate::core::registry::Registry;
|
||||
use crate::core::scenegraph::MethodResponseSender;
|
||||
use crate::wayland::WAYLAND_DISPLAY;
|
||||
use crate::STARDUST_INSTANCE;
|
||||
use color_eyre::eyre::Result;
|
||||
use glam::Mat4;
|
||||
use rustc_hash::FxHashMap;
|
||||
use stardust_xr::schemas::flex::{deserialize, serialize};
|
||||
use tracing::instrument;
|
||||
|
||||
use std::future::Future;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
static ROOT_REGISTRY: Registry<Root> = Registry::new();
|
||||
|
||||
pub struct Root {
|
||||
node: Arc<Node>,
|
||||
pub node: Arc<Node>,
|
||||
send_frame_event: AtomicBool,
|
||||
}
|
||||
impl Root {
|
||||
@@ -21,17 +27,13 @@ impl Root {
|
||||
let node = Node::create(client, "", "", false);
|
||||
node.add_local_signal("subscribe_frame", Root::subscribe_frame_flex);
|
||||
node.add_local_signal("set_base_prefixes", Root::set_base_prefixes_flex);
|
||||
let node = node.add_to_scenegraph()?;
|
||||
let _ = Spatial::add_to(
|
||||
&node,
|
||||
None,
|
||||
client
|
||||
.startup_settings
|
||||
.as_ref()
|
||||
.map(|settings| settings.transform)
|
||||
.unwrap_or(Mat4::IDENTITY),
|
||||
false,
|
||||
node.add_local_method("state_token", Root::state_token_flex);
|
||||
node.add_local_method(
|
||||
"get_connection_environment",
|
||||
get_connection_environment_flex,
|
||||
);
|
||||
let node = node.add_to_scenegraph()?;
|
||||
let _ = Spatial::add_to(&node, None, client.state.root, false);
|
||||
|
||||
Ok(ROOT_REGISTRY.add(Root {
|
||||
node,
|
||||
@@ -72,6 +74,31 @@ impl Root {
|
||||
*calling_client.base_resource_prefixes.lock() = deserialize(message.as_ref())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn state_token_flex(
|
||||
_node: &Node,
|
||||
calling_client: Arc<Client>,
|
||||
message: Message,
|
||||
response: MethodResponseSender,
|
||||
) {
|
||||
response.wrap_sync(|| {
|
||||
let state: ClientStateInternal = deserialize(message.as_ref())?;
|
||||
let token = ClientState::from_deserialized(&calling_client, state).token();
|
||||
Ok(serialize(token)?.into())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_transform(&self, transform: Mat4) {
|
||||
let spatial = self.node.spatial.get().unwrap();
|
||||
spatial.set_spatial_parent(None).unwrap();
|
||||
spatial.set_local_transform(transform);
|
||||
}
|
||||
pub fn save_state(&self) -> impl Future<Output = Result<ClientStateInternal>> {
|
||||
let future = self
|
||||
.node
|
||||
.execute_remote_method("save_state", Message::default());
|
||||
async move { Ok(deserialize(&future?.await?.data)?) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Root {
|
||||
@@ -79,3 +106,33 @@ impl Drop for Root {
|
||||
ROOT_REGISTRY.remove(self);
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! var_env_insert {
|
||||
($env:ident, $name:ident) => {
|
||||
$env.insert(stringify!($name).to_string(), $name.get().unwrap().clone());
|
||||
};
|
||||
}
|
||||
pub fn get_connection_environment_flex(
|
||||
_node: &Node,
|
||||
_calling_client: Arc<Client>,
|
||||
_message: Message,
|
||||
response: MethodResponseSender,
|
||||
) {
|
||||
response.wrap_sync(move || {
|
||||
let mut env: FxHashMap<String, String> = FxHashMap::default();
|
||||
var_env_insert!(env, STARDUST_INSTANCE);
|
||||
#[cfg(feature = "wayland")]
|
||||
{
|
||||
var_env_insert!(env, WAYLAND_DISPLAY);
|
||||
#[cfg(feature = "xwayland")]
|
||||
var_env_insert!(env, DISPLAY);
|
||||
env.insert("GDK_BACKEND".to_string(), "wayland".to_string());
|
||||
env.insert("QT_QPA_PLATFORM".to_string(), "wayland".to_string());
|
||||
env.insert("MOZ_ENABLE_WAYLAND".to_string(), "1".to_string());
|
||||
env.insert("CLUTTER_BACKEND".to_string(), "wayland".to_string());
|
||||
env.insert("SDL_VIDEODRIVER".to_string(), "wayland".to_string());
|
||||
}
|
||||
|
||||
Ok(serialize(env)?.into())
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,165 +0,0 @@
|
||||
#[cfg(feature = "xwayland")]
|
||||
use crate::wayland::xwayland::DISPLAY;
|
||||
use crate::{
|
||||
core::{client::Client, scenegraph::MethodResponseSender},
|
||||
wayland::WAYLAND_DISPLAY,
|
||||
STARDUST_INSTANCE,
|
||||
};
|
||||
|
||||
use super::{
|
||||
items::{ItemAcceptor, TypeInfo},
|
||||
spatial::find_spatial,
|
||||
Message, Node,
|
||||
};
|
||||
use color_eyre::eyre::Result;
|
||||
use glam::Mat4;
|
||||
use parking_lot::Mutex;
|
||||
use rustc_hash::FxHashMap;
|
||||
use stardust_xr::schemas::flex::{deserialize, serialize};
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
sync::{Arc, Weak},
|
||||
};
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref STARTUP_SETTINGS: Mutex<FxHashMap<String, StartupSettings>> = Default::default();
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct StartupSettings {
|
||||
pub transform: Mat4,
|
||||
pub acceptors: FxHashMap<&'static TypeInfo, Weak<ItemAcceptor>>,
|
||||
}
|
||||
impl StartupSettings {
|
||||
pub fn add_to(node: &Arc<Node>) {
|
||||
let _ = node
|
||||
.startup_settings
|
||||
.set(Mutex::new(StartupSettings::default()));
|
||||
}
|
||||
|
||||
fn set_root_flex(node: &Node, calling_client: Arc<Client>, message: Message) -> Result<()> {
|
||||
let spatial = find_spatial(
|
||||
&calling_client,
|
||||
"Root spatial",
|
||||
deserialize(message.as_ref())?,
|
||||
)?;
|
||||
node.startup_settings.get().unwrap().lock().transform = spatial.global_transform();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn add_automatic_acceptor_flex(
|
||||
node: &Node,
|
||||
calling_client: Arc<Client>,
|
||||
message: Message,
|
||||
) -> Result<()> {
|
||||
let acceptor_node =
|
||||
calling_client.get_node("Item acceptor", deserialize(message.as_ref())?)?;
|
||||
let acceptor =
|
||||
acceptor_node.get_aspect("Item acceptor", "item acceptor", |n| &n.item_acceptor)?;
|
||||
let mut startup_settings = node.startup_settings.get().unwrap().lock();
|
||||
startup_settings
|
||||
.acceptors
|
||||
.insert(acceptor.type_info, Arc::downgrade(acceptor));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn generate_startup_token_flex(
|
||||
node: &Node,
|
||||
_calling_client: Arc<Client>,
|
||||
_message: Message,
|
||||
response: MethodResponseSender,
|
||||
) {
|
||||
response.wrap_sync(move || {
|
||||
let id = nanoid::nanoid!();
|
||||
let data = serialize(&id)?;
|
||||
STARTUP_SETTINGS
|
||||
.lock()
|
||||
.insert(id, node.startup_settings.get().unwrap().lock().clone());
|
||||
Ok(data.into())
|
||||
});
|
||||
}
|
||||
}
|
||||
impl Debug for StartupSettings {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("StartupSettings")
|
||||
.field("transform", &self.transform)
|
||||
.field(
|
||||
"acceptors",
|
||||
&self
|
||||
.acceptors
|
||||
.iter()
|
||||
.map(|(k, _)| k.type_name)
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_interface(client: &Arc<Client>) -> Result<()> {
|
||||
let node = Node::create(client, "", "startup", false);
|
||||
node.add_local_signal("create_startup_settings", create_startup_settings_flex);
|
||||
node.add_local_method(
|
||||
"get_connection_environment",
|
||||
get_connection_environment_flex,
|
||||
);
|
||||
node.add_to_scenegraph().map(|_| ())
|
||||
}
|
||||
|
||||
pub fn create_startup_settings_flex(
|
||||
_node: &Node,
|
||||
calling_client: Arc<Client>,
|
||||
message: Message,
|
||||
) -> Result<()> {
|
||||
let node = Node::create(
|
||||
&calling_client,
|
||||
"/startup/settings",
|
||||
deserialize(message.as_ref())?,
|
||||
true,
|
||||
)
|
||||
.add_to_scenegraph()?;
|
||||
StartupSettings::add_to(&node);
|
||||
|
||||
node.add_local_signal("set_root", StartupSettings::set_root_flex);
|
||||
node.add_local_signal(
|
||||
"add_automatic_acceptor",
|
||||
StartupSettings::add_automatic_acceptor_flex,
|
||||
);
|
||||
node.add_local_method(
|
||||
"generate_startup_token",
|
||||
StartupSettings::generate_startup_token_flex,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
macro_rules! var_env_insert {
|
||||
($env:ident, $name:ident) => {
|
||||
$env.insert(stringify!($name).to_string(), $name.get().unwrap().clone());
|
||||
};
|
||||
}
|
||||
pub fn get_connection_environment_flex(
|
||||
_node: &Node,
|
||||
_calling_client: Arc<Client>,
|
||||
_message: Message,
|
||||
response: MethodResponseSender,
|
||||
) {
|
||||
response.wrap_sync(move || {
|
||||
let mut env: FxHashMap<String, String> = FxHashMap::default();
|
||||
var_env_insert!(env, STARDUST_INSTANCE);
|
||||
#[cfg(feature = "wayland")]
|
||||
{
|
||||
var_env_insert!(env, WAYLAND_DISPLAY);
|
||||
#[cfg(feature = "xwayland")]
|
||||
var_env_insert!(env, DISPLAY);
|
||||
env.insert("GDK_BACKEND".to_string(), "wayland".to_string());
|
||||
env.insert("QT_QPA_PLATFORM".to_string(), "wayland".to_string());
|
||||
env.insert("MOZ_ENABLE_WAYLAND".to_string(), "1".to_string());
|
||||
env.insert("CLUTTER_BACKEND".to_string(), "wayland".to_string());
|
||||
env.insert("SDL_VIDEODRIVER".to_string(), "wayland".to_string());
|
||||
}
|
||||
|
||||
Ok(serialize(env)?.into())
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user