feat: session restore!
This commit is contained in:
57
Cargo.lock
generated
57
Cargo.lock
generated
@@ -1648,7 +1648,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
|
checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"toml_edit",
|
"toml_edit 0.19.15",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1974,6 +1974,15 @@ dependencies = [
|
|||||||
"syn 2.0.37",
|
"syn 2.0.37",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_spanned"
|
||||||
|
version = "0.6.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sharded-slab"
|
name = "sharded-slab"
|
||||||
version = "0.1.4"
|
version = "0.1.4"
|
||||||
@@ -2149,6 +2158,7 @@ dependencies = [
|
|||||||
"stardust-xr-server-codegen",
|
"stardust-xr-server-codegen",
|
||||||
"stereokit",
|
"stereokit",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"toml",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
"tracing-tracy",
|
"tracing-tracy",
|
||||||
@@ -2347,10 +2357,25 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_datetime"
|
name = "toml"
|
||||||
version = "0.6.3"
|
version = "0.8.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
|
checksum = "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_spanned",
|
||||||
|
"toml_datetime",
|
||||||
|
"toml_edit 0.22.6",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_datetime"
|
||||||
|
version = "0.6.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_edit"
|
name = "toml_edit"
|
||||||
@@ -2360,7 +2385,20 @@ checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap 2.0.0",
|
"indexmap 2.0.0",
|
||||||
"toml_datetime",
|
"toml_datetime",
|
||||||
"winnow",
|
"winnow 0.5.15",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_edit"
|
||||||
|
version = "0.22.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2c1b5fd4128cc8d3e0cb74d4ed9a9cc7c7284becd4df68f5f940e1ad123606f6"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap 2.0.0",
|
||||||
|
"serde",
|
||||||
|
"serde_spanned",
|
||||||
|
"toml_datetime",
|
||||||
|
"winnow 0.6.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2779,6 +2817,15 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a4191c47f15cc3ec71fcb4913cb83d58def65dd3787610213c649283b5ce178"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "x11rb"
|
name = "x11rb"
|
||||||
version = "0.13.0"
|
version = "0.13.0"
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ wayland-scanner = "0.31.1"
|
|||||||
wayland-backend = "0.3.3"
|
wayland-backend = "0.3.3"
|
||||||
cluFlock = "1.2.7"
|
cluFlock = "1.2.7"
|
||||||
fxtypemap = "0.2.0"
|
fxtypemap = "0.2.0"
|
||||||
|
toml = "0.8.10"
|
||||||
|
|
||||||
[dependencies.smithay]
|
[dependencies.smithay]
|
||||||
# git = "https://github.com/technobaboo/smithay.git" # Until we get stereokit to understand OES samplers and external textures
|
# git = "https://github.com/technobaboo/smithay.git" # Until we get stereokit to understand OES samplers and external textures
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ use stardust_xr::{
|
|||||||
messenger::{self, MessageSenderHandle},
|
messenger::{self, MessageSenderHandle},
|
||||||
schemas::flex::serialize,
|
schemas::flex::serialize,
|
||||||
};
|
};
|
||||||
use std::{fs, iter::FromIterator, path::PathBuf, sync::Arc};
|
use std::{fmt::Debug, fs, iter::FromIterator, path::PathBuf, sync::Arc};
|
||||||
use tokio::{net::UnixStream, task::JoinHandle};
|
use tokio::{net::UnixStream, task::JoinHandle};
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
||||||
@@ -215,6 +215,19 @@ impl Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl Debug for Client {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("Client")
|
||||||
|
.field("pid", &self.pid)
|
||||||
|
.field("exe", &self.exe)
|
||||||
|
.field("dispatch_join_handle", &self.dispatch_join_handle)
|
||||||
|
.field("flush_join_handle", &self.flush_join_handle)
|
||||||
|
.field("disconnect_status", &self.disconnect_status)
|
||||||
|
.field("base_resource_prefixes", &self.base_resource_prefixes)
|
||||||
|
.field("state", &self.state)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
impl Drop for Client {
|
impl Drop for Client {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
info!(
|
info!(
|
||||||
|
|||||||
@@ -4,7 +4,11 @@ use glam::Mat4;
|
|||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{io::Write, path::PathBuf, sync::Arc};
|
use std::{
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
process::Command,
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
pub static ref CLIENT_STATES: Mutex<FxHashMap<String, Arc<ClientState>>> = Default::default();
|
pub static ref CLIENT_STATES: Mutex<FxHashMap<String, Arc<ClientState>>> = Default::default();
|
||||||
@@ -58,14 +62,23 @@ impl ClientState {
|
|||||||
CLIENT_STATES.lock().insert(token.clone(), Arc::new(self));
|
CLIENT_STATES.lock().insert(token.clone(), Arc::new(self));
|
||||||
token
|
token
|
||||||
}
|
}
|
||||||
pub fn to_file(self) {
|
pub fn from_file(file: &Path) -> Option<Self> {
|
||||||
let project_dirs = directories::ProjectDirs::from("", "", "stardust").unwrap();
|
let file_string = std::fs::read_to_string(file).ok()?;
|
||||||
let state_dir = project_dirs.state_dir().unwrap();
|
toml::from_str(&file_string).ok()
|
||||||
std::fs::create_dir_all(state_dir).unwrap();
|
|
||||||
let mut file = std::fs::File::create(state_dir.join(nanoid::nanoid!())).unwrap();
|
|
||||||
file.write_all(&stardust_xr::schemas::flex::flexbuffers::to_vec(self).unwrap())
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
pub fn to_file(self, directory: &Path) {
|
||||||
|
let app_name = self
|
||||||
|
.launch_info
|
||||||
|
.as_ref()
|
||||||
|
.map(|l| l.cmdline.get(0).unwrap().split('/').last().unwrap())
|
||||||
|
.unwrap_or("unknown");
|
||||||
|
let state_file_path = directory
|
||||||
|
.join(format!("{app_name}-{}", nanoid::nanoid!()))
|
||||||
|
.with_extension("toml");
|
||||||
|
|
||||||
|
std::fs::write(state_file_path, toml::to_string(&self).unwrap()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn apply_to(&self, client: &Arc<Client>) -> ClientStateInternal {
|
pub fn apply_to(&self, client: &Arc<Client>) -> ClientStateInternal {
|
||||||
if let Some(root) = client.root.get() {
|
if let Some(root) = client.root.get() {
|
||||||
root.set_transform(self.root)
|
root.set_transform(self.root)
|
||||||
@@ -88,6 +101,16 @@ impl ClientState {
|
|||||||
.collect(),
|
.collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn launch_command(self) -> Option<Command> {
|
||||||
|
let launch_info = self.launch_info.as_ref()?;
|
||||||
|
let mut cmdline = launch_info.cmdline.iter();
|
||||||
|
let mut command = Command::new(cmdline.next()?);
|
||||||
|
command.args(cmdline);
|
||||||
|
command.current_dir(&launch_info.cwd);
|
||||||
|
command.envs(launch_info.env.iter());
|
||||||
|
command.env("STARDUST_STARTUP_TOKEN", self.token());
|
||||||
|
Some(command)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl Default for ClientState {
|
impl Default for ClientState {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
|
|||||||
204
src/main.rs
204
src/main.rs
@@ -5,7 +5,6 @@ mod objects;
|
|||||||
mod wayland;
|
mod wayland;
|
||||||
|
|
||||||
use crate::core::client::CLIENTS;
|
use crate::core::client::CLIENTS;
|
||||||
use crate::core::client_state::ClientState;
|
|
||||||
use crate::core::destroy_queue;
|
use crate::core::destroy_queue;
|
||||||
use crate::nodes::items::camera;
|
use crate::nodes::items::camera;
|
||||||
use crate::nodes::{audio, drawable, hmd, input};
|
use crate::nodes::{audio, drawable, hmd, input};
|
||||||
@@ -18,12 +17,13 @@ use crate::wayland::X_DISPLAY;
|
|||||||
|
|
||||||
use self::core::eventloop::EventLoop;
|
use self::core::eventloop::EventLoop;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
use core::client_state::ClientState;
|
||||||
use directories::ProjectDirs;
|
use directories::ProjectDirs;
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
use stardust_xr::server;
|
use stardust_xr::server;
|
||||||
use std::os::unix::process::CommandExt;
|
use std::os::unix::fs::PermissionsExt;
|
||||||
use std::path::PathBuf;
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Child, Command, Stdio};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use stereokit::{
|
use stereokit::{
|
||||||
@@ -56,6 +56,10 @@ struct CliArgs {
|
|||||||
/// Run a script when ready for clients to connect. If this is not set the script at $HOME/.config/stardust/startup will be ran if it exists.
|
/// Run a script when ready for clients to connect. If this is not set the script at $HOME/.config/stardust/startup will be ran if it exists.
|
||||||
#[clap(id = "PATH", short = 'e', long = "execute-startup-script", action)]
|
#[clap(id = "PATH", short = 'e', long = "execute-startup-script", action)]
|
||||||
startup_script: Option<PathBuf>,
|
startup_script: Option<PathBuf>,
|
||||||
|
|
||||||
|
/// Restore the session with the given ID (or `latest`), ignoring the startup script. Sessions are stored in directories at `~/.local/state/stardust/`.
|
||||||
|
#[clap(id = "SESSION_ID", long = "restore", action)]
|
||||||
|
restore: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
static STARDUST_INSTANCE: OnceCell<String> = OnceCell::new();
|
static STARDUST_INSTANCE: OnceCell<String> = OnceCell::new();
|
||||||
@@ -197,7 +201,10 @@ fn main() {
|
|||||||
let (info_sender, info_receiver) = oneshot::channel::<EventLoopInfo>();
|
let (info_sender, info_receiver) = oneshot::channel::<EventLoopInfo>();
|
||||||
let event_thread = std::thread::Builder::new()
|
let event_thread = std::thread::Builder::new()
|
||||||
.name("event_loop".to_owned())
|
.name("event_loop".to_owned())
|
||||||
.spawn(move || event_loop(info_sender))
|
.spawn({
|
||||||
|
let project_dirs = project_dirs.clone();
|
||||||
|
move || event_loop(info_sender, project_dirs.clone())
|
||||||
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let event_loop_info = info_receiver.blocking_recv().unwrap();
|
let event_loop_info = info_receiver.blocking_recv().unwrap();
|
||||||
let _tokio_handle = event_loop_info.tokio_handle.enter();
|
let _tokio_handle = event_loop_info.tokio_handle.enter();
|
||||||
@@ -206,56 +213,18 @@ fn main() {
|
|||||||
let mut wayland = wayland::Wayland::new().expect("Could not initialize wayland");
|
let mut wayland = wayland::Wayland::new().expect("Could not initialize wayland");
|
||||||
info!("Stardust ready!");
|
info!("Stardust ready!");
|
||||||
|
|
||||||
let mut startup_child = (|| {
|
let mut startup_children = project_dirs
|
||||||
let project_dirs = project_dirs.as_ref()?;
|
.as_ref()
|
||||||
let startup_script_path = cli_args
|
.map(|project_dirs| {
|
||||||
.startup_script
|
launch_start(
|
||||||
.clone()
|
&cli_args,
|
||||||
.and_then(|p| p.canonicalize().ok())
|
project_dirs,
|
||||||
.unwrap_or_else(|| project_dirs.config_dir().join("startup"));
|
&event_loop_info,
|
||||||
let mut startup_command = Command::new("bash");
|
#[cfg(feature = "wayland")]
|
||||||
startup_command.arg(startup_script_path);
|
&wayland,
|
||||||
startup_command.arg("&");
|
)
|
||||||
|
})
|
||||||
startup_command.stdin(Stdio::null());
|
.unwrap_or_default();
|
||||||
startup_command.stdout(Stdio::null());
|
|
||||||
startup_command.stderr(Stdio::null());
|
|
||||||
startup_command.env(
|
|
||||||
"FLAT_WAYLAND_DISPLAY",
|
|
||||||
std::env::var_os("WAYLAND_DISPLAY").unwrap_or_default(),
|
|
||||||
);
|
|
||||||
startup_command.env(
|
|
||||||
"STARDUST_INSTANCE",
|
|
||||||
event_loop_info
|
|
||||||
.socket_path
|
|
||||||
.file_name()
|
|
||||||
.expect("Stardust socket path not found"),
|
|
||||||
);
|
|
||||||
#[cfg(feature = "wayland")]
|
|
||||||
{
|
|
||||||
if let Some(wayland_socket) = wayland.socket_name.as_ref() {
|
|
||||||
startup_command.env("WAYLAND_DISPLAY", &wayland_socket);
|
|
||||||
}
|
|
||||||
startup_command.env(
|
|
||||||
"DISPLAY",
|
|
||||||
format!(":{}", X_DISPLAY.get().cloned().unwrap_or_default()),
|
|
||||||
);
|
|
||||||
startup_command.env("GDK_BACKEND", "wayland");
|
|
||||||
startup_command.env("QT_QPA_PLATFORM", "wayland");
|
|
||||||
startup_command.env("MOZ_ENABLE_WAYLAND", "1");
|
|
||||||
startup_command.env("CLUTTER_BACKEND", "wayland");
|
|
||||||
startup_command.env("SDL_VIDEODRIVER", "wayland");
|
|
||||||
}
|
|
||||||
unsafe {
|
|
||||||
startup_command.pre_exec(|| {
|
|
||||||
nix::unistd::setsid()
|
|
||||||
.map(|_| ())
|
|
||||||
.map_err(|_| std::io::ErrorKind::Other.into())
|
|
||||||
})
|
|
||||||
};
|
|
||||||
let child = startup_command.spawn().ok()?;
|
|
||||||
Some(child)
|
|
||||||
})();
|
|
||||||
|
|
||||||
let mut last_frame_delta = Duration::ZERO;
|
let mut last_frame_delta = Duration::ZERO;
|
||||||
let mut sleep_duration = Duration::ZERO;
|
let mut sleep_duration = Duration::ZERO;
|
||||||
@@ -306,10 +275,6 @@ fn main() {
|
|||||||
},
|
},
|
||||||
|_sk| {
|
|_sk| {
|
||||||
info!("Cleanly shut down StereoKit");
|
info!("Cleanly shut down StereoKit");
|
||||||
|
|
||||||
if let Some(mut startup_child) = startup_child.take() {
|
|
||||||
let _ = startup_child.kill();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
@@ -322,6 +287,9 @@ fn main() {
|
|||||||
.join()
|
.join()
|
||||||
.expect("Failed to cleanly shut down event loop")
|
.expect("Failed to cleanly shut down event loop")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
for mut startup_child in startup_children.drain(..) {
|
||||||
|
let _ = startup_child.kill();
|
||||||
|
}
|
||||||
|
|
||||||
info!("Cleanly shut down Stardust");
|
info!("Cleanly shut down Stardust");
|
||||||
}
|
}
|
||||||
@@ -351,7 +319,10 @@ fn adaptive_sleep(
|
|||||||
|
|
||||||
// #[tokio::main]
|
// #[tokio::main]
|
||||||
#[tokio::main(flavor = "current_thread")]
|
#[tokio::main(flavor = "current_thread")]
|
||||||
async fn event_loop(info_sender: oneshot::Sender<EventLoopInfo>) -> color_eyre::eyre::Result<()> {
|
async fn event_loop(
|
||||||
|
info_sender: oneshot::Sender<EventLoopInfo>,
|
||||||
|
project_dirs: Option<ProjectDirs>,
|
||||||
|
) -> color_eyre::eyre::Result<()> {
|
||||||
let socket_path =
|
let socket_path =
|
||||||
server::get_free_socket_path().expect("Unable to find a free stardust socket path");
|
server::get_free_socket_path().expect("Unable to find a free stardust socket path");
|
||||||
STARDUST_INSTANCE.set(socket_path.file_name().unwrap().to_string_lossy().into_owned()).expect("Someone hasn't done their job, yell at Nova because how is this set multiple times what the hell");
|
STARDUST_INSTANCE.set(socket_path.file_name().unwrap().to_string_lossy().into_owned()).expect("Someone hasn't done their job, yell at Nova because how is this set multiple times what the hell");
|
||||||
@@ -368,7 +339,9 @@ async fn event_loop(info_sender: oneshot::Sender<EventLoopInfo>) -> color_eyre::
|
|||||||
|
|
||||||
STOP_NOTIFIER.notified().await;
|
STOP_NOTIFIER.notified().await;
|
||||||
println!("Stopping...");
|
println!("Stopping...");
|
||||||
save_clients().await;
|
if let Some(project_dirs) = project_dirs {
|
||||||
|
save_session(&project_dirs).await;
|
||||||
|
}
|
||||||
|
|
||||||
info!("Cleanly shut down event loop");
|
info!("Cleanly shut down event loop");
|
||||||
|
|
||||||
@@ -379,16 +352,121 @@ async fn event_loop(info_sender: oneshot::Sender<EventLoopInfo>) -> color_eyre::
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn save_clients() {
|
fn launch_start(
|
||||||
|
cli_args: &CliArgs,
|
||||||
|
project_dirs: &ProjectDirs,
|
||||||
|
event_loop_info: &EventLoopInfo,
|
||||||
|
#[cfg(feature = "wayland")] wayland: &wayland::Wayland,
|
||||||
|
) -> Vec<Child> {
|
||||||
|
if let Some(session_id) = &cli_args.restore {
|
||||||
|
let session_dir = project_dirs.state_dir().unwrap().join(session_id);
|
||||||
|
return restore_session(&session_dir, event_loop_info, wayland);
|
||||||
|
}
|
||||||
|
let startup_script_path = cli_args
|
||||||
|
.startup_script
|
||||||
|
.clone()
|
||||||
|
.and_then(|p| p.canonicalize().ok())
|
||||||
|
.unwrap_or_else(|| project_dirs.config_dir().join("startup"));
|
||||||
|
run_script(&startup_script_path, event_loop_info, wayland)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn restore_session(
|
||||||
|
session_dir: &Path,
|
||||||
|
event_loop_info: &EventLoopInfo,
|
||||||
|
#[cfg(feature = "wayland")] wayland: &wayland::Wayland,
|
||||||
|
) -> Vec<Child> {
|
||||||
|
let Ok(clients) = session_dir.read_dir() else {
|
||||||
|
return Vec::new();
|
||||||
|
};
|
||||||
|
clients
|
||||||
|
.filter_map(Result::ok)
|
||||||
|
.filter_map(|c| ClientState::from_file(&c.path()))
|
||||||
|
.filter_map(ClientState::launch_command)
|
||||||
|
.filter_map(|startup_command| {
|
||||||
|
run_client(
|
||||||
|
startup_command,
|
||||||
|
event_loop_info,
|
||||||
|
#[cfg(feature = "wayland")]
|
||||||
|
wayland,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_script(
|
||||||
|
script_path: &Path,
|
||||||
|
event_loop_info: &EventLoopInfo,
|
||||||
|
#[cfg(feature = "wayland")] wayland: &wayland::Wayland,
|
||||||
|
) -> Vec<Child> {
|
||||||
|
let _ = std::fs::set_permissions(script_path, std::fs::Permissions::from_mode(0o755));
|
||||||
|
let startup_command = Command::new(script_path);
|
||||||
|
run_client(
|
||||||
|
startup_command,
|
||||||
|
event_loop_info,
|
||||||
|
#[cfg(feature = "wayland")]
|
||||||
|
wayland,
|
||||||
|
)
|
||||||
|
.map(|c| vec![c])
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_client(
|
||||||
|
mut command: Command,
|
||||||
|
event_loop_info: &EventLoopInfo,
|
||||||
|
#[cfg(feature = "wayland")] wayland: &wayland::Wayland,
|
||||||
|
) -> Option<Child> {
|
||||||
|
command.stdin(Stdio::null());
|
||||||
|
command.stdout(Stdio::null());
|
||||||
|
command.stderr(Stdio::null());
|
||||||
|
command.env(
|
||||||
|
"FLAT_WAYLAND_DISPLAY",
|
||||||
|
std::env::var_os("WAYLAND_DISPLAY").unwrap_or_default(),
|
||||||
|
);
|
||||||
|
command.env(
|
||||||
|
"STARDUST_INSTANCE",
|
||||||
|
event_loop_info
|
||||||
|
.socket_path
|
||||||
|
.file_name()
|
||||||
|
.expect("Stardust socket path not found"),
|
||||||
|
);
|
||||||
|
#[cfg(feature = "wayland")]
|
||||||
|
{
|
||||||
|
if let Some(wayland_socket) = wayland.socket_name.as_ref() {
|
||||||
|
command.env("WAYLAND_DISPLAY", &wayland_socket);
|
||||||
|
}
|
||||||
|
command.env(
|
||||||
|
"DISPLAY",
|
||||||
|
format!(":{}", X_DISPLAY.get().cloned().unwrap_or_default()),
|
||||||
|
);
|
||||||
|
command.env("GDK_BACKEND", "wayland");
|
||||||
|
command.env("QT_QPA_PLATFORM", "wayland");
|
||||||
|
command.env("MOZ_ENABLE_WAYLAND", "1");
|
||||||
|
command.env("CLUTTER_BACKEND", "wayland");
|
||||||
|
command.env("SDL_VIDEODRIVER", "wayland");
|
||||||
|
}
|
||||||
|
let child = command.spawn().ok()?;
|
||||||
|
Some(child)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn save_session(project_dirs: &ProjectDirs) {
|
||||||
|
let session_id = nanoid::nanoid!();
|
||||||
|
let state_dir = project_dirs.state_dir().unwrap();
|
||||||
|
let session_dir = state_dir.join(&session_id);
|
||||||
|
std::fs::create_dir_all(&session_dir).unwrap();
|
||||||
|
let _ = std::fs::remove_dir_all(state_dir.join("latest"));
|
||||||
|
std::os::unix::fs::symlink(&session_dir, state_dir.join("latest")).unwrap();
|
||||||
|
|
||||||
let local_set = LocalSet::new();
|
let local_set = LocalSet::new();
|
||||||
for client in CLIENTS.get_vec() {
|
for client in CLIENTS.get_vec() {
|
||||||
|
let session_dir = session_dir.clone();
|
||||||
local_set.spawn_local(async move {
|
local_set.spawn_local(async move {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
biased;
|
biased;
|
||||||
s = client.save_state() => {s.map(ClientState::to_file);},
|
s = client.save_state() => {s.map(|s| s.to_file(&session_dir));},
|
||||||
_ = tokio::time::sleep(Duration::from_millis(100)) => (),
|
_ = tokio::time::sleep(Duration::from_millis(100)) => (),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
local_set.await;
|
local_set.await;
|
||||||
|
println!("Session ID for restore is {session_id}");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,11 +95,10 @@ impl Root {
|
|||||||
spatial.set_local_transform(transform);
|
spatial.set_local_transform(transform);
|
||||||
}
|
}
|
||||||
pub async fn save_state(&self) -> Result<ClientStateInternal> {
|
pub async fn save_state(&self) -> Result<ClientStateInternal> {
|
||||||
Ok(self
|
self.node
|
||||||
.node
|
|
||||||
.execute_remote_method_typed("save_state", (), Vec::new())
|
.execute_remote_method_typed("save_state", (), Vec::new())
|
||||||
.await?
|
.await
|
||||||
.0)
|
.map(|(m, _)| m)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user