Compare commits
8 Commits
main
...
AnnoyingRa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3dc32ed3ae | ||
|
|
01ec90c699 | ||
|
|
e60463081e | ||
|
|
cbd99c2cc7 | ||
|
|
62b0fe8240 | ||
|
|
f5008ff5b5 | ||
|
|
e28a8e8e1e | ||
|
|
c5c4840452 |
721
Cargo.lock
generated
721
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,5 @@
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = ["app_grid", "hexagon_launcher", "protostar", "single", "sirius"]
|
||||
|
||||
[workspace.dependencies]
|
||||
|
||||
25
README.md
25
README.md
@@ -1,11 +1,24 @@
|
||||
# protostar
|
||||
A collection of application launchers for Stardust XR
|
||||
> [!IMPORTANT]
|
||||
> Requires the [Stardust XR Server](https://github.com/StardustXR/server) to be running. For launching 2D applications, [Flatland](https://github.com/StardustXR/flatland) also needs to be running.
|
||||
|
||||
Prototype application launcher for Stardust XR
|
||||
If you installed the Stardust XR server via:
|
||||
```note
|
||||
sudo dnf group install stardust-xr
|
||||
```
|
||||
Or if you installed via the [installation script](https://github.com/cyberneticmelon/usefulscripts/blob/main/stardustxr_setup.sh), Protostar comes pre-installed
|
||||
|
||||
Protostar provides an easy to use crate to write applications launchers. See the [examples](examples) to learn more!
|
||||
# How to Use
|
||||
Protostar itself can be used to build various kinds of app launchers, but two are built in. Most likely the one you will want to use will be `hexagon_launcher`. After launching, in flastcreen mode drag applications out of the app launcher, hold down `Shift + ~`
|
||||

|
||||
**Quest 3 Hand tracking**:
|
||||
Pinch to drag and drop, grasp with full hand for grabbing, point and click with pointer finger to click or pinch from a distance
|
||||
|
||||
# TODO:
|
||||

|
||||
|
||||
1. Implement our own (more reliable) way to get icons
|
||||
2. Untangle the code (eg: make it so that the user can decide sizes, models, etc... )
|
||||
3. Implement [configuration files](https://docs.rs/confy/latest/confy/) in the examples
|
||||
## Manual Installation
|
||||
Clone the repository and after the server is running:
|
||||
```sh
|
||||
cargo run
|
||||
```
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "app_grid"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "1.32.0", features = ["rt", "tokio-macros", "sync"] }
|
||||
|
||||
@@ -13,11 +13,11 @@ use stardust_xr_fusion::{
|
||||
YAlign,
|
||||
},
|
||||
fields::{Field, Shape},
|
||||
node::NodeType,
|
||||
root::{ClientState, FrameInfo, RootAspect, RootHandler},
|
||||
root::{ClientState, FrameInfo, RootAspect},
|
||||
spatial::{Spatial, SpatialAspect, SpatialRefAspect, Transform},
|
||||
ClientHandle,
|
||||
};
|
||||
use stardust_xr_molecules::{Grabbable, GrabbableSettings};
|
||||
use stardust_xr_molecules::{FrameSensitive, Grabbable, GrabbableSettings, UIElement};
|
||||
use std::f32::consts::PI;
|
||||
|
||||
const APP_LIMIT: usize = 300;
|
||||
@@ -32,16 +32,34 @@ async fn main() -> Result<()> {
|
||||
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
|
||||
.pretty()
|
||||
.init();
|
||||
let (client, event_loop) = Client::connect_with_async_loop().await?;
|
||||
let owned_client = Client::connect().await?;
|
||||
let client = owned_client.handle();
|
||||
let async_loop = owned_client.async_event_loop();
|
||||
client
|
||||
.set_base_prefixes(&[directory_relative_path!("../res")])
|
||||
.get_root()
|
||||
.set_base_prefixes(&[directory_relative_path!("../res").to_string()])
|
||||
.unwrap();
|
||||
|
||||
let _root = client.get_root().alias().wrap(AppGrid::new(&client))?;
|
||||
let mut grid = AppGrid::new(&client);
|
||||
let mut owned_client = async_loop.stop().await.unwrap();
|
||||
let event_loop = owned_client.sync_event_loop(|handle, _| {
|
||||
let Some(event) = handle.get_root().recv_root_event() else {
|
||||
return;
|
||||
};
|
||||
match event {
|
||||
stardust_xr_fusion::root::RootEvent::Ping { response } => response.send(Ok(())),
|
||||
stardust_xr_fusion::root::RootEvent::Frame { info } => {
|
||||
grid.frame(info);
|
||||
}
|
||||
stardust_xr_fusion::root::RootEvent::SaveState { response } => {
|
||||
response.send(grid.save_state());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
tokio::select! {
|
||||
_ = tokio::signal::ctrl_c() => (),
|
||||
e = event_loop => e??,
|
||||
e = event_loop => e?,
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
@@ -51,7 +69,7 @@ struct AppGrid {
|
||||
//style: TextStyle,
|
||||
}
|
||||
impl AppGrid {
|
||||
fn new(client: &Client) -> Self {
|
||||
fn new(client: &ClientHandle) -> Self {
|
||||
let apps = get_desktop_files()
|
||||
.filter_map(|d| parse_desktop_file(d).ok())
|
||||
.filter(|d| !d.no_display)
|
||||
@@ -74,7 +92,7 @@ impl AppGrid {
|
||||
AppGrid { apps }
|
||||
}
|
||||
}
|
||||
impl RootHandler for AppGrid {
|
||||
impl AppGrid {
|
||||
fn frame(&mut self, info: FrameInfo) {
|
||||
for app in &mut self.apps {
|
||||
app.frame(&info);
|
||||
@@ -198,7 +216,10 @@ impl App {
|
||||
// }
|
||||
|
||||
fn frame(&mut self, info: &FrameInfo) {
|
||||
let _ = self.grabbable.update(info);
|
||||
if !self.grabbable.handle_events() {
|
||||
return;
|
||||
}
|
||||
self.grabbable.frame(info);
|
||||
|
||||
if self.grabbable.grab_action().actor_stopped() {
|
||||
self.grabbable.cancel_angular_velocity();
|
||||
@@ -210,8 +231,8 @@ impl App {
|
||||
// }
|
||||
|
||||
let application = self.application.clone();
|
||||
let space = self.content_parent().alias();
|
||||
let root = self.root.alias();
|
||||
let space = self.content_parent().clone();
|
||||
let root = self.root.clone();
|
||||
|
||||
tokio::task::spawn(async move {
|
||||
let Ok(transform) = space.get_transform(&root).await else {
|
||||
|
||||
17
flake.lock
generated
17
flake.lock
generated
@@ -1,17 +1,12 @@
|
||||
{
|
||||
"nodes": {
|
||||
"crane": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1712681629,
|
||||
"narHash": "sha256-bMDXn4AkTXLCpoZbII6pDGoSeSe9gI87jxPsHRXgu/E=",
|
||||
"lastModified": 1755993354,
|
||||
"narHash": "sha256-FCRRAzSaL/+umLIm3RU3O/+fJ2ssaPHseI2SSFL8yZU=",
|
||||
"owner": "ipetkov",
|
||||
"repo": "crane",
|
||||
"rev": "220387ac8e99cbee0ca4c95b621c4bc782b6a235",
|
||||
"rev": "25bd41b24426c7734278c2ff02e53258851db914",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -22,11 +17,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1712791164,
|
||||
"narHash": "sha256-3sbWO1mbpWsLepZGbWaMovSO7ndZeFqDSdX0hZ9nVyw=",
|
||||
"lastModified": 1756386758,
|
||||
"narHash": "sha256-1wxxznpW2CKvI9VdniaUnTT2Os6rdRJcRUf65ZK9OtE=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "1042fd8b148a9105f3c0aca3a6177fd1d9360ba5",
|
||||
"rev": "dfb2f12e899db4876308eba6d93455ab7da304cd",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
crane = {
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
url = "github:ipetkov/crane";
|
||||
};
|
||||
};
|
||||
@@ -13,9 +12,9 @@
|
||||
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
|
||||
nixpkgsFor = forAllSystems (system: import nixpkgs { inherit system; });
|
||||
in {
|
||||
packages = forAllSystems (system: let pkgs = nixpkgsFor.${system}; in {
|
||||
default = crane.lib.${system}.buildPackage {
|
||||
pname = "protostar";
|
||||
packages = forAllSystems (system: let pkgs = nixpkgsFor.${system}; craneLib = crane.mkLib pkgs; in {
|
||||
default = craneLib.buildPackage {
|
||||
pname = "hexagon-launcher";
|
||||
version = "0.1.0";
|
||||
src = ./.;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "hexagon_launcher"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
protostar = { path = "../protostar" }
|
||||
|
||||
@@ -15,7 +15,7 @@ use stardust_xr_fusion::{
|
||||
root::FrameInfo,
|
||||
spatial::{Spatial, SpatialAspect, SpatialRefAspect, Transform},
|
||||
};
|
||||
use stardust_xr_molecules::{Grabbable, GrabbableSettings};
|
||||
use stardust_xr_molecules::{FrameSensitive as _, Grabbable, GrabbableSettings, UIElement};
|
||||
use std::f32::consts::PI;
|
||||
use tween::{QuartInOut, Tweener};
|
||||
|
||||
@@ -160,7 +160,7 @@ impl App {
|
||||
}
|
||||
|
||||
Ok(App {
|
||||
parent: parent.alias(),
|
||||
parent: parent.clone(),
|
||||
position,
|
||||
grabbable,
|
||||
_field: field,
|
||||
@@ -190,7 +190,9 @@ impl App {
|
||||
}
|
||||
|
||||
pub fn frame(&mut self, info: &FrameInfo, state: &State) {
|
||||
let _ = self.grabbable.update(info);
|
||||
if self.grabbable.handle_events() {
|
||||
self.grabbable.frame(info);
|
||||
}
|
||||
|
||||
if let Some(grabbable_move) = &mut self.grabbable_move {
|
||||
if !grabbable_move.is_finished() {
|
||||
@@ -265,8 +267,8 @@ impl App {
|
||||
self.grabbable_shrink = Some(Tweener::quart_in_out(APP_SIZE * 0.5, 0.0001, 0.25));
|
||||
|
||||
let application = self.application.clone();
|
||||
let space = self.content_parent().alias();
|
||||
let parent = self.parent.alias();
|
||||
let space = self.content_parent().clone();
|
||||
let parent = self.parent.clone();
|
||||
|
||||
//TODO: split the executable string for the args
|
||||
tokio::task::spawn(async move {
|
||||
|
||||
@@ -5,23 +5,24 @@ use app::App;
|
||||
use color_eyre::eyre::Result;
|
||||
use glam::Quat;
|
||||
use hex::{HEX_CENTER, HEX_DIRECTION_VECTORS};
|
||||
use manifest_dir_macros::directory_relative_path;
|
||||
use protostar::xdg::{get_desktop_files, parse_desktop_file, DesktopFile};
|
||||
use protostar::xdg::{DesktopFile, get_desktop_files, parse_desktop_file};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use stardust_xr_fusion::{
|
||||
ClientHandle,
|
||||
client::Client,
|
||||
core::values::{
|
||||
color::{color_space::LinearRgb, rgba_linear, Rgba},
|
||||
ResourceID,
|
||||
color::{Rgba, color_space::LinearRgb, rgba_linear},
|
||||
},
|
||||
drawable::{MaterialParameter, Model, ModelPartAspect},
|
||||
node::{NodeError, NodeType},
|
||||
root::{ClientState, FrameInfo, RootAspect, RootHandler},
|
||||
node::NodeError,
|
||||
project_local_resources,
|
||||
root::{ClientState, FrameInfo, RootAspect, RootEvent},
|
||||
spatial::{Spatial, SpatialAspect, Transform},
|
||||
};
|
||||
use stardust_xr_molecules::{
|
||||
FrameSensitive, Grabbable, GrabbableSettings, PointerMode, UIElement,
|
||||
button::{Button, ButtonSettings},
|
||||
Grabbable, GrabbableSettings, PointerMode,
|
||||
};
|
||||
use std::{f32::consts::PI, sync::Arc, time::Duration};
|
||||
|
||||
@@ -41,19 +42,30 @@ async fn main() -> Result<()> {
|
||||
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
|
||||
.pretty()
|
||||
.init();
|
||||
let (client, event_loop) = Client::connect_with_async_loop().await?;
|
||||
client
|
||||
.set_base_prefixes(&[directory_relative_path!("../res")])
|
||||
.unwrap();
|
||||
|
||||
let _root = client
|
||||
.get_root()
|
||||
.alias()
|
||||
.wrap(AppHexGrid::new(&client).await)?;
|
||||
let owned_client = Client::connect().await?;
|
||||
owned_client.setup_resources(&[&project_local_resources!("../res")])?;
|
||||
let client = owned_client.handle();
|
||||
let async_loop = owned_client.async_event_loop();
|
||||
let mut grid = AppHexGrid::new(&client).await;
|
||||
let mut owned_client = async_loop.stop().await.unwrap();
|
||||
let event_loop = owned_client.sync_event_loop(|handle, _| {
|
||||
let Some(event) = handle.get_root().recv_root_event() else {
|
||||
return;
|
||||
};
|
||||
match event {
|
||||
RootEvent::Ping { response } => response.send(Ok(())),
|
||||
RootEvent::Frame { info } => {
|
||||
grid.frame(info);
|
||||
}
|
||||
RootEvent::SaveState { response } => {
|
||||
response.send(grid.save_state());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
tokio::select! {
|
||||
_ = tokio::signal::ctrl_c() => (),
|
||||
e = event_loop => e??,
|
||||
e = event_loop => e?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -70,13 +82,14 @@ struct AppHexGrid {
|
||||
state: State,
|
||||
}
|
||||
impl AppHexGrid {
|
||||
async fn new(client: &Arc<Client>) -> Self {
|
||||
let state = client.get_state().data().unwrap_or_default();
|
||||
async fn new(client: &Arc<ClientHandle>) -> Self {
|
||||
let client_state = client.get_root().get_state().await.unwrap();
|
||||
let state = client_state.data().unwrap_or_default();
|
||||
|
||||
let movable_root =
|
||||
Spatial::create(client.get_root(), Transform::identity(), false).unwrap();
|
||||
|
||||
let button = CenterButton::new(client, client.get_state()).unwrap();
|
||||
let button = CenterButton::new(client, &client_state).unwrap();
|
||||
tokio::time::sleep(Duration::from_millis(10)).await; // give it a bit of time to send the messages properly
|
||||
|
||||
let mut desktop_files: Vec<DesktopFile> = get_desktop_files()
|
||||
@@ -120,7 +133,7 @@ impl AppHexGrid {
|
||||
}
|
||||
}
|
||||
}
|
||||
impl RootHandler for AppHexGrid {
|
||||
impl AppHexGrid {
|
||||
fn frame(&mut self, info: FrameInfo) {
|
||||
self.button.frame(&info);
|
||||
if self.button.button.pressed() {
|
||||
@@ -173,7 +186,7 @@ struct CenterButton {
|
||||
model: Model,
|
||||
}
|
||||
impl CenterButton {
|
||||
fn new(client: &Arc<Client>, state: &ClientState) -> Result<Self, NodeError> {
|
||||
fn new(client: &Arc<ClientHandle>, state: &ClientState) -> Result<Self, NodeError> {
|
||||
// (APP_SIZE + PADDING) / 2.0,
|
||||
let button = Button::create(
|
||||
client.get_root(),
|
||||
@@ -224,7 +237,9 @@ impl CenterButton {
|
||||
}
|
||||
|
||||
fn frame(&mut self, info: &FrameInfo) {
|
||||
let _ = self.grabbable.update(info);
|
||||
self.button.update();
|
||||
if self.grabbable.handle_events() {
|
||||
self.grabbable.frame(info);
|
||||
}
|
||||
self.button.handle_events();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "protostar"
|
||||
version = "0.4.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.1.3", features = ["derive"] }
|
||||
|
||||
@@ -44,9 +44,9 @@ impl Application {
|
||||
icon.and_then(|i| i.cached_process(preferred_px_size).ok())
|
||||
}
|
||||
|
||||
pub fn launch(&self, launch_space: &impl SpatialRefAspect) -> NodeResult<()> {
|
||||
pub fn launch<T: SpatialRefAspect + Clone>(&self, launch_space: &T) -> NodeResult<()> {
|
||||
let client = launch_space.node().client()?;
|
||||
let launch_space = launch_space.alias();
|
||||
let launch_space = launch_space.clone();
|
||||
|
||||
let executable = self
|
||||
.desktop_file
|
||||
@@ -66,10 +66,16 @@ impl Application {
|
||||
return;
|
||||
};
|
||||
for (k, v) in connection_env.into_iter() {
|
||||
std::env::set_var(k, v);
|
||||
// this should be fine, probably?
|
||||
unsafe {
|
||||
std::env::set_var(k, v);
|
||||
}
|
||||
}
|
||||
|
||||
std::env::set_var("STARDUST_STARTUP_TOKEN", startup_token);
|
||||
// this should be fine, probably?
|
||||
unsafe {
|
||||
std::env::set_var("STARDUST_STARTUP_TOKEN", startup_token);
|
||||
}
|
||||
|
||||
// Strip/ignore field codes https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s07.html
|
||||
let re = Regex::new(r"%[fFuUdDnNickvm]").unwrap();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "single"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
protostar = { path = "../protostar" }
|
||||
|
||||
@@ -4,7 +4,7 @@ use clap::Parser;
|
||||
use color_eyre::{eyre::Result, Report};
|
||||
use manifest_dir_macros::directory_relative_path;
|
||||
use protostar::xdg::parse_desktop_file;
|
||||
use stardust_xr_fusion::{client::Client, node::NodeType, root::RootAspect};
|
||||
use stardust_xr_fusion::{client::Client, root::RootAspect};
|
||||
use std::path::PathBuf;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
@@ -25,20 +25,38 @@ async fn main() -> Result<()> {
|
||||
.init();
|
||||
color_eyre::install()?;
|
||||
let args = Args::parse();
|
||||
let (client, event_loop) = Client::connect_with_async_loop().await?;
|
||||
client.set_base_prefixes(&[directory_relative_path!("../res")])?;
|
||||
let owned_client = Client::connect().await?;
|
||||
let client = owned_client.handle();
|
||||
let async_loop = owned_client.async_event_loop();
|
||||
client
|
||||
.get_root()
|
||||
.set_base_prefixes(&[directory_relative_path!("../res").to_string()])?;
|
||||
|
||||
let protostar = Single::create_from_desktop_file(
|
||||
let mut protostar = Single::create_from_desktop_file(
|
||||
client.get_root(),
|
||||
[0.0, 0.0, 0.0],
|
||||
parse_desktop_file(args.desktop_file).map_err(Report::msg)?,
|
||||
)?;
|
||||
|
||||
let _root = client.get_root().alias().wrap(protostar)?;
|
||||
let mut owned_client = async_loop.stop().await.unwrap();
|
||||
let event_loop = owned_client.sync_event_loop(|handle, _| {
|
||||
let Some(event) = handle.get_root().recv_root_event() else {
|
||||
return;
|
||||
};
|
||||
match event {
|
||||
stardust_xr_fusion::root::RootEvent::Ping { response } => response.send(Ok(())),
|
||||
stardust_xr_fusion::root::RootEvent::Frame { info } => {
|
||||
protostar.frame(info);
|
||||
}
|
||||
stardust_xr_fusion::root::RootEvent::SaveState { response } => {
|
||||
response.send(protostar.save_state());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
tokio::select! {
|
||||
_ = tokio::signal::ctrl_c() => (),
|
||||
e = event_loop => e??,
|
||||
e = event_loop => e?,
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -12,10 +12,10 @@ use stardust_xr_fusion::{
|
||||
},
|
||||
fields::{Field, Shape},
|
||||
node::NodeType,
|
||||
root::{ClientState, FrameInfo, RootHandler},
|
||||
root::{ClientState, FrameInfo},
|
||||
spatial::{Spatial, SpatialAspect, SpatialRefAspect, Transform},
|
||||
};
|
||||
use stardust_xr_molecules::{Grabbable, GrabbableSettings};
|
||||
use stardust_xr_molecules::{FrameSensitive, Grabbable, GrabbableSettings, UIElement};
|
||||
use std::f32::consts::PI;
|
||||
use tween::{QuartInOut, Tweener};
|
||||
|
||||
@@ -146,9 +146,11 @@ impl Single {
|
||||
self.grabbable.content_parent()
|
||||
}
|
||||
}
|
||||
impl RootHandler for Single {
|
||||
fn frame(&mut self, info: FrameInfo) {
|
||||
let _ = self.grabbable.update(&info);
|
||||
impl Single {
|
||||
pub fn frame(&mut self, info: FrameInfo) {
|
||||
if self.grabbable.handle_events() {
|
||||
self.grabbable.frame(&info);
|
||||
}
|
||||
|
||||
if let Some(grabbable_move) = &mut self.grabbable_move {
|
||||
if !grabbable_move.is_finished() {
|
||||
@@ -224,8 +226,8 @@ impl RootHandler for Single {
|
||||
self.grabbable_shrink = Some(Tweener::quart_in_out(MODEL_SCALE, 0.0001, 0.25));
|
||||
|
||||
let application = self.application.clone();
|
||||
let space = self.content_parent().alias();
|
||||
let root = self.root.alias();
|
||||
let space = self.content_parent().clone();
|
||||
let root = self.root.clone();
|
||||
|
||||
//TODO: split the executable string for the args
|
||||
tokio::task::spawn(async move {
|
||||
@@ -244,7 +246,7 @@ impl RootHandler for Single {
|
||||
}
|
||||
}
|
||||
|
||||
fn save_state(&mut self) -> color_eyre::eyre::Result<ClientState> {
|
||||
pub fn save_state(&mut self) -> color_eyre::eyre::Result<ClientState> {
|
||||
ClientState::from_root(self.content_parent())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "sirius"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
protostar = { path = "../protostar" }
|
||||
|
||||
@@ -16,12 +16,12 @@ use stardust_xr_fusion::{
|
||||
},
|
||||
fields::{Field, Shape},
|
||||
node::{NodeError, NodeType},
|
||||
root::{ClientState, FrameInfo, RootAspect, RootHandler},
|
||||
spatial::{Spatial, SpatialAspect, SpatialRefAspect, Transform},
|
||||
root::{ClientState, FrameInfo, RootAspect},
|
||||
spatial::{Spatial, SpatialAspect, SpatialRefAspect, Transform}, ClientHandle,
|
||||
};
|
||||
use stardust_xr_molecules::{
|
||||
button::{Button, ButtonSettings},
|
||||
Grabbable, GrabbableSettings,
|
||||
FrameSensitive, Grabbable, GrabbableSettings, UIElement,
|
||||
};
|
||||
use std::{f32::consts::PI, path::PathBuf};
|
||||
|
||||
@@ -50,17 +50,33 @@ async fn main() -> Result<()> {
|
||||
)
|
||||
}
|
||||
|
||||
let (client, event_loop) = Client::connect_with_async_loop().await?;
|
||||
client.set_base_prefixes(&[directory_relative_path!("../res")])?;
|
||||
|
||||
let _wrapped_root = client
|
||||
let owned_client = Client::connect().await?;
|
||||
let client = owned_client.handle();
|
||||
let async_loop = owned_client.async_event_loop();
|
||||
client
|
||||
.get_root()
|
||||
.alias()
|
||||
.wrap(Sirius::new(&client, args)?)?;
|
||||
.set_base_prefixes(&[directory_relative_path!("../res").to_string()])?;
|
||||
let mut sirius = Sirius::new(&client, args)?;
|
||||
|
||||
let mut owned_client = async_loop.stop().await.unwrap();
|
||||
let event_loop = owned_client.sync_event_loop(|handle, _| {
|
||||
let Some(event) = handle.get_root().recv_root_event() else {
|
||||
return;
|
||||
};
|
||||
match event {
|
||||
stardust_xr_fusion::root::RootEvent::Ping { response } => response.send(Ok(())),
|
||||
stardust_xr_fusion::root::RootEvent::Frame { info } => {
|
||||
sirius.frame(info);
|
||||
}
|
||||
stardust_xr_fusion::root::RootEvent::SaveState { response } => {
|
||||
response.send(sirius.save_state());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
tokio::select! {
|
||||
_ = tokio::signal::ctrl_c() => (),
|
||||
e = event_loop => e??,
|
||||
e = event_loop => e?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -78,7 +94,7 @@ struct Sirius {
|
||||
grabbable: Grabbable,
|
||||
}
|
||||
impl Sirius {
|
||||
fn new(client: &Client, args: Args) -> Result<Self, NodeError> {
|
||||
fn new(client: &ClientHandle, args: Args) -> Result<Self, NodeError> {
|
||||
let root = Spatial::create(client.get_root(), Transform::identity(), false).unwrap();
|
||||
|
||||
let field =
|
||||
@@ -141,15 +157,16 @@ impl Sirius {
|
||||
// }
|
||||
// }
|
||||
}
|
||||
impl RootHandler for Sirius {
|
||||
impl Sirius {
|
||||
fn frame(&mut self, info: FrameInfo) {
|
||||
for app in &mut self.clients {
|
||||
app.frame(&info);
|
||||
}
|
||||
|
||||
self.grabbable.update(&info).unwrap();
|
||||
self.button.update();
|
||||
if self.button.pressed() {
|
||||
if self.grabbable.handle_events() {
|
||||
self.grabbable.frame(&info);
|
||||
};
|
||||
if self.button.handle_events() && self.button.pressed() {
|
||||
println!("Touch started");
|
||||
self.state.visible = !self.state.visible;
|
||||
match self.state.visible {
|
||||
@@ -343,7 +360,7 @@ impl App {
|
||||
.ok()
|
||||
});
|
||||
Ok(App {
|
||||
parent: parent.alias(),
|
||||
parent: parent.clone(),
|
||||
position,
|
||||
grabbable,
|
||||
_field: field,
|
||||
@@ -374,7 +391,10 @@ impl App {
|
||||
}
|
||||
|
||||
fn frame(&mut self, info: &FrameInfo) {
|
||||
let _ = self.grabbable.update(info);
|
||||
if !self.grabbable.handle_events() {
|
||||
return;
|
||||
}
|
||||
self.grabbable.frame(info);
|
||||
|
||||
if let Some(grabbable_move) = &mut self.grabbable_move {
|
||||
if !grabbable_move.is_finished() {
|
||||
@@ -449,8 +469,8 @@ impl App {
|
||||
self.grabbable_shrink = Some(Tweener::quart_in_out(APP_SIZE * 0.5, 0.0001, 0.25));
|
||||
|
||||
let application = self.application.clone();
|
||||
let space = self.content_parent().alias();
|
||||
let parent = self.parent.alias();
|
||||
let space = self.content_parent().clone();
|
||||
let parent = self.parent.clone();
|
||||
|
||||
//TODO: split the executable string for the args
|
||||
tokio::task::spawn(async move {
|
||||
|
||||
Reference in New Issue
Block a user