22 Commits

Author SHA1 Message Date
Nova
bf89b73e8f feat: version bump 2023-07-22 18:31:09 -04:00
Nova
2e252279bb fix: states 2023-07-19 06:04:15 -07:00
Nova
9cf43ec535 fix: surface not mapping 2023-07-19 06:04:08 -07:00
Nova
f15578f7df feat: formatting 2023-07-19 06:03:28 -07:00
Nova
f63ca4a25b feat: play space 2023-07-16 10:42:35 -07:00
technobaboo
89741508e3 feat: make readme more readable 2023-07-11 11:44:38 -07:00
technobaboo
81be807749 fix(ci): add semicolons 2023-07-11 11:16:15 -07:00
technobaboo
fcdb8a7edf fix(ci): appimagetool 2023-07-11 11:11:07 -07:00
technobaboo
90ce185f29 fix(ci): xcb glx 2023-07-11 10:59:26 -07:00
technobaboo
d6353035ae fix(ci): ninja-build instead of ninja 2023-07-11 10:55:08 -07:00
technobaboo
ceb1b23264 feat: ci take 2 2023-07-11 10:53:31 -07:00
Nova
199e6f70b3 refactor: use dmabuf v4 instead of bind_display 2023-06-27 05:53:45 -04:00
Nova
641db4face refactor: disable shader injection 2023-06-26 20:49:21 -04:00
Nova
80d292b511 feat: match stereokit to log level 2023-06-26 20:43:02 -04:00
Nova
7fbcc92d02 fix: unwrap in main fn 2023-06-26 20:33:04 -04:00
Nova
de46726d01 feat: hardware accelerated wayland apps 2023-06-26 20:31:38 -04:00
Nova
6efa3a909e feat: proper dmabuf import 2023-06-26 20:09:20 -04:00
Nova
ea0f174da7 feat: shaders!! working!! 2023-06-26 19:49:10 -04:00
Nova
444146fa21 feat: it borken 2023-06-26 04:37:38 -04:00
Nova
a7930760e8 feat: glsl simula text shaders 2023-06-25 10:05:18 -04:00
Nova
668c32f583 fix: ctrl+c in tty 2023-06-21 01:47:10 -04:00
Nova
927e1c48e2 fix: mouse pointer keyboard ray direction 2023-06-14 23:00:17 -04:00
27 changed files with 971 additions and 441 deletions

46
.github/workflows/build.yml vendored Normal file
View 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

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
[package] [package]
edition = "2021" edition = "2021"
name = "stardust-xr-server" name = "stardust-xr-server"
version = "0.42.0" version = "0.42.1"
authors = ["Nova King <technobaboo@proton.me>"] authors = ["Nova King <technobaboo@proton.me>"]
description = "Stardust XR reference display server" description = "Stardust XR reference display server"
license = "GPLv2" license = "GPLv2"
@@ -58,6 +58,7 @@ tracing = "0.1.37"
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
global_counter = "0.2.2" global_counter = "0.2.2"
rand = "0.8.5" rand = "0.8.5"
atty = "0.2.14"
[dependencies.stereokit] [dependencies.stereokit]
default-features = false default-features = false
@@ -65,11 +66,11 @@ features = ["linux-egl"]
version = "0.16.7" version = "0.16.7"
[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
# git = "https://github.com/smithay/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" # path = "../smithay"
default-features = false default-features = false
features = ["desktop", "renderer_gl", "wayland_frontend"] features = ["desktop", "backend_drm", "renderer_gl", "wayland_frontend"]
version = "*" version = "*"
optional = true optional = true

View File

@@ -15,11 +15,52 @@ This project is a usable Linux display server that reinvents human-computer inte
```bash ```bash
cargo build cargo build
``` ```
The latest stable server is automatically built to an appimage at https://github.com/StardustXR/server/releases for easy testing.
## Install ## Usage
```bash
cargo install 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:
```
![A pitch black void with a single bleach white hand in the middle](/img/xr_mode_windowed_blank.png)
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):
![A pitch black window representing Stardust in flatscreen mode](/img/flatscreen_3.png)
### 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.
![A black void representing Stardust in XR mode with a hand skeleton in the middle](/img/flatscreen_2.png)
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:
![A pitch black window representing Stardust in flatscreen mode](/img/flatscreen_1.png)
#### 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 ## Test
@@ -41,5 +82,4 @@ cargo install
##### Everything ##### 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

BIN
img/flatscreen_2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
img/flatscreen_3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 789 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -10,15 +10,16 @@ use crate::objects::input::eye_pointer::EyePointer;
use crate::objects::input::mouse_pointer::MousePointer; use crate::objects::input::mouse_pointer::MousePointer;
use crate::objects::input::sk_controller::SkController; use crate::objects::input::sk_controller::SkController;
use crate::objects::input::sk_hand::SkHand; use crate::objects::input::sk_hand::SkHand;
use crate::objects::play_space::PlaySpace;
use self::core::eventloop::EventLoop; use self::core::eventloop::EventLoop;
use clap::Parser; use clap::Parser;
use color_eyre::eyre::Result;
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::mem::ManuallyDrop;
use std::path::PathBuf; use std::path::PathBuf;
use std::process::Command; use std::process::{Command, Stdio};
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use stereokit::{ use stereokit::{
@@ -27,6 +28,7 @@ use stereokit::{
}; };
use stereokit::{DisplayBlend, Sk}; use stereokit::{DisplayBlend, Sk};
use tokio::{runtime::Handle, sync::oneshot}; use tokio::{runtime::Handle, sync::oneshot};
use tracing::metadata::LevelFilter;
use tracing::{debug_span, error, info}; use tracing::{debug_span, error, info};
use tracing_subscriber::{fmt, prelude::*, EnvFilter}; use tracing_subscriber::{fmt, prelude::*, EnvFilter};
@@ -58,7 +60,7 @@ struct EventLoopInfo {
socket_path: PathBuf, socket_path: PathBuf,
} }
fn main() -> Result<()> { fn main() {
let registry = tracing_subscriber::registry(); let registry = tracing_subscriber::registry();
#[cfg(feature = "profile_app")] #[cfg(feature = "profile_app")]
let (chrome_layer, _guard) = tracing_chrome::ChromeLayerBuilder::new() let (chrome_layer, _guard) = tracing_chrome::ChromeLayerBuilder::new()
@@ -94,7 +96,15 @@ fn main() -> Result<()> {
}, },
blend_preference: DisplayBlend::AnyTransparent, blend_preference: DisplayBlend::AnyTransparent,
depth_mode: DepthMode::D32, 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_app: cli_args.overlay_priority.is_some(),
overlay_priority: cli_args.overlay_priority.unwrap_or(u32::MAX), overlay_priority: cli_args.overlay_priority.unwrap_or(u32::MAX),
disable_desktop_input_window: true, disable_desktop_input_window: true,
@@ -106,8 +116,8 @@ fn main() -> Result<()> {
info!("Init StereoKit"); info!("Init StereoKit");
sk.material_set_shader( sk.material_set_shader(
sk.material_find("default/material_pbr")?, sk.material_find("default/material_pbr").unwrap(),
sk.shader_find("default/shader_pbr_clip")?, sk.shader_find("default/shader_pbr_clip").unwrap(),
); );
// Skytex/light stuff // 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) let mut hands = (!cli_args.flatscreen)
.then(|| { .then(|| {
let left = SkHand::new(Handed::Left).ok(); let left = SkHand::new(Handed::Left).ok();
@@ -152,23 +166,30 @@ fn main() -> Result<()> {
.flatten(); .flatten();
let eye_pointer = (!cli_args.flatscreen && sk.device_has_eye_gaze()) let eye_pointer = (!cli_args.flatscreen && sk.device_has_eye_gaze())
.then(EyePointer::new) .then(EyePointer::new)
.transpose()?; .transpose()
.unwrap();
if hands.is_none() { if hands.is_none() {
sk.input_hand_visible(Handed::Left, false); sk.input_hand_visible(Handed::Left, false);
sk.input_hand_visible(Handed::Right, 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 (event_stop_tx, event_stop_rx) = oneshot::channel::<()>();
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, event_stop_rx))?; .spawn(move || event_loop(info_sender, event_stop_rx))
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();
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
let mut wayland = wayland::Wayland::new()?; let mut wayland = wayland::Wayland::new().unwrap();
info!("Stardust ready!"); info!("Stardust ready!");
if let Some(project_dirs) = project_dirs.as_ref() { if let Some(project_dirs) = project_dirs.as_ref() {
@@ -178,6 +199,7 @@ fn main() -> Result<()> {
.and_then(|p| p.canonicalize().ok()) .and_then(|p| p.canonicalize().ok())
.unwrap_or_else(|| project_dirs.config_dir().join("startup")); .unwrap_or_else(|| project_dirs.config_dir().join("startup"));
let _startup = Command::new(startup_script_path) let _startup = Command::new(startup_script_path)
.stdin(Stdio::null())
.env( .env(
"FLAT_WAYLAND_DISPLAY", "FLAT_WAYLAND_DISPLAY",
std::env::var_os("WAYLAND_DISPLAY").unwrap_or_default(), std::env::var_os("WAYLAND_DISPLAY").unwrap_or_default(),
@@ -225,6 +247,9 @@ fn main() -> Result<()> {
if let Some(eye_pointer) = &eye_pointer { if let Some(eye_pointer) = &eye_pointer {
eye_pointer.update(sk); eye_pointer.update(sk);
} }
if let Some(play_space) = &play_space {
play_space.update(sk);
}
input::process_input(); input::process_input();
nodes::root::Root::send_frame_events(sk.time_elapsed_unscaled()); nodes::root::Root::send_frame_events(sk.time_elapsed_unscaled());
adaptive_sleep( adaptive_sleep(
@@ -248,14 +273,14 @@ fn main() -> Result<()> {
}); });
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
drop(wayland); let _wayland = ManuallyDrop::new(wayland);
let _ = event_stop_tx.send(()); let _ = event_stop_tx.send(());
event_thread event_thread
.join() .join()
.expect("Failed to cleanly shut down event loop")?; .expect("Failed to cleanly shut down event loop")
.unwrap();
info!("Cleanly shut down Stardust"); info!("Cleanly shut down Stardust");
Ok(())
} }
fn adaptive_sleep( fn adaptive_sleep(
@@ -301,11 +326,15 @@ async fn event_loop(
socket_path, socket_path,
}); });
let result = tokio::select! { if atty::is(atty::Stream::Stdin) {
biased; stop_rx.await?;
_ = tokio::signal::ctrl_c() => Ok(()), } else {
_ = stop_rx => Ok(()), tokio::select! {
}; biased;
_ = tokio::signal::ctrl_c() => (),
_ = stop_rx => (),
};
}
info!("Cleanly shut down event loop"); info!("Cleanly shut down event loop");
@@ -313,5 +342,5 @@ async fn event_loop(
stereokit::sys::sk_quit(); stereokit::sys::sk_quit();
} }
result Ok(())
} }

View File

@@ -35,6 +35,11 @@ pub fn mask_matches(mask_map_lesser: &Mask, mask_map_greater: &Mask) -> bool {
pub struct Mask(pub Vec<u8>); pub struct Mask(pub Vec<u8>);
impl Mask { 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]>> { pub fn get_mask(&self) -> Result<flexbuffers::MapReader<&[u8]>> {
flexbuffers::Reader::get_root(self.0.as_slice()) flexbuffers::Reader::get_root(self.0.as_slice())
.map_err(|_| eyre!("Mask is not a valid flexbuffer"))? .map_err(|_| eyre!("Mask is not a valid flexbuffer"))?
@@ -182,7 +187,7 @@ pub struct PulseReceiver {
pub mask: Mask, pub mask: Mask,
} }
impl PulseReceiver { 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!( ensure!(
node.spatial.get().is_some(), node.spatial.get().is_some(),
"Internal: Node does not have a spatial attached!" "Internal: Node does not have a spatial attached!"
@@ -199,8 +204,8 @@ impl PulseReceiver {
for sender in PULSE_SENDER_REGISTRY.get_valid_contents() { for sender in PULSE_SENDER_REGISTRY.get_valid_contents() {
sender.handle_new_receiver(&receiver); sender.handle_new_receiver(&receiver);
} }
let _ = node.pulse_receiver.set(receiver); let _ = node.pulse_receiver.set(receiver.clone());
Ok(()) Ok(receiver)
} }
pub fn send_data(&self, uid: &str, data: Vec<u8>) -> Result<()> { pub fn send_data(&self, uid: &str, data: Vec<u8>) -> Result<()> {

View File

@@ -1,5 +1,6 @@
pub mod lines; pub mod lines;
pub mod model; pub mod model;
pub mod shaders;
pub mod text; pub mod text;
use self::{ use self::{

View 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(())
}

View File

@@ -16,7 +16,7 @@ pub struct BoxField {
} }
impl 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!( ensure!(
node.spatial.get().is_some(), node.spatial.get().is_some(),
"Internal: Node does not have a spatial attached!" "Internal: Node does not have a spatial attached!"
@@ -31,8 +31,9 @@ impl BoxField {
}; };
box_field.add_field_methods(node); box_field.add_field_methods(node);
node.add_local_signal("set_size", BoxField::set_size_flex); node.add_local_signal("set_size", BoxField::set_size_flex);
let _ = node.field.set(Arc::new(Field::Box(box_field))); let field = Arc::new(Field::Box(box_field));
Ok(()) let _ = node.field.set(field.clone());
Ok(field)
} }
pub fn set_size(&self, size: Vector3<f32>) { pub fn set_size(&self, size: Vector3<f32>) {

View File

@@ -1,7 +1,7 @@
mod r#box; pub mod r#box;
mod cylinder; mod cylinder;
mod sphere; mod sphere;
pub mod torus; mod torus;
use self::cylinder::{create_cylinder_field_flex, CylinderField}; use self::cylinder::{create_cylinder_field_flex, CylinderField};
use self::r#box::{create_box_field_flex, BoxField}; use self::r#box::{create_box_field_flex, BoxField};

View File

@@ -107,7 +107,7 @@ impl MousePointer {
.map(|rx| { .map(|rx| {
let result = rx.field.ray_march(Ray { let result = rx.field.ray_march(Ray {
origin: vec3(0.0, 0.0, 0.0), 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(), space: self.spatial.clone(),
}); });
(rx, result) (rx, result)

View File

@@ -1 +1,2 @@
pub mod input; pub mod input;
pub mod play_space;

73
src/objects/play_space.rs Normal file
View 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(),
);
}
}

View File

@@ -16,8 +16,10 @@ use global_counter::primitive::exact::CounterU32;
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use parking_lot::Mutex; use parking_lot::Mutex;
use sk::StereoKitDraw; use sk::StereoKitDraw;
use smithay::backend::allocator::dmabuf::Dmabuf;
use smithay::backend::egl::EGLContext; use smithay::backend::egl::EGLContext;
use smithay::backend::renderer::gles::GlesRenderer; use smithay::backend::renderer::gles::GlesRenderer;
use smithay::backend::renderer::ImportDma;
use smithay::reexports::wayland_server::{backend::GlobalId, Display, ListeningSocket}; use smithay::reexports::wayland_server::{backend::GlobalId, Display, ListeningSocket};
use std::os::unix::prelude::AsRawFd; use std::os::unix::prelude::AsRawFd;
use std::{ use std::{
@@ -26,6 +28,7 @@ use std::{
sync::Arc, sync::Arc,
}; };
use stereokit as sk; use stereokit as sk;
use tokio::sync::mpsc::UnboundedReceiver;
use tokio::{ use tokio::{
io::unix::AsyncFd, net::UnixListener as AsyncUnixListener, sync::mpsc, task::JoinHandle, io::unix::AsyncFd, net::UnixListener as AsyncUnixListener, sync::mpsc, task::JoinHandle,
}; };
@@ -63,6 +66,7 @@ pub struct Wayland {
pub socket_name: String, pub socket_name: String,
join_handle: JoinHandle<Result<()>>, join_handle: JoinHandle<Result<()>>,
renderer: GlesRenderer, renderer: GlesRenderer,
dmabuf_rx: UnboundedReceiver<Dmabuf>,
state: Arc<Mutex<WaylandState>>, state: Arc<Mutex<WaylandState>>,
} }
impl Wayland { impl Wayland {
@@ -79,8 +83,9 @@ impl Wayland {
let display: Display<WaylandState> = Display::new()?; let display: Display<WaylandState> = Display::new()?;
let display_handle = display.handle(); let display_handle = display.handle();
let (dmabuf_tx, dmabuf_rx) = mpsc::unbounded_channel();
let display = Arc::new(Mutex::new(display)); 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); let (global_destroy_queue_in, global_destroy_queue) = mpsc::channel(8);
GLOBAL_DESTROY_QUEUE.set(global_destroy_queue_in).unwrap(); GLOBAL_DESTROY_QUEUE.set(global_destroy_queue_in).unwrap();
@@ -100,6 +105,7 @@ impl Wayland {
socket_name, socket_name,
join_handle, join_handle,
renderer, renderer,
dmabuf_rx,
state, state,
}) })
} }
@@ -150,6 +156,9 @@ impl Wayland {
#[instrument(level = "debug", name = "Wayland frame", skip(self, sk))] #[instrument(level = "debug", name = "Wayland frame", skip(self, sk))]
pub fn update(&mut self, sk: &impl StereoKitDraw) { 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() { for core_surface in CORE_SURFACES.get_valid_contents() {
core_surface.process(sk, &mut self.renderer); core_surface.process(sk, &mut self.renderer);
} }

View File

@@ -470,14 +470,22 @@ impl PanelItem {
xdg_toplevel.configure_bounds(bounds.x as i32, bounds.y as i32); 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( xdg_toplevel.configure(
size.x as i32, size.x as i32,
size.y as i32, size.y as i32,
info.states info.states.into_iter().flat_map(u32::to_ne_bytes).collect(),
.into_iter()
.flat_map(|state| state.to_ne_bytes())
.collect::<Vec<_>>(),
); );
xdg_surface.configure(SERIAL_COUNTER.inc()); xdg_surface.configure(SERIAL_COUNTER.inc());
core_surface.flush_clients(); core_surface.flush_clients();

View File

@@ -446,7 +446,7 @@ impl Dispatch<WlPointer, Arc<SeatData>, WaylandState> for WaylandState {
hotspot_y, hotspot_y,
} => { } => {
if let Some(surface) = surface.as_ref() { 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| { compositor::with_states(surface, |data| {
data.data_map.insert_if_missing_threadsafe(|| { data.data_map.insert_if_missing_threadsafe(|| {
Arc::new(Mutex::new(Cursor { Arc::new(Mutex::new(Cursor {

View 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);
}

View File

@@ -5,3 +5,9 @@
// Simula shader with fancy lanzcos sampling // Simula shader with fancy lanzcos sampling
pub const PANEL_SHADER_BYTES: &[u8] = include_bytes!("shader_unlit_simula.sks"); 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");

View 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;
}

View 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);
}

View File

@@ -4,6 +4,7 @@ use rustc_hash::FxHashMap;
use smithay::{ use smithay::{
backend::{ backend::{
allocator::dmabuf::Dmabuf, allocator::dmabuf::Dmabuf,
egl::EGLDevice,
renderer::{gles::GlesRenderer, ImportDma}, renderer::{gles::GlesRenderer, ImportDma},
}, },
delegate_dmabuf, delegate_output, delegate_shm, delegate_dmabuf, delegate_output, delegate_shm,
@@ -24,13 +25,17 @@ use smithay::{
wayland::{ wayland::{
buffer::BufferHandler, buffer::BufferHandler,
compositor::{CompositorClientState, CompositorState}, compositor::{CompositorClientState, CompositorState},
dmabuf::{self, DmabufGlobal, DmabufHandler, DmabufState}, dmabuf::{
self, DmabufFeedback, DmabufFeedbackBuilder, DmabufGlobal, DmabufHandler, DmabufState,
ImportError,
},
shell::kde::decoration::KdeDecorationState, shell::kde::decoration::KdeDecorationState,
shm::{ShmHandler, ShmState}, shm::{ShmHandler, ShmState},
}, },
}; };
use std::sync::{Arc, Weak}; use std::sync::{Arc, Weak};
use tracing::info; use tokio::sync::mpsc::UnboundedSender;
use tracing::{info, warn};
#[derive(Default)] #[derive(Default)]
pub struct ClientState { pub struct ClientState {
@@ -58,9 +63,8 @@ pub struct WaylandState {
// pub xdg_activation_state: XdgActivationState, // pub xdg_activation_state: XdgActivationState,
pub kde_decoration_state: KdeDecorationState, pub kde_decoration_state: KdeDecorationState,
pub shm_state: ShmState, pub shm_state: ShmState,
pub dmabuf_state: DmabufState, dmabuf_state: (DmabufState, DmabufGlobal, Option<DmabufFeedback>),
pub dmabuf_global: DmabufGlobal, dmabuf_tx: UnboundedSender<Dmabuf>,
pub pending_dmabufs: Vec<Dmabuf>,
pub output: Output, pub output: Output,
pub seats: FxHashMap<ClientId, Arc<SeatData>>, pub seats: FxHashMap<ClientId, Arc<SeatData>>,
} }
@@ -70,17 +74,51 @@ impl WaylandState {
display: Arc<Mutex<Display<WaylandState>>>, display: Arc<Mutex<Display<WaylandState>>>,
display_handle: DisplayHandle, display_handle: DisplayHandle,
renderer: &GlesRenderer, renderer: &GlesRenderer,
dmabuf_tx: UnboundedSender<Dmabuf>,
) -> Arc<Mutex<Self>> { ) -> Arc<Mutex<Self>> {
let compositor_state = CompositorState::new::<Self>(&display_handle); let compositor_state = CompositorState::new::<Self>(&display_handle);
// let xdg_activation_state = XdgActivationState::new::<Self, _>(&display_handle); // let xdg_activation_state = XdgActivationState::new::<Self, _>(&display_handle);
let kde_decoration_state = let kde_decoration_state =
KdeDecorationState::new::<Self>(&display_handle, DecorationMode::Server); KdeDecorationState::new::<Self>(&display_handle, DecorationMode::Server);
let shm_state = ShmState::new::<Self>(&display_handle, vec![]); let shm_state = ShmState::new::<Self>(&display_handle, vec![]);
let mut dmabuf_state = DmabufState::new(); let render_node = EGLDevice::device_for_display(renderer.egl_context().display())
let dmabuf_global = dmabuf_state.create_global::<Self>( .and_then(|device| device.try_get_render_node());
&display_handle,
renderer.dmabuf_formats().collect::<Vec<_>>(), 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( let output = Output::new(
"1x".to_owned(), "1x".to_owned(),
smithay::output::PhysicalProperties { smithay::output::PhysicalProperties {
@@ -119,8 +157,7 @@ impl WaylandState {
kde_decoration_state, kde_decoration_state,
shm_state, shm_state,
dmabuf_state, dmabuf_state,
dmabuf_global, dmabuf_tx,
pending_dmabufs: Vec::new(),
output, output,
seats: FxHashMap::default(), seats: FxHashMap::default(),
}) })
@@ -147,15 +184,15 @@ impl ShmHandler for WaylandState {
} }
impl DmabufHandler for WaylandState { impl DmabufHandler for WaylandState {
fn dmabuf_state(&mut self) -> &mut DmabufState { fn dmabuf_state(&mut self) -> &mut DmabufState {
&mut self.dmabuf_state &mut self.dmabuf_state.0
} }
fn dmabuf_imported( fn dmabuf_imported(
&mut self, &mut self,
_global: &DmabufGlobal, _global: &DmabufGlobal,
_dmabuf: Dmabuf, dmabuf: Dmabuf,
) -> Result<(), dmabuf::ImportError> { ) -> Result<(), dmabuf::ImportError> {
Ok(()) self.dmabuf_tx.send(dmabuf).map_err(|_| ImportError::Failed)
} }
} }
delegate_dmabuf!(WaylandState); delegate_dmabuf!(WaylandState);

View File

@@ -50,6 +50,7 @@ pub struct CoreSurface {
sk_tex: OnceCell<SendWrapper<Tex>>, sk_tex: OnceCell<SendWrapper<Tex>>,
sk_mat: OnceCell<Arc<SendWrapper<Material>>>, sk_mat: OnceCell<Arc<SendWrapper<Material>>>,
material_offset: Mutex<Delta<u32>>, material_offset: Mutex<Delta<u32>>,
on_mapped: Box<dyn Fn() + Send + Sync>,
on_commit: Box<dyn Fn(u32) + Send + Sync>, on_commit: Box<dyn Fn(u32) + Send + Sync>,
pub pending_material_applications: Registry<ModelPart>, pub pending_material_applications: Registry<ModelPart>,
} }
@@ -59,6 +60,7 @@ impl CoreSurface {
display: &Arc<Mutex<Display<WaylandState>>>, display: &Arc<Mutex<Display<WaylandState>>>,
dh: DisplayHandle, dh: DisplayHandle,
surface: &WlSurface, surface: &WlSurface,
on_mapped: impl Fn() + Send + Sync + 'static,
on_commit: impl Fn(u32) + Send + Sync + 'static, on_commit: impl Fn(u32) + Send + Sync + 'static,
) { ) {
compositor::with_states(surface, |data| { compositor::with_states(surface, |data| {
@@ -71,6 +73,7 @@ impl CoreSurface {
sk_tex: OnceCell::new(), sk_tex: OnceCell::new(),
sk_mat: OnceCell::new(), sk_mat: OnceCell::new(),
material_offset: Mutex::new(Delta::new(0)), 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>, on_commit: Box::new(on_commit) as Box<dyn Fn(u32) + Send + Sync>,
pending_material_applications: Registry::new(), pending_material_applications: Registry::new(),
}) })
@@ -96,6 +99,10 @@ impl CoreSurface {
}); });
self.sk_mat.get_or_init(|| { self.sk_mat.get_or_init(|| {
let shader = sk.shader_create_mem(&PANEL_SHADER_BYTES).unwrap(); 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); let mat = sk.material_create(&shader);
sk.material_set_texture(&mat, "diffuse", sk_tex.as_ref()); sk.material_set_texture(&mat, "diffuse", sk_tex.as_ref());
sk.material_set_transparency(&mat, Transparency::Blend); sk.material_set_transparency(&mat, Transparency::Blend);
@@ -121,9 +128,8 @@ impl CoreSurface {
} }
let mut mapped_data = self.mapped_data.lock(); let mut mapped_data = self.mapped_data.lock();
let just_mapped = mapped_data.is_none();
self.with_states(|data| { self.with_states(|data| {
// let just_mapped = mapped_data.is_none();
// if just_mapped {
let renderer_surface_state = data let renderer_surface_state = data
.data_map .data_map
.get::<RendererSurfaceStateUserData>() .get::<RendererSurfaceStateUserData>()
@@ -161,6 +167,10 @@ impl CoreSurface {
}; };
*mapped_data = Some(new_mapped_data); *mapped_data = Some(new_mapped_data);
}); });
drop(mapped_data);
if just_mapped {
(self.on_mapped)();
}
self.apply_surface_materials(); self.apply_surface_materials();
} }

View File

@@ -253,21 +253,32 @@ impl Dispatch<XdgSurface, Mutex<XdgSurfaceData>, WaylandState> for WaylandState
debug!(?toplevel, ?xdg_surface, "Create XDG toplevel"); debug!(?toplevel, ?xdg_surface, "Create XDG toplevel");
if toplevel.version() >= EVT_WM_CAPABILITIES_SINCE { 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()); xdg_surface.configure(SERIAL_COUNTER.inc());
let client_credentials = client.get_credentials(&state.display_handle).ok(); let client_credentials = client.get_credentials(&state.display_handle).ok();
let seat_data = state.seats.get(&client.id()).unwrap().clone(); let seat_data = state.seats.get(&client.id()).unwrap().clone();
let toplevel_weak = toplevel.downgrade();
CoreSurface::add_to( CoreSurface::add_to(
&state.display, &state.display,
state.display_handle.clone(), state.display_handle.clone(),
&xdg_surface_data.lock().wl_surface(), &xdg_surface_data.lock().wl_surface(),
move |c| match c { {
0 => { let toplevel = toplevel.downgrade();
let toplevel = toplevel_weak.upgrade().unwrap(); move || {
let toplevel = toplevel.upgrade().unwrap();
let toplevel_data = ToplevelData::get(&toplevel); let toplevel_data = ToplevelData::get(&toplevel);
let xdg_surface = toplevel_data.lock().xdg_surface(); let xdg_surface = toplevel_data.lock().xdg_surface();
let xdg_surface_data = XdgSurfaceData::get(&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); toplevel_data.lock().panel_item_node.replace(node);
xdg_surface_data.lock().panel_item = Arc::downgrade(&panel_item); 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 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(); panel_item.commit_toplevel();
} }
}, },
@@ -335,19 +363,19 @@ impl Dispatch<XdgSurface, Mutex<XdgSurfaceData>, WaylandState> for WaylandState
&state.display, &state.display,
state.display_handle.clone(), state.display_handle.clone(),
&xdg_surface_data.lock().wl_surface.upgrade().unwrap(), &xdg_surface_data.lock().wl_surface.upgrade().unwrap(),
move |commit_count| match commit_count { move || {
0 => xdg_surface let xdg_popup = xdg_popup.upgrade().unwrap();
.upgrade() let popup_data = PopupData::get(&xdg_popup);
.unwrap() let popup_data = popup_data.lock();
.configure(SERIAL_COUNTER.inc()), // panel_item.commit_popup(popup_data);
c => { panel_item.new_popup(&xdg_popup, &*popup_data);
let xdg_popup = xdg_popup.upgrade().unwrap(); },
let popup_data = PopupData::get(&xdg_popup); move |commit_count| {
let popup_data = popup_data.lock(); if commit_count == 0 {
// panel_item.commit_popup(popup_data); xdg_surface
if c == 1 { .upgrade()
panel_item.new_popup(&xdg_popup, &*popup_data); .unwrap()
} .configure(SERIAL_COUNTER.inc())
} }
}, },
); );