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]
|
[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
|
||||||
|
|
||||||
|
|||||||
52
README.md
52
README.md
@@ -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:
|
||||||
```
|
|
||||||
|

|
||||||
|
|
||||||
|
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
|
## 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
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::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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<()> {
|
||||||
|
|||||||
@@ -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::{
|
||||||
|
|||||||
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 {
|
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>) {
|
||||||
|
|||||||
@@ -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};
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
pub mod input;
|
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 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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
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
|
// 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");
|
||||||
|
|||||||
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::{
|
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);
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user