Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf89b73e8f | ||
|
|
2e252279bb | ||
|
|
9cf43ec535 | ||
|
|
f15578f7df | ||
|
|
f63ca4a25b | ||
|
|
89741508e3 | ||
|
|
81be807749 | ||
|
|
fcdb8a7edf | ||
|
|
90ce185f29 | ||
|
|
d6353035ae | ||
|
|
ceb1b23264 | ||
|
|
199e6f70b3 | ||
|
|
641db4face | ||
|
|
80d292b511 | ||
|
|
7fbcc92d02 | ||
|
|
de46726d01 | ||
|
|
6efa3a909e | ||
|
|
ea0f174da7 | ||
|
|
444146fa21 | ||
|
|
a7930760e8 | ||
|
|
668c32f583 | ||
|
|
927e1c48e2 |
46
.github/workflows/build.yml
vendored
Normal file
46
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
build_and_package:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install runtime dependencies
|
||||
run: sudo apt install -y --no-install-recommends libxkbcommon-dev libstdc++6 libopenxr-dev libx11-dev libxfixes-dev libgl1-mesa-dev libegl1-mesa-dev libgbm-dev libfontconfig-dev libjsoncpp-dev libxcb1-dev libglx-dev libxcb-glx0-dev libdrm-dev libwayland-dev libfreetype-dev libpng-dev
|
||||
|
||||
- name: Install build dependencies
|
||||
run: sudo apt install -y --no-install-recommends cmake ninja-build
|
||||
|
||||
- name: Set up Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
|
||||
- name: Build server
|
||||
run: cargo build --release
|
||||
|
||||
|
||||
- name: Install appimagetool
|
||||
run: |
|
||||
wget https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-$(uname -m).AppImage -O /usr/local/bin/appimagetool; \
|
||||
chmod +x /usr/local/bin/appimagetool; \
|
||||
sed -i 's|AI\x02|\x00\x00\x00|' /usr/local/bin/appimagetool
|
||||
- name: Install cargo-appimage
|
||||
run: cargo install cargo-appimage
|
||||
|
||||
- name: Generate AppImage
|
||||
run: cargo appimage
|
||||
|
||||
- name: Upload AppImage
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: appimage
|
||||
path: '*.AppImage'
|
||||
656
Cargo.lock
generated
656
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
edition = "2021"
|
||||
name = "stardust-xr-server"
|
||||
version = "0.42.0"
|
||||
version = "0.42.1"
|
||||
authors = ["Nova King <technobaboo@proton.me>"]
|
||||
description = "Stardust XR reference display server"
|
||||
license = "GPLv2"
|
||||
@@ -58,6 +58,7 @@ tracing = "0.1.37"
|
||||
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
|
||||
global_counter = "0.2.2"
|
||||
rand = "0.8.5"
|
||||
atty = "0.2.14"
|
||||
|
||||
[dependencies.stereokit]
|
||||
default-features = false
|
||||
@@ -65,11 +66,11 @@ features = ["linux-egl"]
|
||||
version = "0.16.7"
|
||||
|
||||
[dependencies.smithay]
|
||||
git = "https://github.com/technobaboo/smithay.git" # Until we get stereokit to understand OES samplers and external textures
|
||||
# git = "https://github.com/smithay/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
|
||||
git = "https://github.com/smithay/smithay.git" # Until we get stereokit to understand OES samplers and external textures
|
||||
# path = "../smithay"
|
||||
default-features = false
|
||||
features = ["desktop", "renderer_gl", "wayland_frontend"]
|
||||
features = ["desktop", "backend_drm", "renderer_gl", "wayland_frontend"]
|
||||
version = "*"
|
||||
optional = true
|
||||
|
||||
|
||||
52
README.md
52
README.md
@@ -15,11 +15,52 @@ This project is a usable Linux display server that reinvents human-computer inte
|
||||
```bash
|
||||
cargo build
|
||||
```
|
||||
The latest stable server is automatically built to an appimage at https://github.com/StardustXR/server/releases for easy testing.
|
||||
|
||||
## Install
|
||||
```bash
|
||||
cargo install
|
||||
```
|
||||
## Usage
|
||||
|
||||
First, try running `cargo run` in a terminal window. If a headset is plugged in and OpenXR is working no window will show up. However, the headset should show the same things as the window that opens:
|
||||
|
||||

|
||||
|
||||
Stardust won't do anything interesting without clients! Try some from https://github.com/StardustXR.
|
||||
|
||||
### Default Sky
|
||||
|
||||
You can set a default skytex/skylight by putting your favorite HDRI equirectangular sky in `~/.config/stardust/skytex.hdr`. Certain clients can override this.
|
||||
|
||||
Flatscreen mode when the default skybox is [Zhengyang Gate](https://polyhaven.com/a/zhengyang_gate):
|
||||

|
||||
|
||||
### Windowed Mode
|
||||
|
||||
If the stardust server can't connect to an OpenXR runtime or you force it into flatscreen mode with `-f`, the server will show in a window.
|
||||

|
||||
|
||||
You can navigate around by right click + dragging to look around, Shift+W/A/S/D/Q/E to move. If you have a virtual hand, left click pinches, right click points, both make a fist.
|
||||
|
||||
### Flags
|
||||
#### Flatscreen (-f)
|
||||
|
||||
The server will show up in windowed mode no matter what with your mouse pointer being turned into a 3D pointer. Keyboard input will be sent to whatever your mouse is hovering over like visionOS simulator.
|
||||
Flatscreen mode upon initial startup:
|
||||

|
||||
|
||||
#### Overlay (-o \<PRIORITY>)
|
||||
|
||||
The server will, if in XR mode, be overlaid using the OpenXR overlay extension with the given priority.
|
||||
|
||||
#### Disable controller (--disable-controller)
|
||||
|
||||
Some runtimes such as Monado may emulate a controller using a hand, and this messes with Stardust's input system. Set this flag to ignore the controllers that the OpenXR runtime provides.
|
||||
|
||||
#### Execute (-e </path/to/executable>)
|
||||
|
||||
When wayland and OpenXR and such are initialized, run the given executable (such as a bash script) with all the environment variables needed to connect all clients of any type to the server. If not set, the server will run the executable at `~/.config/stardust/startup` if it exists. This is how stardust desktop environments can be made.
|
||||
|
||||
#### Help (-h, --help)
|
||||
|
||||
help
|
||||
|
||||
## Test
|
||||
|
||||
@@ -41,5 +82,4 @@ cargo install
|
||||
|
||||
##### Everything
|
||||
|
||||
`nix flake check` will build every test underneath of the `checks` attribute in the `flake.nix`
|
||||
|
||||
`nix flake check` will build every test underneath of the `checks` attribute in the `flake.nix`
|
||||
BIN
img/flatscreen_1.png
Normal file
BIN
img/flatscreen_1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.6 KiB |
BIN
img/flatscreen_2.png
Normal file
BIN
img/flatscreen_2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
BIN
img/flatscreen_3.png
Normal file
BIN
img/flatscreen_3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 789 KiB |
BIN
img/xr_mode_windowed_blank.png
Normal file
BIN
img/xr_mode_windowed_blank.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
69
src/main.rs
69
src/main.rs
@@ -10,15 +10,16 @@ use crate::objects::input::eye_pointer::EyePointer;
|
||||
use crate::objects::input::mouse_pointer::MousePointer;
|
||||
use crate::objects::input::sk_controller::SkController;
|
||||
use crate::objects::input::sk_hand::SkHand;
|
||||
use crate::objects::play_space::PlaySpace;
|
||||
|
||||
use self::core::eventloop::EventLoop;
|
||||
use clap::Parser;
|
||||
use color_eyre::eyre::Result;
|
||||
use directories::ProjectDirs;
|
||||
use once_cell::sync::OnceCell;
|
||||
use stardust_xr::server;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use std::process::{Command, Stdio};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use stereokit::{
|
||||
@@ -27,6 +28,7 @@ use stereokit::{
|
||||
};
|
||||
use stereokit::{DisplayBlend, Sk};
|
||||
use tokio::{runtime::Handle, sync::oneshot};
|
||||
use tracing::metadata::LevelFilter;
|
||||
use tracing::{debug_span, error, info};
|
||||
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
|
||||
|
||||
@@ -58,7 +60,7 @@ struct EventLoopInfo {
|
||||
socket_path: PathBuf,
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
fn main() {
|
||||
let registry = tracing_subscriber::registry();
|
||||
#[cfg(feature = "profile_app")]
|
||||
let (chrome_layer, _guard) = tracing_chrome::ChromeLayerBuilder::new()
|
||||
@@ -94,7 +96,15 @@ fn main() -> Result<()> {
|
||||
},
|
||||
blend_preference: DisplayBlend::AnyTransparent,
|
||||
depth_mode: DepthMode::D32,
|
||||
log_filter: LogLevel::None,
|
||||
log_filter: match EnvFilter::from_default_env().max_level_hint() {
|
||||
Some(LevelFilter::ERROR) => LogLevel::Error,
|
||||
Some(LevelFilter::WARN) => LogLevel::Warning,
|
||||
Some(LevelFilter::INFO) => LogLevel::Inform,
|
||||
Some(LevelFilter::DEBUG) => LogLevel::Diagnostic,
|
||||
Some(LevelFilter::TRACE) => LogLevel::Diagnostic,
|
||||
Some(LevelFilter::OFF) => LogLevel::None,
|
||||
None => LogLevel::Warning,
|
||||
},
|
||||
overlay_app: cli_args.overlay_priority.is_some(),
|
||||
overlay_priority: cli_args.overlay_priority.unwrap_or(u32::MAX),
|
||||
disable_desktop_input_window: true,
|
||||
@@ -106,8 +116,8 @@ fn main() -> Result<()> {
|
||||
info!("Init StereoKit");
|
||||
|
||||
sk.material_set_shader(
|
||||
sk.material_find("default/material_pbr")?,
|
||||
sk.shader_find("default/shader_pbr_clip")?,
|
||||
sk.material_find("default/material_pbr").unwrap(),
|
||||
sk.shader_find("default/shader_pbr_clip").unwrap(),
|
||||
);
|
||||
|
||||
// Skytex/light stuff
|
||||
@@ -135,7 +145,11 @@ fn main() -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
let mouse_pointer = cli_args.flatscreen.then(MousePointer::new).transpose()?;
|
||||
let mouse_pointer = cli_args
|
||||
.flatscreen
|
||||
.then(MousePointer::new)
|
||||
.transpose()
|
||||
.unwrap();
|
||||
let mut hands = (!cli_args.flatscreen)
|
||||
.then(|| {
|
||||
let left = SkHand::new(Handed::Left).ok();
|
||||
@@ -152,23 +166,30 @@ fn main() -> Result<()> {
|
||||
.flatten();
|
||||
let eye_pointer = (!cli_args.flatscreen && sk.device_has_eye_gaze())
|
||||
.then(EyePointer::new)
|
||||
.transpose()?;
|
||||
.transpose()
|
||||
.unwrap();
|
||||
|
||||
if hands.is_none() {
|
||||
sk.input_hand_visible(Handed::Left, false);
|
||||
sk.input_hand_visible(Handed::Right, false);
|
||||
}
|
||||
|
||||
let play_space = sk
|
||||
.world_has_bounds()
|
||||
.then(|| PlaySpace::new().ok())
|
||||
.flatten();
|
||||
|
||||
let (event_stop_tx, event_stop_rx) = oneshot::channel::<()>();
|
||||
let (info_sender, info_receiver) = oneshot::channel::<EventLoopInfo>();
|
||||
let event_thread = std::thread::Builder::new()
|
||||
.name("event_loop".to_owned())
|
||||
.spawn(move || event_loop(info_sender, event_stop_rx))?;
|
||||
let event_loop_info = info_receiver.blocking_recv()?;
|
||||
.spawn(move || event_loop(info_sender, event_stop_rx))
|
||||
.unwrap();
|
||||
let event_loop_info = info_receiver.blocking_recv().unwrap();
|
||||
let _tokio_handle = event_loop_info.tokio_handle.enter();
|
||||
|
||||
#[cfg(feature = "wayland")]
|
||||
let mut wayland = wayland::Wayland::new()?;
|
||||
let mut wayland = wayland::Wayland::new().unwrap();
|
||||
info!("Stardust ready!");
|
||||
|
||||
if let Some(project_dirs) = project_dirs.as_ref() {
|
||||
@@ -178,6 +199,7 @@ fn main() -> Result<()> {
|
||||
.and_then(|p| p.canonicalize().ok())
|
||||
.unwrap_or_else(|| project_dirs.config_dir().join("startup"));
|
||||
let _startup = Command::new(startup_script_path)
|
||||
.stdin(Stdio::null())
|
||||
.env(
|
||||
"FLAT_WAYLAND_DISPLAY",
|
||||
std::env::var_os("WAYLAND_DISPLAY").unwrap_or_default(),
|
||||
@@ -225,6 +247,9 @@ fn main() -> Result<()> {
|
||||
if let Some(eye_pointer) = &eye_pointer {
|
||||
eye_pointer.update(sk);
|
||||
}
|
||||
if let Some(play_space) = &play_space {
|
||||
play_space.update(sk);
|
||||
}
|
||||
input::process_input();
|
||||
nodes::root::Root::send_frame_events(sk.time_elapsed_unscaled());
|
||||
adaptive_sleep(
|
||||
@@ -248,14 +273,14 @@ fn main() -> Result<()> {
|
||||
});
|
||||
|
||||
#[cfg(feature = "wayland")]
|
||||
drop(wayland);
|
||||
let _wayland = ManuallyDrop::new(wayland);
|
||||
|
||||
let _ = event_stop_tx.send(());
|
||||
event_thread
|
||||
.join()
|
||||
.expect("Failed to cleanly shut down event loop")?;
|
||||
.expect("Failed to cleanly shut down event loop")
|
||||
.unwrap();
|
||||
info!("Cleanly shut down Stardust");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn adaptive_sleep(
|
||||
@@ -301,11 +326,15 @@ async fn event_loop(
|
||||
socket_path,
|
||||
});
|
||||
|
||||
let result = tokio::select! {
|
||||
biased;
|
||||
_ = tokio::signal::ctrl_c() => Ok(()),
|
||||
_ = stop_rx => Ok(()),
|
||||
};
|
||||
if atty::is(atty::Stream::Stdin) {
|
||||
stop_rx.await?;
|
||||
} else {
|
||||
tokio::select! {
|
||||
biased;
|
||||
_ = tokio::signal::ctrl_c() => (),
|
||||
_ = stop_rx => (),
|
||||
};
|
||||
}
|
||||
|
||||
info!("Cleanly shut down event loop");
|
||||
|
||||
@@ -313,5 +342,5 @@ async fn event_loop(
|
||||
stereokit::sys::sk_quit();
|
||||
}
|
||||
|
||||
result
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -35,6 +35,11 @@ pub fn mask_matches(mask_map_lesser: &Mask, mask_map_greater: &Mask) -> bool {
|
||||
|
||||
pub struct Mask(pub Vec<u8>);
|
||||
impl Mask {
|
||||
pub fn from_struct<T: Default + Serialize>() -> Self {
|
||||
let mut serializer = flexbuffers::FlexbufferSerializer::new();
|
||||
T::default().serialize(&mut serializer).unwrap();
|
||||
Mask(serializer.take_buffer())
|
||||
}
|
||||
pub fn get_mask(&self) -> Result<flexbuffers::MapReader<&[u8]>> {
|
||||
flexbuffers::Reader::get_root(self.0.as_slice())
|
||||
.map_err(|_| eyre!("Mask is not a valid flexbuffer"))?
|
||||
@@ -182,7 +187,7 @@ pub struct PulseReceiver {
|
||||
pub mask: Mask,
|
||||
}
|
||||
impl PulseReceiver {
|
||||
pub fn add_to(node: &Arc<Node>, field: Arc<Field>, mask: Mask) -> Result<()> {
|
||||
pub fn add_to(node: &Arc<Node>, field: Arc<Field>, mask: Mask) -> Result<Arc<PulseReceiver>> {
|
||||
ensure!(
|
||||
node.spatial.get().is_some(),
|
||||
"Internal: Node does not have a spatial attached!"
|
||||
@@ -199,8 +204,8 @@ impl PulseReceiver {
|
||||
for sender in PULSE_SENDER_REGISTRY.get_valid_contents() {
|
||||
sender.handle_new_receiver(&receiver);
|
||||
}
|
||||
let _ = node.pulse_receiver.set(receiver);
|
||||
Ok(())
|
||||
let _ = node.pulse_receiver.set(receiver.clone());
|
||||
Ok(receiver)
|
||||
}
|
||||
|
||||
pub fn send_data(&self, uid: &str, data: Vec<u8>) -> Result<()> {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
pub mod lines;
|
||||
pub mod model;
|
||||
pub mod shaders;
|
||||
pub mod text;
|
||||
|
||||
use self::{
|
||||
|
||||
139
src/nodes/drawable/shaders.rs
Normal file
139
src/nodes/drawable/shaders.rs
Normal file
@@ -0,0 +1,139 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use smithay::backend::renderer::gles::{
|
||||
ffi::{self, Gles2, FRAGMENT_SHADER, VERTEX_SHADER},
|
||||
GlesError,
|
||||
};
|
||||
use std::mem::transmute;
|
||||
use stereokit::Shader;
|
||||
use tracing::error;
|
||||
|
||||
struct FfiAssetHeader {
|
||||
asset_type: i32,
|
||||
asset_state: i32,
|
||||
id: u64,
|
||||
index: u64,
|
||||
refs: i32,
|
||||
debug: *mut u8,
|
||||
}
|
||||
|
||||
struct FfiSkgShader {
|
||||
meta: *mut u8,
|
||||
vertex: u32,
|
||||
pixel: u32,
|
||||
program: u32,
|
||||
compute: u32,
|
||||
}
|
||||
|
||||
struct FfiShader {
|
||||
header: FfiAssetHeader,
|
||||
shader: FfiSkgShader,
|
||||
}
|
||||
|
||||
unsafe fn compile_shader(
|
||||
gl: &ffi::Gles2,
|
||||
variant: ffi::types::GLuint,
|
||||
src: &str,
|
||||
) -> Result<ffi::types::GLuint, GlesError> {
|
||||
let shader = gl.CreateShader(variant);
|
||||
if shader == 0 {
|
||||
return Err(GlesError::CreateShaderObject);
|
||||
}
|
||||
|
||||
gl.ShaderSource(
|
||||
shader,
|
||||
1,
|
||||
&src.as_ptr() as *const *const u8 as *const *const ffi::types::GLchar,
|
||||
&(src.len() as i32) as *const _,
|
||||
);
|
||||
gl.CompileShader(shader);
|
||||
|
||||
let mut status = ffi::FALSE as i32;
|
||||
gl.GetShaderiv(shader, ffi::COMPILE_STATUS, &mut status as *mut _);
|
||||
if status == ffi::FALSE as i32 {
|
||||
let mut max_len = 0;
|
||||
gl.GetShaderiv(shader, ffi::INFO_LOG_LENGTH, &mut max_len as *mut _);
|
||||
|
||||
let mut error = Vec::with_capacity(max_len as usize);
|
||||
let mut len = 0;
|
||||
gl.GetShaderInfoLog(
|
||||
shader,
|
||||
max_len as _,
|
||||
&mut len as *mut _,
|
||||
error.as_mut_ptr() as *mut _,
|
||||
);
|
||||
error.set_len(len as usize);
|
||||
|
||||
error!(
|
||||
"[GL] {}",
|
||||
std::str::from_utf8(&error).unwrap_or("<Error Message no utf8>")
|
||||
);
|
||||
|
||||
gl.DeleteShader(shader);
|
||||
return Err(GlesError::ShaderCompileError);
|
||||
}
|
||||
|
||||
Ok(shader)
|
||||
}
|
||||
|
||||
unsafe fn link_program(
|
||||
gl: &ffi::Gles2,
|
||||
vert: ffi::types::GLuint,
|
||||
frag: ffi::types::GLuint,
|
||||
) -> Result<ffi::types::GLuint, GlesError> {
|
||||
let program = gl.CreateProgram();
|
||||
gl.AttachShader(program, vert);
|
||||
gl.AttachShader(program, frag);
|
||||
gl.LinkProgram(program);
|
||||
// gl.DetachShader(program, vert);
|
||||
// gl.DetachShader(program, frag);
|
||||
// gl.DeleteShader(vert);
|
||||
// gl.DeleteShader(frag);
|
||||
|
||||
let mut status = ffi::FALSE as i32;
|
||||
gl.GetProgramiv(program, ffi::LINK_STATUS, &mut status as *mut _);
|
||||
if status == ffi::FALSE as i32 {
|
||||
let mut max_len = 0;
|
||||
gl.GetProgramiv(program, ffi::INFO_LOG_LENGTH, &mut max_len as *mut _);
|
||||
|
||||
let mut error = Vec::with_capacity(max_len as usize);
|
||||
let mut len = 0;
|
||||
gl.GetProgramInfoLog(
|
||||
program,
|
||||
max_len as _,
|
||||
&mut len as *mut _,
|
||||
error.as_mut_ptr() as *mut _,
|
||||
);
|
||||
error.set_len(len as usize);
|
||||
|
||||
error!(
|
||||
"[GL] {}",
|
||||
std::str::from_utf8(&error).unwrap_or("<Error Message no utf8>")
|
||||
);
|
||||
|
||||
gl.DeleteProgram(program);
|
||||
return Err(GlesError::ProgramLinkError);
|
||||
}
|
||||
|
||||
Ok(program)
|
||||
}
|
||||
|
||||
pub unsafe fn shader_inject(
|
||||
c: &Gles2,
|
||||
sk_shader: &mut Shader,
|
||||
vert_str: &str,
|
||||
frag_str: &str,
|
||||
) -> Result<(), GlesError> {
|
||||
let gl_vert = compile_shader(c, VERTEX_SHADER, vert_str)?;
|
||||
let gl_frag = compile_shader(c, FRAGMENT_SHADER, frag_str)?;
|
||||
let gl_prog = link_program(c, gl_vert, gl_frag)?;
|
||||
|
||||
let shader: *mut FfiShader = transmute(sk_shader.0.as_mut());
|
||||
if let Some(shader) = shader.as_mut() {
|
||||
shader.shader.vertex = gl_vert;
|
||||
shader.shader.pixel = gl_frag;
|
||||
shader.shader.program = gl_prog;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -16,7 +16,7 @@ pub struct BoxField {
|
||||
}
|
||||
|
||||
impl BoxField {
|
||||
pub fn add_to(node: &Arc<Node>, size: Vector3<f32>) -> Result<()> {
|
||||
pub fn add_to(node: &Arc<Node>, size: Vector3<f32>) -> Result<Arc<Field>> {
|
||||
ensure!(
|
||||
node.spatial.get().is_some(),
|
||||
"Internal: Node does not have a spatial attached!"
|
||||
@@ -31,8 +31,9 @@ impl BoxField {
|
||||
};
|
||||
box_field.add_field_methods(node);
|
||||
node.add_local_signal("set_size", BoxField::set_size_flex);
|
||||
let _ = node.field.set(Arc::new(Field::Box(box_field)));
|
||||
Ok(())
|
||||
let field = Arc::new(Field::Box(box_field));
|
||||
let _ = node.field.set(field.clone());
|
||||
Ok(field)
|
||||
}
|
||||
|
||||
pub fn set_size(&self, size: Vector3<f32>) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
mod r#box;
|
||||
pub mod r#box;
|
||||
mod cylinder;
|
||||
mod sphere;
|
||||
pub mod torus;
|
||||
mod torus;
|
||||
|
||||
use self::cylinder::{create_cylinder_field_flex, CylinderField};
|
||||
use self::r#box::{create_box_field_flex, BoxField};
|
||||
|
||||
@@ -107,7 +107,7 @@ impl MousePointer {
|
||||
.map(|rx| {
|
||||
let result = rx.field.ray_march(Ray {
|
||||
origin: vec3(0.0, 0.0, 0.0),
|
||||
direction: vec3(0.0, 0.0, 1.0),
|
||||
direction: vec3(0.0, 0.0, -1.0),
|
||||
space: self.spatial.clone(),
|
||||
});
|
||||
(rx, result)
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
pub mod input;
|
||||
pub mod play_space;
|
||||
|
||||
73
src/objects/play_space.rs
Normal file
73
src/objects/play_space.rs
Normal file
@@ -0,0 +1,73 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use color_eyre::eyre::Result;
|
||||
use glam::Mat4;
|
||||
use mint::Vector2;
|
||||
use nanoid::nanoid;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use stereokit::StereoKitMultiThread;
|
||||
|
||||
use crate::{
|
||||
core::client::INTERNAL_CLIENT,
|
||||
nodes::{
|
||||
data::{Mask, PulseReceiver},
|
||||
fields::{r#box::BoxField, Field},
|
||||
spatial::Spatial,
|
||||
Node,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
struct PlaySpaceMap {
|
||||
play_space: (),
|
||||
size: Vector2<f32>,
|
||||
}
|
||||
impl Default for PlaySpaceMap {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
play_space: (),
|
||||
size: [0.0; 2].into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PlaySpace {
|
||||
_node: Arc<Node>,
|
||||
spatial: Arc<Spatial>,
|
||||
field: Arc<Field>,
|
||||
_pulse_rx: Arc<PulseReceiver>,
|
||||
}
|
||||
impl PlaySpace {
|
||||
pub fn new() -> Result<Self> {
|
||||
let node = Node::create(&INTERNAL_CLIENT, "", &nanoid!(), false).add_to_scenegraph()?;
|
||||
let spatial = Spatial::add_to(&node, None, Mat4::IDENTITY, false)?;
|
||||
let field = BoxField::add_to(&node, [0.0; 3].into())?;
|
||||
|
||||
let pulse_rx =
|
||||
PulseReceiver::add_to(&node, field.clone(), Mask::from_struct::<PlaySpaceMap>())?;
|
||||
|
||||
Ok(PlaySpace {
|
||||
_node: node,
|
||||
spatial,
|
||||
field,
|
||||
_pulse_rx: pulse_rx,
|
||||
})
|
||||
}
|
||||
pub fn update(&self, sk: &impl StereoKitMultiThread) {
|
||||
let pose = sk.world_get_bounds_pose();
|
||||
self.spatial
|
||||
.set_local_transform(Mat4::from_rotation_translation(
|
||||
pose.orientation,
|
||||
pose.position,
|
||||
));
|
||||
let Field::Box(box_field) = self.field.as_ref() else {return};
|
||||
box_field.set_size(
|
||||
[
|
||||
sk.world_get_bounds_size().x,
|
||||
0.0,
|
||||
sk.world_get_bounds_size().y,
|
||||
]
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -16,8 +16,10 @@ use global_counter::primitive::exact::CounterU32;
|
||||
use once_cell::sync::OnceCell;
|
||||
use parking_lot::Mutex;
|
||||
use sk::StereoKitDraw;
|
||||
use smithay::backend::allocator::dmabuf::Dmabuf;
|
||||
use smithay::backend::egl::EGLContext;
|
||||
use smithay::backend::renderer::gles::GlesRenderer;
|
||||
use smithay::backend::renderer::ImportDma;
|
||||
use smithay::reexports::wayland_server::{backend::GlobalId, Display, ListeningSocket};
|
||||
use std::os::unix::prelude::AsRawFd;
|
||||
use std::{
|
||||
@@ -26,6 +28,7 @@ use std::{
|
||||
sync::Arc,
|
||||
};
|
||||
use stereokit as sk;
|
||||
use tokio::sync::mpsc::UnboundedReceiver;
|
||||
use tokio::{
|
||||
io::unix::AsyncFd, net::UnixListener as AsyncUnixListener, sync::mpsc, task::JoinHandle,
|
||||
};
|
||||
@@ -63,6 +66,7 @@ pub struct Wayland {
|
||||
pub socket_name: String,
|
||||
join_handle: JoinHandle<Result<()>>,
|
||||
renderer: GlesRenderer,
|
||||
dmabuf_rx: UnboundedReceiver<Dmabuf>,
|
||||
state: Arc<Mutex<WaylandState>>,
|
||||
}
|
||||
impl Wayland {
|
||||
@@ -79,8 +83,9 @@ impl Wayland {
|
||||
let display: Display<WaylandState> = Display::new()?;
|
||||
let display_handle = display.handle();
|
||||
|
||||
let (dmabuf_tx, dmabuf_rx) = mpsc::unbounded_channel();
|
||||
let display = Arc::new(Mutex::new(display));
|
||||
let state = WaylandState::new(display.clone(), display_handle, &renderer);
|
||||
let state = WaylandState::new(display.clone(), display_handle, &renderer, dmabuf_tx);
|
||||
|
||||
let (global_destroy_queue_in, global_destroy_queue) = mpsc::channel(8);
|
||||
GLOBAL_DESTROY_QUEUE.set(global_destroy_queue_in).unwrap();
|
||||
@@ -100,6 +105,7 @@ impl Wayland {
|
||||
socket_name,
|
||||
join_handle,
|
||||
renderer,
|
||||
dmabuf_rx,
|
||||
state,
|
||||
})
|
||||
}
|
||||
@@ -150,6 +156,9 @@ impl Wayland {
|
||||
|
||||
#[instrument(level = "debug", name = "Wayland frame", skip(self, sk))]
|
||||
pub fn update(&mut self, sk: &impl StereoKitDraw) {
|
||||
while let Ok(dmabuf) = self.dmabuf_rx.try_recv() {
|
||||
let _ = self.renderer.import_dmabuf(&dmabuf, None);
|
||||
}
|
||||
for core_surface in CORE_SURFACES.get_valid_contents() {
|
||||
core_surface.process(sk, &mut self.renderer);
|
||||
}
|
||||
|
||||
@@ -470,14 +470,22 @@ impl PanelItem {
|
||||
xdg_toplevel.configure_bounds(bounds.x as i32, bounds.y as i32);
|
||||
}
|
||||
}
|
||||
let size = info.size.unwrap_or(Vector2::from([0; 2]));
|
||||
let zero_size = Vector2::from([0; 2]);
|
||||
let size = info.size.unwrap_or(zero_size);
|
||||
// if size == zero_size && (info.states.contains(1) || info.states.contains(2)) {
|
||||
// xdg_toplevel.configure(
|
||||
// size.x as i32,
|
||||
// size.y as i32,
|
||||
// info.states
|
||||
// .into_iter()
|
||||
// .flat_map(|state| state.to_ne_bytes())
|
||||
// .collect(),
|
||||
// );
|
||||
// }
|
||||
xdg_toplevel.configure(
|
||||
size.x as i32,
|
||||
size.y as i32,
|
||||
info.states
|
||||
.into_iter()
|
||||
.flat_map(|state| state.to_ne_bytes())
|
||||
.collect::<Vec<_>>(),
|
||||
info.states.into_iter().flat_map(u32::to_ne_bytes).collect(),
|
||||
);
|
||||
xdg_surface.configure(SERIAL_COUNTER.inc());
|
||||
core_surface.flush_clients();
|
||||
|
||||
@@ -446,7 +446,7 @@ impl Dispatch<WlPointer, Arc<SeatData>, WaylandState> for WaylandState {
|
||||
hotspot_y,
|
||||
} => {
|
||||
if let Some(surface) = surface.as_ref() {
|
||||
CoreSurface::add_to(&state.display, dh.clone(), surface, |_| ());
|
||||
CoreSurface::add_to(&state.display, dh.clone(), surface, || (), |_| ());
|
||||
compositor::with_states(surface, |data| {
|
||||
data.data_map.insert_if_missing_threadsafe(|| {
|
||||
Arc::new(Mutex::new(Cursor {
|
||||
|
||||
16
src/wayland/shaders/gamma.frag
Normal file
16
src/wayland/shaders/gamma.frag
Normal file
@@ -0,0 +1,16 @@
|
||||
#version 320 es
|
||||
precision mediump float;
|
||||
precision highp int;
|
||||
|
||||
layout(binding = 0) uniform highp sampler2D diffuse;
|
||||
|
||||
layout(location = 0) in highp vec2 fs_uv;
|
||||
layout(location = 0) out highp vec4 _entryPointOutput;
|
||||
|
||||
void main()
|
||||
{
|
||||
highp vec4 _101 = texture(diffuse, fs_uv);
|
||||
highp vec3 _104 = pow(_101.xyz, vec3(2.2000000476837158203125));
|
||||
_entryPointOutput = vec4(_104.x, _104.y, _104.z, _101.w);
|
||||
}
|
||||
|
||||
@@ -5,3 +5,9 @@
|
||||
|
||||
// Simula shader with fancy lanzcos sampling
|
||||
pub const PANEL_SHADER_BYTES: &[u8] = include_bytes!("shader_unlit_simula.sks");
|
||||
|
||||
// Simula text shader (fragment)
|
||||
// pub const SIMULA_FRAG_STR: &str = include_str!("simula.frag");
|
||||
|
||||
// Simula text shader (vertex)
|
||||
// pub const SIMULA_VERT_STR: &str = include_str!("simula.vert");
|
||||
|
||||
75
src/wayland/shaders/simula.frag
Normal file
75
src/wayland/shaders/simula.frag
Normal file
@@ -0,0 +1,75 @@
|
||||
#version 320 es
|
||||
#extension GL_OES_EGL_image_external : require
|
||||
precision mediump float;
|
||||
precision highp int;
|
||||
|
||||
layout(binding = 0, std140) uniform _Global
|
||||
{
|
||||
highp vec4 diffuse_i;
|
||||
highp vec2 uv_scale;
|
||||
highp vec2 uv_offset;
|
||||
highp float fcFactor;
|
||||
highp float ripple;
|
||||
highp float alpha_min;
|
||||
highp float alpha_max;
|
||||
} uniforms;
|
||||
|
||||
layout(binding = 0) uniform highp samplerExternalOES diffuse;
|
||||
|
||||
layout(location = 0) in highp vec2 fs_uv;
|
||||
layout(location = 0) out highp vec4 fragColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
highp vec2 dx_uv = dFdx(fs_uv);
|
||||
highp vec2 dy_uv = dFdy(fs_uv);
|
||||
highp vec2 width = fs_uv * uniforms.diffuse_i.xy;
|
||||
ivec2 _475 = ivec2(width);
|
||||
highp vec2 _477 = clamp(floor(abs(vec2(dx_uv.x, dy_uv.y)) * uniforms.diffuse_i.xy), vec2(1.0), vec2(2.0));
|
||||
ivec2 _480 = ivec2(_477);
|
||||
ivec2 _481 = _475 - _480;
|
||||
ivec2 _485 = _475 + _480;
|
||||
int _487 = _481.y;
|
||||
highp vec4 _671;
|
||||
highp float _672;
|
||||
_672 = 0.0;
|
||||
_671 = vec4(0.0);
|
||||
highp vec4 _679;
|
||||
highp float _681;
|
||||
for (int _670 = _487; _670 <= _485.y; _672 = _681, _671 = _679, _670++)
|
||||
{
|
||||
int _496 = _481.x;
|
||||
_681 = _672;
|
||||
_679 = _671;
|
||||
highp vec4 _553;
|
||||
highp float _556;
|
||||
for (int _673 = _496; _673 <= _485.x; _681 = _556, _679 = _553, _673++)
|
||||
{
|
||||
highp float _509 = float(_673);
|
||||
highp float _514 = (uniforms.fcFactor * (width.x - _509)) / _477.x;
|
||||
highp float _520 = float(_670);
|
||||
highp float _525 = (uniforms.fcFactor * (width.y - _520)) / _477.y;
|
||||
highp float _533 = sqrt((_514 * _514) + (_525 * _525));
|
||||
highp float _675;
|
||||
do
|
||||
{
|
||||
if (_533 > 1.0)
|
||||
{
|
||||
_675 = 0.0;
|
||||
break;
|
||||
}
|
||||
highp float _592 = pow(uniforms.ripple * sqrt(1.0 - (_533 * _533)), 2.0);
|
||||
_675 = 1.0 + (_592 * (0.25 + (_592 * (0.015625 + (_592 * (0.00043402801384218037128448486328125 + (_592 * (6.7816799855791032314300537109375e-06 + (_592 * (6.7816799287356843706220388412476e-08 + (_592 * (4.709500012189948847662890329957e-10 + (_592 * (2.4028099388645474121517509047408e-12 + (_592 * (9.3859703944590075486154034933861e-15 + (_592 * (2.8968999943407451927966655969016e-17 + (7.242260299760125752555485045131e-20 * _592)))))))))))))))))));
|
||||
break;
|
||||
} while(false);
|
||||
_553 = _679 + (texture2D(diffuse, (vec2(_509, _520) + vec2(0.5)) / uniforms.diffuse_i.xy) * _675);
|
||||
_556 = _681 + _675;
|
||||
}
|
||||
}
|
||||
highp vec4 _568 = _671 / vec4(_672);
|
||||
highp vec3 _417 = pow(_568.xyz, vec3(2.2000000476837158203125));
|
||||
highp vec4 _669 = vec4(_417.x, _417.y, _417.z, _568.w);
|
||||
_669.w = uniforms.alpha_min + (_568.w * (uniforms.alpha_max - uniforms.alpha_min));
|
||||
fragColor = _669;
|
||||
}
|
||||
|
||||
61
src/wayland/shaders/simula.vert
Normal file
61
src/wayland/shaders/simula.vert
Normal file
@@ -0,0 +1,61 @@
|
||||
#version 320 es
|
||||
// #ifdef GL_AMD_vertex_shader_layer
|
||||
// #extension GL_AMD_vertex_shader_layer : enable
|
||||
// #elif defined(GL_NV_viewport_array2)
|
||||
// #extension GL_NV_viewport_array2 : enable
|
||||
// #else
|
||||
// #define gl_Layer int _dummy_gl_layer_var
|
||||
// #endif
|
||||
|
||||
struct Inst
|
||||
{
|
||||
mat4 world;
|
||||
vec4 color;
|
||||
};
|
||||
|
||||
layout(binding = 1, std140) uniform StereoKitBuffer
|
||||
{
|
||||
layout(row_major) mat4 sk_view[2];
|
||||
layout(row_major) mat4 sk_proj[2];
|
||||
layout(row_major) mat4 sk_proj_inv[2];
|
||||
layout(row_major) mat4 sk_viewproj[2];
|
||||
vec4 sk_lighting_sh[9];
|
||||
vec4 sk_camera_pos[2];
|
||||
vec4 sk_camera_dir[2];
|
||||
vec4 sk_fingertip[2];
|
||||
vec4 sk_cubemap_i;
|
||||
float sk_time;
|
||||
uint sk_view_count;
|
||||
} _38;
|
||||
|
||||
layout(binding = 2, std140) uniform TransformBuffer
|
||||
{
|
||||
layout(row_major) Inst sk_inst[819];
|
||||
} _56;
|
||||
|
||||
layout(binding = 0, std140) uniform _Global
|
||||
{
|
||||
vec4 diffuse_i;
|
||||
vec2 uv_scale;
|
||||
vec2 uv_offset;
|
||||
float fcFactor;
|
||||
float ripple;
|
||||
float alpha_min;
|
||||
float alpha_max;
|
||||
} _91;
|
||||
|
||||
layout(location = 0) in vec4 input_pos;
|
||||
layout(location = 1) in vec3 input_norm;
|
||||
layout(location = 2) in vec2 input_uv;
|
||||
layout(location = 0) out vec2 fs_uv;
|
||||
|
||||
mat4 spvWorkaroundRowMajor(mat4 wrap) { return wrap; }
|
||||
|
||||
void main()
|
||||
{
|
||||
uint _155 = uint(gl_InstanceID) % _38.sk_view_count;
|
||||
gl_Position = spvWorkaroundRowMajor(_38.sk_viewproj[_155]) * vec4((spvWorkaroundRowMajor(_56.sk_inst[uint(gl_InstanceID) / _38.sk_view_count].world) * vec4(input_pos.xyz, 1.0)).xyz, 1.0);
|
||||
fs_uv = (input_uv + _91.uv_offset) * _91.uv_scale;
|
||||
// gl_Layer = int(_155);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ use rustc_hash::FxHashMap;
|
||||
use smithay::{
|
||||
backend::{
|
||||
allocator::dmabuf::Dmabuf,
|
||||
egl::EGLDevice,
|
||||
renderer::{gles::GlesRenderer, ImportDma},
|
||||
},
|
||||
delegate_dmabuf, delegate_output, delegate_shm,
|
||||
@@ -24,13 +25,17 @@ use smithay::{
|
||||
wayland::{
|
||||
buffer::BufferHandler,
|
||||
compositor::{CompositorClientState, CompositorState},
|
||||
dmabuf::{self, DmabufGlobal, DmabufHandler, DmabufState},
|
||||
dmabuf::{
|
||||
self, DmabufFeedback, DmabufFeedbackBuilder, DmabufGlobal, DmabufHandler, DmabufState,
|
||||
ImportError,
|
||||
},
|
||||
shell::kde::decoration::KdeDecorationState,
|
||||
shm::{ShmHandler, ShmState},
|
||||
},
|
||||
};
|
||||
use std::sync::{Arc, Weak};
|
||||
use tracing::info;
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
use tracing::{info, warn};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ClientState {
|
||||
@@ -58,9 +63,8 @@ pub struct WaylandState {
|
||||
// pub xdg_activation_state: XdgActivationState,
|
||||
pub kde_decoration_state: KdeDecorationState,
|
||||
pub shm_state: ShmState,
|
||||
pub dmabuf_state: DmabufState,
|
||||
pub dmabuf_global: DmabufGlobal,
|
||||
pub pending_dmabufs: Vec<Dmabuf>,
|
||||
dmabuf_state: (DmabufState, DmabufGlobal, Option<DmabufFeedback>),
|
||||
dmabuf_tx: UnboundedSender<Dmabuf>,
|
||||
pub output: Output,
|
||||
pub seats: FxHashMap<ClientId, Arc<SeatData>>,
|
||||
}
|
||||
@@ -70,17 +74,51 @@ impl WaylandState {
|
||||
display: Arc<Mutex<Display<WaylandState>>>,
|
||||
display_handle: DisplayHandle,
|
||||
renderer: &GlesRenderer,
|
||||
dmabuf_tx: UnboundedSender<Dmabuf>,
|
||||
) -> Arc<Mutex<Self>> {
|
||||
let compositor_state = CompositorState::new::<Self>(&display_handle);
|
||||
// let xdg_activation_state = XdgActivationState::new::<Self, _>(&display_handle);
|
||||
let kde_decoration_state =
|
||||
KdeDecorationState::new::<Self>(&display_handle, DecorationMode::Server);
|
||||
let shm_state = ShmState::new::<Self>(&display_handle, vec![]);
|
||||
let mut dmabuf_state = DmabufState::new();
|
||||
let dmabuf_global = dmabuf_state.create_global::<Self>(
|
||||
&display_handle,
|
||||
renderer.dmabuf_formats().collect::<Vec<_>>(),
|
||||
);
|
||||
let render_node = EGLDevice::device_for_display(renderer.egl_context().display())
|
||||
.and_then(|device| device.try_get_render_node());
|
||||
|
||||
let dmabuf_default_feedback = match render_node {
|
||||
Ok(Some(node)) => {
|
||||
let dmabuf_formats = renderer.dmabuf_formats().collect::<Vec<_>>();
|
||||
let dmabuf_default_feedback =
|
||||
DmabufFeedbackBuilder::new(node.dev_id(), dmabuf_formats)
|
||||
.build()
|
||||
.unwrap();
|
||||
Some(dmabuf_default_feedback)
|
||||
}
|
||||
Ok(None) => {
|
||||
warn!("failed to query render node, dmabuf will use v3");
|
||||
None
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(?err, "failed to egl device for display, dmabuf will use v3");
|
||||
None
|
||||
}
|
||||
};
|
||||
// if we failed to build dmabuf feedback we fall back to dmabuf v3
|
||||
// Note: egl on Mesa requires either v4 or wl_drm (initialized with bind_wl_display)
|
||||
let dmabuf_state = if let Some(default_feedback) = dmabuf_default_feedback {
|
||||
let mut dmabuf_state = DmabufState::new();
|
||||
let dmabuf_global = dmabuf_state.create_global_with_default_feedback::<WaylandState>(
|
||||
&display_handle,
|
||||
&default_feedback,
|
||||
);
|
||||
(dmabuf_state, dmabuf_global, Some(default_feedback))
|
||||
} else {
|
||||
let dmabuf_formats = renderer.dmabuf_formats().collect::<Vec<_>>();
|
||||
let mut dmabuf_state = DmabufState::new();
|
||||
let dmabuf_global =
|
||||
dmabuf_state.create_global::<WaylandState>(&display_handle, dmabuf_formats);
|
||||
(dmabuf_state, dmabuf_global, None)
|
||||
};
|
||||
|
||||
let output = Output::new(
|
||||
"1x".to_owned(),
|
||||
smithay::output::PhysicalProperties {
|
||||
@@ -119,8 +157,7 @@ impl WaylandState {
|
||||
kde_decoration_state,
|
||||
shm_state,
|
||||
dmabuf_state,
|
||||
dmabuf_global,
|
||||
pending_dmabufs: Vec::new(),
|
||||
dmabuf_tx,
|
||||
output,
|
||||
seats: FxHashMap::default(),
|
||||
})
|
||||
@@ -147,15 +184,15 @@ impl ShmHandler for WaylandState {
|
||||
}
|
||||
impl DmabufHandler for WaylandState {
|
||||
fn dmabuf_state(&mut self) -> &mut DmabufState {
|
||||
&mut self.dmabuf_state
|
||||
&mut self.dmabuf_state.0
|
||||
}
|
||||
|
||||
fn dmabuf_imported(
|
||||
&mut self,
|
||||
_global: &DmabufGlobal,
|
||||
_dmabuf: Dmabuf,
|
||||
dmabuf: Dmabuf,
|
||||
) -> Result<(), dmabuf::ImportError> {
|
||||
Ok(())
|
||||
self.dmabuf_tx.send(dmabuf).map_err(|_| ImportError::Failed)
|
||||
}
|
||||
}
|
||||
delegate_dmabuf!(WaylandState);
|
||||
|
||||
@@ -50,6 +50,7 @@ pub struct CoreSurface {
|
||||
sk_tex: OnceCell<SendWrapper<Tex>>,
|
||||
sk_mat: OnceCell<Arc<SendWrapper<Material>>>,
|
||||
material_offset: Mutex<Delta<u32>>,
|
||||
on_mapped: Box<dyn Fn() + Send + Sync>,
|
||||
on_commit: Box<dyn Fn(u32) + Send + Sync>,
|
||||
pub pending_material_applications: Registry<ModelPart>,
|
||||
}
|
||||
@@ -59,6 +60,7 @@ impl CoreSurface {
|
||||
display: &Arc<Mutex<Display<WaylandState>>>,
|
||||
dh: DisplayHandle,
|
||||
surface: &WlSurface,
|
||||
on_mapped: impl Fn() + Send + Sync + 'static,
|
||||
on_commit: impl Fn(u32) + Send + Sync + 'static,
|
||||
) {
|
||||
compositor::with_states(surface, |data| {
|
||||
@@ -71,6 +73,7 @@ impl CoreSurface {
|
||||
sk_tex: OnceCell::new(),
|
||||
sk_mat: OnceCell::new(),
|
||||
material_offset: Mutex::new(Delta::new(0)),
|
||||
on_mapped: Box::new(on_mapped) as Box<dyn Fn() + Send + Sync>,
|
||||
on_commit: Box::new(on_commit) as Box<dyn Fn(u32) + Send + Sync>,
|
||||
pending_material_applications: Registry::new(),
|
||||
})
|
||||
@@ -96,6 +99,10 @@ impl CoreSurface {
|
||||
});
|
||||
self.sk_mat.get_or_init(|| {
|
||||
let shader = sk.shader_create_mem(&PANEL_SHADER_BYTES).unwrap();
|
||||
// let _ = renderer.with_context(|c| unsafe {
|
||||
// shader_inject(c, &mut shader, SIMULA_VERT_STR, SIMULA_FRAG_STR)
|
||||
// });
|
||||
|
||||
let mat = sk.material_create(&shader);
|
||||
sk.material_set_texture(&mat, "diffuse", sk_tex.as_ref());
|
||||
sk.material_set_transparency(&mat, Transparency::Blend);
|
||||
@@ -121,9 +128,8 @@ impl CoreSurface {
|
||||
}
|
||||
|
||||
let mut mapped_data = self.mapped_data.lock();
|
||||
let just_mapped = mapped_data.is_none();
|
||||
self.with_states(|data| {
|
||||
// let just_mapped = mapped_data.is_none();
|
||||
// if just_mapped {
|
||||
let renderer_surface_state = data
|
||||
.data_map
|
||||
.get::<RendererSurfaceStateUserData>()
|
||||
@@ -161,6 +167,10 @@ impl CoreSurface {
|
||||
};
|
||||
*mapped_data = Some(new_mapped_data);
|
||||
});
|
||||
drop(mapped_data);
|
||||
if just_mapped {
|
||||
(self.on_mapped)();
|
||||
}
|
||||
self.apply_surface_materials();
|
||||
}
|
||||
|
||||
|
||||
@@ -253,21 +253,32 @@ impl Dispatch<XdgSurface, Mutex<XdgSurfaceData>, WaylandState> for WaylandState
|
||||
debug!(?toplevel, ?xdg_surface, "Create XDG toplevel");
|
||||
|
||||
if toplevel.version() >= EVT_WM_CAPABILITIES_SINCE {
|
||||
toplevel.wm_capabilities(vec![]);
|
||||
toplevel.wm_capabilities(vec![2, 3, 4]);
|
||||
}
|
||||
toplevel.configure(0, 0, vec![]);
|
||||
toplevel.configure(
|
||||
0,
|
||||
0,
|
||||
if toplevel.version() >= 2 {
|
||||
vec![5, 6, 7, 8]
|
||||
.into_iter()
|
||||
.flat_map(u32::to_ne_bytes)
|
||||
.collect()
|
||||
} else {
|
||||
vec![]
|
||||
},
|
||||
);
|
||||
xdg_surface.configure(SERIAL_COUNTER.inc());
|
||||
|
||||
let client_credentials = client.get_credentials(&state.display_handle).ok();
|
||||
let seat_data = state.seats.get(&client.id()).unwrap().clone();
|
||||
let toplevel_weak = toplevel.downgrade();
|
||||
CoreSurface::add_to(
|
||||
&state.display,
|
||||
state.display_handle.clone(),
|
||||
&xdg_surface_data.lock().wl_surface(),
|
||||
move |c| match c {
|
||||
0 => {
|
||||
let toplevel = toplevel_weak.upgrade().unwrap();
|
||||
{
|
||||
let toplevel = toplevel.downgrade();
|
||||
move || {
|
||||
let toplevel = toplevel.upgrade().unwrap();
|
||||
let toplevel_data = ToplevelData::get(&toplevel);
|
||||
let xdg_surface = toplevel_data.lock().xdg_surface();
|
||||
let xdg_surface_data = XdgSurfaceData::get(&xdg_surface);
|
||||
@@ -283,10 +294,27 @@ impl Dispatch<XdgSurface, Mutex<XdgSurfaceData>, WaylandState> for WaylandState
|
||||
toplevel_data.lock().panel_item_node.replace(node);
|
||||
xdg_surface_data.lock().panel_item = Arc::downgrade(&panel_item);
|
||||
}
|
||||
_ => {
|
||||
let toplevel = toplevel_weak.upgrade().unwrap();
|
||||
},
|
||||
{
|
||||
let toplevel = toplevel.downgrade();
|
||||
move |_| {
|
||||
let toplevel = toplevel.upgrade().unwrap();
|
||||
let toplevel_data = ToplevelData::get(&toplevel);
|
||||
let panel_item = toplevel_data.lock().panel_item().unwrap();
|
||||
let Some(panel_item) = toplevel_data.lock().panel_item() else {
|
||||
// if the wayland toplevel isn't mapped, hammer it again with a configure until it cooperates
|
||||
toplevel.configure(
|
||||
0,
|
||||
0,
|
||||
if toplevel.version() >= 2 {
|
||||
vec![5, 6, 7, 8].into_iter().flat_map(u32::to_ne_bytes).collect()
|
||||
} else {
|
||||
vec![]
|
||||
},
|
||||
);
|
||||
let xdg_surface = toplevel_data.lock().xdg_surface();
|
||||
xdg_surface.configure(SERIAL_COUNTER.inc());
|
||||
return
|
||||
};
|
||||
panel_item.commit_toplevel();
|
||||
}
|
||||
},
|
||||
@@ -335,19 +363,19 @@ impl Dispatch<XdgSurface, Mutex<XdgSurfaceData>, WaylandState> for WaylandState
|
||||
&state.display,
|
||||
state.display_handle.clone(),
|
||||
&xdg_surface_data.lock().wl_surface.upgrade().unwrap(),
|
||||
move |commit_count| match commit_count {
|
||||
0 => xdg_surface
|
||||
.upgrade()
|
||||
.unwrap()
|
||||
.configure(SERIAL_COUNTER.inc()),
|
||||
c => {
|
||||
let xdg_popup = xdg_popup.upgrade().unwrap();
|
||||
let popup_data = PopupData::get(&xdg_popup);
|
||||
let popup_data = popup_data.lock();
|
||||
// panel_item.commit_popup(popup_data);
|
||||
if c == 1 {
|
||||
panel_item.new_popup(&xdg_popup, &*popup_data);
|
||||
}
|
||||
move || {
|
||||
let xdg_popup = xdg_popup.upgrade().unwrap();
|
||||
let popup_data = PopupData::get(&xdg_popup);
|
||||
let popup_data = popup_data.lock();
|
||||
// panel_item.commit_popup(popup_data);
|
||||
panel_item.new_popup(&xdg_popup, &*popup_data);
|
||||
},
|
||||
move |commit_count| {
|
||||
if commit_count == 0 {
|
||||
xdg_surface
|
||||
.upgrade()
|
||||
.unwrap()
|
||||
.configure(SERIAL_COUNTER.inc())
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user