mirror of
https://github.com/wayvr-org/wayvr.git
synced 2026-10-28 13:02:53 +01:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bcc9f5d277 | ||
|
|
5723d668e3 | ||
|
|
1ae50a35ec | ||
|
|
5b23de0599 | ||
|
|
cdbe3c4ad3 | ||
|
|
412f199f91 | ||
|
|
4e30bb8cbe | ||
|
|
45e53c15a3 | ||
|
|
21a4797d2a | ||
|
|
a8ecc973a4 | ||
|
|
f12d151abf | ||
|
|
5ca634b552 | ||
|
|
409b89585e | ||
|
|
92c59850b6 | ||
|
|
bffb4c12cf | ||
|
|
feac111949 | ||
|
|
f13a473048 | ||
|
|
b2a7d3c4cb | ||
|
|
4cb12a2a01 | ||
|
|
b830c826a0 | ||
|
|
45ab38c310 | ||
|
|
bc6c510af1 | ||
|
|
3dd31ec468 | ||
|
|
60850d8d70 | ||
|
|
7ac3072506 | ||
|
|
2952a3cea1 | ||
|
|
bb2f8380fd | ||
|
|
1deae19f92 | ||
|
|
3365aefc13 | ||
|
|
cf29682e66 |
No files matched your search
@@ -0,0 +1,42 @@
|
||||
---
|
||||
name: Bug Report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Description
|
||||
<!--
|
||||
If this is a regression, please mention which version was working previously.
|
||||
-->
|
||||
|
||||
## System Info
|
||||
**Linux Distribution**:
|
||||
|
||||
**Desktop Environment**:
|
||||
|
||||
<!-- Paste output of `uname -r` -->
|
||||
**Kernel version**:
|
||||
|
||||
**VR Runtime**:
|
||||
- [ ] Monado/WiVRn
|
||||
- [ ] SteamVR/ALVR
|
||||
|
||||
<!-- Run `vulkaninfo --summary` and paste the devices section from the bottom. -->
|
||||
**GPU models and driver versions**:
|
||||
|
||||
## Overlay Logs
|
||||
|
||||
<!-- Start the overlay once more with the following environment variables:
|
||||
RUST_BACKTRACE=full
|
||||
RUST_LOG=debug
|
||||
If your issue is graphical or crash or freeze, also add:
|
||||
VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation
|
||||
|
||||
Be sure to go and reproduce the issue once more, after these have been set.
|
||||
|
||||
Upload the log file from: /tmp/wlx.log
|
||||
-->
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Check Full
|
||||
name: Check All Features
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
@@ -23,6 +23,6 @@ jobs:
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y fuse cmake pkg-config fontconfig libasound2-dev libxkbcommon-dev libxkbcommon-x11-0 libxkbcommon-x11-dev libopenxr-dev libfontconfig-dev libdbus-1-dev libpipewire-0.3-0 libpipewire-0.3-dev libspa-0.2-dev libx11-6 libxext6 libxrandr2 libx11-dev libxext-dev libxrandr-dev libopenvr-dev libopenvr-api1 libwayland-dev libegl-dev
|
||||
- name: Build
|
||||
run: cargo build --verbose
|
||||
run: cargo build --verbose --all-features
|
||||
- name: Run tests
|
||||
run: cargo test --verbose
|
||||
run: cargo test --verbose --all-features
|
||||
@@ -0,0 +1,32 @@
|
||||
name: Check Default
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ "!main" ]
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
SCCACHE_GHA_ENABLED: "true"
|
||||
RUSTC_WRAPPER: "sccache"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup sccache
|
||||
uses: mozilla-actions/sccache-action@v0.0.3
|
||||
- name: Prepare Environment
|
||||
run: |
|
||||
sudo add-apt-repository -syn universe
|
||||
sudo add-apt-repository -syn ppa:pipewire-debian/pipewire-upstream || sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 25088A0359807596
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y fuse cmake pkg-config fontconfig libasound2-dev libxkbcommon-dev libxkbcommon-x11-0 libxkbcommon-x11-dev libopenxr-dev libfontconfig-dev libdbus-1-dev libpipewire-0.3-0 libpipewire-0.3-dev libspa-0.2-dev libx11-6 libxext6 libxrandr2 libx11-dev libxext-dev libxrandr-dev libopenvr-dev libopenvr-api1 libwayland-dev libegl-dev
|
||||
- name: Run cargo fmt
|
||||
run: cargo fmt --check
|
||||
- name: Build
|
||||
run: cargo build --verbose
|
||||
- name: Run clippy
|
||||
run: cargo clippy --no-deps
|
||||
- name: Run tests
|
||||
run: cargo test --verbose
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup sccache
|
||||
uses: mozilla-actions/sccache-action@v0.0.3
|
||||
uses: mozilla-actions/sccache-action@v0.0.9
|
||||
- name: Prepare Environment
|
||||
run: |
|
||||
sudo add-apt-repository -syn universe
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
- name: Cargo Build
|
||||
run: |
|
||||
cargo build --release
|
||||
mv target/release/wlx-overlay-s ${APPDIR}/usr/bin
|
||||
cp target/release/wlx-overlay-s ${APPDIR}/usr/bin
|
||||
chmod +x ${APPDIR}/usr/bin/wlx-overlay-s
|
||||
|
||||
- name: Package AppImage
|
||||
@@ -62,6 +62,16 @@ jobs:
|
||||
draft: true
|
||||
prerelease: false
|
||||
|
||||
- name: Upload ELF
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.RELEASE_KEY }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: target/release/wlx-overlay-s
|
||||
asset_name: wlx-overlay-s
|
||||
asset_content_type: application/octet-stream
|
||||
|
||||
- name: Upload AppImage
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
|
||||
Generated
+639
-539
File diff suppressed because it is too large.
Load diff
+20
-16
@@ -4,25 +4,30 @@ debug = true
|
||||
|
||||
[package]
|
||||
name = "wlx-overlay-s"
|
||||
version = "25.4.0"
|
||||
version = "25.4.2"
|
||||
edition = "2021"
|
||||
license = "GPL-3.0-only"
|
||||
authors = ["galister"]
|
||||
description = "Access your Wayland/X11 desktop from Monado/WiVRn/SteamVR. Now with Vulkan!"
|
||||
repository = "https://github.com/galister/wlx-overlay-s"
|
||||
keywords = ["linux", "openvr", "openxr", "x11", "wayland", "openvr-overlay", "openxr-overlay"]
|
||||
categories = ["games"]
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.89"
|
||||
ash = "^0.37.2"
|
||||
ash = "^0.38.0" # must match vulkano
|
||||
chrono = "0.4.38"
|
||||
chrono-tz = "0.10.0"
|
||||
clap = { version = "4.5.6", features = ["derive"] }
|
||||
config = "0.14.0"
|
||||
cstr = "0.2.12"
|
||||
config = "0.15.11"
|
||||
ctrlc = { version = "3.4.4", features = ["termination"] }
|
||||
dbus = { version = "0.9.7" }
|
||||
fontconfig-rs = "0.1.1"
|
||||
freetype-rs = "0.36.0" # latest version supported on ubuntu 22.04
|
||||
futures = "0.3.30"
|
||||
glam = { version = "0.29.2", features = ["approx", "mint", "serde"] }
|
||||
glam = { version = "0.30.1", features = ["approx", "mint", "serde"] }
|
||||
idmap = { version = "0.2.21", features = ["serde"] }
|
||||
idmap-derive = "0.1.2"
|
||||
input-linux = "0.7.0"
|
||||
@@ -30,7 +35,6 @@ json = { version = "0.12.4", optional = true }
|
||||
json5 = "0.4.1"
|
||||
libc = "0.2.155"
|
||||
log = "0.4.21"
|
||||
once_cell = "1.19.0"
|
||||
openxr = { git = "https://github.com/galister/openxrs", rev = "af4a55d", features = [
|
||||
"linked",
|
||||
"mint",
|
||||
@@ -39,32 +43,32 @@ ovr_overlay = { features = [
|
||||
"ovr_input",
|
||||
"ovr_system",
|
||||
], git = "https://github.com/galister/ovr_overlay_oyasumi", optional = true }
|
||||
regex = "1.10.4"
|
||||
regex = "1.11.1"
|
||||
rodio = { version = "0.20.1", default-features = false, features = [
|
||||
"wav",
|
||||
"hound",
|
||||
] }
|
||||
rosc = { version = "0.10.1", optional = true }
|
||||
rosc = { version = "0.11.4", optional = true }
|
||||
serde = { version = "1.0.203", features = ["derive", "rc"] }
|
||||
serde_json = "1.0.117"
|
||||
serde_yaml = "0.9.34"
|
||||
smallvec = "1.13.2"
|
||||
strum = { version = "0.26.2", features = ["derive"] }
|
||||
sysinfo = { version = "0.32.0" }
|
||||
strum = { version = "0.27.1", features = ["derive"] }
|
||||
sysinfo = { version = "0.34.2" }
|
||||
thiserror = "2.0.3"
|
||||
vulkano = { git = "https://github.com/vulkano-rs/vulkano", rev = "94f50f1" }
|
||||
vulkano-shaders = { git = "https://github.com/vulkano-rs/vulkano", rev = "94f50f1" }
|
||||
wlx-capture = { git = "https://github.com/galister/wlx-capture", tag = "v0.4.2", default-features = false }
|
||||
vulkano = { version = "0.35.1" }
|
||||
vulkano-shaders = { version = "0.35.0" }
|
||||
wlx-capture = { git = "https://github.com/galister/wlx-capture", tag = "v0.5.2", default-features = false }
|
||||
libmonado = { git = "https://github.com/technobaboo/libmonado-rs", rev = "256895b18c8f9368174fad8a6232ff07764eeacb", optional = true }
|
||||
winit = { version = "0.30.0", optional = true }
|
||||
xdg = "2.5.2"
|
||||
log-panics = { version = "2.1.0", features = ["with-backtrace"] }
|
||||
serde_json5 = "0.1.0"
|
||||
serde_json5 = "0.2.1"
|
||||
xkbcommon = { version = "0.8.0" }
|
||||
xcb = { version = "1.4.0", optional = true, features = [
|
||||
"as-raw-xcb-connection",
|
||||
] }
|
||||
image_dds = { version = "0.6.0", default-features = false, features = [
|
||||
image_dds = { version = "0.7.2", default-features = false, features = [
|
||||
"ddsfile",
|
||||
] }
|
||||
mint = "0.5.9"
|
||||
@@ -91,7 +95,7 @@ wayvr_ipc = { git = "https://github.com/olekolek1000/wayvr-ipc.git", rev = "a725
|
||||
################################
|
||||
|
||||
[build-dependencies]
|
||||
regex = { version = "*" }
|
||||
regex = { version = "1.11.1" }
|
||||
|
||||
[features]
|
||||
default = ["openvr", "openxr", "osc", "x11", "wayland", "wayvr"]
|
||||
|
||||
+12
-11
@@ -1,6 +1,5 @@
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, LazyLock};
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
#[cfg(feature = "openxr")]
|
||||
use openxr as xr;
|
||||
|
||||
@@ -69,17 +68,17 @@ where
|
||||
app.screens.clear();
|
||||
let data = if let Some(wl) = wl.as_mut() {
|
||||
keymap = get_keymap_wl()
|
||||
.map_err(|f| log::warn!("Could not load keyboard layout: {}", f))
|
||||
.map_err(|f| log::warn!("Could not load keyboard layout: {f}"))
|
||||
.ok();
|
||||
crate::overlays::screen::create_screens_wayland(wl, app)?
|
||||
crate::overlays::screen::create_screens_wayland(wl, app)
|
||||
} else {
|
||||
keymap = get_keymap_x11()
|
||||
.map_err(|f| log::warn!("Could not load keyboard layout: {}", f))
|
||||
.map_err(|f| log::warn!("Could not load keyboard layout: {f}"))
|
||||
.ok();
|
||||
match crate::overlays::screen::create_screens_x11pw(app) {
|
||||
Ok(data) => data,
|
||||
Err(e) => {
|
||||
log::info!("Will not use PipeWire capture: {:?}", e);
|
||||
log::info!("Will not use PipeWire capture: {e:?}");
|
||||
crate::overlays::screen::create_screens_xshm(app)?
|
||||
}
|
||||
}
|
||||
@@ -127,6 +126,8 @@ where
|
||||
Ok(vec![])
|
||||
}
|
||||
#[cfg(feature = "wayland")]
|
||||
#[allow(clippy::too_many_lines, clippy::cognitive_complexity)]
|
||||
#[allow(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)]
|
||||
pub fn update(&mut self, app: &mut AppState) -> anyhow::Result<Vec<OverlayData<T>>> {
|
||||
use crate::overlays::{
|
||||
screen::{create_screen_interaction, create_screen_renderer_wl, load_pw_token_config},
|
||||
@@ -154,7 +155,7 @@ where
|
||||
if create_ran {
|
||||
continue;
|
||||
}
|
||||
let data = crate::overlays::screen::create_screens_wayland(wl, app)?;
|
||||
let data = crate::overlays::screen::create_screens_wayland(wl, app);
|
||||
create_ran = true;
|
||||
for (meta, state, backend) in data.screens {
|
||||
self.overlays.insert(
|
||||
@@ -228,7 +229,7 @@ where
|
||||
has_wlr_dmabuf,
|
||||
has_wlr_screencopy,
|
||||
pw_token_store,
|
||||
&app.session,
|
||||
&app,
|
||||
) {
|
||||
overlay.backend.set_renderer(Box::new(renderer));
|
||||
}
|
||||
@@ -317,8 +318,8 @@ where
|
||||
.any(|o| o.state.show_hide && o.state.want_visible);
|
||||
|
||||
if !any_shown {
|
||||
static ANCHOR_LOCAL: Lazy<Affine3A> =
|
||||
Lazy::new(|| Affine3A::from_translation(Vec3::NEG_Z));
|
||||
static ANCHOR_LOCAL: LazyLock<Affine3A> =
|
||||
LazyLock::new(|| Affine3A::from_translation(Vec3::NEG_Z));
|
||||
let hmd = snap_upright(app.input_state.hmd, Vec3A::Y);
|
||||
app.anchor = hmd * *ANCHOR_LOCAL;
|
||||
}
|
||||
@@ -337,7 +338,7 @@ where
|
||||
if !any_shown && *o.state.name == *WATCH_NAME {
|
||||
o.state.reset(app, true);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+88
-75
@@ -50,7 +50,7 @@ impl InputState {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pre_update(&mut self) {
|
||||
pub const fn pre_update(&mut self) {
|
||||
self.pointers[0].before = self.pointers[0].now;
|
||||
self.pointers[1].before = self.pointers[1].now;
|
||||
}
|
||||
@@ -58,50 +58,7 @@ impl InputState {
|
||||
pub fn post_update(&mut self, session: &AppSession) {
|
||||
for hand in &mut self.pointers {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
if hand.now.click != hand.before.click {
|
||||
log::debug!("Hand {}: click {}", hand.idx, hand.now.click);
|
||||
}
|
||||
if hand.now.grab != hand.before.grab {
|
||||
log::debug!("Hand {}: grab {}", hand.idx, hand.now.grab);
|
||||
}
|
||||
if hand.now.alt_click != hand.before.alt_click {
|
||||
log::debug!("Hand {}: alt_click {}", hand.idx, hand.now.alt_click);
|
||||
}
|
||||
if hand.now.show_hide != hand.before.show_hide {
|
||||
log::debug!("Hand {}: show_hide {}", hand.idx, hand.now.show_hide);
|
||||
}
|
||||
if hand.now.toggle_dashboard != hand.before.toggle_dashboard {
|
||||
log::debug!(
|
||||
"Hand {}: toggle_dashboard {}",
|
||||
hand.idx,
|
||||
hand.now.toggle_dashboard
|
||||
);
|
||||
}
|
||||
if hand.now.space_drag != hand.before.space_drag {
|
||||
log::debug!("Hand {}: space_drag {}", hand.idx, hand.now.space_drag);
|
||||
}
|
||||
if hand.now.space_rotate != hand.before.space_rotate {
|
||||
log::debug!("Hand {}: space_rotate {}", hand.idx, hand.now.space_rotate);
|
||||
}
|
||||
if hand.now.space_reset != hand.before.space_reset {
|
||||
log::debug!("Hand {}: space_reset {}", hand.idx, hand.now.space_reset);
|
||||
}
|
||||
if hand.now.click_modifier_right != hand.before.click_modifier_right {
|
||||
log::debug!(
|
||||
"Hand {}: click_modifier_right {}",
|
||||
hand.idx,
|
||||
hand.now.click_modifier_right
|
||||
);
|
||||
}
|
||||
if hand.now.click_modifier_middle != hand.before.click_modifier_middle {
|
||||
log::debug!(
|
||||
"Hand {}: click_modifier_middle {}",
|
||||
hand.idx,
|
||||
hand.now.click_modifier_middle
|
||||
);
|
||||
}
|
||||
}
|
||||
debug_print_hand(hand);
|
||||
|
||||
if hand.now.click {
|
||||
hand.last_click = Instant::now();
|
||||
@@ -118,8 +75,8 @@ impl InputState {
|
||||
}
|
||||
|
||||
let hmd_up = self.hmd.transform_vector3a(Vec3A::Y);
|
||||
let dot =
|
||||
hmd_up.dot(hand.pose.transform_vector3a(Vec3A::X)) * (1.0 - 2.0 * hand.idx as f32);
|
||||
let dot = hmd_up.dot(hand.pose.transform_vector3a(Vec3A::X))
|
||||
* 2.0f32.mul_add(-(hand.idx as f32), 1.0);
|
||||
|
||||
hand.interaction.mode = if dot < -0.85 {
|
||||
PointerMode::Right
|
||||
@@ -143,7 +100,7 @@ impl InputState {
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
|
||||
if hand.now.alt_click != hand.before.alt_click {
|
||||
// Reap previous processes
|
||||
@@ -166,6 +123,54 @@ impl InputState {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
fn debug_print_hand(hand: &Pointer) {
|
||||
{
|
||||
if hand.now.click != hand.before.click {
|
||||
log::debug!("Hand {}: click {}", hand.idx, hand.now.click);
|
||||
}
|
||||
if hand.now.grab != hand.before.grab {
|
||||
log::debug!("Hand {}: grab {}", hand.idx, hand.now.grab);
|
||||
}
|
||||
if hand.now.alt_click != hand.before.alt_click {
|
||||
log::debug!("Hand {}: alt_click {}", hand.idx, hand.now.alt_click);
|
||||
}
|
||||
if hand.now.show_hide != hand.before.show_hide {
|
||||
log::debug!("Hand {}: show_hide {}", hand.idx, hand.now.show_hide);
|
||||
}
|
||||
if hand.now.toggle_dashboard != hand.before.toggle_dashboard {
|
||||
log::debug!(
|
||||
"Hand {}: toggle_dashboard {}",
|
||||
hand.idx,
|
||||
hand.now.toggle_dashboard
|
||||
);
|
||||
}
|
||||
if hand.now.space_drag != hand.before.space_drag {
|
||||
log::debug!("Hand {}: space_drag {}", hand.idx, hand.now.space_drag);
|
||||
}
|
||||
if hand.now.space_rotate != hand.before.space_rotate {
|
||||
log::debug!("Hand {}: space_rotate {}", hand.idx, hand.now.space_rotate);
|
||||
}
|
||||
if hand.now.space_reset != hand.before.space_reset {
|
||||
log::debug!("Hand {}: space_reset {}", hand.idx, hand.now.space_reset);
|
||||
}
|
||||
if hand.now.click_modifier_right != hand.before.click_modifier_right {
|
||||
log::debug!(
|
||||
"Hand {}: click_modifier_right {}",
|
||||
hand.idx,
|
||||
hand.now.click_modifier_right
|
||||
);
|
||||
}
|
||||
if hand.now.click_modifier_middle != hand.before.click_modifier_middle {
|
||||
log::debug!(
|
||||
"Hand {}: click_modifier_middle {}",
|
||||
hand.idx,
|
||||
hand.now.click_modifier_middle
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InteractionState {
|
||||
pub mode: PointerMode,
|
||||
pub grabbed: Option<GrabData>,
|
||||
@@ -207,10 +212,10 @@ impl Pointer {
|
||||
idx,
|
||||
pose: Affine3A::IDENTITY,
|
||||
raw_pose: Affine3A::IDENTITY,
|
||||
now: Default::default(),
|
||||
before: Default::default(),
|
||||
now: PointerState::default(),
|
||||
before: PointerState::default(),
|
||||
last_click: Instant::now(),
|
||||
interaction: Default::default(),
|
||||
interaction: InteractionState::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -320,6 +325,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines, clippy::cognitive_complexity)]
|
||||
fn interact_hand<O>(
|
||||
idx: usize,
|
||||
overlays: &mut OverlayContainer<O>,
|
||||
@@ -429,12 +435,12 @@ where
|
||||
.is_some_and(|x| x.grabbed_id == hit.overlay)
|
||||
{
|
||||
let can_curve = hovered
|
||||
.frame_transform()
|
||||
.frame_meta()
|
||||
.is_some_and(|e| e.extent[0] >= e.extent[1]);
|
||||
|
||||
if can_curve {
|
||||
let cur = hovered.state.curvature.unwrap_or(0.0);
|
||||
let new = (cur - scroll_y * 0.01).min(0.5);
|
||||
let new = scroll_y.mul_add(-0.01, cur).min(0.5);
|
||||
if new <= f32::EPSILON {
|
||||
hovered.state.curvature = None;
|
||||
} else {
|
||||
@@ -480,7 +486,7 @@ impl Pointer {
|
||||
if let Some(hit) = self.ray_test(
|
||||
overlay.state.id,
|
||||
&overlay.state.transform,
|
||||
&overlay.state.curvature,
|
||||
overlay.state.curvature.as_ref(),
|
||||
) {
|
||||
if hit.dist.is_infinite() || hit.dist.is_nan() {
|
||||
continue;
|
||||
@@ -491,7 +497,7 @@ impl Pointer {
|
||||
|
||||
hits.sort_by(|a, b| a.dist.total_cmp(&b.dist));
|
||||
|
||||
for hit in hits.iter() {
|
||||
for hit in &hits {
|
||||
let overlay = overlays.get_by_id(hit.overlay).unwrap(); // safe because we just got the id from the overlay
|
||||
|
||||
let uv = overlay
|
||||
@@ -573,7 +579,7 @@ impl Pointer {
|
||||
.state
|
||||
.transform
|
||||
.matrix3
|
||||
.mul_scalar(1.0 - 0.025 * self.now.scroll_y);
|
||||
.mul_scalar(0.025f32.mul_add(-self.now.scroll_y, 1.0));
|
||||
} else if config.allow_sliding && self.now.scroll_y.is_finite() {
|
||||
grab_data.offset.z -= self.now.scroll_y * 0.05;
|
||||
}
|
||||
@@ -620,12 +626,19 @@ impl Pointer {
|
||||
&self,
|
||||
overlay: OverlayID,
|
||||
transform: &Affine3A,
|
||||
curvature: &Option<f32>,
|
||||
curvature: Option<&f32>,
|
||||
) -> Option<RayHit> {
|
||||
let (dist, local_pos) = match curvature {
|
||||
Some(curvature) => raycast_cylinder(&self.pose, Vec3A::NEG_Z, transform, *curvature),
|
||||
_ => raycast_plane(&self.pose, Vec3A::NEG_Z, transform, Vec3A::NEG_Z),
|
||||
}?;
|
||||
let (dist, local_pos) = curvature.map_or_else(
|
||||
|| {
|
||||
Some(raycast_plane(
|
||||
&self.pose,
|
||||
Vec3A::NEG_Z,
|
||||
transform,
|
||||
Vec3A::NEG_Z,
|
||||
))
|
||||
},
|
||||
|curvature| raycast_cylinder(&self.pose, Vec3A::NEG_Z, transform, *curvature),
|
||||
)?;
|
||||
|
||||
if dist < 0.0 {
|
||||
// hit is behind us
|
||||
@@ -646,7 +659,7 @@ fn raycast_plane(
|
||||
source_fwd: Vec3A,
|
||||
plane: &Affine3A,
|
||||
plane_norm: Vec3A,
|
||||
) -> Option<(f32, Vec2)> {
|
||||
) -> (f32, Vec2) {
|
||||
let plane_normal = plane.transform_vector3a(plane_norm);
|
||||
let ray_dir = source.transform_vector3a(source_fwd);
|
||||
|
||||
@@ -658,7 +671,7 @@ fn raycast_plane(
|
||||
.transform_point3a(source.translation + ray_dir * dist)
|
||||
.xy();
|
||||
|
||||
Some((dist, hit_local))
|
||||
(dist, hit_local)
|
||||
}
|
||||
|
||||
fn raycast_cylinder(
|
||||
@@ -675,27 +688,27 @@ fn raycast_cylinder(
|
||||
}
|
||||
.inverse();
|
||||
|
||||
let r = size / (2.0 * PI * curvature);
|
||||
let radius = size / (2.0 * PI * curvature);
|
||||
|
||||
let ray_dir = to_local.transform_vector3a(source.transform_vector3a(source_fwd));
|
||||
let ray_origin = to_local.transform_point3a(source.translation) + Vec3A::NEG_Z * r;
|
||||
let ray_origin = to_local.transform_point3a(source.translation) + Vec3A::NEG_Z * radius;
|
||||
|
||||
let d = ray_dir.xz();
|
||||
let s = ray_origin.xz();
|
||||
let v_dir = ray_dir.xz();
|
||||
let v_pos = ray_origin.xz();
|
||||
|
||||
let a = d.dot(d);
|
||||
let b = d.dot(s);
|
||||
let c = s.dot(s) - r * r;
|
||||
let l_dir = v_dir.dot(v_dir);
|
||||
let l_pos = v_dir.dot(v_pos);
|
||||
let c = radius.mul_add(-radius, v_pos.dot(v_pos));
|
||||
|
||||
let d = (b * b) - (a * c);
|
||||
let d = l_pos.mul_add(l_pos, -(l_dir * c));
|
||||
if d < f32::EPSILON {
|
||||
return None;
|
||||
}
|
||||
|
||||
let sqrt_d = d.sqrt();
|
||||
|
||||
let t1 = (-b - sqrt_d) / a;
|
||||
let t2 = (-b + sqrt_d) / a;
|
||||
let t1 = (-l_pos - sqrt_d) / l_dir;
|
||||
let t2 = (-l_pos + sqrt_d) / l_dir;
|
||||
|
||||
let t = t1.max(t2);
|
||||
|
||||
@@ -709,8 +722,8 @@ fn raycast_cylinder(
|
||||
return None;
|
||||
}
|
||||
|
||||
let max_angle = 2.0 * (size / (2.0 * r));
|
||||
let x_angle = (hit_local.x / r).asin();
|
||||
let max_angle = 2.0 * (size / (2.0 * radius));
|
||||
let x_angle = (hit_local.x / radius).asin();
|
||||
|
||||
hit_local.x = x_angle / max_angle;
|
||||
hit_local.y /= size;
|
||||
|
||||
@@ -57,8 +57,7 @@ impl NotificationManager {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
log::error!(
|
||||
"Failed to connect to dbus. Desktop notifications will not work. Cause: {:?}",
|
||||
e
|
||||
"Failed to connect to dbus. Desktop notifications will not work. Cause: {e:?}"
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -81,55 +80,50 @@ impl NotificationManager {
|
||||
(vec![rule.match_str()], 0u32),
|
||||
);
|
||||
|
||||
match result {
|
||||
Ok(_) => {
|
||||
let sender = self.tx_toast.clone();
|
||||
c.start_receive(
|
||||
rule,
|
||||
Box::new(move |msg, _| {
|
||||
if let Ok(toast) = parse_dbus(&msg) {
|
||||
match sender.try_send(toast) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
log::error!("Failed to send notification: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}),
|
||||
);
|
||||
log::info!("Listening to DBus notifications via BecomeMonitor.");
|
||||
}
|
||||
Err(_) => {
|
||||
let rule_with_eavesdrop = {
|
||||
let mut rule = rule.clone();
|
||||
rule.eavesdrop = true;
|
||||
rule
|
||||
};
|
||||
|
||||
let sender2 = self.tx_toast.clone();
|
||||
let result = c.add_match(rule_with_eavesdrop, move |_: (), _, msg| {
|
||||
if let Ok(toast) = parse_dbus(msg) {
|
||||
match sender2.try_send(toast) {
|
||||
Ok(_) => {}
|
||||
if matches!(result, Ok(())) {
|
||||
let sender = self.tx_toast.clone();
|
||||
c.start_receive(
|
||||
rule,
|
||||
Box::new(move |msg, _| {
|
||||
if let Ok(toast) = parse_dbus(&msg) {
|
||||
match sender.try_send(toast) {
|
||||
Ok(()) => {}
|
||||
Err(e) => {
|
||||
log::error!("Failed to send notification: {:?}", e);
|
||||
log::error!("Failed to send notification: {e:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
});
|
||||
}),
|
||||
);
|
||||
log::info!("Listening to DBus notifications via BecomeMonitor.");
|
||||
} else {
|
||||
let rule_with_eavesdrop = {
|
||||
let mut rule = rule.clone();
|
||||
rule.eavesdrop = true;
|
||||
rule
|
||||
};
|
||||
|
||||
match result {
|
||||
Ok(_) => {
|
||||
log::info!("Listening to DBus notifications via eavesdrop.");
|
||||
}
|
||||
Err(_) => {
|
||||
log::error!(
|
||||
"Failed to add DBus match. Desktop notifications will not work.",
|
||||
);
|
||||
let sender2 = self.tx_toast.clone();
|
||||
let result = c.add_match(rule_with_eavesdrop, move |(): (), _, msg| {
|
||||
if let Ok(toast) = parse_dbus(msg) {
|
||||
match sender2.try_send(toast) {
|
||||
Ok(()) => {}
|
||||
Err(e) => {
|
||||
log::error!("Failed to send notification: {e:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
});
|
||||
|
||||
match result {
|
||||
Ok(_) => {
|
||||
log::info!("Listening to DBus notifications via eavesdrop.");
|
||||
}
|
||||
Err(_) => {
|
||||
log::error!("Failed to add DBus match. Desktop notifications will not work.",);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,12 +138,12 @@ impl NotificationManager {
|
||||
let socket = match std::net::UdpSocket::bind(addr) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
log::error!("Failed to bind notification socket @ {}: {:?}", addr, e);
|
||||
log::error!("Failed to bind notification socket @ {addr}: {e:?}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
if let Err(err) = socket.set_read_timeout(Some(Duration::from_millis(200))) {
|
||||
log::error!("Failed to set read timeout: {:?}", err);
|
||||
log::error!("Failed to set read timeout: {err:?}");
|
||||
}
|
||||
|
||||
let mut buf = [0u8; 1024 * 16]; // vrcx embeds icons as b64
|
||||
@@ -159,14 +153,14 @@ impl NotificationManager {
|
||||
let json_str = match std::str::from_utf8(&buf[..num_bytes]) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
log::error!("Failed to receive notification message: {:?}", e);
|
||||
log::error!("Failed to receive notification message: {e:?}");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let msg = match serde_json::from_str::<XsoMessage>(json_str) {
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
log::error!("Failed to parse notification message: {:?}", e);
|
||||
log::error!("Failed to parse notification message: {e:?}");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
@@ -184,9 +178,9 @@ impl NotificationManager {
|
||||
.with_sound(msg.volume.unwrap_or(-1.) >= 0.); // XSOverlay still plays at 0,
|
||||
|
||||
match sender.try_send(toast) {
|
||||
Ok(_) => {}
|
||||
Ok(()) => {}
|
||||
Err(e) => {
|
||||
log::error!("Failed to send notification: {:?}", e);
|
||||
log::error!("Failed to send notification: {e:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -278,6 +272,7 @@ fn parse_dbus(msg: &dbus::Message) -> anyhow::Result<Toast> {
|
||||
// leave the audio part to the desktop env
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct XsoMessage {
|
||||
|
||||
@@ -36,7 +36,7 @@ impl arg::AppendAll for OrgFreedesktopDBusPropertiesPropertiesChanged {
|
||||
|
||||
impl arg::ReadAll for OrgFreedesktopDBusPropertiesPropertiesChanged {
|
||||
fn read(i: &mut arg::Iter) -> Result<Self, arg::TypeMismatchError> {
|
||||
Ok(OrgFreedesktopDBusPropertiesPropertiesChanged {
|
||||
Ok(Self {
|
||||
interface_name: i.read()?,
|
||||
changed_properties: i.read()?,
|
||||
invalidated_properties: i.read()?,
|
||||
@@ -158,7 +158,7 @@ impl arg::AppendAll for OrgFreedesktopNotificationsOnDndToggle {
|
||||
|
||||
impl arg::ReadAll for OrgFreedesktopNotificationsOnDndToggle {
|
||||
fn read(i: &mut arg::Iter) -> Result<Self, arg::TypeMismatchError> {
|
||||
Ok(OrgFreedesktopNotificationsOnDndToggle { dnd: i.read()? })
|
||||
Ok(Self { dnd: i.read()? })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ impl arg::AppendAll for OrgFreedesktopNotificationsNotificationClosed {
|
||||
|
||||
impl arg::ReadAll for OrgFreedesktopNotificationsNotificationClosed {
|
||||
fn read(i: &mut arg::Iter) -> Result<Self, arg::TypeMismatchError> {
|
||||
Ok(OrgFreedesktopNotificationsNotificationClosed {
|
||||
Ok(Self {
|
||||
id: i.read()?,
|
||||
reason: i.read()?,
|
||||
})
|
||||
@@ -209,7 +209,7 @@ impl arg::AppendAll for OrgFreedesktopNotificationsActionInvoked {
|
||||
|
||||
impl arg::ReadAll for OrgFreedesktopNotificationsActionInvoked {
|
||||
fn read(i: &mut arg::Iter) -> Result<Self, arg::TypeMismatchError> {
|
||||
Ok(OrgFreedesktopNotificationsActionInvoked {
|
||||
Ok(Self {
|
||||
id: i.read()?,
|
||||
action_key: i.read()?,
|
||||
})
|
||||
@@ -236,7 +236,7 @@ impl arg::AppendAll for OrgFreedesktopNotificationsNotificationReplied {
|
||||
|
||||
impl arg::ReadAll for OrgFreedesktopNotificationsNotificationReplied {
|
||||
fn read(i: &mut arg::Iter) -> Result<Self, arg::TypeMismatchError> {
|
||||
Ok(OrgFreedesktopNotificationsNotificationReplied {
|
||||
Ok(Self {
|
||||
id: i.read()?,
|
||||
text: i.read()?,
|
||||
})
|
||||
|
||||
@@ -13,7 +13,7 @@ pub trait Affine3AConvert {
|
||||
|
||||
impl Affine3AConvert for Matrix3x4 {
|
||||
fn from_affine(affine: &Affine3A) -> Self {
|
||||
Matrix3x4([
|
||||
Self([
|
||||
[
|
||||
affine.matrix3.x_axis.x,
|
||||
affine.matrix3.y_axis.x,
|
||||
@@ -47,7 +47,7 @@ impl Affine3AConvert for Matrix3x4 {
|
||||
|
||||
impl Affine3AConvert for HmdMatrix34_t {
|
||||
fn from_affine(affine: &Affine3A) -> Self {
|
||||
HmdMatrix34_t {
|
||||
Self {
|
||||
m: [
|
||||
[
|
||||
affine.matrix3.x_axis.x,
|
||||
@@ -89,22 +89,21 @@ pub(super) enum OVRError {
|
||||
|
||||
impl From<ovr_overlay::errors::EVRInputError> for OVRError {
|
||||
fn from(e: ovr_overlay::errors::EVRInputError) -> Self {
|
||||
OVRError::InputError(e.description())
|
||||
Self::InputError(e.description())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<OVRError> for BackendError {
|
||||
fn from(e: OVRError) -> Self {
|
||||
BackendError::Fatal(anyhow::Error::new(e))
|
||||
Self::Fatal(anyhow::Error::new(e))
|
||||
}
|
||||
}
|
||||
|
||||
use cstr::cstr;
|
||||
const STEAMVR_SECTION: &CStr = cstr!("steamvr");
|
||||
const STEAMVR_SECTION: &CStr = c"steamvr";
|
||||
const COLOR_GAIN_CSTR: [&CStr; 3] = [
|
||||
cstr!("hmdDisplayColorGainR"),
|
||||
cstr!("hmdDisplayColorGainG"),
|
||||
cstr!("hmdDisplayColorGainB"),
|
||||
c"hmdDisplayColorGainR",
|
||||
c"hmdDisplayColorGainG",
|
||||
c"hmdDisplayColorGainB",
|
||||
];
|
||||
|
||||
pub(super) fn adjust_gain(
|
||||
|
||||
@@ -107,7 +107,8 @@ impl OpenVrInputSource {
|
||||
haptics_hnd: haptics_hnd[i],
|
||||
});
|
||||
|
||||
Ok(OpenVrInputSource {
|
||||
Ok(Self {
|
||||
hands,
|
||||
set_hnd,
|
||||
click_hnd,
|
||||
grab_hnd,
|
||||
@@ -120,14 +121,13 @@ impl OpenVrInputSource {
|
||||
click_modifier_right_hnd,
|
||||
click_modifier_middle_hnd,
|
||||
move_mouse_hnd,
|
||||
hands,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn haptics(&mut self, input: &mut InputManager, hand: usize, haptics: &Haptics) {
|
||||
let hnd = self.hands[hand].haptics_hnd;
|
||||
let action_handle = self.hands[hand].haptics_hnd;
|
||||
let _ = input.trigger_haptic_vibration_action(
|
||||
hnd,
|
||||
action_handle,
|
||||
0.0,
|
||||
Duration::from_secs_f32(haptics.duration),
|
||||
haptics.frequency,
|
||||
@@ -277,8 +277,8 @@ impl OpenVrInputSource {
|
||||
}
|
||||
|
||||
app.input_state.devices.sort_by(|a, b| {
|
||||
(a.soc.is_none() as u8)
|
||||
.cmp(&(b.soc.is_none() as u8))
|
||||
u8::from(a.soc.is_none())
|
||||
.cmp(&u8::from(b.soc.is_none()))
|
||||
.then((a.role as u8).cmp(&(b.role as u8)))
|
||||
.then(a.soc.unwrap_or(999.).total_cmp(&b.soc.unwrap_or(999.)))
|
||||
});
|
||||
@@ -332,7 +332,7 @@ pub fn set_action_manifest(input: &mut InputManager) -> anyhow::Result<()> {
|
||||
if let Err(e) = File::create(&action_path)
|
||||
.and_then(|mut f| f.write_all(include_bytes!("../../res/actions.json")))
|
||||
{
|
||||
log::warn!("Could not write action manifest: {}", e);
|
||||
log::warn!("Could not write action manifest: {e}");
|
||||
}
|
||||
|
||||
let binding_path = config_io::get_config_root().join("actions_binding_knuckles.json");
|
||||
|
||||
+24
-21
@@ -12,9 +12,10 @@ use vulkano::image::view::ImageView;
|
||||
use vulkano::image::ImageLayout;
|
||||
|
||||
use crate::backend::overlay::{
|
||||
FrameTransform, OverlayData, OverlayRenderer, OverlayState, SplitOverlayBackend, Z_ORDER_LINES,
|
||||
FrameMeta, OverlayData, OverlayRenderer, OverlayState, ShouldRender, SplitOverlayBackend,
|
||||
Z_ORDER_LINES,
|
||||
};
|
||||
use crate::graphics::WlxGraphics;
|
||||
use crate::graphics::{CommandBuffers, WlxGraphics};
|
||||
use crate::state::AppState;
|
||||
|
||||
use super::overlay::OpenVrOverlayData;
|
||||
@@ -29,8 +30,10 @@ pub(super) struct LinePool {
|
||||
|
||||
impl LinePool {
|
||||
pub fn new(graphics: Arc<WlxGraphics>) -> anyhow::Result<Self> {
|
||||
let mut command_buffer =
|
||||
graphics.create_command_buffer(CommandBufferUsage::OneTimeSubmit)?;
|
||||
let mut command_buffer = graphics.create_uploads_command_buffer(
|
||||
graphics.transfer_queue.clone(),
|
||||
CommandBufferUsage::OneTimeSubmit,
|
||||
)?;
|
||||
|
||||
let buf = vec![255; 16];
|
||||
|
||||
@@ -47,7 +50,7 @@ impl LinePool {
|
||||
|
||||
let view = ImageView::new_default(texture)?;
|
||||
|
||||
Ok(LinePool {
|
||||
Ok(Self {
|
||||
lines: IdMap::new(),
|
||||
view,
|
||||
colors: [
|
||||
@@ -65,7 +68,7 @@ impl LinePool {
|
||||
|
||||
let mut data = OverlayData::<OpenVrOverlayData> {
|
||||
state: OverlayState {
|
||||
name: Arc::from(format!("wlx-line{}", id)),
|
||||
name: Arc::from(format!("wlx-line{id}")),
|
||||
show_hide: true,
|
||||
..Default::default()
|
||||
},
|
||||
@@ -78,6 +81,8 @@ impl LinePool {
|
||||
data: OpenVrOverlayData {
|
||||
width: 0.002,
|
||||
override_width: true,
|
||||
image_view: Some(self.view.clone()),
|
||||
image_dirty: true,
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
@@ -131,15 +136,7 @@ impl LinePool {
|
||||
data.state.transform = transform;
|
||||
data.data.color = color;
|
||||
} else {
|
||||
log::warn!("Line {} does not exist", id);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hide(&mut self, id: usize) {
|
||||
if let Some(data) = self.lines.get_mut(id) {
|
||||
data.state.want_visible = false;
|
||||
} else {
|
||||
log::warn!("Line {} does not exist", id);
|
||||
log::warn!("Line {id} does not exist");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,14 +182,20 @@ impl OverlayRenderer for StaticRenderer {
|
||||
fn resume(&mut self, _app: &mut AppState) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn render(&mut self, _app: &mut AppState) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
fn should_render(&mut self, _app: &mut AppState) -> anyhow::Result<ShouldRender> {
|
||||
Ok(ShouldRender::Unable)
|
||||
}
|
||||
fn view(&mut self) -> Option<Arc<ImageView>> {
|
||||
Some(self.view.clone())
|
||||
fn render(
|
||||
&mut self,
|
||||
_app: &mut AppState,
|
||||
_tgt: Arc<ImageView>,
|
||||
_buf: &mut CommandBuffers,
|
||||
_alpha: f32,
|
||||
) -> anyhow::Result<bool> {
|
||||
Ok(false)
|
||||
}
|
||||
fn frame_transform(&mut self) -> Option<FrameTransform> {
|
||||
Some(FrameTransform {
|
||||
fn frame_meta(&mut self) -> Option<FrameMeta> {
|
||||
Some(FrameMeta {
|
||||
extent: self.view.image().extent(),
|
||||
..Default::default()
|
||||
})
|
||||
|
||||
@@ -21,7 +21,7 @@ pub(super) fn install_manifest(app_mgr: &mut ApplicationsManager) -> anyhow::Res
|
||||
.ok_or_else(|| anyhow::anyhow!("Invalid executable path"))?,
|
||||
};
|
||||
|
||||
if let Ok(true) = app_mgr.is_application_installed(APP_KEY) {
|
||||
if app_mgr.is_application_installed(APP_KEY) == Ok(true) {
|
||||
if let Ok(mut file) = File::open(&manifest_path) {
|
||||
let mut buf = String::new();
|
||||
if file.read_to_string(&mut buf).is_ok() {
|
||||
@@ -62,15 +62,15 @@ pub(super) fn install_manifest(app_mgr: &mut ApplicationsManager) -> anyhow::Res
|
||||
manifest_path,
|
||||
e
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
if let Err(e) = app_mgr.add_application_manifest(&manifest_path, false) {
|
||||
bail!("Failed to add manifest to OpenVR: {}", e.description());
|
||||
};
|
||||
}
|
||||
|
||||
if let Err(e) = app_mgr.set_application_auto_launch(APP_KEY, true) {
|
||||
bail!("Failed to set auto launch: {}", e.description());
|
||||
};
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -78,10 +78,10 @@ pub(super) fn install_manifest(app_mgr: &mut ApplicationsManager) -> anyhow::Res
|
||||
pub(super) fn uninstall_manifest(app_mgr: &mut ApplicationsManager) -> anyhow::Result<()> {
|
||||
let manifest_path = config_io::get_config_root().join("wlx-overlay-s.vrmanifest");
|
||||
|
||||
if let Ok(true) = app_mgr.is_application_installed(APP_KEY) {
|
||||
if app_mgr.is_application_installed(APP_KEY) == Ok(true) {
|
||||
if let Err(e) = app_mgr.remove_application_manifest(&manifest_path) {
|
||||
bail!("Failed to remove manifest from OpenVR: {}", e.description());
|
||||
};
|
||||
}
|
||||
log::info!("Uninstalled manifest");
|
||||
}
|
||||
Ok(())
|
||||
|
||||
+36
-22
@@ -13,11 +13,7 @@ use ovr_overlay::{
|
||||
sys::{ETrackedDeviceProperty, EVRApplicationType, EVREventType},
|
||||
TrackedDeviceIndex,
|
||||
};
|
||||
use vulkano::{
|
||||
device::{physical::PhysicalDevice, DeviceExtensions},
|
||||
instance::InstanceExtensions,
|
||||
Handle, VulkanObject,
|
||||
};
|
||||
use vulkano::{device::physical::PhysicalDevice, Handle, VulkanObject};
|
||||
|
||||
use crate::{
|
||||
backend::{
|
||||
@@ -31,10 +27,10 @@ use crate::{
|
||||
manifest::{install_manifest, uninstall_manifest},
|
||||
overlay::OpenVrOverlayData,
|
||||
},
|
||||
overlay::OverlayData,
|
||||
overlay::{OverlayData, ShouldRender},
|
||||
task::{SystemTask, TaskType},
|
||||
},
|
||||
graphics::WlxGraphics,
|
||||
graphics::{CommandBuffers, WlxGraphics},
|
||||
overlays::{
|
||||
toast::{Toast, ToastTopic},
|
||||
watch::{watch_fade, WATCH_NAME},
|
||||
@@ -65,6 +61,7 @@ pub fn openvr_uninstall() {
|
||||
let _ = uninstall_manifest(&mut app_mgr);
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines, clippy::cognitive_complexity)]
|
||||
pub fn openvr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(), BackendError> {
|
||||
let app_type = EVRApplicationType::VRApplication_Overlay;
|
||||
let Ok(context) = ovr_overlay::Context::init(app_type) else {
|
||||
@@ -84,14 +81,13 @@ pub fn openvr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
|
||||
|
||||
let device_extensions_fn = |device: &PhysicalDevice| {
|
||||
let names = compositor_mgr.get_vulkan_device_extensions_required(device.handle().as_raw());
|
||||
let ext = DeviceExtensions::from_iter(names.iter().map(|s| s.as_str()));
|
||||
ext
|
||||
names.iter().map(std::string::String::as_str).collect()
|
||||
};
|
||||
|
||||
let mut compositor_mngr = context.compositor_mngr();
|
||||
let mut compositor_mgr = context.compositor_mngr();
|
||||
let instance_extensions = {
|
||||
let names = compositor_mngr.get_vulkan_instance_extensions_required();
|
||||
InstanceExtensions::from_iter(names.iter().map(|s| s.as_str()))
|
||||
let names = compositor_mgr.get_vulkan_instance_extensions_required();
|
||||
names.iter().map(std::string::String::as_str).collect()
|
||||
};
|
||||
|
||||
let mut state = {
|
||||
@@ -103,7 +99,7 @@ pub fn openvr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
|
||||
state.tasks.enqueue_at(
|
||||
TaskType::System(SystemTask::ShowHide),
|
||||
Instant::now().add(Duration::from_secs(1)),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if let Ok(ipd) = system_mgr.get_tracked_device_property::<f32>(
|
||||
@@ -137,7 +133,7 @@ pub fn openvr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
|
||||
)));
|
||||
};
|
||||
|
||||
log::info!("HMD running @ {} Hz", refresh_rate);
|
||||
log::info!("HMD running @ {refresh_rate} Hz");
|
||||
|
||||
let watch_id = overlays.get_by_name(WATCH_NAME).unwrap().state.id; // want panic
|
||||
|
||||
@@ -188,7 +184,7 @@ pub fn openvr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
|
||||
Toast::new(
|
||||
ToastTopic::IpdChange,
|
||||
"IPD".into(),
|
||||
format!("{:.1} mm", ipd).into(),
|
||||
format!("{ipd:.1} mm").into(),
|
||||
)
|
||||
.submit(&mut state);
|
||||
}
|
||||
@@ -209,18 +205,17 @@ pub fn openvr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
|
||||
state.tasks.retrieve_due(&mut due_tasks);
|
||||
|
||||
let mut removed_overlays = overlays.update(&mut state)?;
|
||||
for o in removed_overlays.iter_mut() {
|
||||
for o in &mut removed_overlays {
|
||||
o.destroy(&mut overlay_mgr);
|
||||
}
|
||||
|
||||
while let Some(task) = due_tasks.pop_front() {
|
||||
match task {
|
||||
TaskType::Global(f) => f(&mut state),
|
||||
TaskType::Overlay(sel, f) => {
|
||||
if let Some(o) = overlays.mut_by_selector(&sel) {
|
||||
f(&mut state, &mut o.state);
|
||||
} else {
|
||||
log::warn!("Overlay not found for task: {:?}", sel);
|
||||
log::warn!("Overlay not found for task: {sel:?}");
|
||||
}
|
||||
}
|
||||
TaskType::CreateOverlay(sel, f) => {
|
||||
@@ -316,11 +311,12 @@ pub fn openvr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
|
||||
&state.input_state.hmd,
|
||||
);
|
||||
if let Some(haptics) = haptics {
|
||||
input_source.haptics(&mut input_mgr, idx, haptics)
|
||||
input_source.haptics(&mut input_mgr, idx, haptics);
|
||||
}
|
||||
}
|
||||
|
||||
state.hid_provider.commit();
|
||||
let mut buffers = CommandBuffers::default();
|
||||
|
||||
lines.update(universe.clone(), &mut overlay_mgr, &mut state)?;
|
||||
|
||||
@@ -331,25 +327,43 @@ pub fn openvr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
|
||||
#[cfg(feature = "osc")]
|
||||
if let Some(ref mut sender) = state.osc_sender {
|
||||
let _ = sender.send_params(&overlays, &state.input_state.devices);
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "wayvr")]
|
||||
if let Err(e) =
|
||||
crate::overlays::wayvr::tick_events::<OpenVrOverlayData>(&mut state, &mut overlays)
|
||||
{
|
||||
log::error!("WayVR tick_events failed: {:?}", e);
|
||||
log::error!("WayVR tick_events failed: {e:?}");
|
||||
}
|
||||
|
||||
log::trace!("Rendering frame");
|
||||
|
||||
for o in overlays.iter_mut() {
|
||||
if o.state.want_visible {
|
||||
o.render(&mut state)?;
|
||||
let ShouldRender::Should = o.should_render(&mut state)? else {
|
||||
continue;
|
||||
};
|
||||
if !o.ensure_image_allocated(&mut state)? {
|
||||
continue;
|
||||
}
|
||||
o.data.image_dirty = o.render(
|
||||
&mut state,
|
||||
o.data.image_view.as_ref().unwrap().clone(),
|
||||
&mut buffers,
|
||||
1.0, // alpha is instead set using OVR API
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
log::trace!("Rendering overlays");
|
||||
|
||||
if let Some(mut future) = buffers.execute_now(state.graphics.graphics_queue.clone())? {
|
||||
if let Err(e) = future.flush() {
|
||||
return Err(BackendError::Fatal(e.into()));
|
||||
}
|
||||
future.cleanup_finished();
|
||||
}
|
||||
|
||||
overlays
|
||||
.iter_mut()
|
||||
.for_each(|o| o.after_render(universe.clone(), &mut overlay_mgr, &state.graphics));
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use core::f32;
|
||||
use std::sync::Arc;
|
||||
|
||||
use glam::Vec4;
|
||||
use ovr_overlay::{
|
||||
@@ -6,25 +7,21 @@ use ovr_overlay::{
|
||||
pose::Matrix3x4,
|
||||
sys::{ETrackingUniverseOrigin, VRVulkanTextureData_t},
|
||||
};
|
||||
use vulkano::{Handle, VulkanObject};
|
||||
use vulkano::{image::view::ImageView, Handle, VulkanObject};
|
||||
|
||||
use crate::{
|
||||
backend::overlay::{OverlayData, RelativeTo},
|
||||
graphics::WlxGraphics,
|
||||
state::AppState,
|
||||
};
|
||||
use crate::{backend::overlay::OverlayData, graphics::WlxGraphics, state::AppState};
|
||||
|
||||
use super::helpers::Affine3AConvert;
|
||||
|
||||
#[derive(Default)]
|
||||
pub(super) struct OpenVrOverlayData {
|
||||
pub(super) handle: Option<OverlayHandle>,
|
||||
pub(super) last_image: Option<u64>,
|
||||
pub(super) visible: bool,
|
||||
pub(super) color: Vec4,
|
||||
pub(crate) width: f32,
|
||||
pub(super) override_width: bool,
|
||||
pub(super) relative_to: RelativeTo,
|
||||
pub(super) image_view: Option<Arc<ImageView>>,
|
||||
pub(super) image_dirty: bool,
|
||||
}
|
||||
|
||||
impl OverlayData<OpenVrOverlayData> {
|
||||
@@ -38,7 +35,7 @@ impl OverlayData<OpenVrOverlayData> {
|
||||
let handle = match overlay.create_overlay(&key, &key) {
|
||||
Ok(handle) => handle,
|
||||
Err(e) => {
|
||||
panic!("Failed to create overlay: {}", e);
|
||||
panic!("Failed to create overlay: {e}");
|
||||
}
|
||||
};
|
||||
log::debug!("{}: initialize", self.state.name);
|
||||
@@ -61,6 +58,22 @@ impl OverlayData<OpenVrOverlayData> {
|
||||
Ok(handle)
|
||||
}
|
||||
|
||||
pub(super) fn ensure_image_allocated(&mut self, app: &mut AppState) -> anyhow::Result<bool> {
|
||||
if self.data.image_view.is_some() {
|
||||
return Ok(true);
|
||||
}
|
||||
let Some(meta) = self.backend.frame_meta() else {
|
||||
return Ok(false);
|
||||
};
|
||||
let image = app.graphics.render_texture(
|
||||
meta.extent[0],
|
||||
meta.extent[1],
|
||||
app.graphics.native_format,
|
||||
)?;
|
||||
self.data.image_view = Some(ImageView::new_default(image)?);
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
pub(super) fn after_input(
|
||||
&mut self,
|
||||
overlay: &mut OverlayManager,
|
||||
@@ -200,7 +213,7 @@ impl OverlayData<OpenVrOverlayData> {
|
||||
let effective = self.state.transform
|
||||
* self
|
||||
.backend
|
||||
.frame_transform()
|
||||
.frame_meta()
|
||||
.map(|f| f.transform)
|
||||
.unwrap_or_default();
|
||||
|
||||
@@ -221,21 +234,17 @@ impl OverlayData<OpenVrOverlayData> {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(view) = self.backend.view() else {
|
||||
let Some(view) = self.data.image_view.as_ref() else {
|
||||
log::debug!("{}: Not rendered", self.state.name);
|
||||
return;
|
||||
};
|
||||
|
||||
let image = view.image().clone();
|
||||
|
||||
let raw_image = image.handle().as_raw();
|
||||
|
||||
if let Some(last_image) = self.data.last_image {
|
||||
if last_image == raw_image {
|
||||
return;
|
||||
}
|
||||
if !self.data.image_dirty {
|
||||
return;
|
||||
}
|
||||
self.data.image_dirty = false;
|
||||
|
||||
let image = view.image().clone();
|
||||
let dimensions = image.extent();
|
||||
if !self.data.override_width {
|
||||
let new_width = ((dimensions[0] as f32) / (dimensions[1] as f32)).min(1.0);
|
||||
@@ -246,6 +255,7 @@ impl OverlayData<OpenVrOverlayData> {
|
||||
}
|
||||
}
|
||||
|
||||
let raw_image = image.handle().as_raw();
|
||||
let format = image.format();
|
||||
|
||||
let mut texture = VRVulkanTextureData_t {
|
||||
@@ -257,8 +267,8 @@ impl OverlayData<OpenVrOverlayData> {
|
||||
m_pDevice: graphics.device.handle().as_raw() as *mut _,
|
||||
m_pPhysicalDevice: graphics.device.physical_device().handle().as_raw() as *mut _,
|
||||
m_pInstance: graphics.instance.handle().as_raw() as *mut _,
|
||||
m_pQueue: graphics.queue.handle().as_raw() as *mut _,
|
||||
m_nQueueFamilyIndex: graphics.queue.queue_family_index(),
|
||||
m_pQueue: graphics.graphics_queue.handle().as_raw() as *mut _,
|
||||
m_nQueueFamilyIndex: graphics.graphics_queue.queue_family_index(),
|
||||
};
|
||||
log::trace!(
|
||||
"{}: UploadTex {:?}, {}x{}, {:?}",
|
||||
|
||||
@@ -25,7 +25,7 @@ pub(super) struct PlayspaceMover {
|
||||
}
|
||||
|
||||
impl PlayspaceMover {
|
||||
pub fn new() -> Self {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
universe: ETrackingUniverseOrigin::TrackingUniverseRawAndUncalibrated,
|
||||
drag: None,
|
||||
@@ -33,6 +33,7 @@ impl PlayspaceMover {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines, clippy::cognitive_complexity)]
|
||||
pub fn update(
|
||||
&mut self,
|
||||
chaperone_mgr: &mut ChaperoneSetupManager,
|
||||
@@ -54,8 +55,8 @@ impl PlayspaceMover {
|
||||
|
||||
let dq = new_hand * data.hand_pose.conjugate();
|
||||
let rel_y = f32::atan2(
|
||||
2.0 * (dq.y * dq.w + dq.x * dq.z),
|
||||
(2.0 * (dq.w * dq.w + dq.x * dq.x)) - 1.0,
|
||||
2.0 * dq.y.mul_add(dq.w, dq.x * dq.z),
|
||||
2.0f32.mul_add(dq.w.mul_add(dq.w, dq.x * dq.x), -1.0),
|
||||
);
|
||||
|
||||
let mut space_transform = Affine3A::from_rotation_y(rel_y);
|
||||
@@ -241,7 +242,7 @@ impl PlayspaceMover {
|
||||
}
|
||||
}
|
||||
|
||||
fn universe_str(universe: &ETrackingUniverseOrigin) -> &'static str {
|
||||
const fn universe_str(universe: &ETrackingUniverseOrigin) -> &'static str {
|
||||
match universe {
|
||||
ETrackingUniverseOrigin::TrackingUniverseSeated => "Seated",
|
||||
ETrackingUniverseOrigin::TrackingUniverseStanding => "Standing",
|
||||
@@ -271,31 +272,31 @@ fn set_working_copy(
|
||||
let mat = HmdMatrix34_t::from_affine(mat);
|
||||
match universe {
|
||||
ETrackingUniverseOrigin::TrackingUniverseStanding => {
|
||||
chaperone_mgr.set_working_standing_zero_pose_to_raw_tracking_pose(&mat)
|
||||
chaperone_mgr.set_working_standing_zero_pose_to_raw_tracking_pose(&mat);
|
||||
}
|
||||
_ => chaperone_mgr.set_working_seated_zero_pose_to_raw_tracking_pose(&mat),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_chaperone_offset(offset: Vec3A, chaperone_mgr: &mut ChaperoneSetupManager) {
|
||||
let mut quads = chaperone_mgr.get_live_collision_bounds_info();
|
||||
quads.iter_mut().for_each(|quad| {
|
||||
for quad in &mut quads {
|
||||
quad.vCorners.iter_mut().for_each(|corner| {
|
||||
corner.v[0] += offset.x;
|
||||
corner.v[2] += offset.z;
|
||||
});
|
||||
});
|
||||
}
|
||||
chaperone_mgr.set_working_collision_bounds_info(quads.as_mut_slice());
|
||||
}
|
||||
|
||||
fn apply_chaperone_transform(transform: Affine3A, chaperone_mgr: &mut ChaperoneSetupManager) {
|
||||
let mut quads = chaperone_mgr.get_live_collision_bounds_info();
|
||||
quads.iter_mut().for_each(|quad| {
|
||||
for quad in &mut quads {
|
||||
quad.vCorners.iter_mut().for_each(|corner| {
|
||||
let coord = transform.transform_point3a(Vec3A::from_slice(&corner.v));
|
||||
corner.v[0] = coord.x;
|
||||
corner.v[2] = coord.z;
|
||||
});
|
||||
});
|
||||
}
|
||||
chaperone_mgr.set_working_collision_bounds_info(quads.as_mut_slice());
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
use libmonado::{ClientState, Monado};
|
||||
use log::{info, warn};
|
||||
|
||||
use crate::{backend::overlay::OverlayID, state::AppState};
|
||||
|
||||
pub(super) struct InputBlocker {
|
||||
hovered_last_frame: bool,
|
||||
}
|
||||
|
||||
impl InputBlocker {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
hovered_last_frame: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, state: &AppState, watch_id: OverlayID, monado: &mut Monado) {
|
||||
if !state.session.config.block_game_input {
|
||||
return;
|
||||
}
|
||||
|
||||
let any_hovered = state.input_state.pointers.iter().any(|p| {
|
||||
p.interaction.hovered_id.is_some_and(|id| {
|
||||
id != watch_id || !state.session.config.block_game_input_ignore_watch
|
||||
})
|
||||
});
|
||||
|
||||
match (any_hovered, self.hovered_last_frame) {
|
||||
(true, false) => {
|
||||
info!("Blocking input");
|
||||
set_clients_io_active(monado, false);
|
||||
}
|
||||
(false, true) => {
|
||||
info!("Unblocking input");
|
||||
set_clients_io_active(monado, true);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
self.hovered_last_frame = any_hovered;
|
||||
}
|
||||
}
|
||||
|
||||
fn set_clients_io_active(monado: &mut Monado, active: bool) {
|
||||
match monado.clients() {
|
||||
Ok(clients) => {
|
||||
for mut client in clients {
|
||||
let name = match client.name() {
|
||||
Ok(n) => n,
|
||||
Err(e) => {
|
||||
warn!("Failed to get client name: {e}");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let state = match client.state() {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
warn!("Failed to get client state: {e}");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
if name != "wlx-overlay-s" && state.contains(ClientState::ClientSessionVisible) {
|
||||
if let Err(e) = client.set_io_active(active) {
|
||||
warn!("Failed to set io active for client: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => warn!("Failed to get clients from Monado: {e}"),
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
use anyhow::{bail, ensure};
|
||||
use glam::{Affine3A, Quat, Vec3, Vec3A};
|
||||
use openxr as xr;
|
||||
use openxr::{self as xr, SessionCreateFlags};
|
||||
use xr::OverlaySessionCreateFlagsEXTX;
|
||||
|
||||
pub(super) fn init_xr() -> Result<(xr::Instance, xr::SystemId), anyhow::Error> {
|
||||
@@ -110,7 +110,7 @@ pub(super) unsafe fn create_overlay_session(
|
||||
};
|
||||
let binding = xr::sys::GraphicsBindingVulkanKHR {
|
||||
ty: xr::sys::GraphicsBindingVulkanKHR::TYPE,
|
||||
next: &overlay as *const _ as *const _,
|
||||
next: (&raw const overlay).cast(),
|
||||
instance: info.instance,
|
||||
physical_device: info.physical_device,
|
||||
device: info.device,
|
||||
@@ -119,8 +119,8 @@ pub(super) unsafe fn create_overlay_session(
|
||||
};
|
||||
let info = xr::sys::SessionCreateInfo {
|
||||
ty: xr::sys::SessionCreateInfo::TYPE,
|
||||
next: &binding as *const _ as *const _,
|
||||
create_flags: Default::default(),
|
||||
next: (&raw const binding).cast(),
|
||||
create_flags: SessionCreateFlags::default(),
|
||||
system_id: system,
|
||||
};
|
||||
let mut out = xr::sys::Session::NULL;
|
||||
|
||||
+127
-152
@@ -17,16 +17,12 @@ use crate::{
|
||||
|
||||
use super::{helpers::posef_to_transform, XrState};
|
||||
|
||||
type XrSession = xr::Session<xr::Vulkan>;
|
||||
|
||||
static CLICK_TIMES: [Duration; 3] = [
|
||||
Duration::ZERO,
|
||||
Duration::from_millis(500),
|
||||
Duration::from_millis(750),
|
||||
];
|
||||
|
||||
pub(super) struct OpenXrAction {}
|
||||
|
||||
pub(super) struct OpenXrInputSource {
|
||||
action_set: xr::ActionSet,
|
||||
hands: [OpenXrHand; 2],
|
||||
@@ -48,7 +44,7 @@ pub struct MultiClickHandler<const COUNT: usize> {
|
||||
|
||||
impl<const COUNT: usize> MultiClickHandler<COUNT> {
|
||||
fn new(action_set: &xr::ActionSet, action_name: &str, side: &str) -> anyhow::Result<Self> {
|
||||
let name = format!("{}_{}-{}", side, COUNT, action_name);
|
||||
let name = format!("{side}_{COUNT}-{action_name}");
|
||||
let name_f32 = format!("{}_value", &name);
|
||||
|
||||
let action_bool = action_set.create_action::<bool>(&name, &name, &[])?;
|
||||
@@ -97,10 +93,10 @@ impl<const COUNT: usize> MultiClickHandler<COUNT> {
|
||||
self.held_inactive = false;
|
||||
|
||||
// reset to no prior clicks
|
||||
let long_ago = Instant::now() - Duration::from_secs(10);
|
||||
let long_ago = Instant::now().checked_sub(Duration::from_secs(10)).unwrap();
|
||||
self.previous
|
||||
.iter_mut()
|
||||
.for_each(|instant| *instant = long_ago)
|
||||
.for_each(|instant| *instant = long_ago);
|
||||
} else if COUNT > 0 {
|
||||
log::trace!("{}: rotate", self.name);
|
||||
self.previous.rotate_right(1);
|
||||
@@ -149,20 +145,20 @@ impl CustomClickAction {
|
||||
}
|
||||
|
||||
pub(super) struct OpenXrHandSource {
|
||||
action_pose: xr::Action<xr::Posef>,
|
||||
action_click: CustomClickAction,
|
||||
action_grab: CustomClickAction,
|
||||
action_alt_click: CustomClickAction,
|
||||
action_show_hide: CustomClickAction,
|
||||
action_toggle_dashboard: CustomClickAction,
|
||||
action_space_drag: CustomClickAction,
|
||||
action_space_rotate: CustomClickAction,
|
||||
action_space_reset: CustomClickAction,
|
||||
action_modifier_right: CustomClickAction,
|
||||
action_modifier_middle: CustomClickAction,
|
||||
action_move_mouse: CustomClickAction,
|
||||
action_scroll: xr::Action<Vector2f>,
|
||||
action_haptics: xr::Action<xr::Haptic>,
|
||||
pose: xr::Action<xr::Posef>,
|
||||
click: CustomClickAction,
|
||||
grab: CustomClickAction,
|
||||
alt_click: CustomClickAction,
|
||||
show_hide: CustomClickAction,
|
||||
toggle_dashboard: CustomClickAction,
|
||||
space_drag: CustomClickAction,
|
||||
space_rotate: CustomClickAction,
|
||||
space_reset: CustomClickAction,
|
||||
modifier_right: CustomClickAction,
|
||||
modifier_middle: CustomClickAction,
|
||||
move_mouse: CustomClickAction,
|
||||
scroll: xr::Action<Vector2f>,
|
||||
haptics: xr::Action<xr::Haptic>,
|
||||
}
|
||||
|
||||
impl OpenXrInputSource {
|
||||
@@ -175,7 +171,7 @@ impl OpenXrInputSource {
|
||||
let left_source = OpenXrHandSource::new(&mut action_set, "left")?;
|
||||
let right_source = OpenXrHandSource::new(&mut action_set, "right")?;
|
||||
|
||||
suggest_bindings(&xr.instance, &[&left_source, &right_source])?;
|
||||
suggest_bindings(&xr.instance, &[&left_source, &right_source]);
|
||||
|
||||
xr.session.attach_action_sets(&[&action_set])?;
|
||||
|
||||
@@ -189,9 +185,9 @@ impl OpenXrInputSource {
|
||||
}
|
||||
|
||||
pub fn haptics(&self, xr: &XrState, hand: usize, haptics: &Haptics) {
|
||||
let action = &self.hands[hand].source.action_haptics;
|
||||
let action = &self.hands[hand].source.haptics;
|
||||
|
||||
let duration_nanos = (haptics.duration as f64) * 1_000_000_000.0;
|
||||
let duration_nanos = f64::from(haptics.duration) * 1_000_000_000.0;
|
||||
|
||||
let _ = action.apply_feedback(
|
||||
&xr.session,
|
||||
@@ -251,7 +247,7 @@ impl OpenXrInputSource {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_devices(&mut self, app: &mut AppState, monado: &mut mnd::Monado) {
|
||||
pub fn update_devices(app: &mut AppState, monado: &mut mnd::Monado) {
|
||||
app.input_state.devices.clear();
|
||||
|
||||
let roles = [
|
||||
@@ -293,8 +289,8 @@ impl OpenXrInputSource {
|
||||
}
|
||||
|
||||
app.input_state.devices.sort_by(|a, b| {
|
||||
(a.soc.is_none() as u8)
|
||||
.cmp(&(b.soc.is_none() as u8))
|
||||
u8::from(a.soc.is_none())
|
||||
.cmp(&u8::from(b.soc.is_none()))
|
||||
.then((a.role as u8).cmp(&(b.role as u8)))
|
||||
.then(a.soc.unwrap_or(999.).total_cmp(&b.soc.unwrap_or(999.)))
|
||||
});
|
||||
@@ -303,11 +299,10 @@ impl OpenXrInputSource {
|
||||
|
||||
impl OpenXrHand {
|
||||
pub(super) fn new(xr: &XrState, source: OpenXrHandSource) -> Result<Self, xr::sys::Result> {
|
||||
let space = source.action_pose.create_space(
|
||||
xr.session.clone(),
|
||||
xr::Path::NULL,
|
||||
xr::Posef::IDENTITY,
|
||||
)?;
|
||||
let space =
|
||||
source
|
||||
.pose
|
||||
.create_space(xr.session.clone(), xr::Path::NULL, xr::Posef::IDENTITY)?;
|
||||
|
||||
Ok(Self { source, space })
|
||||
}
|
||||
@@ -340,19 +335,13 @@ impl OpenXrHand {
|
||||
);
|
||||
}
|
||||
|
||||
pointer.now.click = self
|
||||
.source
|
||||
.action_click
|
||||
.state(pointer.before.click, xr, session)?;
|
||||
pointer.now.click = self.source.click.state(pointer.before.click, xr, session)?;
|
||||
|
||||
pointer.now.grab = self
|
||||
.source
|
||||
.action_grab
|
||||
.state(pointer.before.grab, xr, session)?;
|
||||
pointer.now.grab = self.source.grab.state(pointer.before.grab, xr, session)?;
|
||||
|
||||
let scroll = self
|
||||
.source
|
||||
.action_scroll
|
||||
.scroll
|
||||
.state(&xr.session, xr::Path::NULL)?
|
||||
.current_state;
|
||||
|
||||
@@ -361,50 +350,47 @@ impl OpenXrHand {
|
||||
|
||||
pointer.now.alt_click =
|
||||
self.source
|
||||
.action_alt_click
|
||||
.alt_click
|
||||
.state(pointer.before.alt_click, xr, session)?;
|
||||
|
||||
pointer.now.show_hide =
|
||||
self.source
|
||||
.action_show_hide
|
||||
.show_hide
|
||||
.state(pointer.before.show_hide, xr, session)?;
|
||||
|
||||
pointer.now.click_modifier_right = self.source.action_modifier_right.state(
|
||||
pointer.before.click_modifier_right,
|
||||
xr,
|
||||
session,
|
||||
)?;
|
||||
pointer.now.click_modifier_right =
|
||||
self.source
|
||||
.modifier_right
|
||||
.state(pointer.before.click_modifier_right, xr, session)?;
|
||||
|
||||
pointer.now.toggle_dashboard = self.source.action_toggle_dashboard.state(
|
||||
pointer.before.toggle_dashboard,
|
||||
xr,
|
||||
session,
|
||||
)?;
|
||||
pointer.now.toggle_dashboard =
|
||||
self.source
|
||||
.toggle_dashboard
|
||||
.state(pointer.before.toggle_dashboard, xr, session)?;
|
||||
|
||||
pointer.now.click_modifier_middle = self.source.action_modifier_middle.state(
|
||||
pointer.before.click_modifier_middle,
|
||||
xr,
|
||||
session,
|
||||
)?;
|
||||
pointer.now.click_modifier_middle =
|
||||
self.source
|
||||
.modifier_middle
|
||||
.state(pointer.before.click_modifier_middle, xr, session)?;
|
||||
|
||||
pointer.now.move_mouse =
|
||||
self.source
|
||||
.action_move_mouse
|
||||
.move_mouse
|
||||
.state(pointer.before.move_mouse, xr, session)?;
|
||||
|
||||
pointer.now.space_drag =
|
||||
self.source
|
||||
.action_space_drag
|
||||
.space_drag
|
||||
.state(pointer.before.space_drag, xr, session)?;
|
||||
|
||||
pointer.now.space_rotate =
|
||||
self.source
|
||||
.action_space_rotate
|
||||
.space_rotate
|
||||
.state(pointer.before.space_rotate, xr, session)?;
|
||||
|
||||
pointer.now.space_reset =
|
||||
self.source
|
||||
.action_space_reset
|
||||
.space_reset
|
||||
.state(pointer.before.space_reset, xr, session)?;
|
||||
|
||||
Ok(())
|
||||
@@ -415,76 +401,66 @@ impl OpenXrHand {
|
||||
impl OpenXrHandSource {
|
||||
pub(super) fn new(action_set: &mut xr::ActionSet, side: &str) -> anyhow::Result<Self> {
|
||||
let action_pose = action_set.create_action::<xr::Posef>(
|
||||
&format!("{}_hand", side),
|
||||
&format!("{} hand pose", side),
|
||||
&format!("{side}_hand"),
|
||||
&format!("{side} hand pose"),
|
||||
&[],
|
||||
)?;
|
||||
|
||||
let action_scroll = action_set.create_action::<Vector2f>(
|
||||
&format!("{}_scroll", side),
|
||||
&format!("{} hand scroll", side),
|
||||
&format!("{side}_scroll"),
|
||||
&format!("{side} hand scroll"),
|
||||
&[],
|
||||
)?;
|
||||
let action_haptics = action_set.create_action::<xr::Haptic>(
|
||||
&format!("{}_haptics", side),
|
||||
&format!("{} hand haptics", side),
|
||||
&format!("{side}_haptics"),
|
||||
&format!("{side} hand haptics"),
|
||||
&[],
|
||||
)?;
|
||||
|
||||
Ok(Self {
|
||||
action_pose,
|
||||
action_click: CustomClickAction::new(action_set, "click", side)?,
|
||||
action_grab: CustomClickAction::new(action_set, "grab", side)?,
|
||||
action_scroll,
|
||||
action_alt_click: CustomClickAction::new(action_set, "alt_click", side)?,
|
||||
action_show_hide: CustomClickAction::new(action_set, "show_hide", side)?,
|
||||
action_toggle_dashboard: CustomClickAction::new(action_set, "toggle_dashboard", side)?,
|
||||
action_space_drag: CustomClickAction::new(action_set, "space_drag", side)?,
|
||||
action_space_rotate: CustomClickAction::new(action_set, "space_rotate", side)?,
|
||||
action_space_reset: CustomClickAction::new(action_set, "space_reset", side)?,
|
||||
action_modifier_right: CustomClickAction::new(
|
||||
action_set,
|
||||
"click_modifier_right",
|
||||
side,
|
||||
)?,
|
||||
action_modifier_middle: CustomClickAction::new(
|
||||
action_set,
|
||||
"click_modifier_middle",
|
||||
side,
|
||||
)?,
|
||||
action_move_mouse: CustomClickAction::new(action_set, "move_mouse", side)?,
|
||||
action_haptics,
|
||||
pose: action_pose,
|
||||
click: CustomClickAction::new(action_set, "click", side)?,
|
||||
grab: CustomClickAction::new(action_set, "grab", side)?,
|
||||
scroll: action_scroll,
|
||||
alt_click: CustomClickAction::new(action_set, "alt_click", side)?,
|
||||
show_hide: CustomClickAction::new(action_set, "show_hide", side)?,
|
||||
toggle_dashboard: CustomClickAction::new(action_set, "toggle_dashboard", side)?,
|
||||
space_drag: CustomClickAction::new(action_set, "space_drag", side)?,
|
||||
space_rotate: CustomClickAction::new(action_set, "space_rotate", side)?,
|
||||
space_reset: CustomClickAction::new(action_set, "space_reset", side)?,
|
||||
modifier_right: CustomClickAction::new(action_set, "click_modifier_right", side)?,
|
||||
modifier_middle: CustomClickAction::new(action_set, "click_modifier_middle", side)?,
|
||||
move_mouse: CustomClickAction::new(action_set, "move_mouse", side)?,
|
||||
haptics: action_haptics,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn to_path(maybe_path_str: &Option<String>, instance: &xr::Instance) -> Option<xr::Path> {
|
||||
maybe_path_str
|
||||
.as_ref()
|
||||
.and_then(|s| match instance.string_to_path(s) {
|
||||
Ok(path) => Some(path),
|
||||
Err(_) => {
|
||||
log::warn!("Invalid binding path: {}", s);
|
||||
None
|
||||
}
|
||||
})
|
||||
fn to_path(maybe_path_str: Option<&String>, instance: &xr::Instance) -> Option<xr::Path> {
|
||||
maybe_path_str.as_ref().and_then(|s| {
|
||||
instance
|
||||
.string_to_path(s)
|
||||
.inspect_err(|_| {
|
||||
log::warn!("Invalid binding path: {s}");
|
||||
})
|
||||
.ok()
|
||||
})
|
||||
}
|
||||
|
||||
fn is_bool(maybe_type_str: &Option<String>) -> bool {
|
||||
fn is_bool(maybe_type_str: Option<&String>) -> bool {
|
||||
maybe_type_str
|
||||
.as_ref()
|
||||
.unwrap() // want panic
|
||||
.split('/')
|
||||
.last()
|
||||
.map(|last| matches!(last, "click" | "touch"))
|
||||
.unwrap_or(false)
|
||||
.next_back()
|
||||
.is_some_and(|last| matches!(last, "click" | "touch"))
|
||||
}
|
||||
|
||||
macro_rules! add_custom {
|
||||
($action:expr, $left:expr, $right:expr, $bindings:expr, $instance:expr) => {
|
||||
if let Some(action) = $action.as_ref() {
|
||||
if let Some(p) = to_path(&action.left, $instance) {
|
||||
if is_bool(&action.left) {
|
||||
if let Some(p) = to_path(action.left.as_ref(), $instance) {
|
||||
if is_bool(action.left.as_ref()) {
|
||||
if action.triple_click.unwrap_or(false) {
|
||||
$bindings.push(xr::Binding::new(&$left.triple.action_bool, p));
|
||||
} else if action.double_click.unwrap_or(false) {
|
||||
@@ -502,8 +478,8 @@ macro_rules! add_custom {
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(p) = to_path(&action.right, $instance) {
|
||||
if is_bool(&action.right) {
|
||||
if let Some(p) = to_path(action.right.as_ref(), $instance) {
|
||||
if is_bool(action.right.as_ref()) {
|
||||
if action.triple_click.unwrap_or(false) {
|
||||
$bindings.push(xr::Binding::new(&$right.triple.action_bool, p));
|
||||
} else if action.double_click.unwrap_or(false) {
|
||||
@@ -525,8 +501,9 @@ macro_rules! add_custom {
|
||||
};
|
||||
}
|
||||
|
||||
fn suggest_bindings(instance: &xr::Instance, hands: &[&OpenXrHandSource; 2]) -> anyhow::Result<()> {
|
||||
let profiles = load_action_profiles()?;
|
||||
#[allow(clippy::too_many_lines, clippy::cognitive_complexity)]
|
||||
fn suggest_bindings(instance: &xr::Instance, hands: &[&OpenXrHandSource; 2]) {
|
||||
let profiles = load_action_profiles();
|
||||
|
||||
for profile in profiles {
|
||||
let Ok(profile_path) = instance.string_to_path(&profile.profile) else {
|
||||
@@ -537,116 +514,116 @@ fn suggest_bindings(instance: &xr::Instance, hands: &[&OpenXrHandSource; 2]) ->
|
||||
let mut bindings: Vec<xr::Binding> = vec![];
|
||||
|
||||
if let Some(action) = profile.pose {
|
||||
if let Some(p) = to_path(&action.left, instance) {
|
||||
bindings.push(xr::Binding::new(&hands[0].action_pose, p));
|
||||
if let Some(p) = to_path(action.left.as_ref(), instance) {
|
||||
bindings.push(xr::Binding::new(&hands[0].pose, p));
|
||||
}
|
||||
if let Some(p) = to_path(&action.right, instance) {
|
||||
bindings.push(xr::Binding::new(&hands[1].action_pose, p));
|
||||
if let Some(p) = to_path(action.right.as_ref(), instance) {
|
||||
bindings.push(xr::Binding::new(&hands[1].pose, p));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(action) = profile.haptic {
|
||||
if let Some(p) = to_path(&action.left, instance) {
|
||||
bindings.push(xr::Binding::new(&hands[0].action_haptics, p));
|
||||
if let Some(p) = to_path(action.left.as_ref(), instance) {
|
||||
bindings.push(xr::Binding::new(&hands[0].haptics, p));
|
||||
}
|
||||
if let Some(p) = to_path(&action.right, instance) {
|
||||
bindings.push(xr::Binding::new(&hands[1].action_haptics, p));
|
||||
if let Some(p) = to_path(action.right.as_ref(), instance) {
|
||||
bindings.push(xr::Binding::new(&hands[1].haptics, p));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(action) = profile.scroll {
|
||||
if let Some(p) = to_path(&action.left, instance) {
|
||||
bindings.push(xr::Binding::new(&hands[0].action_scroll, p));
|
||||
if let Some(p) = to_path(action.left.as_ref(), instance) {
|
||||
bindings.push(xr::Binding::new(&hands[0].scroll, p));
|
||||
}
|
||||
if let Some(p) = to_path(&action.right, instance) {
|
||||
bindings.push(xr::Binding::new(&hands[1].action_scroll, p));
|
||||
if let Some(p) = to_path(action.right.as_ref(), instance) {
|
||||
bindings.push(xr::Binding::new(&hands[1].scroll, p));
|
||||
}
|
||||
}
|
||||
|
||||
add_custom!(
|
||||
profile.click,
|
||||
hands[0].action_click,
|
||||
hands[1].action_click,
|
||||
hands[0].click,
|
||||
hands[1].click,
|
||||
bindings,
|
||||
instance
|
||||
);
|
||||
|
||||
add_custom!(
|
||||
profile.alt_click,
|
||||
&hands[0].action_alt_click,
|
||||
&hands[1].action_alt_click,
|
||||
&hands[0].alt_click,
|
||||
&hands[1].alt_click,
|
||||
bindings,
|
||||
instance
|
||||
);
|
||||
|
||||
add_custom!(
|
||||
profile.grab,
|
||||
&hands[0].action_grab,
|
||||
&hands[1].action_grab,
|
||||
&hands[0].grab,
|
||||
&hands[1].grab,
|
||||
bindings,
|
||||
instance
|
||||
);
|
||||
|
||||
add_custom!(
|
||||
profile.show_hide,
|
||||
&hands[0].action_show_hide,
|
||||
&hands[1].action_show_hide,
|
||||
&hands[0].show_hide,
|
||||
&hands[1].show_hide,
|
||||
bindings,
|
||||
instance
|
||||
);
|
||||
|
||||
add_custom!(
|
||||
profile.toggle_dashboard,
|
||||
&hands[0].action_toggle_dashboard,
|
||||
&hands[1].action_toggle_dashboard,
|
||||
&hands[0].toggle_dashboard,
|
||||
&hands[1].toggle_dashboard,
|
||||
bindings,
|
||||
instance
|
||||
);
|
||||
|
||||
add_custom!(
|
||||
profile.space_drag,
|
||||
&hands[0].action_space_drag,
|
||||
&hands[1].action_space_drag,
|
||||
&hands[0].space_drag,
|
||||
&hands[1].space_drag,
|
||||
bindings,
|
||||
instance
|
||||
);
|
||||
|
||||
add_custom!(
|
||||
profile.space_rotate,
|
||||
&hands[0].action_space_rotate,
|
||||
&hands[1].action_space_rotate,
|
||||
&hands[0].space_rotate,
|
||||
&hands[1].space_rotate,
|
||||
bindings,
|
||||
instance
|
||||
);
|
||||
|
||||
add_custom!(
|
||||
profile.space_reset,
|
||||
&hands[0].action_space_reset,
|
||||
&hands[1].action_space_reset,
|
||||
&hands[0].space_reset,
|
||||
&hands[1].space_reset,
|
||||
bindings,
|
||||
instance
|
||||
);
|
||||
|
||||
add_custom!(
|
||||
profile.click_modifier_right,
|
||||
&hands[0].action_modifier_right,
|
||||
&hands[1].action_modifier_right,
|
||||
&hands[0].modifier_right,
|
||||
&hands[1].modifier_right,
|
||||
bindings,
|
||||
instance
|
||||
);
|
||||
|
||||
add_custom!(
|
||||
profile.click_modifier_middle,
|
||||
&hands[0].action_modifier_middle,
|
||||
&hands[1].action_modifier_middle,
|
||||
&hands[0].modifier_middle,
|
||||
&hands[1].modifier_middle,
|
||||
bindings,
|
||||
instance
|
||||
);
|
||||
|
||||
add_custom!(
|
||||
profile.move_mouse,
|
||||
&hands[0].action_move_mouse,
|
||||
&hands[1].action_move_mouse,
|
||||
&hands[0].move_mouse,
|
||||
&hands[1].move_mouse,
|
||||
bindings,
|
||||
instance
|
||||
);
|
||||
@@ -659,8 +636,6 @@ fn suggest_bindings(instance: &xr::Instance, hands: &[&OpenXrHandSource; 2]) ->
|
||||
log::error!("Verify config: ~/.config/wlxoverlay/openxr_actions.json5");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@@ -693,26 +668,26 @@ struct OpenXrActionConfProfile {
|
||||
|
||||
const DEFAULT_PROFILES: &str = include_str!("openxr_actions.json5");
|
||||
|
||||
fn load_action_profiles() -> anyhow::Result<Vec<OpenXrActionConfProfile>> {
|
||||
fn load_action_profiles() -> Vec<OpenXrActionConfProfile> {
|
||||
let mut profiles: Vec<OpenXrActionConfProfile> =
|
||||
serde_json5::from_str(DEFAULT_PROFILES).unwrap(); // want panic
|
||||
|
||||
let Some(conf) = config_io::load("openxr_actions.json5") else {
|
||||
return Ok(profiles);
|
||||
return profiles;
|
||||
};
|
||||
|
||||
match serde_json5::from_str::<Vec<OpenXrActionConfProfile>>(&conf) {
|
||||
Ok(override_profiles) => {
|
||||
override_profiles.into_iter().for_each(|new| {
|
||||
for new in override_profiles {
|
||||
if let Some(i) = profiles.iter().position(|old| old.profile == new.profile) {
|
||||
profiles[i] = new;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Failed to load openxr_actions.json5: {}", e);
|
||||
log::error!("Failed to load openxr_actions.json5: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(profiles)
|
||||
profiles
|
||||
}
|
||||
+66
-50
@@ -9,55 +9,53 @@ use std::{
|
||||
},
|
||||
};
|
||||
|
||||
use vulkano::{command_buffer::CommandBufferUsage, format::Format, image::view::ImageView};
|
||||
use vulkano::command_buffer::CommandBufferUsage;
|
||||
|
||||
use crate::{
|
||||
backend::openxr::helpers,
|
||||
graphics::{WlxCommandBuffer, WlxGraphics},
|
||||
graphics::{CommandBuffers, WlxGraphics, WlxPipeline},
|
||||
};
|
||||
|
||||
use super::{
|
||||
swapchain::{create_swapchain_render_data, SwapchainOpts, SwapchainRenderData},
|
||||
swapchain::{create_swapchain, SwapchainOpts, WlxSwapchain},
|
||||
CompositionLayer, XrState,
|
||||
};
|
||||
|
||||
static LINE_AUTO_INCREMENT: AtomicUsize = AtomicUsize::new(1);
|
||||
pub(super) const LINE_WIDTH: f32 = 0.002;
|
||||
|
||||
// TODO customizable colors
|
||||
static COLORS: [[f32; 6]; 5] = {
|
||||
[
|
||||
[1., 1., 1., 1., 0., 0.],
|
||||
[0., 0.375, 0.5, 1., 0., 0.],
|
||||
[0.69, 0.188, 0., 1., 0., 0.],
|
||||
[0.375, 0., 0.5, 1., 0., 0.],
|
||||
[1., 0., 0., 1., 0., 0.],
|
||||
]
|
||||
};
|
||||
|
||||
pub(super) struct LinePool {
|
||||
lines: IdMap<usize, LineContainer>,
|
||||
colors: Vec<Arc<ImageView>>,
|
||||
pipeline: Arc<WlxPipeline>,
|
||||
}
|
||||
|
||||
impl LinePool {
|
||||
pub(super) fn new(graphics: Arc<WlxGraphics>) -> anyhow::Result<Self> {
|
||||
let mut command_buffer =
|
||||
graphics.create_command_buffer(CommandBufferUsage::OneTimeSubmit)?;
|
||||
let Ok(shaders) = graphics.shared_shaders.read() else {
|
||||
anyhow::bail!("Failed to lock shared shaders for reading");
|
||||
};
|
||||
|
||||
// TODO customizable colors
|
||||
let colors = [
|
||||
[0xff, 0xff, 0xff, 0xff],
|
||||
[0x00, 0x60, 0x80, 0xff],
|
||||
[0xB0, 0x30, 0x00, 0xff],
|
||||
[0x60, 0x00, 0x80, 0xff],
|
||||
[0xff, 0x00, 0x00, 0xff],
|
||||
];
|
||||
let pipeline = graphics.create_pipeline(
|
||||
shaders.get("vert_common").unwrap().clone(), // want panic
|
||||
shaders.get("frag_color").unwrap().clone(), // want panic
|
||||
graphics.native_format,
|
||||
None,
|
||||
)?;
|
||||
|
||||
let views: anyhow::Result<Vec<Arc<ImageView>>> = colors
|
||||
.into_iter()
|
||||
.map(
|
||||
|color| match command_buffer.texture2d_raw(1, 1, Format::R8G8B8A8_UNORM, &color) {
|
||||
Ok(tex) => ImageView::new_default(tex).map_err(|e| anyhow::anyhow!(e)),
|
||||
Err(e) => Err(e),
|
||||
},
|
||||
)
|
||||
.collect();
|
||||
|
||||
command_buffer.build_and_execute_now()?;
|
||||
|
||||
Ok(LinePool {
|
||||
Ok(Self {
|
||||
lines: IdMap::new(),
|
||||
colors: views?,
|
||||
pipeline,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -68,8 +66,7 @@ impl LinePool {
|
||||
) -> anyhow::Result<usize> {
|
||||
let id = LINE_AUTO_INCREMENT.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
let srd =
|
||||
create_swapchain_render_data(xr, graphics, [1, 1, 1], SwapchainOpts::new().srgb())?;
|
||||
let srd = create_swapchain(xr, graphics, [1, 1, 1], SwapchainOpts::new())?;
|
||||
self.lines.insert(
|
||||
id,
|
||||
LineContainer {
|
||||
@@ -92,10 +89,10 @@ impl LinePool {
|
||||
return;
|
||||
}
|
||||
|
||||
debug_assert!(color < self.colors.len());
|
||||
debug_assert!(color < COLORS.len());
|
||||
|
||||
let Some(line) = self.lines.get_mut(id) else {
|
||||
log::warn!("Line {} not found", id);
|
||||
log::warn!("Line {id} not found");
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -125,28 +122,55 @@ impl LinePool {
|
||||
let posef = helpers::transform_to_posef(&transform);
|
||||
|
||||
line.maybe_line = Some(Line {
|
||||
view: self.colors[color].clone(),
|
||||
color,
|
||||
pose: posef,
|
||||
length: len,
|
||||
});
|
||||
}
|
||||
|
||||
pub(super) fn present_xr<'a>(
|
||||
pub(super) fn render(
|
||||
&mut self,
|
||||
graphics: Arc<WlxGraphics>,
|
||||
buf: &mut CommandBuffers,
|
||||
) -> anyhow::Result<()> {
|
||||
for line in self.lines.values_mut() {
|
||||
if let Some(inner) = line.maybe_line.as_mut() {
|
||||
let tgt = line.swapchain.acquire_wait_image()?;
|
||||
|
||||
let set0 = self
|
||||
.pipeline
|
||||
.uniform_buffer(0, COLORS[inner.color].to_vec())?;
|
||||
|
||||
let pass = self
|
||||
.pipeline
|
||||
.create_pass_for_target(tgt.clone(), vec![set0])?;
|
||||
|
||||
let mut cmd_buffer =
|
||||
graphics.create_command_buffer(CommandBufferUsage::OneTimeSubmit)?;
|
||||
cmd_buffer.begin_rendering(tgt)?;
|
||||
cmd_buffer.run_ref(&pass)?;
|
||||
cmd_buffer.end_rendering()?;
|
||||
|
||||
buf.push(cmd_buffer.build()?);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn present<'a>(
|
||||
&'a mut self,
|
||||
xr: &'a XrState,
|
||||
command_buffer: &mut WlxCommandBuffer,
|
||||
) -> anyhow::Result<Vec<CompositionLayer<'a>>> {
|
||||
let mut quads = Vec::new();
|
||||
|
||||
for line in self.lines.values_mut() {
|
||||
line.swapchain.ensure_image_released()?;
|
||||
|
||||
if let Some(inner) = line.maybe_line.take() {
|
||||
let quad = xr::CompositionLayerQuad::new()
|
||||
.pose(inner.pose)
|
||||
.sub_image(line.swapchain.acquire_present_release(
|
||||
command_buffer,
|
||||
inner.view,
|
||||
1.0,
|
||||
)?)
|
||||
.sub_image(line.swapchain.get_subimage())
|
||||
.eye_visibility(xr::EyeVisibility::BOTH)
|
||||
.space(&xr.stage)
|
||||
.size(xr::Extent2Df {
|
||||
@@ -160,23 +184,15 @@ impl LinePool {
|
||||
|
||||
Ok(quads)
|
||||
}
|
||||
|
||||
/// the number of lines that are waiting to be drawn
|
||||
pub(super) fn num_pending(&self) -> usize {
|
||||
self.lines
|
||||
.values()
|
||||
.filter(|l| l.maybe_line.is_some())
|
||||
.count()
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct Line {
|
||||
pub(super) view: Arc<ImageView>,
|
||||
pub(super) color: usize,
|
||||
pub(super) pose: xr::Posef,
|
||||
pub(super) length: f32,
|
||||
}
|
||||
|
||||
struct LineContainer {
|
||||
swapchain: SwapchainRenderData,
|
||||
swapchain: WlxSwapchain,
|
||||
maybe_line: Option<Line>,
|
||||
}
|
||||
+149
-111
@@ -9,10 +9,11 @@ use std::{
|
||||
};
|
||||
|
||||
use glam::{Affine3A, Vec3};
|
||||
use input::OpenXrInputSource;
|
||||
use libmonado::Monado;
|
||||
use openxr as xr;
|
||||
use skybox::create_skybox;
|
||||
use vulkano::{command_buffer::CommandBufferUsage, Handle, VulkanObject};
|
||||
use vulkano::{Handle, VulkanObject};
|
||||
|
||||
use crate::{
|
||||
backend::{
|
||||
@@ -20,10 +21,10 @@ use crate::{
|
||||
input::interact,
|
||||
notifications::NotificationManager,
|
||||
openxr::{lines::LinePool, overlay::OpenXrOverlayData},
|
||||
overlay::OverlayData,
|
||||
overlay::{OverlayData, ShouldRender},
|
||||
task::{SystemTask, TaskType},
|
||||
},
|
||||
graphics::WlxGraphics,
|
||||
graphics::{CommandBuffers, WlxGraphics},
|
||||
overlays::{
|
||||
toast::{Toast, ToastTopic},
|
||||
watch::{watch_fade, WATCH_NAME},
|
||||
@@ -34,6 +35,7 @@ use crate::{
|
||||
#[cfg(feature = "wayvr")]
|
||||
use crate::{gui::modular::button::WayVRAction, overlays::wayvr::wayvr_action};
|
||||
|
||||
mod blocker;
|
||||
mod helpers;
|
||||
mod input;
|
||||
mod lines;
|
||||
@@ -43,30 +45,28 @@ mod skybox;
|
||||
mod swapchain;
|
||||
|
||||
const VIEW_TYPE: xr::ViewConfigurationType = xr::ViewConfigurationType::PRIMARY_STEREO;
|
||||
const VIEW_COUNT: u32 = 2;
|
||||
static FRAME_COUNTER: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
struct XrState {
|
||||
instance: xr::Instance,
|
||||
system: xr::SystemId,
|
||||
session: xr::Session<xr::Vulkan>,
|
||||
predicted_display_time: xr::Time,
|
||||
fps: f32,
|
||||
stage: Arc<xr::Space>,
|
||||
view: Arc<xr::Space>,
|
||||
stage_offset: Affine3A,
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines, clippy::cognitive_complexity)]
|
||||
pub fn openxr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(), BackendError> {
|
||||
let (xr_instance, system) = match helpers::init_xr() {
|
||||
Ok((xr_instance, system)) => (xr_instance, system),
|
||||
Err(e) => {
|
||||
log::warn!("Will not use OpenXR: {}", e);
|
||||
log::warn!("Will not use OpenXR: {e}");
|
||||
return Err(BackendError::NotSupported);
|
||||
}
|
||||
};
|
||||
|
||||
let mut app_state = {
|
||||
let mut app = {
|
||||
let graphics = WlxGraphics::new_openxr(xr_instance.clone(), system)?;
|
||||
AppState::from_graphics(graphics)?
|
||||
};
|
||||
@@ -74,24 +74,24 @@ pub fn openxr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
|
||||
let environment_blend_mode = {
|
||||
let modes = xr_instance.enumerate_environment_blend_modes(system, VIEW_TYPE)?;
|
||||
if modes.contains(&xr::EnvironmentBlendMode::ALPHA_BLEND)
|
||||
&& app_state.session.config.use_passthrough
|
||||
&& app.session.config.use_passthrough
|
||||
{
|
||||
xr::EnvironmentBlendMode::ALPHA_BLEND
|
||||
} else {
|
||||
modes[0]
|
||||
}
|
||||
};
|
||||
log::info!("Using environment blend mode: {:?}", environment_blend_mode);
|
||||
log::info!("Using environment blend mode: {environment_blend_mode:?}");
|
||||
|
||||
if show_by_default {
|
||||
app_state.tasks.enqueue_at(
|
||||
app.tasks.enqueue_at(
|
||||
TaskType::System(SystemTask::ShowHide),
|
||||
Instant::now().add(Duration::from_secs(1)),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
let mut overlays = OverlayContainer::<OpenXrOverlayData>::new(&mut app_state)?;
|
||||
let mut lines = LinePool::new(app_state.graphics.clone())?;
|
||||
let mut overlays = OverlayContainer::<OpenXrOverlayData>::new(&mut app)?;
|
||||
let mut lines = LinePool::new(app.graphics.clone())?;
|
||||
|
||||
let mut notifications = NotificationManager::new();
|
||||
notifications.run_dbus();
|
||||
@@ -100,29 +100,26 @@ pub fn openxr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
|
||||
let mut delete_queue = vec![];
|
||||
|
||||
let mut monado = Monado::auto_connect()
|
||||
.map_err(|e| log::warn!("Will not use libmonado: {}", e))
|
||||
.map_err(|e| log::warn!("Will not use libmonado: {e}"))
|
||||
.ok();
|
||||
|
||||
let mut playspace = monado.as_mut().and_then(|m| {
|
||||
playspace::PlayspaceMover::new(m)
|
||||
.map_err(|e| log::warn!("Will not use Monado playspace mover: {}", e))
|
||||
.map_err(|e| log::warn!("Will not use Monado playspace mover: {e}"))
|
||||
.ok()
|
||||
});
|
||||
|
||||
let mut blocker = monado.is_some().then(blocker::InputBlocker::new);
|
||||
|
||||
let (session, mut frame_wait, mut frame_stream) = unsafe {
|
||||
let raw_session = helpers::create_overlay_session(
|
||||
&xr_instance,
|
||||
system,
|
||||
&xr::vulkan::SessionCreateInfo {
|
||||
instance: app_state.graphics.instance.handle().as_raw() as _,
|
||||
physical_device: app_state
|
||||
.graphics
|
||||
.device
|
||||
.physical_device()
|
||||
.handle()
|
||||
.as_raw() as _,
|
||||
device: app_state.graphics.device.handle().as_raw() as _,
|
||||
queue_family_index: app_state.graphics.queue.queue_family_index(),
|
||||
instance: app.graphics.instance.handle().as_raw() as _,
|
||||
physical_device: app.graphics.device.physical_device().handle().as_raw() as _,
|
||||
device: app.graphics.device.handle().as_raw() as _,
|
||||
queue_family_index: app.graphics.graphics_queue.queue_family_index(),
|
||||
queue_index: 0,
|
||||
},
|
||||
)?;
|
||||
@@ -136,24 +133,22 @@ pub fn openxr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
|
||||
|
||||
let mut xr_state = XrState {
|
||||
instance: xr_instance,
|
||||
system,
|
||||
session,
|
||||
predicted_display_time: xr::Time::from_nanos(0),
|
||||
fps: 30.0,
|
||||
stage: Arc::new(stage),
|
||||
view: Arc::new(view),
|
||||
stage_offset: Affine3A::IDENTITY,
|
||||
};
|
||||
|
||||
let mut skybox = if environment_blend_mode == xr::EnvironmentBlendMode::OPAQUE {
|
||||
create_skybox(&xr_state, &app_state)
|
||||
create_skybox(&xr_state, &app)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let pointer_lines = [
|
||||
lines.allocate(&xr_state, app_state.graphics.clone())?,
|
||||
lines.allocate(&xr_state, app_state.graphics.clone())?,
|
||||
lines.allocate(&xr_state, app.graphics.clone())?,
|
||||
lines.allocate(&xr_state, app.graphics.clone())?,
|
||||
];
|
||||
|
||||
let watch_id = overlays.get_by_name(WATCH_NAME).unwrap().state.id; // want panic
|
||||
@@ -176,19 +171,18 @@ pub fn openxr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
|
||||
if !running.load(Ordering::Relaxed) {
|
||||
log::warn!("Received shutdown signal.");
|
||||
match xr_state.session.request_exit() {
|
||||
Ok(_) => log::info!("OpenXR session exit requested."),
|
||||
Ok(()) => log::info!("OpenXR session exit requested."),
|
||||
Err(xr::sys::Result::ERROR_SESSION_NOT_RUNNING) => break 'main_loop,
|
||||
Err(e) => {
|
||||
log::error!("Failed to request OpenXR session exit: {}", e);
|
||||
log::error!("Failed to request OpenXR session exit: {e}");
|
||||
break 'main_loop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while let Some(event) = xr_state.instance.poll_event(&mut event_storage)? {
|
||||
use xr::Event::*;
|
||||
match event {
|
||||
SessionStateChanged(e) => {
|
||||
xr::Event::SessionStateChanged(e) => {
|
||||
// Session state change is where we can begin and end sessions, as well as
|
||||
// find quit messages!
|
||||
log::info!("entered state {:?}", e.state());
|
||||
@@ -207,22 +201,22 @@ pub fn openxr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
InstanceLossPending(_) => {
|
||||
xr::Event::InstanceLossPending(_) => {
|
||||
break 'main_loop;
|
||||
}
|
||||
EventsLost(e) => {
|
||||
xr::Event::EventsLost(e) => {
|
||||
log::warn!("lost {} events", e.lost_event_count());
|
||||
}
|
||||
MainSessionVisibilityChangedEXTX(e) => {
|
||||
xr::Event::MainSessionVisibilityChangedEXTX(e) => {
|
||||
if main_session_visible != e.visible() {
|
||||
main_session_visible = e.visible();
|
||||
log::info!("Main session visible: {}", main_session_visible);
|
||||
log::info!("Main session visible: {main_session_visible}");
|
||||
if main_session_visible {
|
||||
log::debug!("Destroying skybox.");
|
||||
skybox = None;
|
||||
} else if environment_blend_mode == xr::EnvironmentBlendMode::OPAQUE {
|
||||
log::debug!("Allocating skybox.");
|
||||
skybox = create_skybox(&xr_state, &app_state);
|
||||
skybox = create_skybox(&xr_state, &app);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -232,7 +226,7 @@ pub fn openxr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
|
||||
|
||||
if next_device_update <= Instant::now() {
|
||||
if let Some(monado) = &mut monado {
|
||||
input_source.update_devices(&mut app_state, monado);
|
||||
OpenXrInputSource::update_devices(&mut app, monado);
|
||||
next_device_update = Instant::now() + Duration::from_secs(30);
|
||||
}
|
||||
}
|
||||
@@ -259,8 +253,7 @@ pub fn openxr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
|
||||
|
||||
let total_elapsed = fps_counter
|
||||
.front()
|
||||
.map(|time| time.elapsed().as_secs_f32())
|
||||
.unwrap_or(0f32);
|
||||
.map_or(0f32, |time| time.elapsed().as_secs_f32());
|
||||
|
||||
fps_counter.len() as f32 / total_elapsed
|
||||
};
|
||||
@@ -274,46 +267,54 @@ pub fn openxr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
|
||||
continue 'main_loop;
|
||||
}
|
||||
|
||||
app_state.input_state.pre_update();
|
||||
input_source.update(&xr_state, &mut app_state)?;
|
||||
app_state.input_state.post_update(&app_state.session);
|
||||
app.input_state.pre_update();
|
||||
input_source.update(&xr_state, &mut app)?;
|
||||
app.input_state.post_update(&app.session);
|
||||
|
||||
if app_state
|
||||
if let Some(ref mut blocker) = blocker {
|
||||
blocker.update(
|
||||
&app,
|
||||
watch_id,
|
||||
monado.as_mut().unwrap(), // safe
|
||||
);
|
||||
}
|
||||
|
||||
if app
|
||||
.input_state
|
||||
.pointers
|
||||
.iter()
|
||||
.any(|p| p.now.show_hide && !p.before.show_hide)
|
||||
{
|
||||
overlays.show_hide(&mut app_state);
|
||||
overlays.show_hide(&mut app);
|
||||
}
|
||||
|
||||
#[cfg(feature = "wayvr")]
|
||||
if app_state
|
||||
if app
|
||||
.input_state
|
||||
.pointers
|
||||
.iter()
|
||||
.any(|p| p.now.toggle_dashboard && !p.before.toggle_dashboard)
|
||||
{
|
||||
wayvr_action(&mut app_state, &mut overlays, &WayVRAction::ToggleDashboard);
|
||||
wayvr_action(&mut app, &mut overlays, &WayVRAction::ToggleDashboard);
|
||||
}
|
||||
|
||||
watch_fade(&mut app_state, overlays.mut_by_id(watch_id).unwrap()); // want panic
|
||||
watch_fade(&mut app, overlays.mut_by_id(watch_id).unwrap()); // want panic
|
||||
if let Some(ref mut space_mover) = playspace {
|
||||
space_mover.update(
|
||||
&mut overlays,
|
||||
&app_state,
|
||||
&app,
|
||||
monado.as_mut().unwrap(), // safe
|
||||
);
|
||||
}
|
||||
|
||||
for o in overlays.iter_mut() {
|
||||
o.after_input(&mut app_state)?;
|
||||
o.after_input(&mut app)?;
|
||||
}
|
||||
|
||||
#[cfg(feature = "osc")]
|
||||
if let Some(ref mut sender) = app_state.osc_sender {
|
||||
let _ = sender.send_params(&overlays, &app_state.input_state.devices);
|
||||
};
|
||||
if let Some(ref mut sender) = app.osc_sender {
|
||||
let _ = sender.send_params(&overlays, &app.input_state.devices);
|
||||
}
|
||||
|
||||
let (_, views) = xr_state.session.locate_views(
|
||||
VIEW_TYPE,
|
||||
@@ -322,36 +323,36 @@ pub fn openxr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
|
||||
)?;
|
||||
|
||||
let ipd = helpers::ipd_from_views(&views);
|
||||
if (app_state.input_state.ipd - ipd).abs() > 0.01 {
|
||||
log::info!("IPD changed: {} -> {}", app_state.input_state.ipd, ipd);
|
||||
app_state.input_state.ipd = ipd;
|
||||
if (app.input_state.ipd - ipd).abs() > 0.01 {
|
||||
log::info!("IPD changed: {} -> {}", app.input_state.ipd, ipd);
|
||||
app.input_state.ipd = ipd;
|
||||
Toast::new(
|
||||
ToastTopic::IpdChange,
|
||||
"IPD".into(),
|
||||
format!("{:.1} mm", ipd).into(),
|
||||
format!("{ipd:.1} mm").into(),
|
||||
)
|
||||
.submit(&mut app_state);
|
||||
.submit(&mut app);
|
||||
}
|
||||
|
||||
overlays
|
||||
.iter_mut()
|
||||
.for_each(|o| o.state.auto_movement(&mut app_state));
|
||||
.for_each(|o| o.state.auto_movement(&mut app));
|
||||
|
||||
let lengths_haptics = interact(&mut overlays, &mut app_state);
|
||||
let lengths_haptics = interact(&mut overlays, &mut app);
|
||||
for (idx, (len, haptics)) in lengths_haptics.iter().enumerate() {
|
||||
lines.draw_from(
|
||||
pointer_lines[idx],
|
||||
app_state.input_state.pointers[idx].pose,
|
||||
app.input_state.pointers[idx].pose,
|
||||
*len,
|
||||
app_state.input_state.pointers[idx].interaction.mode as usize + 1,
|
||||
&app_state.input_state.hmd,
|
||||
app.input_state.pointers[idx].interaction.mode as usize + 1,
|
||||
&app.input_state.hmd,
|
||||
);
|
||||
if let Some(haptics) = haptics {
|
||||
input_source.haptics(&xr_state, idx, haptics);
|
||||
}
|
||||
}
|
||||
|
||||
app_state.hid_provider.commit();
|
||||
app.hid_provider.commit();
|
||||
|
||||
let watch = overlays.mut_by_id(watch_id).unwrap(); // want panic
|
||||
let watch_transform = watch.state.transform;
|
||||
@@ -364,71 +365,108 @@ pub fn openxr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
|
||||
});
|
||||
}
|
||||
|
||||
let mut layers = vec![];
|
||||
let mut command_buffer = app_state
|
||||
.graphics
|
||||
.create_command_buffer(CommandBufferUsage::OneTimeSubmit)?;
|
||||
#[cfg(feature = "wayvr")]
|
||||
if let Err(e) =
|
||||
crate::overlays::wayvr::tick_events::<OpenXrOverlayData>(&mut app, &mut overlays)
|
||||
{
|
||||
log::error!("WayVR tick_events failed: {e:?}");
|
||||
}
|
||||
|
||||
// Begin rendering
|
||||
let mut buffers = CommandBuffers::default();
|
||||
|
||||
if !main_session_visible {
|
||||
if let Some(skybox) = skybox.as_mut() {
|
||||
for (idx, layer) in skybox
|
||||
.present_xr(&xr_state, app_state.input_state.hmd, &mut command_buffer)?
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
{
|
||||
layers.push((200.0 - 50.0 * (idx as f32), layer));
|
||||
}
|
||||
skybox.render(&xr_state, &app, &mut buffers)?;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "wayvr")]
|
||||
if let Err(e) =
|
||||
crate::overlays::wayvr::tick_events::<OpenXrOverlayData>(&mut app_state, &mut overlays)
|
||||
{
|
||||
log::error!("WayVR tick_events failed: {:?}", e);
|
||||
}
|
||||
|
||||
for o in overlays.iter_mut() {
|
||||
o.data.cur_visible = false;
|
||||
if !o.state.want_visible {
|
||||
continue;
|
||||
}
|
||||
|
||||
if !o.data.init {
|
||||
o.init(&mut app_state)?;
|
||||
o.init(&mut app)?;
|
||||
o.data.init = true;
|
||||
}
|
||||
|
||||
o.render(&mut app_state)?;
|
||||
let should_render = match o.should_render(&mut app)? {
|
||||
ShouldRender::Should => true,
|
||||
ShouldRender::Can => (o.data.last_alpha - o.state.alpha).abs() > f32::EPSILON,
|
||||
ShouldRender::Unable => false, //try show old image if exists
|
||||
};
|
||||
|
||||
let dist_sq = (app_state.input_state.hmd.translation - o.state.transform.translation)
|
||||
.length_squared()
|
||||
+ (100f32 - o.state.z_order as f32);
|
||||
|
||||
if !dist_sq.is_normal() {
|
||||
if should_render {
|
||||
if !o.ensure_swapchain(&app, &xr_state)? {
|
||||
continue;
|
||||
}
|
||||
let tgt = o.data.swapchain.as_mut().unwrap().acquire_wait_image()?; // want
|
||||
if !o.render(&mut app, tgt, &mut buffers, o.state.alpha)? {
|
||||
o.data.swapchain.as_mut().unwrap().ensure_image_released()?; // want
|
||||
continue;
|
||||
}
|
||||
o.data.last_alpha = o.state.alpha;
|
||||
} else if o.data.swapchain.is_none() {
|
||||
continue;
|
||||
}
|
||||
o.data.cur_visible = true;
|
||||
}
|
||||
|
||||
let maybe_layer = o.present_xr(&xr_state, &mut command_buffer)?;
|
||||
if let CompositionLayer::None = maybe_layer {
|
||||
lines.render(app.graphics.clone(), &mut buffers)?;
|
||||
|
||||
let future = buffers.execute_now(app.graphics.graphics_queue.clone())?;
|
||||
if let Some(mut future) = future {
|
||||
if let Err(e) = future.flush() {
|
||||
return Err(BackendError::Fatal(e.into()));
|
||||
}
|
||||
future.cleanup_finished();
|
||||
}
|
||||
// End rendering
|
||||
|
||||
// Layer composition
|
||||
let mut layers = vec![];
|
||||
if !main_session_visible {
|
||||
if let Some(skybox) = skybox.as_mut() {
|
||||
for (idx, layer) in skybox.present(&xr_state, &app)?.into_iter().enumerate() {
|
||||
layers.push(((idx as f32).mul_add(-50.0, 200.0), layer));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for o in overlays.iter_mut() {
|
||||
if !o.data.cur_visible {
|
||||
continue;
|
||||
}
|
||||
let dist_sq = (app.input_state.hmd.translation - o.state.transform.translation)
|
||||
.length_squared()
|
||||
+ (100f32 - o.state.z_order as f32);
|
||||
if !dist_sq.is_normal() {
|
||||
o.data.swapchain.as_mut().unwrap().ensure_image_released()?;
|
||||
continue;
|
||||
}
|
||||
let maybe_layer = o.present(&xr_state)?;
|
||||
if matches!(maybe_layer, CompositionLayer::None) {
|
||||
continue;
|
||||
}
|
||||
layers.push((dist_sq, maybe_layer));
|
||||
}
|
||||
|
||||
for maybe_layer in lines.present_xr(&xr_state, &mut command_buffer)? {
|
||||
if let CompositionLayer::None = maybe_layer {
|
||||
for maybe_layer in lines.present(&xr_state)? {
|
||||
if matches!(maybe_layer, CompositionLayer::None) {
|
||||
continue;
|
||||
}
|
||||
layers.push((0.0, maybe_layer));
|
||||
}
|
||||
// End layer composition
|
||||
|
||||
#[cfg(feature = "wayvr")]
|
||||
if let Some(wayvr) = &app_state.wayvr {
|
||||
if let Some(wayvr) = &app.wayvr {
|
||||
wayvr.borrow_mut().data.tick_finish()?;
|
||||
}
|
||||
|
||||
command_buffer.build_and_execute_now()?;
|
||||
|
||||
// Begin layer submit
|
||||
layers.sort_by(|a, b| b.0.total_cmp(&a.0));
|
||||
|
||||
let frame_ref = layers
|
||||
@@ -446,23 +484,23 @@ pub fn openxr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
|
||||
environment_blend_mode,
|
||||
&frame_ref,
|
||||
)?;
|
||||
// End layer submit
|
||||
|
||||
let removed_overlays = overlays.update(&mut app_state)?;
|
||||
let removed_overlays = overlays.update(&mut app)?;
|
||||
for o in removed_overlays {
|
||||
delete_queue.push((o, cur_frame + 5));
|
||||
}
|
||||
|
||||
notifications.submit_pending(&mut app_state);
|
||||
notifications.submit_pending(&mut app);
|
||||
|
||||
app_state.tasks.retrieve_due(&mut due_tasks);
|
||||
app.tasks.retrieve_due(&mut due_tasks);
|
||||
while let Some(task) = due_tasks.pop_front() {
|
||||
match task {
|
||||
TaskType::Global(f) => f(&mut app_state),
|
||||
TaskType::Overlay(sel, f) => {
|
||||
if let Some(o) = overlays.mut_by_selector(&sel) {
|
||||
f(&mut app_state, &mut o.state);
|
||||
f(&mut app, &mut o.state);
|
||||
} else {
|
||||
log::warn!("Overlay not found for task: {:?}", sel);
|
||||
log::warn!("Overlay not found for task: {sel:?}");
|
||||
}
|
||||
}
|
||||
TaskType::CreateOverlay(sel, f) => {
|
||||
@@ -470,14 +508,14 @@ pub fn openxr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
|
||||
continue;
|
||||
};
|
||||
|
||||
let Some((mut state, backend)) = f(&mut app_state) else {
|
||||
let Some((mut overlay_state, overlay_backend)) = f(&mut app) else {
|
||||
continue;
|
||||
};
|
||||
state.birthframe = cur_frame;
|
||||
overlay_state.birthframe = cur_frame;
|
||||
|
||||
overlays.add(OverlayData {
|
||||
state,
|
||||
backend,
|
||||
state: overlay_state,
|
||||
backend: overlay_backend,
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
@@ -496,7 +534,7 @@ pub fn openxr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
|
||||
SystemTask::FixFloor => {
|
||||
if let Some(ref mut playspace) = playspace {
|
||||
playspace.fix_floor(
|
||||
&app_state.input_state,
|
||||
&app.input_state,
|
||||
monado.as_mut().unwrap(), // safe
|
||||
);
|
||||
}
|
||||
@@ -507,13 +545,13 @@ pub fn openxr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
|
||||
}
|
||||
}
|
||||
SystemTask::ShowHide => {
|
||||
overlays.show_hide(&mut app_state);
|
||||
overlays.show_hide(&mut app);
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
#[cfg(feature = "wayvr")]
|
||||
TaskType::WayVR(action) => {
|
||||
wayvr_action(&mut app_state, &mut overlays, &action);
|
||||
wayvr_action(&mut app, &mut overlays, &action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,6 +92,10 @@
|
||||
space_drag: {
|
||||
left: "/user/hand/left/input/menu/click",
|
||||
},
|
||||
space_reset: {
|
||||
double_click: true,
|
||||
left: "/user/hand/left/input/menu/click",
|
||||
},
|
||||
click_modifier_right: {
|
||||
left: "/user/hand/left/input/y/touch",
|
||||
right: "/user/hand/right/input/b/touch"
|
||||
@@ -138,10 +142,10 @@
|
||||
left: "/user/hand/left/input/thumbstick/x",
|
||||
right: "/user/hand/right/input/thumbstick/x"
|
||||
},
|
||||
toggle_dashboard: {
|
||||
double_click: false,
|
||||
right: "/user/hand/right/input/system/click",
|
||||
},
|
||||
toggle_dashboard: {
|
||||
double_click: false,
|
||||
right: "/user/hand/right/input/system/click",
|
||||
},
|
||||
show_hide: {
|
||||
double_click: true,
|
||||
left: "/user/hand/left/input/b/click",
|
||||
@@ -151,8 +155,8 @@
|
||||
// right trackpad is alt_click
|
||||
},
|
||||
space_reset: {
|
||||
double_click: true,
|
||||
left: "/user/hand/left/input/trackpad/force",
|
||||
double_click: true,
|
||||
},
|
||||
click_modifier_right: {
|
||||
left: "/user/hand/left/input/b/touch",
|
||||
@@ -198,6 +202,10 @@
|
||||
space_drag: {
|
||||
right: "/user/hand/right/input/menu/click",
|
||||
},
|
||||
space_reset: {
|
||||
double_click: true,
|
||||
right: "/user/hand/right/input/menu/click",
|
||||
},
|
||||
haptic: {
|
||||
left: "/user/hand/left/output/haptic",
|
||||
right: "/user/hand/right/output/haptic"
|
||||
@@ -237,6 +245,10 @@
|
||||
space_drag: {
|
||||
right: "/user/hand/right/input/menu/click",
|
||||
},
|
||||
space_reset: {
|
||||
double_click: true,
|
||||
right: "/user/hand/right/input/menu/click",
|
||||
},
|
||||
},
|
||||
|
||||
// HP Reverb G2 controller
|
||||
@@ -272,6 +284,10 @@
|
||||
space_drag: {
|
||||
right: "/user/hand/right/input/menu/click",
|
||||
},
|
||||
space_reset: {
|
||||
double_click: true,
|
||||
right: "/user/hand/right/input/menu/click",
|
||||
},
|
||||
},
|
||||
|
||||
]
|
||||
@@ -1,74 +1,72 @@
|
||||
use glam::Vec3A;
|
||||
use openxr::{self as xr, CompositionLayerFlags};
|
||||
use std::{f32::consts::PI, sync::Arc};
|
||||
use std::f32::consts::PI;
|
||||
use xr::EyeVisibility;
|
||||
|
||||
use super::{helpers, swapchain::SwapchainRenderData, CompositionLayer, XrState};
|
||||
use super::{helpers, swapchain::WlxSwapchain, CompositionLayer, XrState};
|
||||
use crate::{
|
||||
backend::{
|
||||
openxr::swapchain::{create_swapchain_render_data, SwapchainOpts},
|
||||
openxr::swapchain::{create_swapchain, SwapchainOpts},
|
||||
overlay::OverlayData,
|
||||
},
|
||||
graphics::WlxCommandBuffer,
|
||||
state::AppState,
|
||||
};
|
||||
use vulkano::image::view::ImageView;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct OpenXrOverlayData {
|
||||
last_view: Option<Arc<ImageView>>,
|
||||
last_visible: bool,
|
||||
pub(super) swapchain: Option<SwapchainRenderData>,
|
||||
pub(super) swapchain: Option<WlxSwapchain>,
|
||||
pub(super) init: bool,
|
||||
pub(super) cur_visible: bool,
|
||||
pub(super) last_alpha: f32,
|
||||
}
|
||||
|
||||
impl OverlayData<OpenXrOverlayData> {
|
||||
pub(super) fn present_xr<'a>(
|
||||
pub(super) fn ensure_swapchain<'a>(
|
||||
&'a mut self,
|
||||
app: &AppState,
|
||||
xr: &'a XrState,
|
||||
command_buffer: &mut WlxCommandBuffer,
|
||||
) -> anyhow::Result<CompositionLayer<'a>> {
|
||||
if let Some(new_view) = self.view() {
|
||||
self.data.last_view = Some(new_view);
|
||||
) -> anyhow::Result<bool> {
|
||||
if self.data.swapchain.is_some() {
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
let my_view = if let Some(view) = self.data.last_view.as_ref() {
|
||||
view.clone()
|
||||
} else {
|
||||
log::warn!("{}: Will not show - image not ready", self.state.name);
|
||||
let Some(meta) = self.frame_meta() else {
|
||||
log::warn!(
|
||||
"{}: swapchain cannot be created due to missing metadata",
|
||||
self.state.name
|
||||
);
|
||||
return Ok(false);
|
||||
};
|
||||
|
||||
let extent = meta.extent;
|
||||
self.data.swapchain = Some(create_swapchain(
|
||||
xr,
|
||||
app.graphics.clone(),
|
||||
extent,
|
||||
SwapchainOpts::new(),
|
||||
)?);
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
pub(super) fn present<'a>(
|
||||
&'a mut self,
|
||||
xr: &'a XrState,
|
||||
) -> anyhow::Result<CompositionLayer<'a>> {
|
||||
let Some(swapchain) = self.data.swapchain.as_mut() else {
|
||||
log::warn!("{}: swapchain not ready", self.state.name);
|
||||
return Ok(CompositionLayer::None);
|
||||
};
|
||||
if !swapchain.ever_acquired {
|
||||
log::warn!("{}: swapchain not rendered", self.state.name);
|
||||
return Ok(CompositionLayer::None);
|
||||
}
|
||||
swapchain.ensure_image_released()?;
|
||||
|
||||
let frame_transform = self.frame_transform().unwrap(); // want panic
|
||||
let extent = frame_transform.extent;
|
||||
let sub_image = swapchain.get_subimage();
|
||||
let transform = self.state.transform * self.backend.frame_meta().unwrap().transform; // contract
|
||||
|
||||
let data = match self.data.swapchain {
|
||||
Some(ref mut data) => data,
|
||||
None => {
|
||||
let srd = create_swapchain_render_data(
|
||||
xr,
|
||||
command_buffer.graphics.clone(),
|
||||
extent,
|
||||
SwapchainOpts::new(),
|
||||
)?;
|
||||
log::debug!(
|
||||
"{}: Created swapchain {}x{}, {} images, {} MB",
|
||||
self.state.name,
|
||||
extent[0],
|
||||
extent[1],
|
||||
srd.images.len(),
|
||||
extent[0] * extent[1] * 4 * srd.images.len() as u32 / 1024 / 1024
|
||||
);
|
||||
self.data.swapchain = Some(srd);
|
||||
self.data.swapchain.as_mut().unwrap() //safe
|
||||
}
|
||||
};
|
||||
|
||||
let sub_image = data.acquire_present_release(command_buffer, my_view, self.state.alpha)?;
|
||||
|
||||
let transform = self.state.transform * frame_transform.transform;
|
||||
|
||||
let aspect_ratio = extent[1] as f32 / extent[0] as f32;
|
||||
let aspect_ratio = swapchain.extent[1] as f32 / swapchain.extent[0] as f32;
|
||||
let (scale_x, scale_y) = if aspect_ratio < 1.0 {
|
||||
let major = transform.matrix3.col(0).length();
|
||||
(major, major * aspect_ratio)
|
||||
|
||||
@@ -8,13 +8,6 @@ use crate::{
|
||||
|
||||
use super::overlay::OpenXrOverlayData;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default, Debug)]
|
||||
struct XrtPose {
|
||||
orientation: [f32; 4],
|
||||
position: [f32; 3],
|
||||
}
|
||||
|
||||
struct MoverData<T> {
|
||||
pose: Affine3A,
|
||||
hand: usize,
|
||||
@@ -55,7 +48,7 @@ impl PlayspaceMover {
|
||||
state: &AppState,
|
||||
monado: &mut Monado,
|
||||
) {
|
||||
for pointer in state.input_state.pointers.iter() {
|
||||
for pointer in &state.input_state.pointers {
|
||||
if pointer.now.space_reset {
|
||||
if !pointer.before.space_reset {
|
||||
log::info!("Space reset");
|
||||
@@ -81,8 +74,8 @@ impl PlayspaceMover {
|
||||
Affine3A::from_quat(dq)
|
||||
} else {
|
||||
let rel_y = f32::atan2(
|
||||
2.0 * (dq.y * dq.w + dq.x * dq.z),
|
||||
(2.0 * (dq.w * dq.w + dq.x * dq.x)) - 1.0,
|
||||
2.0 * dq.y.mul_add(dq.w, dq.x * dq.z),
|
||||
2.0f32.mul_add(dq.w.mul_add(dq.w, dq.x * dq.x), -1.0),
|
||||
);
|
||||
|
||||
Affine3A::from_rotation_y(rel_y)
|
||||
@@ -96,7 +89,7 @@ impl PlayspaceMover {
|
||||
data.pose *= space_transform;
|
||||
data.hand_pose = new_hand;
|
||||
|
||||
self.apply_offset(data.pose, monado);
|
||||
apply_offset(data.pose, monado);
|
||||
self.rotate = Some(data);
|
||||
} else {
|
||||
for (i, pointer) in state.input_state.pointers.iter().enumerate() {
|
||||
@@ -145,7 +138,7 @@ impl PlayspaceMover {
|
||||
data.pose.translation += relative_pos;
|
||||
data.hand_pose = new_hand;
|
||||
|
||||
self.apply_offset(data.pose, monado);
|
||||
apply_offset(data.pose, monado);
|
||||
self.drag = Some(data);
|
||||
} else {
|
||||
for (i, pointer) in state.input_state.pointers.iter().enumerate() {
|
||||
@@ -176,7 +169,7 @@ impl PlayspaceMover {
|
||||
}
|
||||
|
||||
self.last_transform = Affine3A::IDENTITY;
|
||||
self.apply_offset(self.last_transform, monado);
|
||||
apply_offset(self.last_transform, monado);
|
||||
}
|
||||
|
||||
pub fn fix_floor(&mut self, input: &InputState, monado: &mut Monado) {
|
||||
@@ -193,14 +186,14 @@ impl PlayspaceMover {
|
||||
let y2 = input.pointers[1].raw_pose.translation.y;
|
||||
let delta = y1.min(y2) - 0.03;
|
||||
self.last_transform.translation.y += delta;
|
||||
self.apply_offset(self.last_transform, monado);
|
||||
}
|
||||
|
||||
fn apply_offset(&self, transform: Affine3A, monado: &mut Monado) {
|
||||
let pose = Pose {
|
||||
position: transform.translation.into(),
|
||||
orientation: Quat::from_affine3(&transform).into(),
|
||||
};
|
||||
let _ = monado.set_reference_space_offset(ReferenceSpaceType::Stage, pose);
|
||||
apply_offset(self.last_transform, monado);
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_offset(transform: Affine3A, monado: &mut Monado) {
|
||||
let pose = Pose {
|
||||
position: transform.translation.into(),
|
||||
orientation: Quat::from_affine3(&transform).into(),
|
||||
};
|
||||
let _ = monado.set_reference_space_offset(ReferenceSpaceType::Stage, pose);
|
||||
}
|
||||
+146
-68
@@ -1,32 +1,40 @@
|
||||
use std::{f32::consts::PI, fs::File, sync::Arc};
|
||||
use std::{
|
||||
f32::consts::PI,
|
||||
fs::File,
|
||||
sync::{Arc, LazyLock},
|
||||
};
|
||||
|
||||
use glam::{Affine3A, Quat, Vec3A};
|
||||
use once_cell::sync::Lazy;
|
||||
use glam::{Quat, Vec3A};
|
||||
use openxr::{self as xr, CompositionLayerFlags};
|
||||
use vulkano::{command_buffer::CommandBufferUsage, image::view::ImageView};
|
||||
use vulkano::{
|
||||
command_buffer::CommandBufferUsage, image::view::ImageView,
|
||||
pipeline::graphics::color_blend::AttachmentBlend,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
backend::openxr::{helpers::translation_rotation_to_posef, swapchain::SwapchainOpts},
|
||||
config_io,
|
||||
graphics::{dds::WlxCommandBufferDds, format_is_srgb, WlxCommandBuffer},
|
||||
graphics::{dds::WlxCommandBufferDds, CommandBuffers},
|
||||
state::AppState,
|
||||
};
|
||||
|
||||
use super::{
|
||||
swapchain::{create_swapchain_render_data, SwapchainRenderData},
|
||||
swapchain::{create_swapchain, WlxSwapchain},
|
||||
CompositionLayer, XrState,
|
||||
};
|
||||
|
||||
pub(super) struct Skybox {
|
||||
view: Arc<ImageView>,
|
||||
srd: Option<(SwapchainRenderData, SwapchainRenderData)>,
|
||||
sky: Option<WlxSwapchain>,
|
||||
grid: Option<WlxSwapchain>,
|
||||
}
|
||||
|
||||
impl Skybox {
|
||||
pub fn new(app: &AppState) -> anyhow::Result<Self> {
|
||||
let mut command_buffer = app
|
||||
.graphics
|
||||
.create_command_buffer(CommandBufferUsage::OneTimeSubmit)?;
|
||||
let mut command_buffer = app.graphics.create_uploads_command_buffer(
|
||||
app.graphics.transfer_queue.clone(),
|
||||
CommandBufferUsage::OneTimeSubmit,
|
||||
)?;
|
||||
|
||||
let mut maybe_image = None;
|
||||
|
||||
@@ -52,7 +60,7 @@ impl Skybox {
|
||||
"Could not use custom skybox texture at: {}",
|
||||
app.session.config.skybox_texture
|
||||
);
|
||||
log::warn!("{:?}", e);
|
||||
log::warn!("{e:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,80 +74,149 @@ impl Skybox {
|
||||
|
||||
let view = ImageView::new_default(maybe_image.unwrap())?; // safe unwrap
|
||||
|
||||
Ok(Self { view, srd: None })
|
||||
Ok(Self {
|
||||
view,
|
||||
sky: None,
|
||||
grid: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn present_xr<'a>(
|
||||
fn prepare_sky<'a>(
|
||||
&'a mut self,
|
||||
xr: &'a XrState,
|
||||
hmd: Affine3A,
|
||||
command_buffer: &mut WlxCommandBuffer,
|
||||
app: &AppState,
|
||||
buf: &mut CommandBuffers,
|
||||
) -> anyhow::Result<()> {
|
||||
if self.sky.is_some() {
|
||||
return Ok(());
|
||||
}
|
||||
let opts = SwapchainOpts::new().immutable();
|
||||
|
||||
let Ok(shaders) = app.graphics.shared_shaders.read() else {
|
||||
anyhow::bail!("Failed to lock shared shaders for reading");
|
||||
};
|
||||
|
||||
let extent = self.view.image().extent();
|
||||
let mut swapchain = create_swapchain(xr, app.graphics.clone(), extent, opts)?;
|
||||
let tgt = swapchain.acquire_wait_image()?;
|
||||
let pipeline = app.graphics.create_pipeline(
|
||||
shaders.get("vert_common").unwrap().clone(), // want panic
|
||||
shaders.get("frag_srgb").unwrap().clone(), // want panic
|
||||
app.graphics.native_format,
|
||||
None,
|
||||
)?;
|
||||
|
||||
let set0 =
|
||||
pipeline.uniform_sampler(0, self.view.clone(), app.graphics.texture_filtering)?;
|
||||
|
||||
let set1 = pipeline.uniform_buffer(1, vec![1f32])?;
|
||||
|
||||
let pass = pipeline.create_pass_for_target(tgt.clone(), vec![set0, set1])?;
|
||||
|
||||
let mut cmd_buffer = app
|
||||
.graphics
|
||||
.create_command_buffer(CommandBufferUsage::OneTimeSubmit)?;
|
||||
cmd_buffer.begin_rendering(tgt)?;
|
||||
cmd_buffer.run_ref(&pass)?;
|
||||
cmd_buffer.end_rendering()?;
|
||||
|
||||
buf.push(cmd_buffer.build()?);
|
||||
|
||||
self.sky = Some(swapchain);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn prepare_grid<'a>(
|
||||
&'a mut self,
|
||||
xr: &'a XrState,
|
||||
app: &AppState,
|
||||
buf: &mut CommandBuffers,
|
||||
) -> anyhow::Result<()> {
|
||||
if self.grid.is_some() {
|
||||
return Ok(());
|
||||
}
|
||||
let Ok(shaders) = app.graphics.shared_shaders.read() else {
|
||||
anyhow::bail!("Failed to lock shared shaders for reading");
|
||||
};
|
||||
|
||||
let extent = [1024, 1024, 1];
|
||||
let mut swapchain = create_swapchain(
|
||||
xr,
|
||||
app.graphics.clone(),
|
||||
extent,
|
||||
SwapchainOpts::new().immutable(),
|
||||
)?;
|
||||
let pipeline = app.graphics.create_pipeline(
|
||||
shaders.get("vert_common").unwrap().clone(), // want panic
|
||||
shaders.get("frag_grid").unwrap().clone(), // want panic
|
||||
app.graphics.native_format,
|
||||
Some(AttachmentBlend::alpha()),
|
||||
)?;
|
||||
|
||||
let tgt = swapchain.acquire_wait_image()?;
|
||||
let pass = pipeline.create_pass_for_target(tgt.clone(), vec![])?;
|
||||
|
||||
let mut cmd_buffer = app
|
||||
.graphics
|
||||
.create_command_buffer(CommandBufferUsage::OneTimeSubmit)?;
|
||||
cmd_buffer.begin_rendering(tgt)?;
|
||||
cmd_buffer.run_ref(&pass)?;
|
||||
cmd_buffer.end_rendering()?;
|
||||
|
||||
buf.push(cmd_buffer.build()?);
|
||||
|
||||
self.grid = Some(swapchain);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn render(
|
||||
&mut self,
|
||||
xr: &XrState,
|
||||
app: &AppState,
|
||||
buf: &mut CommandBuffers,
|
||||
) -> anyhow::Result<()> {
|
||||
self.prepare_sky(xr, app, buf)?;
|
||||
self.prepare_grid(xr, app, buf)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn present<'a>(
|
||||
&'a mut self,
|
||||
xr: &'a XrState,
|
||||
app: &AppState,
|
||||
) -> anyhow::Result<Vec<CompositionLayer<'a>>> {
|
||||
let (sky_image, grid_image) = if let Some((ref mut srd_sky, ref mut srd_grid)) = self.srd {
|
||||
(srd_sky.present_last()?, srd_grid.present_last()?)
|
||||
} else {
|
||||
log::debug!("Render skybox.");
|
||||
|
||||
let mut opts = SwapchainOpts::new().immutable();
|
||||
opts.srgb = format_is_srgb(self.view.image().format());
|
||||
|
||||
let srd_sky = create_swapchain_render_data(
|
||||
xr,
|
||||
command_buffer.graphics.clone(),
|
||||
self.view.image().extent(),
|
||||
opts,
|
||||
)?;
|
||||
|
||||
let srd_grid = create_swapchain_render_data(
|
||||
xr,
|
||||
command_buffer.graphics.clone(),
|
||||
[1024, 1024, 1],
|
||||
SwapchainOpts::new().immutable().grid(),
|
||||
)?;
|
||||
|
||||
self.srd = Some((srd_sky, srd_grid));
|
||||
|
||||
let (srd_sky, srd_grid) = self.srd.as_mut().unwrap(); // safe unwrap
|
||||
|
||||
(
|
||||
srd_sky.acquire_present_release(command_buffer, self.view.clone(), 1.0)?,
|
||||
srd_grid.acquire_compute_release(command_buffer)?,
|
||||
)
|
||||
};
|
||||
|
||||
let pose = xr::Posef {
|
||||
orientation: xr::Quaternionf::IDENTITY,
|
||||
position: xr::Vector3f {
|
||||
x: hmd.translation.x,
|
||||
y: hmd.translation.y,
|
||||
z: hmd.translation.z,
|
||||
},
|
||||
};
|
||||
|
||||
// cover the entire sphere
|
||||
const HORIZ_ANGLE: f32 = 2.0 * PI;
|
||||
const HI_VERT_ANGLE: f32 = 0.5 * PI;
|
||||
const LO_VERT_ANGLE: f32 = -0.5 * PI;
|
||||
|
||||
let mut layers = vec![];
|
||||
static GRID_POSE: LazyLock<xr::Posef> = LazyLock::new(|| {
|
||||
translation_rotation_to_posef(Vec3A::ZERO, Quat::from_rotation_x(PI * -0.5))
|
||||
});
|
||||
|
||||
let pose = xr::Posef {
|
||||
orientation: xr::Quaternionf::IDENTITY,
|
||||
position: xr::Vector3f {
|
||||
x: app.input_state.hmd.translation.x,
|
||||
y: app.input_state.hmd.translation.y,
|
||||
z: app.input_state.hmd.translation.z,
|
||||
},
|
||||
};
|
||||
|
||||
self.sky.as_mut().unwrap().ensure_image_released()?;
|
||||
|
||||
let sky = xr::CompositionLayerEquirect2KHR::new()
|
||||
.layer_flags(CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA)
|
||||
.pose(pose)
|
||||
.radius(10.0)
|
||||
.sub_image(sky_image)
|
||||
.sub_image(self.sky.as_ref().unwrap().get_subimage())
|
||||
.eye_visibility(xr::EyeVisibility::BOTH)
|
||||
.space(&xr.stage)
|
||||
.central_horizontal_angle(HORIZ_ANGLE)
|
||||
.upper_vertical_angle(HI_VERT_ANGLE)
|
||||
.lower_vertical_angle(LO_VERT_ANGLE);
|
||||
|
||||
layers.push(CompositionLayer::Equirect2(sky));
|
||||
|
||||
static GRID_POSE: Lazy<xr::Posef> = Lazy::new(|| {
|
||||
translation_rotation_to_posef(Vec3A::ZERO, Quat::from_rotation_x(PI * -0.5))
|
||||
});
|
||||
|
||||
self.grid.as_mut().unwrap().ensure_image_released()?;
|
||||
let grid = xr::CompositionLayerQuad::new()
|
||||
.layer_flags(CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA)
|
||||
.pose(*GRID_POSE)
|
||||
@@ -147,13 +224,14 @@ impl Skybox {
|
||||
width: 10.0,
|
||||
height: 10.0,
|
||||
})
|
||||
.sub_image(grid_image)
|
||||
.sub_image(self.grid.as_ref().unwrap().get_subimage())
|
||||
.eye_visibility(xr::EyeVisibility::BOTH)
|
||||
.space(&xr.stage);
|
||||
|
||||
layers.push(CompositionLayer::Quad(grid));
|
||||
|
||||
Ok(layers)
|
||||
Ok(vec![
|
||||
CompositionLayer::Equirect2(sky),
|
||||
CompositionLayer::Quad(grid),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+30
-132
@@ -1,52 +1,39 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::bail;
|
||||
use ash::vk;
|
||||
use openxr as xr;
|
||||
|
||||
use smallvec::SmallVec;
|
||||
use vulkano::{
|
||||
format::Format,
|
||||
image::{sys::RawImage, view::ImageView, ImageCreateInfo, ImageUsage},
|
||||
pipeline::graphics::color_blend::AttachmentBlend,
|
||||
Handle,
|
||||
};
|
||||
|
||||
use crate::graphics::{WlxCommandBuffer, WlxGraphics, WlxPipeline, WlxPipelineDynamic};
|
||||
use crate::graphics::WlxGraphics;
|
||||
|
||||
use super::XrState;
|
||||
|
||||
#[derive(Default)]
|
||||
pub(super) struct SwapchainOpts {
|
||||
pub immutable: bool,
|
||||
pub srgb: bool,
|
||||
pub grid: bool,
|
||||
}
|
||||
|
||||
impl SwapchainOpts {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
Self::default()
|
||||
}
|
||||
pub fn immutable(mut self) -> Self {
|
||||
pub const fn immutable(mut self) -> Self {
|
||||
self.immutable = true;
|
||||
self
|
||||
}
|
||||
pub fn srgb(mut self) -> Self {
|
||||
self.srgb = true;
|
||||
self
|
||||
}
|
||||
pub fn grid(mut self) -> Self {
|
||||
self.grid = true;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn create_swapchain_render_data(
|
||||
pub(super) fn create_swapchain(
|
||||
xr: &XrState,
|
||||
graphics: Arc<WlxGraphics>,
|
||||
extent: [u32; 3],
|
||||
opts: SwapchainOpts,
|
||||
) -> anyhow::Result<SwapchainRenderData> {
|
||||
) -> anyhow::Result<WlxSwapchain> {
|
||||
let create_flags = if opts.immutable {
|
||||
xr::SwapchainCreateFlags::STATIC_IMAGE
|
||||
} else {
|
||||
@@ -56,7 +43,7 @@ pub(super) fn create_swapchain_render_data(
|
||||
let swapchain = xr.session.create_swapchain(&xr::SwapchainCreateInfo {
|
||||
create_flags,
|
||||
usage_flags: xr::SwapchainUsageFlags::COLOR_ATTACHMENT | xr::SwapchainUsageFlags::SAMPLED,
|
||||
format: Format::R8G8B8A8_SRGB as _,
|
||||
format: graphics.native_format as _,
|
||||
sample_count: 1,
|
||||
width: extent[0],
|
||||
height: extent[1],
|
||||
@@ -65,28 +52,6 @@ pub(super) fn create_swapchain_render_data(
|
||||
mip_count: 1,
|
||||
})?;
|
||||
|
||||
let Ok(shaders) = graphics.shared_shaders.read() else {
|
||||
bail!("Failed to lock shared shaders for reading");
|
||||
};
|
||||
|
||||
let image_fmt = if opts.srgb {
|
||||
Format::R8G8B8A8_SRGB
|
||||
} else {
|
||||
Format::R8G8B8A8_UNORM
|
||||
};
|
||||
|
||||
let frag_shader = if opts.grid {
|
||||
"frag_grid"
|
||||
} else {
|
||||
"frag_swapchain"
|
||||
};
|
||||
let pipeline = graphics.create_pipeline_dynamic(
|
||||
shaders.get("vert_common").unwrap().clone(), // want panic
|
||||
shaders.get(frag_shader).unwrap().clone(), // want panic
|
||||
image_fmt,
|
||||
Some(AttachmentBlend::alpha()),
|
||||
)?;
|
||||
|
||||
let images = swapchain
|
||||
.enumerate_images()?
|
||||
.into_iter()
|
||||
@@ -98,7 +63,7 @@ pub(super) fn create_swapchain_render_data(
|
||||
graphics.device.clone(),
|
||||
vk_image,
|
||||
ImageCreateInfo {
|
||||
format: image_fmt, // actually SRGB but we lie
|
||||
format: graphics.native_format as _,
|
||||
extent,
|
||||
usage: ImageUsage::COLOR_ATTACHMENT,
|
||||
..Default::default()
|
||||
@@ -111,118 +76,51 @@ pub(super) fn create_swapchain_render_data(
|
||||
})
|
||||
.collect::<anyhow::Result<SmallVec<[Arc<ImageView>; 4]>>>()?;
|
||||
|
||||
Ok(SwapchainRenderData {
|
||||
Ok(WlxSwapchain {
|
||||
acquired: false,
|
||||
ever_acquired: false,
|
||||
swapchain,
|
||||
pipeline,
|
||||
images,
|
||||
extent,
|
||||
target_extent: [0, 0, 0],
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) struct SwapchainRenderData {
|
||||
pub(super) struct WlxSwapchain {
|
||||
acquired: bool,
|
||||
pub(super) ever_acquired: bool,
|
||||
pub(super) swapchain: xr::Swapchain<xr::Vulkan>,
|
||||
pub(super) pipeline: Arc<WlxPipeline<WlxPipelineDynamic>>,
|
||||
pub(super) extent: [u32; 3],
|
||||
pub(super) target_extent: [u32; 3],
|
||||
pub(super) images: SmallVec<[Arc<ImageView>; 4]>,
|
||||
}
|
||||
|
||||
impl SwapchainRenderData {
|
||||
pub(super) fn acquire_present_release(
|
||||
&mut self,
|
||||
command_buffer: &mut WlxCommandBuffer,
|
||||
view: Arc<ImageView>,
|
||||
alpha: f32,
|
||||
) -> anyhow::Result<xr::SwapchainSubImage<xr::Vulkan>> {
|
||||
impl WlxSwapchain {
|
||||
pub(super) fn acquire_wait_image(&mut self) -> anyhow::Result<Arc<ImageView>> {
|
||||
let idx = self.swapchain.acquire_image()? as usize;
|
||||
self.swapchain.wait_image(xr::Duration::INFINITE)?;
|
||||
|
||||
let render_target = &mut self.images[idx];
|
||||
command_buffer.begin_rendering(render_target.clone())?;
|
||||
|
||||
self.target_extent = render_target.image().extent();
|
||||
|
||||
let set0 = self.pipeline.uniform_sampler(
|
||||
0,
|
||||
view.clone(),
|
||||
command_buffer.graphics.texture_filtering,
|
||||
)?;
|
||||
|
||||
let set1 = self.pipeline.uniform_buffer(1, vec![alpha])?;
|
||||
|
||||
let pass = self.pipeline.create_pass(
|
||||
[self.target_extent[0] as _, self.target_extent[1] as _],
|
||||
command_buffer.graphics.quad_verts.clone(),
|
||||
command_buffer.graphics.quad_indices.clone(),
|
||||
vec![set0, set1],
|
||||
)?;
|
||||
command_buffer.run_ref(&pass)?;
|
||||
command_buffer.end_rendering()?;
|
||||
|
||||
self.swapchain.release_image()?;
|
||||
|
||||
Ok(xr::SwapchainSubImage::new()
|
||||
.swapchain(&self.swapchain)
|
||||
.image_rect(xr::Rect2Di {
|
||||
offset: xr::Offset2Di { x: 0, y: 0 },
|
||||
extent: xr::Extent2Di {
|
||||
width: self.target_extent[0] as _,
|
||||
height: self.target_extent[1] as _,
|
||||
},
|
||||
})
|
||||
.image_array_index(0))
|
||||
self.ever_acquired = true;
|
||||
self.acquired = true;
|
||||
Ok(self.images[idx].clone())
|
||||
}
|
||||
|
||||
pub(super) fn acquire_compute_release(
|
||||
&mut self,
|
||||
command_buffer: &mut WlxCommandBuffer,
|
||||
) -> anyhow::Result<xr::SwapchainSubImage<xr::Vulkan>> {
|
||||
let idx = self.swapchain.acquire_image()? as usize;
|
||||
self.swapchain.wait_image(xr::Duration::INFINITE)?;
|
||||
|
||||
let render_target = &mut self.images[idx];
|
||||
command_buffer.begin_rendering(render_target.clone())?;
|
||||
|
||||
self.target_extent = render_target.image().extent();
|
||||
|
||||
let pass = self.pipeline.create_pass(
|
||||
[self.target_extent[0] as _, self.target_extent[1] as _],
|
||||
command_buffer.graphics.quad_verts.clone(),
|
||||
command_buffer.graphics.quad_indices.clone(),
|
||||
vec![],
|
||||
)?;
|
||||
command_buffer.run_ref(&pass)?;
|
||||
command_buffer.end_rendering()?;
|
||||
|
||||
self.swapchain.release_image()?;
|
||||
|
||||
Ok(xr::SwapchainSubImage::new()
|
||||
.swapchain(&self.swapchain)
|
||||
.image_rect(xr::Rect2Di {
|
||||
offset: xr::Offset2Di { x: 0, y: 0 },
|
||||
extent: xr::Extent2Di {
|
||||
width: self.target_extent[0] as _,
|
||||
height: self.target_extent[1] as _,
|
||||
},
|
||||
})
|
||||
.image_array_index(0))
|
||||
pub(super) fn ensure_image_released(&mut self) -> anyhow::Result<()> {
|
||||
if self.acquired {
|
||||
self.swapchain.release_image()?;
|
||||
self.acquired = false;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn present_last(&self) -> anyhow::Result<xr::SwapchainSubImage<xr::Vulkan>> {
|
||||
debug_assert!(
|
||||
self.target_extent[0] * self.target_extent[1] != 0,
|
||||
"present_last: target_extent zero"
|
||||
);
|
||||
Ok(xr::SwapchainSubImage::new()
|
||||
pub(super) fn get_subimage(&self) -> xr::SwapchainSubImage<xr::Vulkan> {
|
||||
debug_assert!(self.ever_acquired, "swapchain was never acquired!");
|
||||
xr::SwapchainSubImage::new()
|
||||
.swapchain(&self.swapchain)
|
||||
.image_rect(xr::Rect2Di {
|
||||
offset: xr::Offset2Di { x: 0, y: 0 },
|
||||
extent: xr::Extent2Di {
|
||||
width: self.target_extent[0] as _,
|
||||
height: self.target_extent[1] as _,
|
||||
width: self.extent[0] as _,
|
||||
height: self.extent[1] as _,
|
||||
},
|
||||
})
|
||||
.image_array_index(0))
|
||||
.image_array_index(0)
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -26,7 +26,7 @@ impl OscSender {
|
||||
bail!("Failed to bind UDP socket - OSC will not function.");
|
||||
};
|
||||
|
||||
let Ok(_) = upstream.connect(SocketAddr::new(ip, send_port)) else {
|
||||
let Ok(()) = upstream.connect(SocketAddr::new(ip, send_port)) else {
|
||||
bail!("Failed to connect UDP socket - OSC will not function.");
|
||||
};
|
||||
|
||||
@@ -75,7 +75,7 @@ impl OscSender {
|
||||
KEYBOARD_NAME => has_keyboard = true,
|
||||
_ => {
|
||||
if o.state.interactable {
|
||||
num_overlays += 1
|
||||
num_overlays += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -159,12 +159,12 @@ impl OscSender {
|
||||
self.send_message(
|
||||
String::from("/avatar/parameters/averageControllerBattery"),
|
||||
vec![OscType::Float(
|
||||
controller_total_bat / controller_count as f32,
|
||||
controller_total_bat / f32::from(controller_count),
|
||||
)],
|
||||
)?;
|
||||
self.send_message(
|
||||
String::from("/avatar/parameters/averageTrackerBattery"),
|
||||
vec![OscType::Float(tracker_total_bat / tracker_count as f32)],
|
||||
vec![OscType::Float(tracker_total_bat / f32::from(tracker_count))],
|
||||
)?;
|
||||
}
|
||||
|
||||
|
||||
+77
-53
@@ -9,10 +9,11 @@ use std::{
|
||||
use anyhow::Ok;
|
||||
use glam::{Affine2, Affine3A, Mat3A, Quat, Vec2, Vec3, Vec3A};
|
||||
use serde::Deserialize;
|
||||
use vulkano::image::view::ImageView;
|
||||
use vulkano::{format::Format, image::view::ImageView};
|
||||
|
||||
use crate::{
|
||||
config::AStrMapExt,
|
||||
graphics::CommandBuffers,
|
||||
state::{AppState, KeyboardFocus},
|
||||
};
|
||||
|
||||
@@ -55,14 +56,13 @@ pub struct OverlayState {
|
||||
pub saved_transform: Option<Affine3A>,
|
||||
pub relative_to: RelativeTo,
|
||||
pub curvature: Option<f32>,
|
||||
pub primary_pointer: Option<usize>,
|
||||
pub interaction_transform: Affine2,
|
||||
pub birthframe: usize,
|
||||
}
|
||||
|
||||
impl Default for OverlayState {
|
||||
fn default() -> Self {
|
||||
OverlayState {
|
||||
Self {
|
||||
id: OverlayID(OVERLAY_AUTO_INCREMENT.fetch_add(1, Ordering::Relaxed)),
|
||||
name: Arc::from(""),
|
||||
want_visible: false,
|
||||
@@ -82,7 +82,6 @@ impl Default for OverlayState {
|
||||
spawn_rotation: Quat::IDENTITY,
|
||||
saved_transform: None,
|
||||
transform: Affine3A::IDENTITY,
|
||||
primary_pointer: None,
|
||||
interaction_transform: Affine2::IDENTITY,
|
||||
birthframe: 0,
|
||||
}
|
||||
@@ -104,8 +103,8 @@ where
|
||||
T: Default,
|
||||
{
|
||||
fn default() -> Self {
|
||||
OverlayData {
|
||||
state: Default::default(),
|
||||
Self {
|
||||
state: OverlayState::default(),
|
||||
backend: Box::<SplitOverlayBackend>::default(),
|
||||
primary_pointer: None,
|
||||
data: Default::default(),
|
||||
@@ -114,7 +113,7 @@ where
|
||||
}
|
||||
|
||||
impl OverlayState {
|
||||
pub fn parent_transform(&self, app: &AppState) -> Option<Affine3A> {
|
||||
pub const fn parent_transform(&self, app: &AppState) -> Option<Affine3A> {
|
||||
match self.relative_to {
|
||||
RelativeTo::Head => Some(app.input_state.hmd),
|
||||
RelativeTo::Hand(idx) => Some(app.input_state.pointers[idx].pose),
|
||||
@@ -122,7 +121,7 @@ impl OverlayState {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_anchor(&self, app: &AppState) -> Affine3A {
|
||||
const fn get_anchor(&self, app: &AppState) -> Affine3A {
|
||||
if self.anchored {
|
||||
app.anchor
|
||||
} else {
|
||||
@@ -210,12 +209,14 @@ where
|
||||
T: Default,
|
||||
{
|
||||
pub fn init(&mut self, app: &mut AppState) -> anyhow::Result<()> {
|
||||
self.state.curvature = app
|
||||
.session
|
||||
.config
|
||||
.curve_values
|
||||
.arc_get(self.state.name.as_ref())
|
||||
.copied();
|
||||
if self.state.curvature.is_none() {
|
||||
self.state.curvature = app
|
||||
.session
|
||||
.config
|
||||
.curve_values
|
||||
.arc_get(self.state.name.as_ref())
|
||||
.copied();
|
||||
}
|
||||
|
||||
if matches!(self.state.relative_to, RelativeTo::None) {
|
||||
let hard_reset;
|
||||
@@ -234,33 +235,34 @@ where
|
||||
}
|
||||
self.backend.init(app)
|
||||
}
|
||||
pub fn render(&mut self, app: &mut AppState) -> anyhow::Result<()> {
|
||||
self.backend.render(app)
|
||||
pub fn should_render(&mut self, app: &mut AppState) -> anyhow::Result<ShouldRender> {
|
||||
self.backend.should_render(app)
|
||||
}
|
||||
pub fn view(&mut self) -> Option<Arc<ImageView>> {
|
||||
self.backend.view()
|
||||
pub fn render(
|
||||
&mut self,
|
||||
app: &mut AppState,
|
||||
tgt: Arc<ImageView>,
|
||||
buf: &mut CommandBuffers,
|
||||
alpha: f32,
|
||||
) -> anyhow::Result<bool> {
|
||||
self.backend.render(app, tgt, buf, alpha)
|
||||
}
|
||||
pub fn frame_transform(&mut self) -> Option<FrameTransform> {
|
||||
self.backend.frame_transform()
|
||||
}
|
||||
pub fn set_visible(&mut self, app: &mut AppState, visible: bool) -> anyhow::Result<()> {
|
||||
let old_visible = self.state.want_visible;
|
||||
self.state.want_visible = visible;
|
||||
if visible != old_visible {
|
||||
if visible {
|
||||
self.backend.resume(app)?;
|
||||
} else {
|
||||
self.backend.pause(app)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
pub fn frame_meta(&mut self) -> Option<FrameMeta> {
|
||||
self.backend.frame_meta()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct FrameTransform {
|
||||
#[derive(Default, Clone, Copy)]
|
||||
pub struct FrameMeta {
|
||||
pub extent: [u32; 3],
|
||||
pub transform: Affine3A,
|
||||
pub format: Format,
|
||||
}
|
||||
|
||||
pub enum ShouldRender {
|
||||
Should,
|
||||
Can,
|
||||
Unable,
|
||||
}
|
||||
|
||||
pub trait OverlayRenderer {
|
||||
@@ -268,15 +270,24 @@ pub trait OverlayRenderer {
|
||||
fn init(&mut self, app: &mut AppState) -> anyhow::Result<()>;
|
||||
fn pause(&mut self, app: &mut AppState) -> anyhow::Result<()>;
|
||||
fn resume(&mut self, app: &mut AppState) -> anyhow::Result<()>;
|
||||
|
||||
/// Called when the presentation layer is ready to present a new frame
|
||||
fn render(&mut self, app: &mut AppState) -> anyhow::Result<()>;
|
||||
/// Called to retrieve the current image to be displayed
|
||||
fn view(&mut self) -> Option<Arc<ImageView>>;
|
||||
fn should_render(&mut self, app: &mut AppState) -> anyhow::Result<ShouldRender>;
|
||||
|
||||
/// Called when the contents need to be rendered to the swapchain
|
||||
fn render(
|
||||
&mut self,
|
||||
app: &mut AppState,
|
||||
tgt: Arc<ImageView>,
|
||||
buf: &mut CommandBuffers,
|
||||
alpha: f32,
|
||||
) -> anyhow::Result<bool>;
|
||||
|
||||
/// Called to retrieve the effective extent of the image
|
||||
/// Used for creating swapchains.
|
||||
///
|
||||
/// Muse not be None if view() is also not None
|
||||
fn frame_transform(&mut self) -> Option<FrameTransform>;
|
||||
/// Must be true if should_render was also true on the same frame.
|
||||
fn frame_meta(&mut self) -> Option<FrameMeta>;
|
||||
}
|
||||
|
||||
pub struct FallbackRenderer;
|
||||
@@ -291,13 +302,19 @@ impl OverlayRenderer for FallbackRenderer {
|
||||
fn resume(&mut self, _app: &mut AppState) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn render(&mut self, _app: &mut AppState) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
fn should_render(&mut self, _app: &mut AppState) -> anyhow::Result<ShouldRender> {
|
||||
Ok(ShouldRender::Unable)
|
||||
}
|
||||
fn view(&mut self) -> Option<Arc<ImageView>> {
|
||||
None
|
||||
fn render(
|
||||
&mut self,
|
||||
_app: &mut AppState,
|
||||
_tgt: Arc<ImageView>,
|
||||
_buf: &mut CommandBuffers,
|
||||
_alpha: f32,
|
||||
) -> anyhow::Result<bool> {
|
||||
Ok(false)
|
||||
}
|
||||
fn frame_transform(&mut self) -> Option<FrameTransform> {
|
||||
fn frame_meta(&mut self) -> Option<FrameMeta> {
|
||||
None
|
||||
}
|
||||
}
|
||||
@@ -322,8 +339,8 @@ pub struct SplitOverlayBackend {
|
||||
}
|
||||
|
||||
impl Default for SplitOverlayBackend {
|
||||
fn default() -> SplitOverlayBackend {
|
||||
SplitOverlayBackend {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
renderer: Box::new(FallbackRenderer),
|
||||
interaction: Box::new(DummyInteractionHandler),
|
||||
}
|
||||
@@ -348,16 +365,23 @@ impl OverlayRenderer for SplitOverlayBackend {
|
||||
fn resume(&mut self, app: &mut AppState) -> anyhow::Result<()> {
|
||||
self.renderer.resume(app)
|
||||
}
|
||||
fn render(&mut self, app: &mut AppState) -> anyhow::Result<()> {
|
||||
self.renderer.render(app)
|
||||
fn should_render(&mut self, app: &mut AppState) -> anyhow::Result<ShouldRender> {
|
||||
self.renderer.should_render(app)
|
||||
}
|
||||
fn view(&mut self) -> Option<Arc<ImageView>> {
|
||||
self.renderer.view()
|
||||
fn render(
|
||||
&mut self,
|
||||
app: &mut AppState,
|
||||
tgt: Arc<ImageView>,
|
||||
buf: &mut CommandBuffers,
|
||||
alpha: f32,
|
||||
) -> anyhow::Result<bool> {
|
||||
self.renderer.render(app, tgt, buf, alpha)
|
||||
}
|
||||
fn frame_transform(&mut self) -> Option<FrameTransform> {
|
||||
self.renderer.frame_transform()
|
||||
fn frame_meta(&mut self) -> Option<FrameMeta> {
|
||||
self.renderer.frame_meta()
|
||||
}
|
||||
}
|
||||
|
||||
impl InteractionHandler for SplitOverlayBackend {
|
||||
fn on_left(&mut self, app: &mut AppState, pointer: usize) {
|
||||
self.interaction.on_left(app, pointer);
|
||||
@@ -373,7 +397,7 @@ impl InteractionHandler for SplitOverlayBackend {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ui_transform(extent: &[u32; 2]) -> Affine2 {
|
||||
pub fn ui_transform(extent: [u32; 2]) -> Affine2 {
|
||||
let aspect = extent[0] as f32 / extent[1] as f32;
|
||||
let scale = if aspect < 1.0 {
|
||||
Vec2 {
|
||||
|
||||
+3
-4
@@ -25,12 +25,12 @@ struct AppTask {
|
||||
pub task: TaskType,
|
||||
}
|
||||
|
||||
impl PartialEq<AppTask> for AppTask {
|
||||
impl PartialEq<Self> for AppTask {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.cmp(other) == cmp::Ordering::Equal
|
||||
}
|
||||
}
|
||||
impl PartialOrd<AppTask> for AppTask {
|
||||
impl PartialOrd<Self> for AppTask {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
@@ -57,7 +57,6 @@ pub type CreateOverlayTask =
|
||||
dyn FnOnce(&mut AppState) -> Option<(OverlayState, Box<dyn OverlayBackend>)> + Send;
|
||||
|
||||
pub enum TaskType {
|
||||
Global(Box<dyn FnOnce(&mut AppState) + Send>),
|
||||
Overlay(OverlaySelector, Box<OverlayTask>),
|
||||
CreateOverlay(OverlaySelector, Box<CreateOverlayTask>),
|
||||
DropOverlay(OverlaySelector),
|
||||
@@ -79,7 +78,7 @@ pub struct TaskContainer {
|
||||
}
|
||||
|
||||
impl TaskContainer {
|
||||
pub fn new() -> Self {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
tasks: BinaryHeap::new(),
|
||||
}
|
||||
|
||||
+26
-54
@@ -1,10 +1,10 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use vulkano::{
|
||||
command_buffer::CommandBufferUsage,
|
||||
image::{view::ImageView, ImageUsage},
|
||||
swapchain::{
|
||||
acquire_next_image, Surface, Swapchain, SwapchainCreateInfo, SwapchainPresentInfo,
|
||||
acquire_next_image, Surface, SurfaceInfo, Swapchain, SwapchainCreateInfo,
|
||||
SwapchainPresentInfo,
|
||||
},
|
||||
sync::GpuFuture,
|
||||
Validated, VulkanError,
|
||||
@@ -19,7 +19,7 @@ use winit::{
|
||||
use crate::{
|
||||
config::load_custom_ui,
|
||||
config_io,
|
||||
graphics::{DynamicPass, DynamicPipeline, WlxGraphics, BLEND_ALPHA},
|
||||
graphics::{CommandBuffers, WlxGraphics},
|
||||
gui::{
|
||||
canvas::Canvas,
|
||||
modular::{modular_canvas, ModularData},
|
||||
@@ -37,8 +37,6 @@ static LAST_SIZE: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::n
|
||||
|
||||
struct PreviewState {
|
||||
canvas: Canvas<(), ModularData>,
|
||||
pipeline: Arc<DynamicPipeline>,
|
||||
pass: DynamicPass,
|
||||
swapchain: Arc<Swapchain>,
|
||||
images: Vec<Arc<ImageView>>,
|
||||
}
|
||||
@@ -63,52 +61,27 @@ impl PreviewState {
|
||||
window.set_min_inner_size(Some(logical_size));
|
||||
window.set_max_inner_size(Some(logical_size));
|
||||
LAST_SIZE.store(
|
||||
(config.size[1] as u64) << 32 | config.size[0] as u64,
|
||||
((config.size[1] as u64) << 32) | config.size[0] as u64,
|
||||
std::sync::atomic::Ordering::Relaxed,
|
||||
);
|
||||
}
|
||||
|
||||
let inner_size = window.inner_size();
|
||||
let swapchain_size = [inner_size.width, inner_size.height];
|
||||
let (swapchain, images) =
|
||||
create_swapchain(&state.graphics, surface.clone(), swapchain_size)?;
|
||||
let (swapchain, images) = create_swapchain(&state.graphics, surface, swapchain_size)?;
|
||||
|
||||
let mut canvas = modular_canvas(&config.size, &config.elements, state)?;
|
||||
let mut canvas = modular_canvas(config.size, &config.elements, state)?;
|
||||
canvas.init(state)?;
|
||||
let view = canvas.view().unwrap();
|
||||
|
||||
let pipeline = {
|
||||
let shaders = state.graphics.shared_shaders.read().unwrap();
|
||||
state.graphics.create_pipeline_dynamic(
|
||||
shaders.get("vert_common").unwrap().clone(), // want panic
|
||||
shaders.get("frag_sprite").unwrap().clone(), // want panic
|
||||
state.graphics.native_format,
|
||||
Some(BLEND_ALPHA),
|
||||
)
|
||||
}?;
|
||||
let set0 = pipeline
|
||||
.uniform_sampler(0, view.clone(), pipeline.graphics.texture_filtering)
|
||||
.unwrap();
|
||||
|
||||
let pass = pipeline
|
||||
.create_pass(
|
||||
[swapchain_size[0] as f32, swapchain_size[1] as f32],
|
||||
state.graphics.quad_verts.clone(),
|
||||
state.graphics.quad_indices.clone(),
|
||||
vec![set0],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
Ok(PreviewState {
|
||||
Ok(Self {
|
||||
canvas,
|
||||
pipeline,
|
||||
pass,
|
||||
swapchain,
|
||||
images,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub fn uidev_run(panel_name: &str) -> anyhow::Result<()> {
|
||||
let (graphics, event_loop, window, surface) = WlxGraphics::new_window()?;
|
||||
window.set_resizable(false);
|
||||
@@ -127,11 +100,12 @@ pub fn uidev_run(panel_name: &str) -> anyhow::Result<()> {
|
||||
panel_name,
|
||||
)?);
|
||||
|
||||
let watch_path = config_io::get_config_root().join(format!("{}.yaml", panel_name));
|
||||
let watch_path = config_io::get_config_root().join(format!("{panel_name}.yaml"));
|
||||
let mut path_last_modified = watch_path.metadata()?.modified()?;
|
||||
let mut recreate = false;
|
||||
let mut last_draw = std::time::Instant::now();
|
||||
|
||||
#[allow(deprecated)]
|
||||
event_loop.run(move |event, elwt| {
|
||||
elwt.set_control_flow(ControlFlow::Poll);
|
||||
|
||||
@@ -182,29 +156,27 @@ pub fn uidev_run(panel_name: &str) -> anyhow::Result<()> {
|
||||
Err(e) => panic!("failed to acquire next image: {e}"),
|
||||
};
|
||||
|
||||
if let Err(e) = preview.canvas.render(&mut state) {
|
||||
let mut canvas_cmd_buf = CommandBuffers::default();
|
||||
let tgt = preview.images[image_index as usize].clone();
|
||||
|
||||
if let Err(e) = preview
|
||||
.canvas
|
||||
.render(&mut state, tgt, &mut canvas_cmd_buf, 1.0)
|
||||
{
|
||||
log::error!("failed to render canvas: {e}");
|
||||
window.request_redraw();
|
||||
};
|
||||
}
|
||||
|
||||
let target = preview.images[image_index as usize].clone();
|
||||
|
||||
let mut cmd_buf = state
|
||||
.graphics
|
||||
.create_command_buffer(CommandBufferUsage::OneTimeSubmit)
|
||||
.unwrap();
|
||||
cmd_buf.begin_rendering(target).unwrap();
|
||||
cmd_buf.run_ref(&preview.pass).unwrap();
|
||||
cmd_buf.end_rendering().unwrap();
|
||||
last_draw = std::time::Instant::now();
|
||||
|
||||
let command_buffer = cmd_buf.build().unwrap();
|
||||
vulkano::sync::now(graphics.device.clone())
|
||||
.join(acquire_future)
|
||||
.then_execute(graphics.queue.clone(), command_buffer)
|
||||
canvas_cmd_buf
|
||||
.execute_after(
|
||||
state.graphics.graphics_queue.clone(),
|
||||
Box::new(acquire_future),
|
||||
)
|
||||
.unwrap()
|
||||
.then_swapchain_present(
|
||||
graphics.queue.clone(),
|
||||
graphics.graphics_queue.clone(),
|
||||
SwapchainPresentInfo::swapchain_image_index(
|
||||
preview.swapchain.clone(),
|
||||
image_index,
|
||||
@@ -236,12 +208,12 @@ fn create_swapchain(
|
||||
let surface_capabilities = graphics
|
||||
.device
|
||||
.physical_device()
|
||||
.surface_capabilities(&surface, Default::default())
|
||||
.surface_capabilities(&surface, SurfaceInfo::default())
|
||||
.unwrap(); // want panic
|
||||
|
||||
let (swapchain, images) = Swapchain::new(
|
||||
graphics.device.clone(),
|
||||
surface.clone(),
|
||||
surface,
|
||||
SwapchainCreateInfo {
|
||||
min_image_count: surface_capabilities.min_image_count.max(2),
|
||||
image_format: graphics.native_format,
|
||||
|
||||
@@ -36,7 +36,7 @@ pub struct WayVRCompositor {
|
||||
}
|
||||
|
||||
fn get_wayvr_env_from_pid(pid: i32) -> anyhow::Result<ProcessWayVREnv> {
|
||||
let path = format!("/proc/{}/environ", pid);
|
||||
let path = format!("/proc/{pid}/environ");
|
||||
let mut env_data = String::new();
|
||||
std::fs::File::open(path)?.read_to_string(&mut env_data)?;
|
||||
|
||||
@@ -162,7 +162,7 @@ impl WayVRCompositor {
|
||||
) -> anyhow::Result<()> {
|
||||
if let Some(stream) = self.listener.accept()? {
|
||||
if let Err(e) = self.accept_connection(stream, displays, processes) {
|
||||
log::error!("Failed to accept connection: {}", e);
|
||||
log::error!("Failed to accept connection: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ impl WayVRCompositor {
|
||||
processes: &mut process::ProcessVec,
|
||||
) -> anyhow::Result<()> {
|
||||
if let Err(e) = self.accept_connections(displays, processes) {
|
||||
log::error!("accept_connections failed: {}", e);
|
||||
log::error!("accept_connections failed: {e}");
|
||||
}
|
||||
|
||||
self.display.dispatch_clients(&mut self.state)?;
|
||||
@@ -184,7 +184,7 @@ impl WayVRCompositor {
|
||||
let surf_count = self.state.xdg_shell.toplevel_surfaces().len() as u32;
|
||||
if surf_count != self.toplevel_surf_count {
|
||||
self.toplevel_surf_count = surf_count;
|
||||
log::info!("Toplevel surface count changed: {}", surf_count);
|
||||
log::info!("Toplevel surface count changed: {surf_count}");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -211,11 +211,9 @@ impl WayVRCompositor {
|
||||
const STARTING_WAYLAND_ADDR_IDX: u32 = 20;
|
||||
|
||||
fn export_display_number(display_num: u32) -> anyhow::Result<()> {
|
||||
let mut path = std::env::var("XDG_RUNTIME_DIR")
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|_| PathBuf::from("/tmp"));
|
||||
let mut path = std::env::var("XDG_RUNTIME_DIR").map_or_else(|_| PathBuf::from("/tmp"), PathBuf::from);
|
||||
path.push("wayvr.disp");
|
||||
std::fs::write(path, format!("{}\n", display_num))?;
|
||||
std::fs::write(path, format!("{display_num}\n"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -227,17 +225,15 @@ fn create_wayland_listener() -> anyhow::Result<(super::WaylandEnv, wayland_serve
|
||||
|
||||
let listener = loop {
|
||||
let display_str = env.display_num_string();
|
||||
log::debug!("Trying to open socket \"{}\"", display_str);
|
||||
log::debug!("Trying to open socket \"{display_str}\"");
|
||||
match wayland_server::ListeningSocket::bind(display_str.as_str()) {
|
||||
Ok(listener) => {
|
||||
log::debug!("Listening to {}", display_str);
|
||||
log::debug!("Listening to {display_str}");
|
||||
break listener;
|
||||
}
|
||||
Err(e) => {
|
||||
log::debug!(
|
||||
"Failed to open socket \"{}\" (reason: {}), trying next...",
|
||||
display_str,
|
||||
e
|
||||
"Failed to open socket \"{display_str}\" (reason: {e}), trying next..."
|
||||
);
|
||||
|
||||
env.display_num += 1;
|
||||
|
||||
@@ -122,15 +122,13 @@ pub struct ClientState {
|
||||
|
||||
impl ClientData for ClientState {
|
||||
fn initialized(&self, client_id: ClientId) {
|
||||
log::debug!("Client ID {:?} connected", client_id);
|
||||
log::debug!("Client ID {client_id:?} connected");
|
||||
}
|
||||
|
||||
fn disconnected(&self, client_id: ClientId, reason: DisconnectReason) {
|
||||
*self.disconnected.lock().unwrap() = true;
|
||||
log::debug!(
|
||||
"Client ID {:?} disconnected. Reason: {:?}",
|
||||
client_id,
|
||||
reason
|
||||
"Client ID {client_id:?} disconnected. Reason: {reason:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -202,7 +200,7 @@ impl DmabufHandler for Application {
|
||||
notifier: ImportNotifier,
|
||||
) {
|
||||
if self.gles_renderer.import_dmabuf(&dmabuf, None).is_ok() {
|
||||
let _ = notifier.successful::<Application>();
|
||||
let _ = notifier.successful::<Self>();
|
||||
} else {
|
||||
notifier.failed();
|
||||
}
|
||||
|
||||
@@ -122,8 +122,8 @@ impl Display {
|
||||
let tex_id = params.renderer.with_context(|gl| {
|
||||
smithay_wrapper::create_framebuffer_texture(
|
||||
gl,
|
||||
params.width as u32,
|
||||
params.height as u32,
|
||||
u32::from(params.width),
|
||||
u32::from(params.height),
|
||||
tex_format,
|
||||
internal_format,
|
||||
)
|
||||
@@ -139,7 +139,7 @@ impl Display {
|
||||
};
|
||||
|
||||
let opaque = false;
|
||||
let size = (params.width as i32, params.height as i32).into();
|
||||
let size = (i32::from(params.width), i32::from(params.height)).into();
|
||||
let gles_texture = unsafe {
|
||||
GlesTexture::from_raw(params.renderer, Some(tex_format), opaque, tex_id, size)
|
||||
};
|
||||
@@ -202,7 +202,7 @@ impl Display {
|
||||
match &self.layout {
|
||||
packet_server::WvrDisplayWindowLayout::Tiling => {
|
||||
let mut i = 0;
|
||||
for win in self.displayed_windows.iter_mut() {
|
||||
for win in &mut self.displayed_windows {
|
||||
if let Some(window) = self.wm.borrow_mut().windows.get_mut(&win.window_handle) {
|
||||
if !window.visible {
|
||||
continue;
|
||||
@@ -210,21 +210,21 @@ impl Display {
|
||||
let d_cur = i as f32 / window_count as f32;
|
||||
let d_next = (i + 1) as f32 / window_count as f32;
|
||||
|
||||
let left = (d_cur * self.width as f32) as i32;
|
||||
let right = (d_next * self.width as f32) as i32;
|
||||
let left = (d_cur * f32::from(self.width)) as i32;
|
||||
let right = (d_next * f32::from(self.width)) as i32;
|
||||
|
||||
window.set_pos(left, 0);
|
||||
window.set_size((right - left) as u32, self.height as u32);
|
||||
window.set_size((right - left) as u32, u32::from(self.height));
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
packet_server::WvrDisplayWindowLayout::Stacking(opts) => {
|
||||
let do_margins = |margins: &packet_server::Margins, window: &mut window::Window| {
|
||||
let top = margins.top as i32;
|
||||
let bottom = self.height as i32 - margins.bottom as i32;
|
||||
let left = margins.left as i32;
|
||||
let right = self.width as i32 - margins.right as i32;
|
||||
let top = i32::from(margins.top);
|
||||
let bottom = i32::from(self.height) - i32::from(margins.bottom);
|
||||
let left = i32::from(margins.left);
|
||||
let right = i32::from(self.width) - i32::from(margins.right);
|
||||
let width = right - left;
|
||||
let height = bottom - top;
|
||||
if width < 0 || height < 0 {
|
||||
@@ -236,7 +236,7 @@ impl Display {
|
||||
};
|
||||
|
||||
let mut i = 0;
|
||||
for win in self.displayed_windows.iter_mut() {
|
||||
for win in &mut self.displayed_windows {
|
||||
if let Some(window) = self.wm.borrow_mut().windows.get_mut(&win.window_handle) {
|
||||
if !window.visible {
|
||||
continue;
|
||||
@@ -267,7 +267,7 @@ impl Display {
|
||||
self.no_windows_since = None;
|
||||
} else if let Some(auto_hide_delay) = config.auto_hide_delay {
|
||||
if let Some(s) = self.no_windows_since {
|
||||
if s + (auto_hide_delay as u64) < get_millis() {
|
||||
if s + u64::from(auto_hide_delay) < get_millis() {
|
||||
// Auto-hide after specific time
|
||||
signals.send(WayVRSignal::DisplayVisibility(*handle, false));
|
||||
}
|
||||
@@ -303,7 +303,7 @@ impl Display {
|
||||
pub fn tick_render(&mut self, renderer: &mut GlesRenderer, time_ms: u64) -> anyhow::Result<()> {
|
||||
renderer.bind(self.gles_texture.clone())?;
|
||||
|
||||
let size = Size::from((self.width as i32, self.height as i32));
|
||||
let size = Size::from((i32::from(self.width), i32::from(self.height)));
|
||||
let damage: Rectangle<i32, smithay::utils::Physical> = Rectangle::from_size(size);
|
||||
|
||||
let elements: Vec<WaylandSurfaceRenderElement<GlesRenderer>> = self
|
||||
@@ -358,8 +358,8 @@ impl Display {
|
||||
gl.ReadPixels(
|
||||
0,
|
||||
0,
|
||||
self.width as i32,
|
||||
self.height as i32,
|
||||
i32::from(self.width),
|
||||
i32::from(self.height),
|
||||
ffi::RGBA,
|
||||
ffi::UNSIGNED_BYTE,
|
||||
data.as_mut_ptr().cast(),
|
||||
@@ -401,7 +401,7 @@ impl Display {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn trigger_rerender(&mut self) {
|
||||
pub const fn trigger_rerender(&mut self) {
|
||||
self.wants_redraw = true;
|
||||
}
|
||||
|
||||
@@ -435,7 +435,7 @@ impl Display {
|
||||
y: u32,
|
||||
) {
|
||||
let current_ms = time::get_millis();
|
||||
if self.last_pressed_time_ms + config.click_freeze_time_ms as u64 > current_ms {
|
||||
if self.last_pressed_time_ms + u64::from(config.click_freeze_time_ms) > current_ms {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -444,8 +444,8 @@ impl Display {
|
||||
if let Some(window) = wm.windows.get(&window_handle) {
|
||||
let surf = window.toplevel.wl_surface().clone();
|
||||
let point = Point::<f64, Logical>::from((
|
||||
(x as i32 - window.pos_x) as f64,
|
||||
(y as i32 - window.pos_y) as f64,
|
||||
f64::from(x as i32 - window.pos_x),
|
||||
f64::from(y as i32 - window.pos_y),
|
||||
));
|
||||
|
||||
manager.seat_pointer.motion(
|
||||
@@ -463,7 +463,7 @@ impl Display {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_mouse_index_number(index: super::MouseIndex) -> u32 {
|
||||
const fn get_mouse_index_number(index: super::MouseIndex) -> u32 {
|
||||
match index {
|
||||
super::MouseIndex::Left => 0x110, /* BTN_LEFT */
|
||||
super::MouseIndex::Center => 0x112, /* BTN_MIDDLE */
|
||||
@@ -505,7 +505,7 @@ impl Display {
|
||||
manager.seat_pointer.frame(&mut manager.state);
|
||||
}
|
||||
|
||||
pub fn send_mouse_up(&self, manager: &mut WayVRCompositor, index: super::MouseIndex) {
|
||||
pub fn send_mouse_up(manager: &mut WayVRCompositor, index: super::MouseIndex) {
|
||||
manager.seat_pointer.button(
|
||||
&mut manager.state,
|
||||
&input::pointer::ButtonEvent {
|
||||
@@ -519,7 +519,7 @@ impl Display {
|
||||
manager.seat_pointer.frame(&mut manager.state);
|
||||
}
|
||||
|
||||
pub fn send_mouse_scroll(&self, manager: &mut WayVRCompositor, delta_y: f32, delta_x: f32) {
|
||||
pub fn send_mouse_scroll(manager: &mut WayVRCompositor, delta_y: f32, delta_x: f32) {
|
||||
manager.seat_pointer.axis(
|
||||
&mut manager.state,
|
||||
input::pointer::AxisFrame {
|
||||
@@ -529,7 +529,7 @@ impl Display {
|
||||
smithay::backend::input::AxisRelativeDirection::Identical,
|
||||
),
|
||||
time: 0,
|
||||
axis: (delta_x as f64, -delta_y as f64),
|
||||
axis: (f64::from(delta_x), f64::from(-delta_y)),
|
||||
v120: Some((0, (delta_y * -120.0) as i32)),
|
||||
stop: (false, false),
|
||||
},
|
||||
@@ -549,7 +549,7 @@ impl Display {
|
||||
args: &[&str],
|
||||
env: &[(&str, &str)],
|
||||
) -> anyhow::Result<SpawnProcessResult> {
|
||||
log::info!("Spawning subprocess with exec path \"{}\"", exec_path);
|
||||
log::info!("Spawning subprocess with exec path \"{exec_path}\"");
|
||||
|
||||
let auth_key = generate_auth_key();
|
||||
|
||||
@@ -577,14 +577,14 @@ impl Display {
|
||||
gen_id!(DisplayVec, Display, DisplayCell, DisplayHandle);
|
||||
|
||||
impl DisplayHandle {
|
||||
pub fn from_packet(handle: packet_server::WvrDisplayHandle) -> Self {
|
||||
pub const fn from_packet(handle: packet_server::WvrDisplayHandle) -> Self {
|
||||
Self {
|
||||
generation: handle.generation,
|
||||
idx: handle.idx,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_packet(&self) -> packet_server::WvrDisplayHandle {
|
||||
pub const fn as_packet(&self) -> packet_server::WvrDisplayHandle {
|
||||
packet_server::WvrDisplayHandle {
|
||||
idx: self.idx,
|
||||
generation: self.generation,
|
||||
|
||||
@@ -47,25 +47,25 @@ pub enum RenderData {
|
||||
|
||||
impl EGLData {
|
||||
pub fn load_func(&self, func_name: &str) -> anyhow::Result<extern "system" fn()> {
|
||||
let raw_fn = self.egl.get_proc_address(func_name).ok_or(anyhow::anyhow!(
|
||||
"Required EGL function {} not found",
|
||||
func_name
|
||||
))?;
|
||||
let raw_fn = self
|
||||
.egl
|
||||
.get_proc_address(func_name)
|
||||
.ok_or_else(|| anyhow::anyhow!("Required EGL function {} not found", func_name))?;
|
||||
Ok(raw_fn)
|
||||
}
|
||||
|
||||
pub fn new() -> anyhow::Result<EGLData> {
|
||||
pub fn new() -> anyhow::Result<Self> {
|
||||
unsafe {
|
||||
let egl = khronos_egl::Instance::new(khronos_egl::Static);
|
||||
|
||||
let display = egl
|
||||
.get_display(khronos_egl::DEFAULT_DISPLAY)
|
||||
.ok_or(anyhow!(
|
||||
.ok_or_else(|| anyhow!(
|
||||
"eglGetDisplay failed. This shouldn't happen unless you don't have any display manager running. Cannot continue, check your EGL installation."
|
||||
))?;
|
||||
|
||||
let (major, minor) = egl.initialize(display)?;
|
||||
log::debug!("EGL version: {}.{}", major, minor);
|
||||
log::debug!("EGL version: {major}.{minor}");
|
||||
|
||||
let attrib_list = [
|
||||
khronos_egl::RED_SIZE,
|
||||
@@ -83,7 +83,7 @@ impl EGLData {
|
||||
|
||||
let config = egl
|
||||
.choose_first_config(display, &attrib_list)?
|
||||
.ok_or(anyhow!("Failed to get EGL config"))?;
|
||||
.ok_or_else(|| anyhow!("Failed to get EGL config"))?;
|
||||
|
||||
egl.bind_api(khronos_egl::OPENGL_ES_API)?;
|
||||
|
||||
@@ -104,7 +104,7 @@ impl EGLData {
|
||||
|
||||
egl.make_current(display, None, None, Some(context))?;
|
||||
|
||||
Ok(EGLData {
|
||||
Ok(Self {
|
||||
egl,
|
||||
display,
|
||||
config,
|
||||
@@ -114,7 +114,7 @@ impl EGLData {
|
||||
}
|
||||
|
||||
fn query_dmabuf_mod_info(&self) -> anyhow::Result<DMAbufModifierInfo> {
|
||||
let target_fourcc = 0x34324258; //XB24
|
||||
let target_fourcc = 0x3432_4258; //XB24
|
||||
|
||||
unsafe {
|
||||
use egl_ex::PFNEGLQUERYDMABUFFORMATSEXTPROC;
|
||||
@@ -187,13 +187,13 @@ impl EGLData {
|
||||
&mut num_mods,
|
||||
);
|
||||
|
||||
if mods[0] == 0xFFFFFFFFFFFFFFFF {
|
||||
if mods[0] == 0xFFFF_FFFF_FFFF_FFFF {
|
||||
anyhow::bail!("modifier is -1")
|
||||
}
|
||||
|
||||
log::trace!("Modifier list:");
|
||||
for modifier in &mods {
|
||||
log::trace!("{:#x}", modifier);
|
||||
log::trace!("{modifier:#x}");
|
||||
}
|
||||
|
||||
// We should not change these modifier values. Passing all of them to the Vulkan dmabuf
|
||||
@@ -203,13 +203,13 @@ impl EGLData {
|
||||
// If not, the full list of modifiers will be passed. Further testing is required.
|
||||
// For now, it looks like only NAVI32-based gpus have this problem.
|
||||
let mod_whitelist: [u64; 2] = [
|
||||
0x20000002086bf04, /* AMD RX 7800 XT, Navi32 */
|
||||
0x20000001866bf04, /* AMD RX 7600 XT, Navi33 */
|
||||
0x200_0000_2086_bf04, /* AMD RX 7800 XT, Navi32 */
|
||||
0x200_0000_1866_bf04, /* AMD RX 7600 XT, Navi33 */
|
||||
];
|
||||
|
||||
for modifier in &mod_whitelist {
|
||||
if mods.contains(modifier) {
|
||||
log::warn!("Using whitelisted dmabuf tiling modifier: {:#x}", modifier);
|
||||
log::warn!("Using whitelisted dmabuf tiling modifier: {modifier:#x}");
|
||||
mods = vec![*modifier, 0x0 /* also important (???) */];
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ impl<DataType> SyncEventQueue<DataType> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
data: Rc::new(RefCell::new(Data {
|
||||
queue: Default::default(),
|
||||
queue: VecDeque::default(),
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,27 +30,27 @@ macro_rules! gen_id {
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl $handle_name {
|
||||
pub fn reset(&mut self) {
|
||||
pub const fn reset(&mut self) {
|
||||
self.generation = 0;
|
||||
}
|
||||
|
||||
pub fn is_set(&self) -> bool {
|
||||
pub const fn is_set(&self) -> bool {
|
||||
self.generation > 0
|
||||
}
|
||||
|
||||
pub fn id(&self) -> u32 {
|
||||
pub const fn id(&self) -> u32 {
|
||||
self.idx
|
||||
}
|
||||
|
||||
pub fn new(idx: u32, generation: u64) -> Self {
|
||||
pub const fn new(idx: u32, generation: u64) -> Self {
|
||||
Self { idx, generation }
|
||||
}
|
||||
}
|
||||
|
||||
//ThingVec
|
||||
#[allow(dead_code)]
|
||||
#[allow(dead_code, clippy::iter_not_returning_iterator)]
|
||||
impl $container_name {
|
||||
pub fn new() -> Self {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
vec: Vec::new(),
|
||||
cur_generation: 0,
|
||||
@@ -78,7 +78,7 @@ macro_rules! gen_id {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_handle(cell: &$cell_name, idx: usize) -> $handle_name {
|
||||
pub const fn get_handle(cell: &$cell_name, idx: usize) -> $handle_name {
|
||||
$handle_name {
|
||||
idx: idx as u32,
|
||||
generation: cell.generation,
|
||||
|
||||
+37
-40
@@ -11,7 +11,7 @@ mod smithay_wrapper;
|
||||
mod time;
|
||||
mod window;
|
||||
use comp::Application;
|
||||
use display::{DisplayInitParams, DisplayVec};
|
||||
use display::{Display, DisplayInitParams, DisplayVec};
|
||||
use event_queue::SyncEventQueue;
|
||||
use process::ProcessVec;
|
||||
use server_ipc::WayVRServer;
|
||||
@@ -21,7 +21,7 @@ use smithay::{
|
||||
egl,
|
||||
renderer::{gles::GlesRenderer, ImportDma},
|
||||
},
|
||||
input::SeatState,
|
||||
input::{keyboard::XkbConfig, SeatState},
|
||||
output::{Mode, Output},
|
||||
reexports::wayland_server::{self, backend::ClientId},
|
||||
wayland::{
|
||||
@@ -43,7 +43,6 @@ use wayvr_ipc::{packet_client, packet_server};
|
||||
use crate::{hid::MODS_TO_KEYS, state::AppState};
|
||||
|
||||
const STR_INVALID_HANDLE_DISP: &str = "Invalid display handle";
|
||||
const STR_INVALID_HANDLE_PROCESS: &str = "Invalid process handle";
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WaylandEnv {
|
||||
@@ -96,10 +95,10 @@ pub enum BlitMethod {
|
||||
}
|
||||
|
||||
impl BlitMethod {
|
||||
pub fn from_string(str: &str) -> Option<BlitMethod> {
|
||||
pub fn from_string(str: &str) -> Option<Self> {
|
||||
match str {
|
||||
"dmabuf" => Some(BlitMethod::Dmabuf),
|
||||
"software" => Some(BlitMethod::Software),
|
||||
"dmabuf" => Some(Self::Dmabuf),
|
||||
"software" => Some(Self::Software),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -150,6 +149,7 @@ pub enum TickTask {
|
||||
}
|
||||
|
||||
impl WayVR {
|
||||
#[allow(clippy::too_many_lines, clippy::cognitive_complexity)]
|
||||
pub fn new(config: Config) -> anyhow::Result<Self> {
|
||||
log::info!("Initializing WayVR");
|
||||
let display: wayland_server::Display<Application> = wayland_server::Display::new()?;
|
||||
@@ -208,28 +208,32 @@ impl WayVR {
|
||||
None
|
||||
}
|
||||
Err(err) => {
|
||||
log::warn!("dmabuf: Failed to get egl device for display: {}", err);
|
||||
log::warn!("dmabuf: Failed to get egl device for display: {err}");
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
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::<Application>(
|
||||
&display.handle(),
|
||||
&default_feedback,
|
||||
);
|
||||
(dmabuf_state, dmabuf_global, Some(default_feedback))
|
||||
} else {
|
||||
let dmabuf_formats = gles_renderer.dmabuf_formats();
|
||||
let mut dmabuf_state = DmabufState::new();
|
||||
let dmabuf_global =
|
||||
dmabuf_state.create_global::<Application>(&display.handle(), dmabuf_formats);
|
||||
(dmabuf_state, dmabuf_global, None)
|
||||
};
|
||||
let dmabuf_state = dmabuf_default_feedback.map_or_else(
|
||||
|| {
|
||||
let dmabuf_formats = gles_renderer.dmabuf_formats();
|
||||
let mut dmabuf_state = DmabufState::new();
|
||||
let dmabuf_global =
|
||||
dmabuf_state.create_global::<Application>(&display.handle(), dmabuf_formats);
|
||||
(dmabuf_state, dmabuf_global, None)
|
||||
},
|
||||
|default_feedback| {
|
||||
let mut dmabuf_state = DmabufState::new();
|
||||
let dmabuf_global = dmabuf_state
|
||||
.create_global_with_default_feedback::<Application>(
|
||||
&display.handle(),
|
||||
&default_feedback,
|
||||
);
|
||||
(dmabuf_state, dmabuf_global, Some(default_feedback))
|
||||
},
|
||||
);
|
||||
|
||||
let seat_keyboard = seat.add_keyboard(
|
||||
Default::default(),
|
||||
XkbConfig::default(),
|
||||
config.keyboard_repeat_delay_ms as i32,
|
||||
config.keyboard_repeat_rate as i32,
|
||||
)?;
|
||||
@@ -278,7 +282,7 @@ impl WayVR {
|
||||
.state
|
||||
.displays
|
||||
.get_mut(&display)
|
||||
.ok_or(anyhow::anyhow!(STR_INVALID_HANDLE_DISP))?;
|
||||
.ok_or_else(|| anyhow::anyhow!(STR_INVALID_HANDLE_DISP))?;
|
||||
|
||||
if !display.wants_redraw {
|
||||
// Nothing changed, do not render
|
||||
@@ -298,6 +302,7 @@ impl WayVR {
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines, clippy::cognitive_complexity)]
|
||||
pub fn tick_events(&mut self, app: &AppState) -> anyhow::Result<Vec<TickTask>> {
|
||||
let mut tasks: Vec<TickTask> = Vec::new();
|
||||
|
||||
@@ -305,7 +310,7 @@ impl WayVR {
|
||||
state: &mut self.state,
|
||||
tasks: &mut tasks,
|
||||
app,
|
||||
})?;
|
||||
});
|
||||
|
||||
// Check for redraw events
|
||||
self.state.displays.iter_mut(&mut |_, disp| {
|
||||
@@ -458,6 +463,7 @@ impl WayVR {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn get_primary_display(displays: &DisplayVec) -> Option<display::DisplayHandle> {
|
||||
for (idx, cell) in displays.vec.iter().enumerate() {
|
||||
if let Some(cell) = cell {
|
||||
@@ -503,21 +509,12 @@ impl WayVRState {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_mouse_up(&mut self, display: display::DisplayHandle, index: MouseIndex) {
|
||||
if let Some(display) = self.displays.get(&display) {
|
||||
display.send_mouse_up(&mut self.manager, index);
|
||||
}
|
||||
pub fn send_mouse_up(&mut self, index: MouseIndex) {
|
||||
Display::send_mouse_up(&mut self.manager, index);
|
||||
}
|
||||
|
||||
pub fn send_mouse_scroll(
|
||||
&mut self,
|
||||
display: display::DisplayHandle,
|
||||
delta_y: f32,
|
||||
delta_x: f32,
|
||||
) {
|
||||
if let Some(display) = self.displays.get(&display) {
|
||||
display.send_mouse_scroll(&mut self.manager, delta_y, delta_x);
|
||||
}
|
||||
pub fn send_mouse_scroll(&mut self, delta_y: f32, delta_x: f32) {
|
||||
Display::send_mouse_scroll(&mut self.manager, delta_y, delta_x);
|
||||
}
|
||||
|
||||
pub fn send_key(&mut self, virtual_key: u32, down: bool) {
|
||||
@@ -617,7 +614,7 @@ impl WayVRState {
|
||||
|
||||
self.manager.cleanup_clients();
|
||||
|
||||
for client in self.manager.clients.iter() {
|
||||
for client in &self.manager.clients {
|
||||
if client.display_handle == handle {
|
||||
// This shouldn't happen, but make sure we are all set to destroy this display
|
||||
anyhow::bail!("Wayland client still exists");
|
||||
@@ -684,8 +681,8 @@ impl WayVRState {
|
||||
) -> process::ProcessHandle {
|
||||
self.processes
|
||||
.add(process::Process::External(process::ExternalProcess {
|
||||
display_handle,
|
||||
pid,
|
||||
display_handle,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -700,7 +697,7 @@ impl WayVRState {
|
||||
let display = self
|
||||
.displays
|
||||
.get_mut(&display_handle)
|
||||
.ok_or(anyhow::anyhow!(STR_INVALID_HANDLE_DISP))?;
|
||||
.ok_or_else(|| anyhow::anyhow!(STR_INVALID_HANDLE_DISP))?;
|
||||
|
||||
let res = display.spawn_process(exec_path, args, env)?;
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ use crate::gen_id;
|
||||
use super::display;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
pub struct WayVRProcess {
|
||||
pub auth_key: String,
|
||||
pub child: std::process::Child,
|
||||
@@ -32,45 +33,45 @@ pub enum Process {
|
||||
}
|
||||
|
||||
impl Process {
|
||||
pub fn display_handle(&self) -> display::DisplayHandle {
|
||||
pub const fn display_handle(&self) -> display::DisplayHandle {
|
||||
match self {
|
||||
Process::Managed(p) => p.display_handle,
|
||||
Process::External(p) => p.display_handle,
|
||||
Self::Managed(p) => p.display_handle,
|
||||
Self::External(p) => p.display_handle,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_running(&mut self) -> bool {
|
||||
match self {
|
||||
Process::Managed(p) => p.is_running(),
|
||||
Process::External(p) => p.is_running(),
|
||||
Self::Managed(p) => p.is_running(),
|
||||
Self::External(p) => p.is_running(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn terminate(&mut self) {
|
||||
match self {
|
||||
Process::Managed(p) => p.terminate(),
|
||||
Process::External(p) => p.terminate(),
|
||||
Self::Managed(p) => p.terminate(),
|
||||
Self::External(p) => p.terminate(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_name(&self) -> String {
|
||||
match self {
|
||||
Process::Managed(p) => p.get_name().unwrap_or(String::from("unknown")),
|
||||
Process::External(p) => p.get_name().unwrap_or(String::from("unknown")),
|
||||
Self::Managed(p) => p.get_name().unwrap_or_else(|| String::from("unknown")),
|
||||
Self::External(p) => p.get_name().unwrap_or_else(|| String::from("unknown")),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_packet(&self, handle: ProcessHandle) -> packet_server::WvrProcess {
|
||||
match self {
|
||||
Process::Managed(p) => packet_server::WvrProcess {
|
||||
name: p.get_name().unwrap_or(String::from("unknown")),
|
||||
Self::Managed(p) => packet_server::WvrProcess {
|
||||
name: p.get_name().unwrap_or_else(|| String::from("unknown")),
|
||||
userdata: p.userdata.clone(),
|
||||
display_handle: p.display_handle.as_packet(),
|
||||
handle: handle.as_packet(),
|
||||
},
|
||||
Process::External(p) => packet_server::WvrProcess {
|
||||
name: p.get_name().unwrap_or(String::from("unknown")),
|
||||
userdata: Default::default(),
|
||||
Self::External(p) => packet_server::WvrProcess {
|
||||
name: p.get_name().unwrap_or_else(|| String::from("unknown")),
|
||||
userdata: HashMap::default(),
|
||||
display_handle: p.display_handle.as_packet(),
|
||||
handle: handle.as_packet(),
|
||||
},
|
||||
@@ -89,7 +90,7 @@ impl Drop for WayVRProcess {
|
||||
}
|
||||
|
||||
fn get_process_env_value(pid: i32, key: &str) -> anyhow::Result<Option<String>> {
|
||||
let path = format!("/proc/{}/environ", pid);
|
||||
let path = format!("/proc/{pid}/environ");
|
||||
let mut env_data = String::new();
|
||||
std::fs::File::open(path)?.read_to_string(&mut env_data)?;
|
||||
let lines: Vec<&str> = env_data.split('\0').filter(|s| !s.is_empty()).collect();
|
||||
@@ -112,7 +113,7 @@ impl WayVRProcess {
|
||||
Ok(None) => true,
|
||||
Err(e) => {
|
||||
// this shouldn't happen
|
||||
log::error!("Child::try_wait failed: {}", e);
|
||||
log::error!("Child::try_wait failed: {e}");
|
||||
false
|
||||
}
|
||||
}
|
||||
@@ -131,7 +132,7 @@ impl WayVRProcess {
|
||||
}
|
||||
|
||||
fn get_exec_name_from_pid(pid: u32) -> Option<String> {
|
||||
let path = format!("/proc/{}/exe", pid);
|
||||
let path = format!("/proc/{pid}/exe");
|
||||
match std::fs::read_link(&path) {
|
||||
Ok(buf) => {
|
||||
if let Some(process_name) = buf.file_name().and_then(|s| s.to_str()) {
|
||||
@@ -170,7 +171,7 @@ impl ExternalProcess {
|
||||
gen_id!(ProcessVec, Process, ProcessCell, ProcessHandle);
|
||||
|
||||
pub fn find_by_pid(processes: &ProcessVec, pid: u32) -> Option<ProcessHandle> {
|
||||
log::debug!("Finding process with PID {}", pid);
|
||||
log::debug!("Finding process with PID {pid}");
|
||||
|
||||
for (idx, cell) in processes.vec.iter().enumerate() {
|
||||
let Some(cell) = cell else {
|
||||
@@ -205,19 +206,19 @@ pub fn find_by_pid(processes: &ProcessVec, pid: u32) -> Option<ProcessHandle> {
|
||||
}
|
||||
}
|
||||
|
||||
log::debug!("Process find with PID {} failed", pid);
|
||||
log::debug!("Process find with PID {pid} failed");
|
||||
None
|
||||
}
|
||||
|
||||
impl ProcessHandle {
|
||||
pub fn from_packet(handle: packet_server::WvrProcessHandle) -> Self {
|
||||
pub const fn from_packet(handle: packet_server::WvrProcessHandle) -> Self {
|
||||
Self {
|
||||
generation: handle.generation,
|
||||
idx: handle.idx,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_packet(&self) -> packet_server::WvrProcessHandle {
|
||||
pub const fn as_packet(&self) -> packet_server::WvrProcessHandle {
|
||||
packet_server::WvrProcessHandle {
|
||||
idx: self.idx,
|
||||
generation: self.generation,
|
||||
|
||||
@@ -44,11 +44,11 @@ fn read_check(expected_size: u32, res: std::io::Result<usize>) -> bool {
|
||||
if count == 0 {
|
||||
return false;
|
||||
}
|
||||
if count as u32 != expected_size {
|
||||
log::error!("count {} is not {}", count, expected_size);
|
||||
false
|
||||
} else {
|
||||
if count as u32 == expected_size {
|
||||
true // read succeeded
|
||||
} else {
|
||||
log::error!("count {count} is not {expected_size}");
|
||||
false
|
||||
}
|
||||
}
|
||||
Err(_e) => {
|
||||
@@ -63,10 +63,10 @@ type Payload = SmallVec<[u8; 64]>;
|
||||
fn read_payload(conn: &mut local_socket::Stream, size: u32) -> Option<Payload> {
|
||||
let mut payload = Payload::new();
|
||||
payload.resize(size as usize, 0);
|
||||
if !read_check(size, conn.read(&mut payload)) {
|
||||
None
|
||||
} else {
|
||||
if read_check(size, conn.read(&mut payload)) {
|
||||
Some(payload)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ pub fn gen_env_vec(input: &[String]) -> Vec<(&str, &str)> {
|
||||
}
|
||||
|
||||
impl Connection {
|
||||
fn new(conn: local_socket::Stream) -> Self {
|
||||
const fn new(conn: local_socket::Stream) -> Self {
|
||||
Self {
|
||||
conn,
|
||||
alive: true,
|
||||
@@ -222,10 +222,9 @@ impl Connection {
|
||||
false,
|
||||
)?;
|
||||
|
||||
params.tasks.push(TickTask::NewDisplay(
|
||||
packet_params.clone(),
|
||||
Some(display_handle),
|
||||
));
|
||||
params
|
||||
.tasks
|
||||
.push(TickTask::NewDisplay(packet_params, Some(display_handle)));
|
||||
|
||||
send_packet(
|
||||
&mut self.conn,
|
||||
@@ -246,7 +245,7 @@ impl Connection {
|
||||
let res = params
|
||||
.state
|
||||
.destroy_display(display::DisplayHandle::from_packet(handle))
|
||||
.map_err(|e| format!("{:?}", e));
|
||||
.map_err(|e| format!("{e:?}"));
|
||||
|
||||
send_packet(
|
||||
&mut self.conn,
|
||||
@@ -256,29 +255,25 @@ impl Connection {
|
||||
}
|
||||
|
||||
fn handle_wvr_display_set_visible(
|
||||
&mut self,
|
||||
params: &mut TickParams,
|
||||
handle: packet_server::WvrDisplayHandle,
|
||||
visible: bool,
|
||||
) -> anyhow::Result<()> {
|
||||
) {
|
||||
params.state.signals.send(WayVRSignal::DisplayVisibility(
|
||||
display::DisplayHandle::from_packet(handle),
|
||||
visible,
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_wvr_display_set_window_layout(
|
||||
&mut self,
|
||||
params: &mut TickParams,
|
||||
handle: packet_server::WvrDisplayHandle,
|
||||
layout: packet_server::WvrDisplayWindowLayout,
|
||||
) -> anyhow::Result<()> {
|
||||
) {
|
||||
params.state.signals.send(WayVRSignal::DisplayWindowLayout(
|
||||
display::DisplayHandle::from_packet(handle),
|
||||
layout,
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_wvr_display_window_list(
|
||||
@@ -331,11 +326,10 @@ impl Connection {
|
||||
}
|
||||
|
||||
fn handle_wvr_window_set_visible(
|
||||
&mut self,
|
||||
params: &mut TickParams,
|
||||
handle: packet_server::WvrWindowHandle,
|
||||
visible: bool,
|
||||
) -> anyhow::Result<()> {
|
||||
) {
|
||||
let mut to_resize = None;
|
||||
|
||||
if let Some(window) = params
|
||||
@@ -355,8 +349,6 @@ impl Connection {
|
||||
display.trigger_rerender();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_wvr_process_launch(
|
||||
@@ -392,7 +384,7 @@ impl Connection {
|
||||
serial: ipc::Serial,
|
||||
display_handle: packet_server::WvrDisplayHandle,
|
||||
) -> anyhow::Result<()> {
|
||||
let native_handle = &display::DisplayHandle::from_packet(display_handle.clone());
|
||||
let native_handle = &display::DisplayHandle::from_packet(display_handle);
|
||||
let disp = params
|
||||
.state
|
||||
.displays
|
||||
@@ -440,20 +432,17 @@ impl Connection {
|
||||
|
||||
// This request doesn't return anything to the client
|
||||
fn handle_wvr_process_terminate(
|
||||
&mut self,
|
||||
params: &mut TickParams,
|
||||
process_handle: packet_server::WvrProcessHandle,
|
||||
) -> anyhow::Result<()> {
|
||||
let native_handle = &process::ProcessHandle::from_packet(process_handle.clone());
|
||||
) {
|
||||
let native_handle = &process::ProcessHandle::from_packet(process_handle);
|
||||
let process = params.state.processes.get_mut(native_handle);
|
||||
|
||||
let Some(process) = process else {
|
||||
return Ok(());
|
||||
return;
|
||||
};
|
||||
|
||||
process.terminate();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_wvr_process_get(
|
||||
@@ -462,7 +451,7 @@ impl Connection {
|
||||
serial: ipc::Serial,
|
||||
process_handle: packet_server::WvrProcessHandle,
|
||||
) -> anyhow::Result<()> {
|
||||
let native_handle = &process::ProcessHandle::from_packet(process_handle.clone());
|
||||
let native_handle = &process::ProcessHandle::from_packet(process_handle);
|
||||
let process = params
|
||||
.state
|
||||
.processes
|
||||
@@ -478,10 +467,9 @@ impl Connection {
|
||||
}
|
||||
|
||||
fn handle_wlx_haptics(
|
||||
&mut self,
|
||||
params: &mut TickParams,
|
||||
haptics_params: packet_client::WlxHapticsParams,
|
||||
) -> anyhow::Result<()> {
|
||||
) {
|
||||
params
|
||||
.state
|
||||
.tasks
|
||||
@@ -490,7 +478,6 @@ impl Connection {
|
||||
frequency: haptics_params.frequency,
|
||||
intensity: haptics_params.intensity,
|
||||
}));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_payload(&mut self, params: &mut TickParams, payload: Payload) -> anyhow::Result<()> {
|
||||
@@ -516,16 +503,16 @@ impl Connection {
|
||||
self.handle_wvr_display_remove(params, serial, display_handle)?;
|
||||
}
|
||||
PacketClient::WvrDisplaySetVisible(display_handle, visible) => {
|
||||
self.handle_wvr_display_set_visible(params, display_handle, visible)?;
|
||||
Self::handle_wvr_display_set_visible(params, display_handle, visible);
|
||||
}
|
||||
PacketClient::WvrDisplaySetWindowLayout(display_handle, layout) => {
|
||||
self.handle_wvr_display_set_window_layout(params, display_handle, layout)?;
|
||||
Self::handle_wvr_display_set_window_layout(params, display_handle, layout);
|
||||
}
|
||||
PacketClient::WvrDisplayWindowList(serial, display_handle) => {
|
||||
self.handle_wvr_display_window_list(params, serial, display_handle)?;
|
||||
}
|
||||
PacketClient::WvrWindowSetVisible(window_handle, visible) => {
|
||||
self.handle_wvr_window_set_visible(params, window_handle, visible)?;
|
||||
Self::handle_wvr_window_set_visible(params, window_handle, visible);
|
||||
}
|
||||
PacketClient::WvrProcessGet(serial, process_handle) => {
|
||||
self.handle_wvr_process_get(params, serial, process_handle)?;
|
||||
@@ -540,10 +527,10 @@ impl Connection {
|
||||
self.handle_wvr_display_create(params, serial, packet_params)?;
|
||||
}
|
||||
PacketClient::WvrProcessTerminate(process_handle) => {
|
||||
self.handle_wvr_process_terminate(params, process_handle)?;
|
||||
Self::handle_wvr_process_terminate(params, process_handle);
|
||||
}
|
||||
PacketClient::WlxHaptics(haptics_params) => {
|
||||
self.handle_wlx_haptics(params, haptics_params)?;
|
||||
Self::handle_wlx_haptics(params, haptics_params);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -554,9 +541,9 @@ impl Connection {
|
||||
log::debug!("payload size {}", payload.len());
|
||||
|
||||
if let Err(e) = self.process_payload(params, payload) {
|
||||
log::error!("Invalid payload from the client, closing connection: {}", e);
|
||||
log::error!("Invalid payload from the client, closing connection: {e}");
|
||||
// send also error message directly to the client before disconnecting
|
||||
self.kill(format!("{}", e).as_str());
|
||||
self.kill(format!("{e}").as_str());
|
||||
false
|
||||
} else {
|
||||
true
|
||||
@@ -589,8 +576,7 @@ impl Connection {
|
||||
if payload_size > size_limit {
|
||||
// over 128 KiB?
|
||||
log::error!(
|
||||
"Client sent a packet header with the size over {} bytes, closing connection.",
|
||||
size_limit
|
||||
"Client sent a packet header with the size over {size_limit} bytes, closing connection."
|
||||
);
|
||||
self.kill("Too big packet received (over 128 KiB)");
|
||||
return false;
|
||||
@@ -637,7 +623,7 @@ impl WayVRServer {
|
||||
Err(e) => anyhow::bail!("Failed to start WayVRServer IPC listener. Reason: {}", e),
|
||||
};
|
||||
|
||||
log::info!("WayVRServer IPC running at {}", printname);
|
||||
log::info!("WayVRServer IPC running at {printname}");
|
||||
|
||||
Ok(Self {
|
||||
listener,
|
||||
@@ -662,16 +648,15 @@ impl WayVRServer {
|
||||
self.connections.retain(|c| c.alive);
|
||||
}
|
||||
|
||||
pub fn tick(&mut self, params: &mut TickParams) -> anyhow::Result<()> {
|
||||
pub fn tick(&mut self, params: &mut TickParams) {
|
||||
self.accept_connections();
|
||||
self.tick_connections(params);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn broadcast(&mut self, packet: packet_server::PacketServer) {
|
||||
for connection in &mut self.connections {
|
||||
if let Err(e) = send_packet(&mut connection.conn, &ipc::data_encode(&packet)) {
|
||||
log::error!("failed to broadcast packet: {:?}", e);
|
||||
log::error!("failed to broadcast packet: {e:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ impl Window {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_pos(&mut self, pos_x: i32, pos_y: i32) {
|
||||
pub const fn set_pos(&mut self, pos_x: i32, pos_y: i32) {
|
||||
self.pos_x = pos_x;
|
||||
self.pos_y = pos_y;
|
||||
}
|
||||
@@ -52,7 +52,7 @@ pub struct WindowManager {
|
||||
}
|
||||
|
||||
impl WindowManager {
|
||||
pub fn new() -> Self {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
windows: WindowVec::new(),
|
||||
}
|
||||
@@ -86,14 +86,14 @@ impl WindowManager {
|
||||
gen_id!(WindowVec, Window, WindowCell, WindowHandle);
|
||||
|
||||
impl WindowHandle {
|
||||
pub fn from_packet(handle: packet_server::WvrWindowHandle) -> Self {
|
||||
pub const fn from_packet(handle: packet_server::WvrWindowHandle) -> Self {
|
||||
Self {
|
||||
generation: handle.generation,
|
||||
idx: handle.idx,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_packet(&self) -> packet_server::WvrWindowHandle {
|
||||
pub const fn as_packet(&self) -> packet_server::WvrWindowHandle {
|
||||
packet_server::WvrWindowHandle {
|
||||
idx: self.idx,
|
||||
generation: self.generation,
|
||||
|
||||
+45
-44
@@ -69,79 +69,76 @@ impl AStrSetExt for AStrSet {
|
||||
|
||||
fn arc_rm(&mut self, value: &str) -> bool {
|
||||
let index = self.iter().position(|v| v.as_ref().eq(value));
|
||||
index
|
||||
.map(|i| {
|
||||
self.remove(i);
|
||||
true
|
||||
})
|
||||
.unwrap_or(false)
|
||||
index.is_some_and(|i| {
|
||||
self.remove(i);
|
||||
true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub type PwTokenMap = AStrMap<String>;
|
||||
|
||||
pub fn def_watch_pos() -> Vec3A {
|
||||
pub const fn def_watch_pos() -> Vec3A {
|
||||
vec3a(-0.03, -0.01, 0.125)
|
||||
}
|
||||
|
||||
pub fn def_watch_rot() -> Quat {
|
||||
Quat::from_xyzw(-0.7071066, 0.0007963618, 0.7071066, 0.0)
|
||||
pub const fn def_watch_rot() -> Quat {
|
||||
Quat::from_xyzw(-0.707_106_6, 0.000_796_361_8, 0.707_106_6, 0.0)
|
||||
}
|
||||
|
||||
pub fn def_left() -> LeftRight {
|
||||
pub const fn def_left() -> LeftRight {
|
||||
LeftRight::Left
|
||||
}
|
||||
|
||||
pub fn def_pw_tokens() -> PwTokenMap {
|
||||
pub const fn def_pw_tokens() -> PwTokenMap {
|
||||
AStrMap::new()
|
||||
}
|
||||
|
||||
fn def_mouse_move_interval_ms() -> u32 {
|
||||
const fn def_mouse_move_interval_ms() -> u32 {
|
||||
10 // 100fps
|
||||
}
|
||||
|
||||
fn def_click_freeze_time_ms() -> u32 {
|
||||
const fn def_click_freeze_time_ms() -> u32 {
|
||||
300
|
||||
}
|
||||
|
||||
pub fn def_true() -> bool {
|
||||
pub const fn def_true() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn def_false() -> bool {
|
||||
const fn def_false() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn def_one() -> f32 {
|
||||
const fn def_one() -> f32 {
|
||||
1.0
|
||||
}
|
||||
|
||||
pub fn def_half() -> f32 {
|
||||
pub const fn def_half() -> f32 {
|
||||
0.5
|
||||
}
|
||||
|
||||
pub fn def_point7() -> f32 {
|
||||
pub const fn def_point7() -> f32 {
|
||||
0.7
|
||||
}
|
||||
|
||||
pub fn def_point3() -> f32 {
|
||||
pub const fn def_point3() -> f32 {
|
||||
0.3
|
||||
}
|
||||
|
||||
fn def_osc_port() -> u16 {
|
||||
const fn def_osc_port() -> u16 {
|
||||
9000
|
||||
}
|
||||
|
||||
fn def_empty_vec_string() -> Vec<String> {
|
||||
const fn def_empty_vec_string() -> Vec<String> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
fn def_timezones() -> Vec<String> {
|
||||
let offset = chrono::Local::now().offset().fix();
|
||||
|
||||
const EMEA: i32 = -60 * 60; // UTC-1
|
||||
const APAC: i32 = 5 * 60 * 60; // UTC+5
|
||||
|
||||
let offset = chrono::Local::now().offset().fix();
|
||||
match offset.local_minus_utc() {
|
||||
i32::MIN..EMEA => vec!["Europe/Paris".into(), "Asia/Tokyo".into()],
|
||||
EMEA..APAC => vec!["America/New_York".into(), "Asia/Tokyo".into()],
|
||||
@@ -149,15 +146,15 @@ fn def_timezones() -> Vec<String> {
|
||||
}
|
||||
}
|
||||
|
||||
fn def_screens() -> AStrSet {
|
||||
const fn def_screens() -> AStrSet {
|
||||
AStrSet::new()
|
||||
}
|
||||
|
||||
fn def_curve_values() -> AStrMap<f32> {
|
||||
const fn def_curve_values() -> AStrMap<f32> {
|
||||
AStrMap::new()
|
||||
}
|
||||
|
||||
fn def_transforms() -> AStrMap<Affine3A> {
|
||||
const fn def_transforms() -> AStrMap<Affine3A> {
|
||||
AStrMap::new()
|
||||
}
|
||||
|
||||
@@ -177,7 +174,7 @@ fn def_font() -> Arc<str> {
|
||||
"LiberationSans:style=Bold".into()
|
||||
}
|
||||
|
||||
fn def_max_height() -> u16 {
|
||||
const fn def_max_height() -> u16 {
|
||||
1440
|
||||
}
|
||||
|
||||
@@ -276,6 +273,12 @@ pub struct GeneralConfig {
|
||||
#[serde(default = "def_false")]
|
||||
pub focus_follows_mouse_mode: bool,
|
||||
|
||||
#[serde(default = "def_false")]
|
||||
pub block_game_input: bool,
|
||||
|
||||
#[serde(default = "def_true")]
|
||||
pub block_game_input_ignore_watch: bool,
|
||||
|
||||
#[serde(default = "def_font")]
|
||||
pub primary_font: Arc<str>,
|
||||
|
||||
@@ -294,7 +297,7 @@ pub struct GeneralConfig {
|
||||
#[serde(default = "def_max_height")]
|
||||
pub screen_max_height: u16,
|
||||
|
||||
#[serde(default = "def_true")]
|
||||
#[serde(default = "def_false")]
|
||||
pub screen_render_down: bool,
|
||||
|
||||
#[serde(default = "def_point3")]
|
||||
@@ -315,23 +318,21 @@ pub struct GeneralConfig {
|
||||
|
||||
impl GeneralConfig {
|
||||
fn sanitize_range(name: &str, val: f32, from: f32, to: f32) {
|
||||
if !val.is_normal() || val < from || val > to {
|
||||
panic!(
|
||||
"GeneralConfig: {} needs to be between {} and {}",
|
||||
name, from, to
|
||||
);
|
||||
}
|
||||
assert!(
|
||||
!(!val.is_normal() || val < from || val > to),
|
||||
"GeneralConfig: {name} needs to be between {from} and {to}"
|
||||
);
|
||||
}
|
||||
|
||||
pub fn load_from_disk() -> GeneralConfig {
|
||||
pub fn load_from_disk() -> Self {
|
||||
let config = load_general();
|
||||
config.post_load();
|
||||
config
|
||||
}
|
||||
|
||||
fn post_load(&self) {
|
||||
GeneralConfig::sanitize_range("keyboard_scale", self.keyboard_scale, 0.05, 5.0);
|
||||
GeneralConfig::sanitize_range("desktop_view_scale", self.desktop_view_scale, 0.05, 5.0);
|
||||
Self::sanitize_range("keyboard_scale", self.keyboard_scale, 0.05, 5.0);
|
||||
Self::sanitize_range("desktop_view_scale", self.desktop_view_scale, 0.05, 5.0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,8 +375,8 @@ where
|
||||
match serde_yaml::from_str::<T>(yaml) {
|
||||
Ok(d) => return d,
|
||||
Err(e) => {
|
||||
error!("Failed to parse {}, falling back to defaults.", file_name);
|
||||
error!("{}", e);
|
||||
error!("Failed to parse {file_name}, falling back to defaults.");
|
||||
error!("{e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -384,7 +385,7 @@ where
|
||||
}
|
||||
|
||||
pub fn load_custom_ui(name: &str) -> anyhow::Result<ModularUiConfig> {
|
||||
let filename = format!("{}.yaml", name);
|
||||
let filename = format!("{name}.yaml");
|
||||
let Some(yaml_data) = config_io::load(&filename) else {
|
||||
bail!("Could not read file at {}", &filename);
|
||||
};
|
||||
@@ -416,13 +417,13 @@ where
|
||||
.filter_map(|r| match r {
|
||||
Ok(entry) => Some(entry),
|
||||
Err(e) => {
|
||||
error!("Failed to read conf.d directory: {}", e);
|
||||
error!("Failed to read conf.d directory: {e}");
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
// Sort paths alphabetically
|
||||
paths.sort_by_key(|dir| dir.path());
|
||||
paths.sort_by_key(std::fs::DirEntry::path);
|
||||
for path in paths {
|
||||
log::info!("Loading config file: {}", path.path().to_string_lossy());
|
||||
settings_builder = settings_builder.add_source(File::from(path.path()));
|
||||
@@ -433,11 +434,11 @@ where
|
||||
Ok(settings) => match settings.try_deserialize::<ConfigData>() {
|
||||
Ok(config) => config,
|
||||
Err(e) => {
|
||||
panic!("Failed to deserialize settings: {}", e);
|
||||
panic!("Failed to deserialize settings: {e}");
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
panic!("Failed to build settings: {}", e);
|
||||
panic!("Failed to build settings: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-14
@@ -1,6 +1,5 @@
|
||||
use log::error;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::path::PathBuf;
|
||||
use std::{path::PathBuf, sync::LazyLock};
|
||||
|
||||
pub enum ConfigRoot {
|
||||
Generic,
|
||||
@@ -10,17 +9,14 @@ pub enum ConfigRoot {
|
||||
|
||||
const FALLBACK_CONFIG_PATH: &str = "/tmp/wlxoverlay";
|
||||
|
||||
static CONFIG_ROOT_PATH: Lazy<PathBuf> = Lazy::new(|| {
|
||||
static CONFIG_ROOT_PATH: LazyLock<PathBuf> = LazyLock::new(|| {
|
||||
if let Ok(xdg_dirs) = xdg::BaseDirectories::new() {
|
||||
let mut dir = xdg_dirs.get_config_home();
|
||||
dir.push("wlxoverlay");
|
||||
return dir;
|
||||
}
|
||||
//Return fallback config path
|
||||
error!(
|
||||
"Err: Failed to find config path, using {}",
|
||||
FALLBACK_CONFIG_PATH
|
||||
);
|
||||
error!("Err: Failed to find config path, using {FALLBACK_CONFIG_PATH}");
|
||||
PathBuf::from(FALLBACK_CONFIG_PATH)
|
||||
});
|
||||
|
||||
@@ -31,8 +27,8 @@ pub fn get_config_root() -> PathBuf {
|
||||
impl ConfigRoot {
|
||||
pub fn get_conf_d_path(&self) -> PathBuf {
|
||||
get_config_root().join(match self {
|
||||
ConfigRoot::Generic => "conf.d",
|
||||
ConfigRoot::WayVR => "wayvr.conf.d",
|
||||
Self::Generic => "conf.d",
|
||||
Self::WayVR => "wayvr.conf.d",
|
||||
})
|
||||
}
|
||||
|
||||
@@ -55,9 +51,5 @@ pub fn load(filename: &str) -> Option<String> {
|
||||
let path = get_config_file_path(filename);
|
||||
log::info!("Loading config: {}", path.to_string_lossy());
|
||||
|
||||
if let Ok(data) = std::fs::read_to_string(path) {
|
||||
Some(data)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
std::fs::read_to_string(path).ok()
|
||||
}
|
||||
+20
-20
@@ -33,23 +33,23 @@ pub enum AttachTo {
|
||||
}
|
||||
|
||||
impl AttachTo {
|
||||
pub fn get_relative_to(&self) -> RelativeTo {
|
||||
pub const fn get_relative_to(&self) -> RelativeTo {
|
||||
match self {
|
||||
AttachTo::None => RelativeTo::None,
|
||||
AttachTo::HandLeft => RelativeTo::Hand(0),
|
||||
AttachTo::HandRight => RelativeTo::Hand(1),
|
||||
AttachTo::Stage => RelativeTo::Stage,
|
||||
AttachTo::Head => RelativeTo::Head,
|
||||
Self::None => RelativeTo::None,
|
||||
Self::HandLeft => RelativeTo::Hand(0),
|
||||
Self::HandRight => RelativeTo::Hand(1),
|
||||
Self::Stage => RelativeTo::Stage,
|
||||
Self::Head => RelativeTo::Head,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_packet(input: &wayvr_ipc::packet_client::AttachTo) -> AttachTo {
|
||||
pub const fn from_packet(input: &wayvr_ipc::packet_client::AttachTo) -> Self {
|
||||
match input {
|
||||
wayvr_ipc::packet_client::AttachTo::None => AttachTo::None,
|
||||
wayvr_ipc::packet_client::AttachTo::HandLeft => AttachTo::HandLeft,
|
||||
wayvr_ipc::packet_client::AttachTo::HandRight => AttachTo::HandRight,
|
||||
wayvr_ipc::packet_client::AttachTo::Head => AttachTo::Head,
|
||||
wayvr_ipc::packet_client::AttachTo::Stage => AttachTo::Stage,
|
||||
wayvr_ipc::packet_client::AttachTo::None => Self::None,
|
||||
wayvr_ipc::packet_client::AttachTo::HandLeft => Self::HandLeft,
|
||||
wayvr_ipc::packet_client::AttachTo::HandRight => Self::HandRight,
|
||||
wayvr_ipc::packet_client::AttachTo::Head => Self::Head,
|
||||
wayvr_ipc::packet_client::AttachTo::Stage => Self::Stage,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,23 +92,23 @@ impl WayVRCatalog {
|
||||
}
|
||||
}
|
||||
|
||||
fn def_false() -> bool {
|
||||
const fn def_false() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn def_true() -> bool {
|
||||
const fn def_true() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn def_autohide_delay() -> u32 {
|
||||
const fn def_autohide_delay() -> u32 {
|
||||
750
|
||||
}
|
||||
|
||||
fn def_keyboard_repeat_delay() -> u32 {
|
||||
const fn def_keyboard_repeat_delay() -> u32 {
|
||||
200
|
||||
}
|
||||
|
||||
fn def_keyboard_repeat_rate() -> u32 {
|
||||
const fn def_keyboard_repeat_rate() -> u32 {
|
||||
50
|
||||
}
|
||||
|
||||
@@ -173,14 +173,14 @@ impl WayVRConfig {
|
||||
|
||||
pub fn get_wayvr_config(
|
||||
config_general: &crate::config::GeneralConfig,
|
||||
config_wayvr: &crate::config_wayvr::WayVRConfig,
|
||||
config_wayvr: &Self,
|
||||
) -> anyhow::Result<wayvr::Config> {
|
||||
Ok(wayvr::Config {
|
||||
click_freeze_time_ms: config_general.click_freeze_time_ms,
|
||||
keyboard_repeat_delay_ms: config_wayvr.keyboard_repeat_delay,
|
||||
keyboard_repeat_rate: config_wayvr.keyboard_repeat_rate,
|
||||
blit_method: wayvr::BlitMethod::from_string(&config_wayvr.blit_method)
|
||||
.ok_or(anyhow::anyhow!("Unknown blit method"))?,
|
||||
.ok_or_else(|| anyhow::anyhow!("Unknown blit method"))?,
|
||||
auto_hide_delay: if config_wayvr.auto_hide {
|
||||
Some(config_wayvr.auto_hide_delay)
|
||||
} else {
|
||||
@@ -235,7 +235,7 @@ impl WayVRConfig {
|
||||
|
||||
pub fn load_wayvr() -> WayVRConfig {
|
||||
let config_root_path = config_io::ConfigRoot::WayVR.ensure_dir();
|
||||
log::info!("WayVR Config root path: {:?}", config_root_path);
|
||||
log::info!("WayVR Config root path: {config_root_path:?}");
|
||||
log::info!(
|
||||
"WayVR conf.d path: {:?}",
|
||||
config_io::ConfigRoot::WayVR.get_conf_d_path()
|
||||
|
||||
+2
-2
@@ -9,7 +9,7 @@ use vulkano::{
|
||||
DeviceSize,
|
||||
};
|
||||
|
||||
use super::WlxCommandBuffer;
|
||||
use super::WlxUploadsBuffer;
|
||||
|
||||
pub trait WlxCommandBufferDds {
|
||||
fn texture2d_dds<R>(&mut self, r: R) -> anyhow::Result<Arc<Image>>
|
||||
@@ -17,7 +17,7 @@ pub trait WlxCommandBufferDds {
|
||||
R: Read;
|
||||
}
|
||||
|
||||
impl WlxCommandBufferDds for WlxCommandBuffer {
|
||||
impl WlxCommandBufferDds for WlxUploadsBuffer {
|
||||
fn texture2d_dds<R>(&mut self, r: R) -> anyhow::Result<Arc<Image>>
|
||||
where
|
||||
R: Read,
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
use std::{mem::MaybeUninit, sync::Arc};
|
||||
|
||||
use smallvec::SmallVec;
|
||||
use vulkano::{
|
||||
device::Device,
|
||||
image::{sys::RawImage, ImageCreateInfo, SubresourceLayout},
|
||||
sync::Sharing,
|
||||
VulkanError, VulkanObject,
|
||||
};
|
||||
|
||||
#[allow(clippy::all, clippy::pedantic)]
|
||||
pub(super) unsafe fn create_dmabuf_image(
|
||||
device: Arc<Device>,
|
||||
create_info: ImageCreateInfo,
|
||||
) -> Result<RawImage, VulkanError> {
|
||||
let &ImageCreateInfo {
|
||||
flags,
|
||||
image_type,
|
||||
format,
|
||||
ref view_formats,
|
||||
extent,
|
||||
array_layers,
|
||||
mip_levels,
|
||||
samples,
|
||||
tiling,
|
||||
usage,
|
||||
stencil_usage,
|
||||
ref sharing,
|
||||
initial_layout,
|
||||
ref drm_format_modifiers,
|
||||
ref drm_format_modifier_plane_layouts,
|
||||
external_memory_handle_types,
|
||||
_ne: _,
|
||||
} = &create_info;
|
||||
|
||||
let (sharing_mode, queue_family_index_count, p_queue_family_indices) = match sharing {
|
||||
Sharing::Exclusive => (ash::vk::SharingMode::EXCLUSIVE, 0, &[] as _),
|
||||
Sharing::Concurrent(queue_family_indices) => (
|
||||
ash::vk::SharingMode::CONCURRENT,
|
||||
queue_family_indices.len() as u32,
|
||||
queue_family_indices.as_ptr(),
|
||||
),
|
||||
};
|
||||
|
||||
let mut create_info_vk = ash::vk::ImageCreateInfo {
|
||||
flags: flags.into(),
|
||||
image_type: image_type.into(),
|
||||
format: format.into(),
|
||||
extent: ash::vk::Extent3D {
|
||||
width: extent[0],
|
||||
height: extent[1],
|
||||
depth: extent[2],
|
||||
},
|
||||
mip_levels,
|
||||
array_layers,
|
||||
samples: samples.into(),
|
||||
tiling: tiling.into(),
|
||||
usage: usage.into(),
|
||||
sharing_mode,
|
||||
queue_family_index_count,
|
||||
p_queue_family_indices,
|
||||
initial_layout: initial_layout.into(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut drm_format_modifier_explicit_info_vk = None;
|
||||
let drm_format_modifier_plane_layouts_vk: SmallVec<[_; 4]>;
|
||||
let mut drm_format_modifier_list_info_vk = None;
|
||||
let mut external_memory_info_vk = None;
|
||||
let mut format_list_info_vk = None;
|
||||
let format_list_view_formats_vk: Vec<_>;
|
||||
let mut stencil_usage_info_vk = None;
|
||||
|
||||
if drm_format_modifiers.len() == 1 {
|
||||
drm_format_modifier_plane_layouts_vk = drm_format_modifier_plane_layouts
|
||||
.iter()
|
||||
.map(|subresource_layout| {
|
||||
let &SubresourceLayout {
|
||||
offset,
|
||||
size,
|
||||
row_pitch,
|
||||
array_pitch,
|
||||
depth_pitch,
|
||||
} = subresource_layout;
|
||||
|
||||
ash::vk::SubresourceLayout {
|
||||
offset,
|
||||
size,
|
||||
row_pitch,
|
||||
array_pitch: array_pitch.unwrap_or(0),
|
||||
depth_pitch: depth_pitch.unwrap_or(0),
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let next = drm_format_modifier_explicit_info_vk.insert(
|
||||
ash::vk::ImageDrmFormatModifierExplicitCreateInfoEXT {
|
||||
drm_format_modifier: drm_format_modifiers[0],
|
||||
drm_format_modifier_plane_count: drm_format_modifier_plane_layouts_vk.len() as u32,
|
||||
p_plane_layouts: drm_format_modifier_plane_layouts_vk.as_ptr(),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
next.p_next = create_info_vk.p_next;
|
||||
create_info_vk.p_next = next as *const _ as *const _;
|
||||
} else if drm_format_modifiers.len() > 1 {
|
||||
let next = drm_format_modifier_list_info_vk.insert(
|
||||
ash::vk::ImageDrmFormatModifierListCreateInfoEXT {
|
||||
drm_format_modifier_count: drm_format_modifiers.len() as u32,
|
||||
p_drm_format_modifiers: drm_format_modifiers.as_ptr(),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
next.p_next = create_info_vk.p_next;
|
||||
create_info_vk.p_next = next as *const _ as *const _;
|
||||
}
|
||||
|
||||
if !external_memory_handle_types.is_empty() {
|
||||
let next = external_memory_info_vk.insert(ash::vk::ExternalMemoryImageCreateInfo {
|
||||
handle_types: external_memory_handle_types.into(),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
next.p_next = create_info_vk.p_next;
|
||||
create_info_vk.p_next = next as *const _ as *const _;
|
||||
}
|
||||
|
||||
if !view_formats.is_empty() {
|
||||
format_list_view_formats_vk = view_formats
|
||||
.iter()
|
||||
.copied()
|
||||
.map(ash::vk::Format::from)
|
||||
.collect();
|
||||
|
||||
let next = format_list_info_vk.insert(ash::vk::ImageFormatListCreateInfo {
|
||||
view_format_count: format_list_view_formats_vk.len() as u32,
|
||||
p_view_formats: format_list_view_formats_vk.as_ptr(),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
next.p_next = create_info_vk.p_next;
|
||||
create_info_vk.p_next = next as *const _ as *const _;
|
||||
}
|
||||
|
||||
if let Some(stencil_usage) = stencil_usage {
|
||||
let next = stencil_usage_info_vk.insert(ash::vk::ImageStencilUsageCreateInfo {
|
||||
stencil_usage: stencil_usage.into(),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
next.p_next = create_info_vk.p_next;
|
||||
create_info_vk.p_next = next as *const _ as *const _;
|
||||
}
|
||||
|
||||
let handle = {
|
||||
let fns = device.fns();
|
||||
let mut output = MaybeUninit::uninit();
|
||||
(fns.v1_0.create_image)(
|
||||
device.handle(),
|
||||
&create_info_vk,
|
||||
std::ptr::null(),
|
||||
output.as_mut_ptr(),
|
||||
)
|
||||
.result()
|
||||
.map_err(VulkanError::from)?;
|
||||
output.assume_init()
|
||||
};
|
||||
|
||||
RawImage::from_handle(device, handle, create_info)
|
||||
}
|
||||
+427
-538
File diff suppressed because it is too large.
Load diff
@@ -196,7 +196,7 @@ impl<D, S> CanvasBuilder<D, S> {
|
||||
h,
|
||||
};
|
||||
let rect1 = Rect {
|
||||
x: x + w * 0.5 + 12.,
|
||||
x: w.mul_add(0.5, x) + 12.,
|
||||
y: y + h - (self.font_size as f32) + 8.,
|
||||
w,
|
||||
h,
|
||||
@@ -213,7 +213,7 @@ impl<D, S> CanvasBuilder<D, S> {
|
||||
};
|
||||
let rect1 = Rect {
|
||||
x,
|
||||
y: y + h * 0.5 + 2.0,
|
||||
y: h.mul_add(0.5, y) + 2.0,
|
||||
w,
|
||||
h: h * 0.5,
|
||||
};
|
||||
@@ -234,7 +234,7 @@ impl<D, S> CanvasBuilder<D, S> {
|
||||
h,
|
||||
};
|
||||
let rect2 = Rect {
|
||||
x: x + w * 0.5 + 8.,
|
||||
x: w.mul_add(0.5, x) + 8.,
|
||||
y: y + h - (self.font_size as f32) + 4.,
|
||||
w,
|
||||
h,
|
||||
|
||||
+15
-16
@@ -20,7 +20,7 @@ pub type ControlRendererHl<D, S> = fn(
|
||||
) -> anyhow::Result<()>;
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub(crate) struct Control<D, S> {
|
||||
pub struct Control<D, S> {
|
||||
pub state: Option<S>,
|
||||
pub rect: Rect,
|
||||
pub corner_radius: f32,
|
||||
@@ -30,7 +30,8 @@ pub(crate) struct Control<D, S> {
|
||||
pub size: isize,
|
||||
pub sprite: Option<Arc<ImageView>>,
|
||||
pub sprite_st: Vec4,
|
||||
pub(super) dirty: bool,
|
||||
pub(super) bg_dirty: bool,
|
||||
pub(super) fg_dirty: bool,
|
||||
|
||||
pub on_update: Option<fn(&mut Self, &mut D, &mut AppState)>,
|
||||
pub on_press: Option<fn(&mut Self, &mut D, &mut AppState, PointerMode)>,
|
||||
@@ -58,7 +59,8 @@ impl<D, S> Control<D, S> {
|
||||
text: Arc::from(""),
|
||||
sprite: None,
|
||||
sprite_st: Vec4::new(1., 1., 0., 0.),
|
||||
dirty: true,
|
||||
bg_dirty: true,
|
||||
fg_dirty: true,
|
||||
size: 24,
|
||||
state: None,
|
||||
on_update: None,
|
||||
@@ -72,37 +74,33 @@ impl<D, S> Control<D, S> {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn set_text(&mut self, text: &str) {
|
||||
if *self.text == *text {
|
||||
return;
|
||||
}
|
||||
self.text = text.into();
|
||||
self.dirty = true;
|
||||
self.fg_dirty = true;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn set_sprite(&mut self, sprite: Arc<ImageView>) {
|
||||
self.sprite.replace(sprite);
|
||||
self.dirty = true;
|
||||
self.bg_dirty = true;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn set_sprite_st(&mut self, sprite_st: Vec4) {
|
||||
if self.sprite_st == sprite_st {
|
||||
return;
|
||||
}
|
||||
self.sprite_st = sprite_st;
|
||||
self.dirty = true;
|
||||
self.bg_dirty = true;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn set_fg_color(&mut self, color: GuiColor) {
|
||||
if self.fg_color == color {
|
||||
return;
|
||||
}
|
||||
self.fg_color = color;
|
||||
self.dirty = true;
|
||||
self.fg_dirty = true;
|
||||
}
|
||||
|
||||
pub fn render_rounded_rect(
|
||||
@@ -174,7 +172,7 @@ impl<D, S> Control<D, S> {
|
||||
|
||||
let skew_radius = [clamped_radius / self.rect.w, clamped_radius / self.rect.h];
|
||||
|
||||
let set0 = canvas.pipeline_bg_color.uniform_buffer(
|
||||
let set0 = canvas.pipeline_hl_color.uniform_buffer(
|
||||
0,
|
||||
vec![
|
||||
color.x,
|
||||
@@ -186,9 +184,9 @@ impl<D, S> Control<D, S> {
|
||||
],
|
||||
)?;
|
||||
|
||||
let pass = canvas.pipeline_bg_color.create_pass(
|
||||
let pass = canvas.pipeline_hl_color.create_pass(
|
||||
[canvas.width as _, canvas.height as _],
|
||||
vertex_buffer.clone(),
|
||||
vertex_buffer,
|
||||
canvas.graphics.quad_indices.clone(),
|
||||
vec![set0],
|
||||
)?;
|
||||
@@ -251,9 +249,10 @@ impl<D, S> Control<D, S> {
|
||||
.fc
|
||||
.get_text_size(&self.text, self.size, canvas.graphics.clone())?;
|
||||
|
||||
let mut cur_y = self.rect.y + (self.rect.h) - (h * 0.5) - (self.size as f32 * 0.25);
|
||||
let mut cur_y =
|
||||
(self.size as f32).mul_add(-0.25, h.mul_add(-0.5, self.rect.y + (self.rect.h)));
|
||||
for line in self.text.lines() {
|
||||
let mut cur_x = self.rect.x + (self.rect.w * 0.5) - (w * 0.5);
|
||||
let mut cur_x = w.mul_add(-0.5, self.rect.w.mul_add(0.5, self.rect.x));
|
||||
for glyph in app
|
||||
.fc
|
||||
.get_glyphs(line, self.size, canvas.graphics.clone())?
|
||||
|
||||
+148
-166
@@ -1,21 +1,17 @@
|
||||
pub(crate) mod builder;
|
||||
pub(crate) mod control;
|
||||
pub mod builder;
|
||||
pub mod control;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use glam::{Vec2, Vec4};
|
||||
use vulkano::{
|
||||
command_buffer::CommandBufferUsage,
|
||||
format::Format,
|
||||
image::{view::ImageView, ImageLayout},
|
||||
};
|
||||
use vulkano::{command_buffer::CommandBufferUsage, format::Format, image::view::ImageView};
|
||||
|
||||
use crate::{
|
||||
backend::{
|
||||
input::{Haptics, InteractionHandler, PointerHit},
|
||||
overlay::{FrameTransform, OverlayBackend, OverlayRenderer},
|
||||
overlay::{FrameMeta, OverlayBackend, OverlayRenderer, ShouldRender},
|
||||
},
|
||||
graphics::{WlxGraphics, WlxPass, WlxPipeline, WlxPipelineLegacy, BLEND_ALPHA},
|
||||
graphics::{CommandBuffers, WlxGraphics, WlxPipeline, BLEND_ALPHA},
|
||||
state::AppState,
|
||||
};
|
||||
|
||||
@@ -35,16 +31,16 @@ pub struct CanvasData<D> {
|
||||
|
||||
graphics: Arc<WlxGraphics>,
|
||||
|
||||
pipeline_bg_color: Arc<WlxPipeline<WlxPipelineLegacy>>,
|
||||
pipeline_fg_glyph: Arc<WlxPipeline<WlxPipelineLegacy>>,
|
||||
pipeline_bg_sprite: Arc<WlxPipeline<WlxPipelineLegacy>>,
|
||||
pipeline_hl_sprite: Arc<WlxPipeline<WlxPipelineLegacy>>,
|
||||
pipeline_final: Arc<WlxPipeline<WlxPipelineLegacy>>,
|
||||
pipeline_bg_color: Arc<WlxPipeline>,
|
||||
pipeline_bg_sprite: Arc<WlxPipeline>,
|
||||
pipeline_fg_glyph: Arc<WlxPipeline>,
|
||||
pipeline_hl_color: Arc<WlxPipeline>,
|
||||
pipeline_hl_sprite: Arc<WlxPipeline>,
|
||||
}
|
||||
|
||||
pub struct Canvas<D, S> {
|
||||
controls: Vec<control::Control<D, S>>,
|
||||
canvas: CanvasData<D>,
|
||||
data: CanvasData<D>,
|
||||
|
||||
hover_controls: [Option<usize>; 2],
|
||||
pressed_controls: [Option<usize>; 2],
|
||||
@@ -53,10 +49,16 @@ pub struct Canvas<D, S> {
|
||||
interact_stride: usize,
|
||||
interact_rows: usize,
|
||||
|
||||
view_final: Arc<ImageView>,
|
||||
pipeline_final: Arc<WlxPipeline>,
|
||||
|
||||
pass_fg: WlxPass<WlxPipelineLegacy>,
|
||||
pass_bg: WlxPass<WlxPipelineLegacy>,
|
||||
view_fore: Arc<ImageView>,
|
||||
view_back: Arc<ImageView>,
|
||||
|
||||
format: Format,
|
||||
|
||||
back_dirty: bool,
|
||||
high_dirty: bool,
|
||||
fore_dirty: bool,
|
||||
}
|
||||
|
||||
impl<D, S> Canvas<D, S> {
|
||||
@@ -67,94 +69,74 @@ impl<D, S> Canvas<D, S> {
|
||||
format: Format,
|
||||
data: D,
|
||||
) -> anyhow::Result<Self> {
|
||||
let tex_fg = graphics.render_texture(width as _, height as _, format)?;
|
||||
let tex_bg = graphics.render_texture(width as _, height as _, format)?;
|
||||
let tex_final = graphics.render_texture(width as _, height as _, format)?;
|
||||
let tex_fore = graphics.render_texture(width as _, height as _, format)?;
|
||||
let tex_back = graphics.render_texture(width as _, height as _, format)?;
|
||||
|
||||
let view_fg = ImageView::new_default(tex_fg.clone())?;
|
||||
let view_bg = ImageView::new_default(tex_bg.clone())?;
|
||||
let view_final = ImageView::new_default(tex_final.clone())?;
|
||||
let view_fore = ImageView::new_default(tex_fore)?;
|
||||
let view_back = ImageView::new_default(tex_back)?;
|
||||
|
||||
let Ok(shaders) = graphics.shared_shaders.read() else {
|
||||
anyhow::bail!("Failed to lock shared shaders for reading");
|
||||
};
|
||||
|
||||
let vert = shaders.get("vert_common").unwrap().clone(); // want panic
|
||||
|
||||
let pipeline_bg_color = graphics.create_pipeline(
|
||||
view_bg.clone(),
|
||||
shaders.get("vert_common").unwrap().clone(), // want panic
|
||||
shaders.get("frag_color").unwrap().clone(), // want panic
|
||||
vert.clone(),
|
||||
shaders.get("frag_color").unwrap().clone(), // want panic
|
||||
format,
|
||||
Some(BLEND_ALPHA),
|
||||
)?;
|
||||
|
||||
let pipeline_fg_glyph = graphics.create_pipeline(
|
||||
view_fg.clone(),
|
||||
shaders.get("vert_common").unwrap().clone(), // want panic
|
||||
shaders.get("frag_glyph").unwrap().clone(), // want panic
|
||||
vert.clone(),
|
||||
shaders.get("frag_glyph").unwrap().clone(), // want panic
|
||||
format,
|
||||
Some(BLEND_ALPHA),
|
||||
)?;
|
||||
|
||||
let pipeline_bg_sprite = graphics.create_pipeline(
|
||||
view_fg.clone(),
|
||||
shaders.get("vert_common").unwrap().clone(), // want panic
|
||||
vert.clone(),
|
||||
shaders.get("frag_sprite2").unwrap().clone(), // want panic
|
||||
format,
|
||||
Some(BLEND_ALPHA),
|
||||
)?;
|
||||
|
||||
let pipeline_hl_color = graphics.create_pipeline(
|
||||
vert.clone(),
|
||||
shaders.get("frag_color").unwrap().clone(), // want panic
|
||||
graphics.native_format,
|
||||
Some(BLEND_ALPHA),
|
||||
)?;
|
||||
|
||||
let pipeline_hl_sprite = graphics.create_pipeline(
|
||||
view_fg.clone(),
|
||||
shaders.get("vert_common").unwrap().clone(), // want panic
|
||||
vert.clone(),
|
||||
shaders.get("frag_sprite2_hl").unwrap().clone(), // want panic
|
||||
format,
|
||||
graphics.native_format,
|
||||
Some(BLEND_ALPHA),
|
||||
)?;
|
||||
|
||||
let vertex_buffer =
|
||||
graphics.upload_verts(width as _, height as _, 0., 0., width as _, height as _)?;
|
||||
|
||||
let pipeline_final = graphics.create_pipeline_with_layouts(
|
||||
view_final.clone(),
|
||||
shaders.get("vert_common").unwrap().clone(), // want panic
|
||||
shaders.get("frag_sprite").unwrap().clone(), // want panic
|
||||
format,
|
||||
let pipeline_final = graphics.create_pipeline(
|
||||
vert,
|
||||
shaders.get("frag_srgb").unwrap().clone(), // want panic
|
||||
graphics.native_format,
|
||||
Some(BLEND_ALPHA),
|
||||
ImageLayout::TransferSrcOptimal,
|
||||
ImageLayout::TransferSrcOptimal,
|
||||
)?;
|
||||
|
||||
let set_fg =
|
||||
pipeline_final.uniform_sampler(0, view_fg.clone(), graphics.texture_filtering)?;
|
||||
let set_bg =
|
||||
pipeline_final.uniform_sampler(0, view_bg.clone(), graphics.texture_filtering)?;
|
||||
let pass_fg = pipeline_final.create_pass(
|
||||
[width as _, height as _],
|
||||
vertex_buffer.clone(),
|
||||
graphics.quad_indices.clone(),
|
||||
vec![set_fg],
|
||||
)?;
|
||||
let pass_bg = pipeline_final.create_pass(
|
||||
[width as _, height as _],
|
||||
vertex_buffer.clone(),
|
||||
graphics.quad_indices.clone(),
|
||||
vec![set_bg],
|
||||
)?;
|
||||
|
||||
let stride = width / RES_DIVIDER;
|
||||
let rows = height / RES_DIVIDER;
|
||||
|
||||
Ok(Self {
|
||||
canvas: CanvasData {
|
||||
data: CanvasData {
|
||||
data,
|
||||
width,
|
||||
height,
|
||||
graphics: graphics.clone(),
|
||||
pipeline_bg_color,
|
||||
pipeline_fg_glyph,
|
||||
pipeline_bg_sprite,
|
||||
pipeline_fg_glyph,
|
||||
pipeline_hl_color,
|
||||
pipeline_hl_sprite,
|
||||
pipeline_final,
|
||||
},
|
||||
controls: Vec::new(),
|
||||
hover_controls: [None, None],
|
||||
@@ -162,9 +144,13 @@ impl<D, S> Canvas<D, S> {
|
||||
interact_map: vec![None; stride * rows],
|
||||
interact_stride: stride,
|
||||
interact_rows: rows,
|
||||
view_final,
|
||||
pass_fg,
|
||||
pass_bg,
|
||||
pipeline_final,
|
||||
view_fore,
|
||||
view_back,
|
||||
format,
|
||||
back_dirty: false,
|
||||
high_dirty: false,
|
||||
fore_dirty: false,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -184,67 +170,42 @@ impl<D, S> Canvas<D, S> {
|
||||
}
|
||||
|
||||
fn interactive_get_idx(&self, uv: Vec2) -> Option<usize> {
|
||||
let x = (uv.x * self.canvas.width as f32) as usize;
|
||||
let y = (uv.y * self.canvas.height as f32) as usize;
|
||||
let x = (uv.x * self.data.width as f32) as usize;
|
||||
let y = (uv.y * self.data.height as f32) as usize;
|
||||
let x = (x / RES_DIVIDER).max(0).min(self.interact_stride - 1);
|
||||
let y = (y / RES_DIVIDER).max(0).min(self.interact_rows - 1);
|
||||
self.interact_map[y * self.interact_stride + x].map(|x| x as usize)
|
||||
}
|
||||
|
||||
fn render_bg(&mut self, app: &mut AppState) -> anyhow::Result<()> {
|
||||
let mut cmd_buffer = self
|
||||
.canvas
|
||||
.graphics
|
||||
.create_command_buffer(CommandBufferUsage::OneTimeSubmit)?;
|
||||
cmd_buffer.begin_render_pass(&self.canvas.pipeline_bg_color)?;
|
||||
for c in self.controls.iter_mut() {
|
||||
if let Some(fun) = c.on_render_bg {
|
||||
fun(c, &self.canvas, app, &mut cmd_buffer)?;
|
||||
}
|
||||
}
|
||||
cmd_buffer.end_render_pass()?;
|
||||
cmd_buffer.build_and_execute_now()
|
||||
}
|
||||
|
||||
fn render_fg(&mut self, app: &mut AppState) -> anyhow::Result<()> {
|
||||
let mut cmd_buffer = self
|
||||
.canvas
|
||||
.graphics
|
||||
.create_command_buffer(CommandBufferUsage::OneTimeSubmit)?;
|
||||
cmd_buffer.begin_render_pass(&self.canvas.pipeline_fg_glyph)?;
|
||||
for c in self.controls.iter_mut() {
|
||||
if let Some(fun) = c.on_render_fg {
|
||||
fun(c, &self.canvas, app, &mut cmd_buffer)?;
|
||||
}
|
||||
}
|
||||
cmd_buffer.end_render_pass()?;
|
||||
cmd_buffer.build_and_execute_now()
|
||||
}
|
||||
|
||||
pub fn data_mut(&mut self) -> &mut D {
|
||||
&mut self.canvas.data
|
||||
pub const fn data_mut(&mut self) -> &mut D {
|
||||
&mut self.data.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<D, S> InteractionHandler for Canvas<D, S> {
|
||||
fn on_left(&mut self, _app: &mut AppState, pointer: usize) {
|
||||
self.high_dirty = true;
|
||||
|
||||
self.hover_controls[pointer] = None;
|
||||
}
|
||||
fn on_hover(&mut self, _app: &mut AppState, hit: &PointerHit) -> Option<Haptics> {
|
||||
// render on every frame if we are being hovered
|
||||
self.high_dirty = true;
|
||||
|
||||
let old = self.hover_controls[hit.pointer];
|
||||
if let Some(i) = self.interactive_get_idx(hit.uv) {
|
||||
self.hover_controls[hit.pointer] = Some(i);
|
||||
} else {
|
||||
self.hover_controls[hit.pointer] = None;
|
||||
}
|
||||
if old != self.hover_controls[hit.pointer] {
|
||||
if old == self.hover_controls[hit.pointer] {
|
||||
None
|
||||
} else {
|
||||
Some(Haptics {
|
||||
intensity: 0.1,
|
||||
duration: 0.01,
|
||||
frequency: 5.0,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn on_pointer(&mut self, app: &mut AppState, hit: &PointerHit, pressed: bool) {
|
||||
@@ -259,11 +220,11 @@ impl<D, S> InteractionHandler for Canvas<D, S> {
|
||||
if pressed {
|
||||
if let Some(ref mut f) = c.on_press {
|
||||
self.pressed_controls[hit.pointer] = Some(idx);
|
||||
f(c, &mut self.canvas.data, app, hit.mode);
|
||||
f(c, &mut self.data.data, app, hit.mode);
|
||||
}
|
||||
} else if let Some(ref mut f) = c.on_release {
|
||||
self.pressed_controls[hit.pointer] = None;
|
||||
f(c, &mut self.canvas.data, app);
|
||||
f(c, &mut self.data.data, app);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -273,16 +234,15 @@ impl<D, S> InteractionHandler for Canvas<D, S> {
|
||||
if let Some(idx) = idx {
|
||||
let c = &mut self.controls[idx];
|
||||
if let Some(ref mut f) = c.on_scroll {
|
||||
f(c, &mut self.canvas.data, app, delta_y, delta_x);
|
||||
f(c, &mut self.data.data, app, delta_y, delta_x);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D, S> OverlayRenderer for Canvas<D, S> {
|
||||
fn init(&mut self, app: &mut AppState) -> anyhow::Result<()> {
|
||||
self.render_bg(app)?;
|
||||
self.render_fg(app)
|
||||
fn init(&mut self, _app: &mut AppState) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn pause(&mut self, _app: &mut AppState) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
@@ -290,61 +250,96 @@ impl<D, S> OverlayRenderer for Canvas<D, S> {
|
||||
fn resume(&mut self, _app: &mut AppState) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn render(&mut self, app: &mut AppState) -> anyhow::Result<()> {
|
||||
let mut dirty = false;
|
||||
|
||||
for c in self.controls.iter_mut() {
|
||||
fn should_render(&mut self, app: &mut AppState) -> anyhow::Result<ShouldRender> {
|
||||
for c in &mut self.controls {
|
||||
if let Some(fun) = c.on_update {
|
||||
fun(c, &mut self.canvas.data, app);
|
||||
fun(c, &mut self.data.data, app);
|
||||
}
|
||||
if c.dirty {
|
||||
dirty = true;
|
||||
c.dirty = false;
|
||||
if c.fg_dirty {
|
||||
self.fore_dirty = true;
|
||||
c.fg_dirty = false;
|
||||
}
|
||||
if c.bg_dirty {
|
||||
self.back_dirty = true;
|
||||
c.bg_dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
if dirty {
|
||||
self.render_bg(app)?;
|
||||
self.render_fg(app)?;
|
||||
}
|
||||
|
||||
/*
|
||||
let image = self.view_final.image().clone();
|
||||
if self.first_render {
|
||||
self.first_render = false;
|
||||
if self.back_dirty || self.fore_dirty || self.high_dirty {
|
||||
Ok(ShouldRender::Should)
|
||||
} else {
|
||||
self.canvas
|
||||
.graphics
|
||||
.transition_layout(
|
||||
image.clone(),
|
||||
ImageLayout::TransferSrcOptimal,
|
||||
ImageLayout::ColorAttachmentOptimal,
|
||||
)
|
||||
.wait(None)
|
||||
.unwrap();
|
||||
Ok(ShouldRender::Can)
|
||||
}
|
||||
*/
|
||||
}
|
||||
fn render(
|
||||
&mut self,
|
||||
app: &mut AppState,
|
||||
tgt: Arc<ImageView>,
|
||||
buf: &mut CommandBuffers,
|
||||
alpha: f32,
|
||||
) -> anyhow::Result<bool> {
|
||||
self.high_dirty = false;
|
||||
|
||||
let mut cmd_buffer = self
|
||||
.canvas
|
||||
.data
|
||||
.graphics
|
||||
.create_command_buffer(CommandBufferUsage::OneTimeSubmit)?;
|
||||
cmd_buffer.begin_render_pass(&self.canvas.pipeline_final)?;
|
||||
|
||||
// static background
|
||||
cmd_buffer.run_ref(&self.pass_bg)?;
|
||||
if self.back_dirty {
|
||||
cmd_buffer.begin_rendering(self.view_back.clone())?;
|
||||
for c in &mut self.controls {
|
||||
if let Some(fun) = c.on_render_bg {
|
||||
fun(c, &self.data, app, &mut cmd_buffer)?;
|
||||
}
|
||||
}
|
||||
cmd_buffer.end_rendering()?;
|
||||
self.back_dirty = false;
|
||||
}
|
||||
if self.fore_dirty {
|
||||
cmd_buffer.begin_rendering(self.view_fore.clone())?;
|
||||
for c in &mut self.controls {
|
||||
if let Some(fun) = c.on_render_fg {
|
||||
fun(c, &self.data, app, &mut cmd_buffer)?;
|
||||
}
|
||||
}
|
||||
cmd_buffer.end_rendering()?;
|
||||
self.fore_dirty = false;
|
||||
}
|
||||
|
||||
let set0_fg = self.pipeline_final.uniform_sampler(
|
||||
0,
|
||||
self.view_fore.clone(),
|
||||
app.graphics.texture_filtering,
|
||||
)?;
|
||||
let set0_bg = self.pipeline_final.uniform_sampler(
|
||||
0,
|
||||
self.view_back.clone(),
|
||||
app.graphics.texture_filtering,
|
||||
)?;
|
||||
let set1 = self.pipeline_final.uniform_buffer(1, vec![alpha])?;
|
||||
|
||||
let pass_fore = self
|
||||
.pipeline_final
|
||||
.create_pass_for_target(tgt.clone(), vec![set0_fg, set1.clone()])?;
|
||||
|
||||
let pass_back = self
|
||||
.pipeline_final
|
||||
.create_pass_for_target(tgt.clone(), vec![set0_bg, set1])?;
|
||||
|
||||
cmd_buffer.begin_rendering(tgt)?;
|
||||
cmd_buffer.run_ref(&pass_back)?;
|
||||
|
||||
for (i, c) in self.controls.iter_mut().enumerate() {
|
||||
if let Some(render) = c.on_render_hl {
|
||||
if let Some(test) = c.test_highlight {
|
||||
if let Some(hl_color) = test(c, &mut self.canvas.data, app) {
|
||||
render(c, &self.canvas, app, &mut cmd_buffer, hl_color)?;
|
||||
if let Some(hl_color) = test(c, &mut self.data.data, app) {
|
||||
render(c, &self.data, app, &mut cmd_buffer, hl_color)?;
|
||||
}
|
||||
}
|
||||
if self.hover_controls.contains(&Some(i)) {
|
||||
render(
|
||||
c,
|
||||
&self.canvas,
|
||||
&self.data,
|
||||
app,
|
||||
&mut cmd_buffer,
|
||||
Vec4::new(1., 1., 1., 0.3),
|
||||
@@ -354,30 +349,17 @@ impl<D, S> OverlayRenderer for Canvas<D, S> {
|
||||
}
|
||||
|
||||
// mostly static text
|
||||
cmd_buffer.run_ref(&self.pass_fg)?;
|
||||
cmd_buffer.run_ref(&pass_fore)?;
|
||||
|
||||
cmd_buffer.end_render_pass()?;
|
||||
cmd_buffer.build_and_execute_now()
|
||||
|
||||
/*
|
||||
self.canvas
|
||||
.graphics
|
||||
.transition_layout(
|
||||
image,
|
||||
ImageLayout::ColorAttachmentOptimal,
|
||||
ImageLayout::TransferSrcOptimal,
|
||||
)
|
||||
.wait(None)
|
||||
.unwrap();
|
||||
*/
|
||||
}
|
||||
fn view(&mut self) -> Option<Arc<ImageView>> {
|
||||
Some(self.view_final.clone())
|
||||
cmd_buffer.end_rendering()?;
|
||||
buf.push(cmd_buffer.build()?);
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn frame_transform(&mut self) -> Option<FrameTransform> {
|
||||
Some(FrameTransform {
|
||||
extent: self.view_final.image().extent(),
|
||||
fn frame_meta(&mut self) -> Option<FrameMeta> {
|
||||
Some(FrameMeta {
|
||||
extent: [self.data.width as _, self.data.height as _, 1],
|
||||
format: self.format,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
+42
-13
@@ -5,7 +5,7 @@ use freetype::{bitmap::PixelMode, face::LoadFlag, Face, Library};
|
||||
use idmap::IdMap;
|
||||
use vulkano::{command_buffer::CommandBufferUsage, format::Format, image::Image};
|
||||
|
||||
use crate::graphics::WlxGraphics;
|
||||
use crate::graphics::{WlxGraphics, WlxUploadsBuffer};
|
||||
|
||||
pub struct FontCache {
|
||||
primary_font: Arc<str>,
|
||||
@@ -39,7 +39,7 @@ impl FontCache {
|
||||
let ft = Library::init()?;
|
||||
let fc = FontConfig::default();
|
||||
|
||||
Ok(FontCache {
|
||||
Ok(Self {
|
||||
primary_font,
|
||||
fc,
|
||||
ft,
|
||||
@@ -55,14 +55,15 @@ impl FontCache {
|
||||
) -> anyhow::Result<(f32, f32)> {
|
||||
let sizef = size as f32;
|
||||
|
||||
let height = sizef + ((text.lines().count() as f32) - 1f32) * (sizef * 1.5);
|
||||
let height = ((text.lines().count() as f32) - 1f32).mul_add(sizef * 1.5, sizef);
|
||||
let mut cmd_buffer = None;
|
||||
|
||||
let mut max_w = sizef * 0.33;
|
||||
for line in text.lines() {
|
||||
let w: f32 = line
|
||||
.chars()
|
||||
.filter_map(|c| {
|
||||
self.get_glyph_for_cp(c as usize, size, graphics.clone())
|
||||
self.get_glyph_for_cp(c as usize, size, graphics.clone(), &mut cmd_buffer)
|
||||
.map(|glyph| glyph.advance)
|
||||
.ok()
|
||||
})
|
||||
@@ -72,6 +73,11 @@ impl FontCache {
|
||||
max_w = w;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(cmd_buffer) = cmd_buffer {
|
||||
cmd_buffer.build_and_execute_now()?;
|
||||
}
|
||||
|
||||
Ok((max_w, height))
|
||||
}
|
||||
|
||||
@@ -82,11 +88,23 @@ impl FontCache {
|
||||
graphics: Arc<WlxGraphics>,
|
||||
) -> anyhow::Result<Vec<Rc<Glyph>>> {
|
||||
let mut glyphs = Vec::new();
|
||||
let mut cmd_buffer = None;
|
||||
|
||||
for line in text.lines() {
|
||||
for c in line.chars() {
|
||||
glyphs.push(self.get_glyph_for_cp(c as usize, size, graphics.clone())?);
|
||||
glyphs.push(self.get_glyph_for_cp(
|
||||
c as usize,
|
||||
size,
|
||||
graphics.clone(),
|
||||
&mut cmd_buffer,
|
||||
)?);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(cmd_buffer) = cmd_buffer {
|
||||
cmd_buffer.build_and_execute_now()?;
|
||||
}
|
||||
|
||||
Ok(glyphs)
|
||||
}
|
||||
|
||||
@@ -125,26 +143,26 @@ impl FontCache {
|
||||
|
||||
if let Some(path) = pattern.filename() {
|
||||
let name = pattern.name().unwrap_or(path);
|
||||
log::debug!("Loading font: {} {}pt", name, size);
|
||||
log::debug!("Loading font: {name} {size}pt");
|
||||
|
||||
let font_idx = pattern.face_index().unwrap_or(0);
|
||||
|
||||
let face = match self.ft.new_face(path, font_idx as _) {
|
||||
Ok(face) => face,
|
||||
Err(e) => {
|
||||
log::warn!("Failed to load font at {}: {:?}", path, e);
|
||||
log::warn!("Failed to load font at {path}: {e:?}");
|
||||
coll.cp_map.insert(cp, 0);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
match face.set_char_size(size << 6, size << 6, 96, 96) {
|
||||
Ok(_) => {}
|
||||
Ok(()) => {}
|
||||
Err(e) => {
|
||||
log::warn!("Failed to set font size: {:?}", e);
|
||||
log::warn!("Failed to set font size: {e:?}");
|
||||
coll.cp_map.insert(cp, 0);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let idx = coll.fonts.len();
|
||||
for (cp, _) in face.chars() {
|
||||
@@ -184,6 +202,7 @@ impl FontCache {
|
||||
cp: usize,
|
||||
size: isize,
|
||||
graphics: Arc<WlxGraphics>,
|
||||
cmd_buffer: &mut Option<WlxUploadsBuffer>,
|
||||
) -> anyhow::Result<Rc<Glyph>> {
|
||||
let key = self.get_font_for_cp(cp, size);
|
||||
|
||||
@@ -220,9 +239,19 @@ impl FontCache {
|
||||
_ => return Ok(self.collections[size].zero_glyph.clone()),
|
||||
};
|
||||
|
||||
let mut cmd_buffer = graphics.create_command_buffer(CommandBufferUsage::OneTimeSubmit)?;
|
||||
let texture = cmd_buffer.texture2d_raw(bmp.width() as _, bmp.rows() as _, format, &buf)?;
|
||||
cmd_buffer.build_and_execute_now()?;
|
||||
if cmd_buffer.is_none() {
|
||||
*cmd_buffer = Some(graphics.create_uploads_command_buffer(
|
||||
graphics.transfer_queue.clone(),
|
||||
CommandBufferUsage::OneTimeSubmit,
|
||||
)?);
|
||||
}
|
||||
|
||||
let texture = cmd_buffer.as_mut().unwrap().texture2d_raw(
|
||||
bmp.width() as _,
|
||||
bmp.rows() as _,
|
||||
format,
|
||||
&buf,
|
||||
)?;
|
||||
|
||||
let g = Glyph {
|
||||
tex: Some(texture),
|
||||
|
||||
+10
-9
@@ -1,12 +1,13 @@
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use glam::Vec4;
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
pub mod canvas;
|
||||
pub mod font;
|
||||
pub mod modular;
|
||||
|
||||
pub type GuiColor = Vec4;
|
||||
pub(super) static FALLBACK_COLOR: Lazy<GuiColor> = Lazy::new(|| Vec4::new(1., 0., 1., 1.));
|
||||
pub static FALLBACK_COLOR: LazyLock<GuiColor> = LazyLock::new(|| Vec4::new(1., 0., 1., 1.));
|
||||
|
||||
// Parses a color from a HTML hex string
|
||||
pub fn color_parse(html_hex: &str) -> anyhow::Result<GuiColor> {
|
||||
@@ -17,9 +18,9 @@ pub fn color_parse(html_hex: &str) -> anyhow::Result<GuiColor> {
|
||||
u8::from_str_radix(&html_hex[5..7], 16),
|
||||
) {
|
||||
return Ok(Vec4::new(
|
||||
r as f32 / 255.,
|
||||
g as f32 / 255.,
|
||||
b as f32 / 255.,
|
||||
f32::from(r) / 255.,
|
||||
f32::from(g) / 255.,
|
||||
f32::from(b) / 255.,
|
||||
1.,
|
||||
));
|
||||
}
|
||||
@@ -31,10 +32,10 @@ pub fn color_parse(html_hex: &str) -> anyhow::Result<GuiColor> {
|
||||
u8::from_str_radix(&html_hex[7..9], 16),
|
||||
) {
|
||||
return Ok(Vec4::new(
|
||||
r as f32 / 255.,
|
||||
g as f32 / 255.,
|
||||
b as f32 / 255.,
|
||||
a as f32 / 255.,
|
||||
f32::from(r) / 255.,
|
||||
f32::from(g) / 255.,
|
||||
f32::from(b) / 255.,
|
||||
f32::from(a) / 255.,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
+31
-21
@@ -379,7 +379,7 @@ fn modular_button_highlight(
|
||||
|
||||
fn handle_action(action: &ButtonAction, press: &mut PressData, app: &mut AppState) {
|
||||
match action {
|
||||
ButtonAction::Exec { command, toast } => run_exec(command, toast, press, app),
|
||||
ButtonAction::Exec { command, toast } => run_exec(command, toast.clone(), press, app),
|
||||
ButtonAction::Watch { action } => run_watch(action, app),
|
||||
ButtonAction::Overlay { target, action } => run_overlay(target, action, app),
|
||||
ButtonAction::Window { target, action } => run_window(target, action, app),
|
||||
@@ -439,7 +439,7 @@ fn handle_action(action: &ButtonAction, press: &mut PressData, app: &mut AppStat
|
||||
|
||||
let _ = sender.send_single_param(parameter.to_string(), converted);
|
||||
audio_thump(app); // play sound for feedback
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "osc"))]
|
||||
{
|
||||
@@ -453,6 +453,7 @@ fn handle_action(action: &ButtonAction, press: &mut PressData, app: &mut AppStat
|
||||
|
||||
const ENABLED_DISABLED: [&str; 2] = ["enabled", "disabled"];
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn run_system(action: &SystemAction, app: &mut AppState) {
|
||||
match action {
|
||||
SystemAction::ToggleAllowSliding => {
|
||||
@@ -461,7 +462,7 @@ fn run_system(action: &SystemAction, app: &mut AppState) {
|
||||
ToastTopic::System,
|
||||
format!(
|
||||
"Sliding is {}.",
|
||||
ENABLED_DISABLED[app.session.config.allow_sliding as usize]
|
||||
ENABLED_DISABLED[usize::from(app.session.config.allow_sliding)]
|
||||
)
|
||||
.into(),
|
||||
"".into(),
|
||||
@@ -474,7 +475,7 @@ fn run_system(action: &SystemAction, app: &mut AppState) {
|
||||
ToastTopic::System,
|
||||
format!(
|
||||
"Auto realign is {}.",
|
||||
ENABLED_DISABLED[app.session.config.realign_on_showhide as usize]
|
||||
ENABLED_DISABLED[usize::from(app.session.config.realign_on_showhide)]
|
||||
)
|
||||
.into(),
|
||||
"".into(),
|
||||
@@ -487,7 +488,7 @@ fn run_system(action: &SystemAction, app: &mut AppState) {
|
||||
ToastTopic::System,
|
||||
format!(
|
||||
"Space rotate axis lock now {}.",
|
||||
ENABLED_DISABLED[!app.session.config.space_rotate_unlocked as usize]
|
||||
ENABLED_DISABLED[usize::from(!app.session.config.space_rotate_unlocked)]
|
||||
)
|
||||
.into(),
|
||||
"".into(),
|
||||
@@ -506,7 +507,7 @@ fn run_system(action: &SystemAction, app: &mut AppState) {
|
||||
let display = 5 - i;
|
||||
Toast::new(
|
||||
ToastTopic::System,
|
||||
format!("Fixing floor in {}", display).into(),
|
||||
format!("Fixing floor in {display}").into(),
|
||||
"Place either controller on the floor.".into(),
|
||||
)
|
||||
.with_timeout(1.0)
|
||||
@@ -524,7 +525,7 @@ fn run_system(action: &SystemAction, app: &mut AppState) {
|
||||
ToastTopic::System,
|
||||
format!(
|
||||
"Notifications are {}.",
|
||||
ENABLED_DISABLED[app.session.config.notifications_enabled as usize]
|
||||
ENABLED_DISABLED[usize::from(app.session.config.notifications_enabled)]
|
||||
)
|
||||
.into(),
|
||||
"".into(),
|
||||
@@ -538,7 +539,7 @@ fn run_system(action: &SystemAction, app: &mut AppState) {
|
||||
ToastTopic::System,
|
||||
format!(
|
||||
"Notification sounds are {}.",
|
||||
ENABLED_DISABLED[app.session.config.notifications_sound_enabled as usize]
|
||||
ENABLED_DISABLED[usize::from(app.session.config.notifications_sound_enabled)]
|
||||
)
|
||||
.into(),
|
||||
"".into(),
|
||||
@@ -558,7 +559,7 @@ fn run_system(action: &SystemAction, app: &mut AppState) {
|
||||
}
|
||||
}
|
||||
|
||||
fn run_exec(args: &ExecArgs, toast: &Option<Arc<str>>, press: &mut PressData, app: &mut AppState) {
|
||||
fn run_exec(args: &ExecArgs, toast: Option<Arc<str>>, press: &mut PressData, app: &mut AppState) {
|
||||
if let Some(proc) = press.child.as_mut() {
|
||||
match proc.try_wait() {
|
||||
Ok(Some(code)) => {
|
||||
@@ -577,7 +578,10 @@ fn run_exec(args: &ExecArgs, toast: &Option<Arc<str>>, press: &mut PressData, ap
|
||||
}
|
||||
}
|
||||
}
|
||||
let args = args.iter().map(|s| s.as_ref()).collect::<Vec<&str>>();
|
||||
let args = args
|
||||
.iter()
|
||||
.map(std::convert::AsRef::as_ref)
|
||||
.collect::<Vec<&str>>();
|
||||
match process::Command::new(args[0]).args(&args[1..]).spawn() {
|
||||
Ok(proc) => {
|
||||
press.child = Some(proc);
|
||||
@@ -586,11 +590,12 @@ fn run_exec(args: &ExecArgs, toast: &Option<Arc<str>>, press: &mut PressData, ap
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error_toast(app, &format!("Failed to spawn process {:?}", args), e);
|
||||
error_toast(app, &format!("Failed to spawn process {args:?}"), e);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn run_watch(data: &WatchAction, app: &mut AppState) {
|
||||
match data {
|
||||
WatchAction::Hide => {
|
||||
@@ -621,7 +626,7 @@ fn run_watch(data: &WatchAction, app: &mut AppState) {
|
||||
app.tasks.enqueue(TaskType::Overlay(
|
||||
OverlaySelector::Name(WATCH_NAME.into()),
|
||||
Box::new(|app, o| {
|
||||
if let RelativeTo::Hand(0) = o.relative_to {
|
||||
if matches!(o.relative_to, RelativeTo::Hand(0)) {
|
||||
o.relative_to = RelativeTo::Hand(1);
|
||||
o.spawn_rotation = app.session.config.watch_rot
|
||||
* Quat::from_rotation_x(PI)
|
||||
@@ -701,6 +706,7 @@ fn run_watch(data: &WatchAction, app: &mut AppState) {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn run_overlay(overlay: &OverlaySelector, action: &OverlayAction, app: &mut AppState) {
|
||||
match action {
|
||||
OverlayAction::Reset => {
|
||||
@@ -736,7 +742,7 @@ fn run_overlay(overlay: &OverlaySelector, action: &OverlayAction, app: &mut AppS
|
||||
|
||||
if state_dirty {
|
||||
match save_layout(&app.session.config) {
|
||||
Ok(_) => log::debug!("Saved state"),
|
||||
Ok(()) => log::debug!("Saved state"),
|
||||
Err(e) => {
|
||||
error_toast(app, "Failed to save state", e);
|
||||
}
|
||||
@@ -752,17 +758,17 @@ fn run_overlay(overlay: &OverlaySelector, action: &OverlayAction, app: &mut AppS
|
||||
o.recenter = !o.recenter;
|
||||
o.grabbable = o.recenter;
|
||||
o.show_hide = o.recenter;
|
||||
if !o.recenter {
|
||||
if o.recenter {
|
||||
Toast::new(
|
||||
ToastTopic::System,
|
||||
format!("{} is now locked in place!", o.name).into(),
|
||||
format!("{} is now unlocked!", o.name).into(),
|
||||
"".into(),
|
||||
)
|
||||
.submit(app);
|
||||
} else {
|
||||
Toast::new(
|
||||
ToastTopic::System,
|
||||
format!("{} is now unlocked!", o.name).into(),
|
||||
format!("{} is now locked in place!", o.name).into(),
|
||||
"".into(),
|
||||
)
|
||||
.submit(app);
|
||||
@@ -776,17 +782,17 @@ fn run_overlay(overlay: &OverlaySelector, action: &OverlayAction, app: &mut AppS
|
||||
overlay.clone(),
|
||||
Box::new(|app, o| {
|
||||
o.interactable = !o.interactable;
|
||||
if !o.interactable {
|
||||
if o.interactable {
|
||||
Toast::new(
|
||||
ToastTopic::System,
|
||||
format!("{} is now non-interactable!", o.name).into(),
|
||||
format!("{} is now interactable!", o.name).into(),
|
||||
"".into(),
|
||||
)
|
||||
.submit(app);
|
||||
} else {
|
||||
Toast::new(
|
||||
ToastTopic::System,
|
||||
format!("{} is now interactable!", o.name).into(),
|
||||
format!("{} is now non-interactable!", o.name).into(),
|
||||
"".into(),
|
||||
)
|
||||
.submit(app);
|
||||
@@ -827,7 +833,11 @@ fn run_window(window: &Arc<str>, action: &WindowAction, app: &mut AppState) {
|
||||
)
|
||||
.with_sound(true)
|
||||
.submit(app);
|
||||
crate::overlays::mirror::new_mirror(name.clone(), false, &app.session)
|
||||
Some(crate::overlays::mirror::new_mirror(
|
||||
name,
|
||||
false,
|
||||
&app.session,
|
||||
))
|
||||
}
|
||||
}),
|
||||
));
|
||||
|
||||
+23
-29
@@ -100,15 +100,14 @@ pub fn modular_label_init(label: &mut ModularControl, content: &LabelContent, ap
|
||||
}),
|
||||
LabelContent::Clock { format, timezone } => {
|
||||
let tz_str = match timezone {
|
||||
Some(TimezoneDef::Idx(idx)) => {
|
||||
if let Some(tz) = app.session.config.timezones.get(*idx) {
|
||||
Some(tz.as_str())
|
||||
} else {
|
||||
log::error!("Timezone index out of range '{}'", idx);
|
||||
Some(TimezoneDef::Idx(idx)) => app.session.config.timezones.get(*idx).map_or_else(
|
||||
|| {
|
||||
log::error!("Timezone index out of range '{idx}'");
|
||||
label.set_fg_color(*FALLBACK_COLOR);
|
||||
None
|
||||
}
|
||||
}
|
||||
},
|
||||
|tz| Some(tz.as_str()),
|
||||
),
|
||||
Some(TimezoneDef::Str(tz_str)) => Some(tz_str.as_ref()),
|
||||
None => None,
|
||||
};
|
||||
@@ -127,14 +126,13 @@ pub fn modular_label_init(label: &mut ModularControl, content: &LabelContent, ap
|
||||
}
|
||||
LabelContent::Timezone { timezone } => {
|
||||
if let Some(tz) = app.session.config.timezones.get(*timezone) {
|
||||
let pretty_tz = tz.split('/').last().map(|x| x.replace("_", " "));
|
||||
let pretty_tz = tz.split('/').next_back().map(|x| x.replace('_', " "));
|
||||
|
||||
if let Some(pretty_tz) = pretty_tz {
|
||||
label.set_text(&pretty_tz);
|
||||
return;
|
||||
} else {
|
||||
log::error!("Timezone name not valid '{}'", &tz);
|
||||
}
|
||||
log::error!("Timezone name not valid '{}'", &tz);
|
||||
} else {
|
||||
log::error!("Timezone index out of range '{}'", &timezone);
|
||||
}
|
||||
@@ -166,6 +164,7 @@ pub fn modular_label_init(label: &mut ModularControl, content: &LabelContent, ap
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub(super) fn label_update(control: &mut ModularControl, _: &mut (), app: &mut AppState) {
|
||||
// want panic
|
||||
let ModularData::Label(data) = control.state.as_mut().unwrap() else {
|
||||
@@ -184,9 +183,9 @@ pub(super) fn label_update(control: &mut ModularControl, _: &mut (), app: &mut A
|
||||
let tags = ["", "H", "L", "R", "T"];
|
||||
|
||||
if let Some(device) = device {
|
||||
let (text, color) = device
|
||||
.soc
|
||||
.map(|soc| {
|
||||
let (text, color) = device.soc.map_or_else(
|
||||
|| (String::new(), Vec4::ZERO),
|
||||
|soc| {
|
||||
let text = format!(
|
||||
"{}{}",
|
||||
tags[device.role as usize],
|
||||
@@ -200,8 +199,8 @@ pub(super) fn label_update(control: &mut ModularControl, _: &mut (), app: &mut A
|
||||
*normal_color
|
||||
};
|
||||
(text, color)
|
||||
})
|
||||
.unwrap_or_else(|| ("".into(), Vec4::ZERO));
|
||||
},
|
||||
);
|
||||
|
||||
control.set_text(&text);
|
||||
control.set_fg_color(color);
|
||||
@@ -212,11 +211,11 @@ pub(super) fn label_update(control: &mut ModularControl, _: &mut (), app: &mut A
|
||||
LabelData::Clock { format, timezone } => {
|
||||
let format = format.clone();
|
||||
if let Some(tz) = timezone {
|
||||
let date = Local::now().with_timezone(tz);
|
||||
control.set_text(&format!("{}", &date.format(&format)));
|
||||
let date_time = Local::now().with_timezone(tz);
|
||||
control.set_text(&format!("{}", &date_time.format(&format)));
|
||||
} else {
|
||||
let date = Local::now();
|
||||
control.set_text(&format!("{}", &date.format(&format)));
|
||||
let date_time = Local::now();
|
||||
control.set_text(&format!("{}", &date_time.format(&format)));
|
||||
}
|
||||
}
|
||||
LabelData::Timer { format, start } => {
|
||||
@@ -236,13 +235,7 @@ pub(super) fn label_update(control: &mut ModularControl, _: &mut (), app: &mut A
|
||||
if let Some(mut proc) = child.take() {
|
||||
match proc.try_wait() {
|
||||
Ok(Some(code)) => {
|
||||
if !code.success() {
|
||||
error_toast(
|
||||
app,
|
||||
"LabelData::Exec: Child process exited with code",
|
||||
code,
|
||||
);
|
||||
} else {
|
||||
if code.success() {
|
||||
if let Some(mut stdout) = proc.stdout.take() {
|
||||
let mut buf = String::new();
|
||||
if stdout.read_to_string(&mut buf).is_ok() {
|
||||
@@ -259,6 +252,7 @@ pub(super) fn label_update(control: &mut ModularControl, _: &mut (), app: &mut A
|
||||
log::error!("No stdout for child process");
|
||||
return;
|
||||
}
|
||||
error_toast(app, "LabelData::Exec: Child process exited with code", code);
|
||||
}
|
||||
Ok(None) => {
|
||||
*child = Some(proc);
|
||||
@@ -281,7 +275,7 @@ pub(super) fn label_update(control: &mut ModularControl, _: &mut (), app: &mut A
|
||||
*last_exec = Instant::now();
|
||||
let args = command
|
||||
.iter()
|
||||
.map(|s| s.as_ref())
|
||||
.map(std::convert::AsRef::as_ref)
|
||||
.collect::<SmallVec<[&str; 8]>>();
|
||||
|
||||
match process::Command::new(args[0])
|
||||
@@ -293,9 +287,9 @@ pub(super) fn label_update(control: &mut ModularControl, _: &mut (), app: &mut A
|
||||
*child = Some(proc);
|
||||
}
|
||||
Err(e) => {
|
||||
error_toast(app, &format!("Failed to spawn process {:?}", args), e);
|
||||
error_toast(app, &format!("Failed to spawn process {args:?}"), e);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
LabelData::Ipd { last_ipd } => {
|
||||
|
||||
+11
-9
@@ -149,8 +149,9 @@ pub enum ModularData {
|
||||
Button(Box<ButtonData>),
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines, clippy::many_single_char_names)]
|
||||
pub fn modular_canvas(
|
||||
size: &[u32; 2],
|
||||
size: [u32; 2],
|
||||
elements: &[ModularElement],
|
||||
state: &mut AppState,
|
||||
) -> anyhow::Result<Canvas<(), ModularData>> {
|
||||
@@ -162,7 +163,7 @@ pub fn modular_canvas(
|
||||
(),
|
||||
)?;
|
||||
let empty_str: Arc<str> = Arc::from("");
|
||||
for elem in elements.iter() {
|
||||
for elem in elements {
|
||||
match elem {
|
||||
ModularElement::Panel {
|
||||
rect: [x, y, w, h],
|
||||
@@ -226,7 +227,7 @@ pub fn modular_canvas(
|
||||
sprite.set_sprite_st(st);
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("Could not load custom UI sprite: {:?}", e);
|
||||
log::warn!("Could not load custom UI sprite: {e:?}");
|
||||
}
|
||||
},
|
||||
ModularElement::Button {
|
||||
@@ -326,7 +327,7 @@ pub fn modular_canvas(
|
||||
canvas.fg_color = color_parse(fg_color).unwrap_or(*FALLBACK_COLOR);
|
||||
canvas.font_size = *font_size;
|
||||
|
||||
for screen in state.screens.iter() {
|
||||
for screen in &state.screens {
|
||||
let button = canvas.button(
|
||||
button_x + 2.,
|
||||
button_y + 2.,
|
||||
@@ -465,7 +466,7 @@ pub fn modular_canvas(
|
||||
button_x += button_w;
|
||||
}
|
||||
} else {
|
||||
log::error!("WayVR catalog \"{}\" not found", catalog_name);
|
||||
log::error!("WayVR catalog \"{catalog_name}\" not found");
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "wayvr"))]
|
||||
@@ -535,7 +536,7 @@ pub fn modular_canvas(
|
||||
|
||||
pub fn color_parse_or_default(color: &str) -> GuiColor {
|
||||
color_parse(color).unwrap_or_else(|e| {
|
||||
log::error!("Failed to parse color '{}': {}", color, e);
|
||||
log::error!("Failed to parse color '{color}': {e}");
|
||||
*FALLBACK_COLOR
|
||||
})
|
||||
}
|
||||
@@ -551,9 +552,10 @@ fn sprite_from_path(path: Arc<str>, app: &mut AppState) -> anyhow::Result<Arc<Im
|
||||
anyhow::bail!("Could not open custom sprite at: {}", path);
|
||||
};
|
||||
|
||||
let mut command_buffer = app
|
||||
.graphics
|
||||
.create_command_buffer(CommandBufferUsage::OneTimeSubmit)?;
|
||||
let mut command_buffer = app.graphics.create_uploads_command_buffer(
|
||||
app.graphics.transfer_queue.clone(),
|
||||
CommandBufferUsage::OneTimeSubmit,
|
||||
)?;
|
||||
|
||||
match command_buffer.texture2d_dds(f) {
|
||||
Ok(image) => {
|
||||
|
||||
+17
-20
@@ -6,9 +6,9 @@ use input_linux::{
|
||||
UInputHandle,
|
||||
};
|
||||
use libc::{input_event, timeval};
|
||||
use once_cell::sync::Lazy;
|
||||
use serde::Deserialize;
|
||||
use std::mem::transmute;
|
||||
use std::sync::LazyLock;
|
||||
use std::{fs::File, sync::atomic::AtomicBool};
|
||||
use strum::{EnumIter, EnumString, IntoEnumIterator};
|
||||
use xkbcommon::xkb;
|
||||
@@ -34,10 +34,7 @@ pub fn initialize() -> Box<dyn HidProvider> {
|
||||
log::error!("Could not create uinput provider. Keyboard/Mouse input will not work!");
|
||||
log::error!("To check if you're in input group, run: id -nG");
|
||||
if let Ok(user) = std::env::var("USER") {
|
||||
log::error!(
|
||||
"To add yourself to the input group, run: sudo usermod -aG input {}",
|
||||
user
|
||||
);
|
||||
log::error!("To add yourself to the input group, run: sudo usermod -aG input {user}");
|
||||
log::error!("After adding yourself to the input group, you will need to reboot.");
|
||||
}
|
||||
Box::new(DummyProvider {})
|
||||
@@ -163,28 +160,28 @@ impl UInputProvider {
|
||||
.create(&mouse_id, mouse_name, 0, &abs_info)
|
||||
.ok()?;
|
||||
|
||||
Some(UInputProvider {
|
||||
Some(Self {
|
||||
keyboard_handle,
|
||||
mouse_handle,
|
||||
desktop_extent: Vec2::ZERO,
|
||||
desktop_origin: Vec2::ZERO,
|
||||
current_action: Default::default(),
|
||||
current_action: MouseAction::default(),
|
||||
cur_modifiers: 0,
|
||||
})
|
||||
}
|
||||
fn send_button_internal(&self, button: u16, down: bool) {
|
||||
let time = get_time();
|
||||
let events = [
|
||||
new_event(time, EV_KEY, button, down as _),
|
||||
new_event(time, EV_KEY, button, down.into()),
|
||||
new_event(time, EV_SYN, 0, 0),
|
||||
];
|
||||
if let Err(res) = self.mouse_handle.write(&events) {
|
||||
log::error!("send_button: {}", res.to_string());
|
||||
log::error!("send_button: {res}");
|
||||
}
|
||||
}
|
||||
fn mouse_move_internal(&mut self, pos: Vec2) {
|
||||
#[cfg(debug_assertions)]
|
||||
log::trace!("Mouse move: {:?}", pos);
|
||||
log::trace!("Mouse move: {pos:?}");
|
||||
|
||||
let pos = (pos - self.desktop_origin) * (MOUSE_EXTENT / self.desktop_extent);
|
||||
|
||||
@@ -195,7 +192,7 @@ impl UInputProvider {
|
||||
new_event(time, EV_SYN, 0, 0),
|
||||
];
|
||||
if let Err(res) = self.mouse_handle.write(&events) {
|
||||
log::error!("{}", res.to_string());
|
||||
log::error!("{res}");
|
||||
}
|
||||
}
|
||||
fn wheel_internal(&self, delta_y: i32, delta_x: i32) {
|
||||
@@ -211,7 +208,7 @@ impl UInputProvider {
|
||||
new_event(time, EV_SYN, 0, 0),
|
||||
];
|
||||
if let Err(res) = self.mouse_handle.write(&events) {
|
||||
log::error!("wheel: {}", res.to_string());
|
||||
log::error!("wheel: {res}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -231,15 +228,15 @@ impl HidProvider for UInputProvider {
|
||||
}
|
||||
fn send_key(&self, key: VirtualKey, down: bool) {
|
||||
#[cfg(debug_assertions)]
|
||||
log::trace!("send_key: {:?} {}", key, down);
|
||||
log::trace!("send_key: {key:?} {down}");
|
||||
|
||||
let time = get_time();
|
||||
let events = [
|
||||
new_event(time, EV_KEY, (key as u16) - 8, down as _),
|
||||
new_event(time, EV_KEY, (key as u16) - 8, down.into()),
|
||||
new_event(time, EV_SYN, 0, 0),
|
||||
];
|
||||
if let Err(res) = self.keyboard_handle.write(&events) {
|
||||
log::error!("send_key: {}", res.to_string());
|
||||
log::error!("send_key: {res}");
|
||||
}
|
||||
}
|
||||
fn set_desktop_extent(&mut self, extent: Vec2) {
|
||||
@@ -303,7 +300,7 @@ fn get_time() -> timeval {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn new_event(time: timeval, type_: u16, code: u16, value: i32) -> input_event {
|
||||
const fn new_event(time: timeval, type_: u16, code: u16, value: i32) -> input_event {
|
||||
input_event {
|
||||
time,
|
||||
type_,
|
||||
@@ -323,7 +320,7 @@ pub const META: KeyModifier = 0x80;
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[repr(u16)]
|
||||
#[derive(Debug, Deserialize, PartialEq, Clone, Copy, IntegerId, EnumString, EnumIter)]
|
||||
#[derive(Debug, Deserialize, PartialEq, Eq, Clone, Copy, IntegerId, EnumString, EnumIter)]
|
||||
pub enum VirtualKey {
|
||||
Escape = 9,
|
||||
N1, // number row
|
||||
@@ -479,7 +476,7 @@ pub enum VirtualKey {
|
||||
XF86Search = 225,
|
||||
}
|
||||
|
||||
pub static KEYS_TO_MODS: Lazy<IdMap<VirtualKey, KeyModifier>> = Lazy::new(|| {
|
||||
pub static KEYS_TO_MODS: LazyLock<IdMap<VirtualKey, KeyModifier>> = LazyLock::new(|| {
|
||||
idmap! {
|
||||
VirtualKey::LShift => SHIFT,
|
||||
VirtualKey::RShift => SHIFT,
|
||||
@@ -494,7 +491,7 @@ pub static KEYS_TO_MODS: Lazy<IdMap<VirtualKey, KeyModifier>> = Lazy::new(|| {
|
||||
}
|
||||
});
|
||||
|
||||
pub static MODS_TO_KEYS: Lazy<IdMap<KeyModifier, Vec<VirtualKey>>> = Lazy::new(|| {
|
||||
pub static MODS_TO_KEYS: LazyLock<IdMap<KeyModifier, Vec<VirtualKey>>> = LazyLock::new(|| {
|
||||
idmap! {
|
||||
SHIFT => vec![VirtualKey::LShift, VirtualKey::RShift],
|
||||
CAPS_LOCK => vec![VirtualKey::Caps],
|
||||
@@ -524,7 +521,7 @@ macro_rules! key_is {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn get_key_type(key: VirtualKey) -> KeyType {
|
||||
pub const fn get_key_type(key: VirtualKey) -> KeyType {
|
||||
if key_between!(key, VirtualKey::N1, VirtualKey::Plus)
|
||||
|| key_between!(key, VirtualKey::Q, VirtualKey::Oem6)
|
||||
|| key_between!(key, VirtualKey::A, VirtualKey::Oem3)
|
||||
|
||||
+6
-8
@@ -46,11 +46,9 @@ pub fn get_keymap_wl() -> anyhow::Result<XkbKeymap> {
|
||||
// this gets us the wl_keyboard
|
||||
let _ = queue.blocking_dispatch(&mut me);
|
||||
|
||||
if let Some(keymap) = me.keymap.take() {
|
||||
Ok(keymap)
|
||||
} else {
|
||||
Err(anyhow::anyhow!("Could not load keymap"))
|
||||
}
|
||||
me.keymap
|
||||
.take()
|
||||
.ok_or_else(|| anyhow::anyhow!("Could not load keymap"))
|
||||
}
|
||||
|
||||
impl Dispatch<WlRegistry, GlobalListContents> for WlKeymapHandler {
|
||||
@@ -84,7 +82,7 @@ impl Dispatch<WlSeat, ()> for WlKeymapHandler {
|
||||
}
|
||||
}
|
||||
wl_seat::Event::Name { name } => {
|
||||
log::debug!("Using WlSeat: {}", name);
|
||||
log::debug!("Using WlSeat: {name}");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -127,14 +125,14 @@ impl Dispatch<WlKeyboard, ()> for WlKeymapHandler {
|
||||
log::error!("Default layout will be used.");
|
||||
}
|
||||
Err(err) => {
|
||||
log::error!("Could not load keymap: {}", err);
|
||||
log::error!("Could not load keymap: {err}");
|
||||
log::error!("Default layout will be used.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
wl_keyboard::Event::RepeatInfo { rate, delay } => {
|
||||
log::debug!("WlKeyboard RepeatInfo rate: {}, delay: {}", rate, delay);
|
||||
log::debug!("WlKeyboard RepeatInfo rate: {rate}, delay: {delay}");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
+33
-20
@@ -1,4 +1,18 @@
|
||||
#[allow(dead_code)]
|
||||
#![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)]
|
||||
#![allow(
|
||||
dead_code,
|
||||
clippy::cast_precision_loss,
|
||||
clippy::cast_possible_truncation,
|
||||
clippy::cast_sign_loss,
|
||||
clippy::cast_possible_wrap,
|
||||
clippy::cast_lossless,
|
||||
clippy::match_wildcard_for_single_variants,
|
||||
clippy::doc_markdown,
|
||||
clippy::struct_excessive_bools,
|
||||
clippy::needless_pass_by_value,
|
||||
clippy::needless_pass_by_ref_mut,
|
||||
clippy::multiple_crate_versions
|
||||
)]
|
||||
mod backend;
|
||||
mod config;
|
||||
mod config_io;
|
||||
@@ -65,6 +79,7 @@ struct Args {
|
||||
uidev: Option<String>,
|
||||
}
|
||||
|
||||
#[allow(clippy::unnecessary_wraps)]
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut args = if std::env::args().skip(1).any(|a| !a.is_empty()) {
|
||||
Args::parse()
|
||||
@@ -78,7 +93,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
logging_init(&mut args)?;
|
||||
logging_init(&mut args);
|
||||
|
||||
log::info!(
|
||||
"Welcome to {} version {}!",
|
||||
@@ -122,23 +137,23 @@ fn auto_run(running: Arc<AtomicBool>, args: Args) {
|
||||
Ok(()) => return,
|
||||
Err(BackendError::NotSupported) => (),
|
||||
Err(e) => {
|
||||
log::error!("{}", e.to_string());
|
||||
log::error!("{e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "openvr")]
|
||||
if !args_get_openxr(&args) {
|
||||
use crate::backend::openvr::openvr_run;
|
||||
match openvr_run(running.clone(), args.show) {
|
||||
match openvr_run(running, args.show) {
|
||||
Ok(()) => return,
|
||||
Err(BackendError::NotSupported) => (),
|
||||
Err(e) => {
|
||||
log::error!("{}", e.to_string());
|
||||
log::error!("{e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
log::error!("No more backends to try");
|
||||
@@ -150,10 +165,10 @@ fn auto_run(running: Arc<AtomicBool>, args: Args) {
|
||||
compile_error!("No desktop support! Enable either wayland or x11 features!");
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn args_get_openvr(_args: &Args) -> bool {
|
||||
#[allow(dead_code, unused_variables)]
|
||||
const fn args_get_openvr(args: &Args) -> bool {
|
||||
#[cfg(feature = "openvr")]
|
||||
let ret = _args.openvr;
|
||||
let ret = args.openvr;
|
||||
|
||||
#[cfg(not(feature = "openvr"))]
|
||||
let ret = false;
|
||||
@@ -161,10 +176,10 @@ fn args_get_openvr(_args: &Args) -> bool {
|
||||
ret
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn args_get_openxr(_args: &Args) -> bool {
|
||||
#[allow(dead_code, unused_variables)]
|
||||
const fn args_get_openxr(args: &Args) -> bool {
|
||||
#[cfg(feature = "openxr")]
|
||||
let ret = _args.openxr;
|
||||
let ret = args.openxr;
|
||||
|
||||
#[cfg(not(feature = "openxr"))]
|
||||
let ret = false;
|
||||
@@ -172,12 +187,12 @@ fn args_get_openxr(_args: &Args) -> bool {
|
||||
ret
|
||||
}
|
||||
|
||||
fn logging_init(args: &mut Args) -> anyhow::Result<()> {
|
||||
fn logging_init(args: &mut Args) {
|
||||
let log_file_path = args
|
||||
.log_to
|
||||
.take()
|
||||
.or_else(|| std::env::var("WLX_LOGFILE").ok())
|
||||
.unwrap_or(String::from("/tmp/wlx.log"));
|
||||
.unwrap_or_else(|| String::from("/tmp/wlx.log"));
|
||||
|
||||
let file_writer = match std::fs::OpenOptions::new()
|
||||
.write(true)
|
||||
@@ -189,7 +204,7 @@ fn logging_init(args: &mut Args) -> anyhow::Result<()> {
|
||||
Some(file)
|
||||
}
|
||||
Err(e) => {
|
||||
println!("Failed to open log file (path: {:?}): {}", e, log_file_path);
|
||||
println!("Failed to open log file (path: {e:?}): {log_file_path}");
|
||||
None
|
||||
}
|
||||
};
|
||||
@@ -225,13 +240,11 @@ fn logging_init(args: &mut Args) -> anyhow::Result<()> {
|
||||
}
|
||||
|
||||
log_panics::init();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ensure_single_instance(replace: bool) -> bool {
|
||||
let mut path = std::env::var("XDG_RUNTIME_DIR")
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|_| PathBuf::from("/tmp"));
|
||||
let mut path =
|
||||
std::env::var("XDG_RUNTIME_DIR").map_or_else(|_| PathBuf::from("/tmp"), PathBuf::from);
|
||||
path.push("wlx-overlay-s.pid");
|
||||
|
||||
if path.exists() {
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
use glam::Vec3A;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, LazyLock};
|
||||
|
||||
use crate::backend::overlay::{OverlayData, OverlayState, RelativeTo, Z_ORDER_ANCHOR};
|
||||
use crate::config::{load_known_yaml, ConfigType};
|
||||
use crate::gui::modular::{modular_canvas, ModularUiConfig};
|
||||
use crate::state::AppState;
|
||||
|
||||
pub static ANCHOR_NAME: Lazy<Arc<str>> = Lazy::new(|| Arc::from("anchor"));
|
||||
pub static ANCHOR_NAME: LazyLock<Arc<str>> = LazyLock::new(|| Arc::from("anchor"));
|
||||
|
||||
pub fn create_anchor<O>(state: &mut AppState) -> anyhow::Result<OverlayData<O>>
|
||||
where
|
||||
@@ -27,7 +26,7 @@ where
|
||||
relative_to: RelativeTo::Stage,
|
||||
..Default::default()
|
||||
},
|
||||
backend: Box::new(modular_canvas(&config.size, &config.elements, state)?),
|
||||
backend: Box::new(modular_canvas(config.size, &config.elements, state)?),
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
@@ -21,28 +21,28 @@ pub fn create_custom(
|
||||
match load_custom_ui(&name) {
|
||||
Ok(config) => config,
|
||||
Err(e) => {
|
||||
log::error!("Failed to load custom UI config for {}: {:?}", name, e);
|
||||
log::error!("Failed to load custom UI config for {name}: {e:?}");
|
||||
return None;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let canvas = match modular_canvas(&config.size, &config.elements, state) {
|
||||
let canvas = match modular_canvas(config.size, &config.elements, state) {
|
||||
Ok(canvas) => canvas,
|
||||
Err(e) => {
|
||||
log::error!("Failed to create canvas for {}: {:?}", name, e);
|
||||
log::error!("Failed to create canvas for {name}: {e:?}");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let state = OverlayState {
|
||||
name: name.clone(),
|
||||
name,
|
||||
want_visible: true,
|
||||
interactable: true,
|
||||
grabbable: true,
|
||||
spawn_scale: config.width,
|
||||
spawn_point: Vec3A::from_array(config.spawn_pos.unwrap_or([0., 0., -0.5])),
|
||||
interaction_transform: ui_transform(&config.size),
|
||||
interaction_transform: ui_transform(config.size),
|
||||
..Default::default()
|
||||
};
|
||||
let backend = Box::new(canvas);
|
||||
|
||||
+65
-56
@@ -2,14 +2,18 @@ use std::{
|
||||
collections::HashMap,
|
||||
process::{Child, Command},
|
||||
str::FromStr,
|
||||
sync::{Arc, LazyLock},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
backend::{
|
||||
input::{InteractionHandler, PointerMode},
|
||||
overlay::{FrameTransform, OverlayBackend, OverlayData, OverlayRenderer, OverlayState},
|
||||
overlay::{
|
||||
FrameMeta, OverlayBackend, OverlayData, OverlayRenderer, OverlayState, ShouldRender,
|
||||
},
|
||||
},
|
||||
config::{self, ConfigType},
|
||||
graphics::CommandBuffers,
|
||||
gui::{
|
||||
canvas::{builder::CanvasBuilder, control::Control, Canvas},
|
||||
color_parse, KeyCapType,
|
||||
@@ -21,9 +25,9 @@ use crate::{
|
||||
state::{AppState, KeyboardFocus},
|
||||
};
|
||||
use glam::{vec2, vec3a, Affine2, Vec4};
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use vulkano::image::view::ImageView;
|
||||
|
||||
const PIXELS_PER_UNIT: f32 = 80.;
|
||||
const BUTTON_PADDING: f32 = 4.;
|
||||
@@ -61,6 +65,7 @@ fn set_modifiers(app: &mut AppState, mods: u8) {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub fn create_keyboard<O>(
|
||||
app: &AppState,
|
||||
mut keymap: Option<XkbKeymap>,
|
||||
@@ -101,23 +106,25 @@ where
|
||||
canvas.fg_color = color_parse("#cad3f5").unwrap(); //safe
|
||||
canvas.bg_color = color_parse("#1e2030").unwrap(); //safe
|
||||
|
||||
let has_altgr = keymap.as_ref().is_some_and(|k| k.has_altgr());
|
||||
let has_altgr = keymap
|
||||
.as_ref()
|
||||
.is_some_and(super::super::hid::XkbKeymap::has_altgr);
|
||||
|
||||
if !LAYOUT.auto_labels.unwrap_or(true) {
|
||||
keymap = None;
|
||||
}
|
||||
|
||||
let unit_size = size.x / LAYOUT.row_size;
|
||||
let h = unit_size - 2. * BUTTON_PADDING;
|
||||
let h = 2.0f32.mul_add(-BUTTON_PADDING, unit_size);
|
||||
|
||||
for row in 0..LAYOUT.key_sizes.len() {
|
||||
let y = unit_size * (row as f32) + BUTTON_PADDING;
|
||||
let y = unit_size.mul_add(row as f32, BUTTON_PADDING);
|
||||
let mut sum_size = 0f32;
|
||||
|
||||
for col in 0..LAYOUT.key_sizes[row].len() {
|
||||
let my_size = LAYOUT.key_sizes[row][col];
|
||||
let x = unit_size * sum_size + BUTTON_PADDING;
|
||||
let w = unit_size * my_size - 2. * BUTTON_PADDING;
|
||||
let x = unit_size.mul_add(sum_size, BUTTON_PADDING);
|
||||
let w = unit_size.mul_add(my_size, -(2. * BUTTON_PADDING));
|
||||
|
||||
if let Some(key) = LAYOUT.main_layout[row][col].as_ref() {
|
||||
let mut label = Vec::with_capacity(2);
|
||||
@@ -131,7 +138,7 @@ where
|
||||
let label0 = keymap.label_for_key(vk, 0);
|
||||
let label1 = keymap.label_for_key(vk, SHIFT);
|
||||
|
||||
if label0.chars().next().is_some_and(|f| f.is_alphabetic()) {
|
||||
if label0.chars().next().is_some_and(char::is_alphabetic) {
|
||||
label.push(label1);
|
||||
if has_altgr {
|
||||
cap_type = KeyCapType::RegularAltGr;
|
||||
@@ -171,7 +178,7 @@ where
|
||||
});
|
||||
} else if let Some(exec_args) = LAYOUT.exec_commands.get(key) {
|
||||
if exec_args.is_empty() {
|
||||
log::error!("Keyboard: EXEC args empty for {}", key);
|
||||
log::error!("Keyboard: EXEC args empty for {key}");
|
||||
continue;
|
||||
}
|
||||
let mut iter = exec_args.iter().cloned();
|
||||
@@ -181,10 +188,10 @@ where
|
||||
args: iter.by_ref().take_while(|arg| arg[..] != *"null").collect(),
|
||||
release_program: iter.next(),
|
||||
release_args: iter.collect(),
|
||||
})
|
||||
};
|
||||
});
|
||||
}
|
||||
} else {
|
||||
log::error!("Unknown key: {}", key);
|
||||
log::error!("Unknown key: {key}");
|
||||
}
|
||||
|
||||
if let Some(state) = maybe_state {
|
||||
@@ -235,7 +242,7 @@ fn key_press(
|
||||
) {
|
||||
match control.state.as_mut() {
|
||||
Some(KeyButtonData::Key { vk, pressed }) => {
|
||||
data.key_click(app);
|
||||
key_click(app);
|
||||
|
||||
data.modifiers |= match mode {
|
||||
PointerMode::Right => SHIFT,
|
||||
@@ -251,11 +258,11 @@ fn key_press(
|
||||
Some(KeyButtonData::Modifier { modifier, sticky }) => {
|
||||
*sticky = data.modifiers & *modifier == 0;
|
||||
data.modifiers |= *modifier;
|
||||
data.key_click(app);
|
||||
key_click(app);
|
||||
set_modifiers(app, data.modifiers);
|
||||
}
|
||||
Some(KeyButtonData::Macro { verbs }) => {
|
||||
data.key_click(app);
|
||||
key_click(app);
|
||||
for (vk, press) in verbs {
|
||||
send_key(app, *vk, *press);
|
||||
}
|
||||
@@ -265,7 +272,7 @@ fn key_press(
|
||||
data.processes
|
||||
.retain_mut(|child| !matches!(child.try_wait(), Ok(Some(_))));
|
||||
|
||||
data.key_click(app);
|
||||
key_click(app);
|
||||
if let Ok(child) = Command::new(program).args(args).spawn() {
|
||||
data.processes.push(child);
|
||||
}
|
||||
@@ -284,7 +291,7 @@ fn key_release(
|
||||
send_key(app, *vk, false);
|
||||
*pressed = false;
|
||||
|
||||
for m in AUTO_RELEASE_MODS.iter() {
|
||||
for m in &AUTO_RELEASE_MODS {
|
||||
if data.modifiers & *m != 0 {
|
||||
data.modifiers &= !*m;
|
||||
set_modifiers(app, data.modifiers);
|
||||
@@ -344,11 +351,9 @@ struct KeyboardData {
|
||||
|
||||
const KEY_AUDIO_WAV: &[u8] = include_bytes!("../res/421581.wav");
|
||||
|
||||
impl KeyboardData {
|
||||
fn key_click(&mut self, app: &mut AppState) {
|
||||
if app.session.config.keyboard_sound_enabled {
|
||||
app.audio.play(KEY_AUDIO_WAV);
|
||||
}
|
||||
fn key_click(app: &mut AppState) {
|
||||
if app.session.config.keyboard_sound_enabled {
|
||||
app.audio.play(KEY_AUDIO_WAV);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -372,10 +377,10 @@ enum KeyButtonData {
|
||||
},
|
||||
}
|
||||
|
||||
static LAYOUT: Lazy<Layout> = Lazy::new(Layout::load_from_disk);
|
||||
static LAYOUT: LazyLock<Layout> = LazyLock::new(Layout::load_from_disk);
|
||||
|
||||
static MACRO_REGEX: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r"^([A-Za-z0-9_-]+)(?: +(UP|DOWN))?$").unwrap()); // want panic
|
||||
static MACRO_REGEX: LazyLock<Regex> =
|
||||
LazyLock::new(|| Regex::new(r"^([A-Za-z0-9_-]+)(?: +(UP|DOWN))?$").unwrap()); // want panic
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, Deserialize, Serialize)]
|
||||
#[repr(usize)]
|
||||
@@ -390,6 +395,7 @@ pub enum AltModifier {
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[allow(clippy::struct_field_names)]
|
||||
pub struct Layout {
|
||||
name: String,
|
||||
row_size: f32,
|
||||
@@ -403,8 +409,8 @@ pub struct Layout {
|
||||
}
|
||||
|
||||
impl Layout {
|
||||
fn load_from_disk() -> Layout {
|
||||
let mut layout = config::load_known_yaml::<Layout>(ConfigType::Keyboard);
|
||||
fn load_from_disk() -> Self {
|
||||
let mut layout = config::load_known_yaml::<Self>(ConfigType::Keyboard);
|
||||
layout.post_load();
|
||||
layout
|
||||
}
|
||||
@@ -413,25 +419,25 @@ impl Layout {
|
||||
for i in 0..self.key_sizes.len() {
|
||||
let row = &self.key_sizes[i];
|
||||
let width: f32 = row.iter().sum();
|
||||
if (width - self.row_size).abs() > 0.001 {
|
||||
panic!(
|
||||
"Row {} has a width of {}, but the row size is {}",
|
||||
i, width, self.row_size
|
||||
);
|
||||
}
|
||||
assert!(
|
||||
(width - self.row_size).abs() < 0.001,
|
||||
"Row {} has a width of {}, but the row size is {}",
|
||||
i,
|
||||
width,
|
||||
self.row_size
|
||||
);
|
||||
}
|
||||
|
||||
for i in 0..self.main_layout.len() {
|
||||
let row = &self.main_layout[i];
|
||||
let width = row.len();
|
||||
if width != self.key_sizes[i].len() {
|
||||
panic!(
|
||||
"Row {} has {} keys, needs to have {} according to key_sizes",
|
||||
i,
|
||||
width,
|
||||
self.key_sizes[i].len()
|
||||
);
|
||||
}
|
||||
assert!(
|
||||
(width == self.key_sizes[i].len()),
|
||||
"Row {} has {} keys, needs to have {} according to key_sizes",
|
||||
i,
|
||||
width,
|
||||
self.key_sizes[i].len()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -451,10 +457,7 @@ impl Layout {
|
||||
}
|
||||
if key.contains('_') {
|
||||
key = key.split('_').next().unwrap_or_else(|| {
|
||||
log::error!(
|
||||
"keyboard.yaml: Key '{}' must not start or end with '_'!",
|
||||
key
|
||||
);
|
||||
log::error!("keyboard.yaml: Key '{key}' must not start or end with '_'!");
|
||||
"???"
|
||||
});
|
||||
}
|
||||
@@ -502,10 +505,10 @@ struct KeyboardBackend {
|
||||
|
||||
impl OverlayBackend for KeyboardBackend {
|
||||
fn set_interaction(&mut self, interaction: Box<dyn crate::backend::input::InteractionHandler>) {
|
||||
self.canvas.set_interaction(interaction)
|
||||
self.canvas.set_interaction(interaction);
|
||||
}
|
||||
fn set_renderer(&mut self, renderer: Box<dyn crate::backend::overlay::OverlayRenderer>) {
|
||||
self.canvas.set_renderer(renderer)
|
||||
self.canvas.set_renderer(renderer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -516,7 +519,7 @@ impl InteractionHandler for KeyboardBackend {
|
||||
hit: &crate::backend::input::PointerHit,
|
||||
pressed: bool,
|
||||
) {
|
||||
self.canvas.on_pointer(app, hit, pressed)
|
||||
self.canvas.on_pointer(app, hit, pressed);
|
||||
}
|
||||
fn on_scroll(
|
||||
&mut self,
|
||||
@@ -525,10 +528,10 @@ impl InteractionHandler for KeyboardBackend {
|
||||
delta_y: f32,
|
||||
delta_x: f32,
|
||||
) {
|
||||
self.canvas.on_scroll(app, hit, delta_y, delta_x)
|
||||
self.canvas.on_scroll(app, hit, delta_y, delta_x);
|
||||
}
|
||||
fn on_left(&mut self, app: &mut AppState, pointer: usize) {
|
||||
self.canvas.on_left(app, pointer)
|
||||
self.canvas.on_left(app, pointer);
|
||||
}
|
||||
fn on_hover(
|
||||
&mut self,
|
||||
@@ -543,14 +546,20 @@ impl OverlayRenderer for KeyboardBackend {
|
||||
fn init(&mut self, app: &mut AppState) -> anyhow::Result<()> {
|
||||
self.canvas.init(app)
|
||||
}
|
||||
fn render(&mut self, app: &mut AppState) -> anyhow::Result<()> {
|
||||
self.canvas.render(app)
|
||||
fn should_render(&mut self, app: &mut AppState) -> anyhow::Result<ShouldRender> {
|
||||
self.canvas.should_render(app)
|
||||
}
|
||||
fn frame_transform(&mut self) -> Option<FrameTransform> {
|
||||
self.canvas.frame_transform()
|
||||
fn render(
|
||||
&mut self,
|
||||
app: &mut AppState,
|
||||
tgt: Arc<ImageView>,
|
||||
buf: &mut CommandBuffers,
|
||||
alpha: f32,
|
||||
) -> anyhow::Result<bool> {
|
||||
self.canvas.render(app, tgt, buf, alpha)
|
||||
}
|
||||
fn view(&mut self) -> Option<std::sync::Arc<vulkano::image::view::ImageView>> {
|
||||
self.canvas.view()
|
||||
fn frame_meta(&mut self) -> Option<FrameMeta> {
|
||||
self.canvas.frame_meta()
|
||||
}
|
||||
fn pause(&mut self, app: &mut AppState) -> anyhow::Result<()> {
|
||||
self.canvas.data_mut().modifiers = 0;
|
||||
|
||||
+27
-16
@@ -4,17 +4,19 @@ use std::{
|
||||
};
|
||||
|
||||
use futures::{Future, FutureExt};
|
||||
use vulkano::image::view::ImageView;
|
||||
use wlx_capture::pipewire::{pipewire_select_screen, PipewireCapture, PipewireSelectScreenResult};
|
||||
|
||||
use crate::{
|
||||
backend::{
|
||||
common::OverlaySelector,
|
||||
overlay::{
|
||||
ui_transform, FrameTransform, OverlayBackend, OverlayRenderer, OverlayState,
|
||||
ui_transform, FrameMeta, OverlayBackend, OverlayRenderer, OverlayState, ShouldRender,
|
||||
SplitOverlayBackend,
|
||||
},
|
||||
task::TaskType,
|
||||
},
|
||||
graphics::CommandBuffers,
|
||||
state::{AppSession, AppState},
|
||||
};
|
||||
|
||||
@@ -45,7 +47,18 @@ impl OverlayRenderer for MirrorRenderer {
|
||||
fn init(&mut self, _app: &mut AppState) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn render(&mut self, app: &mut AppState) -> anyhow::Result<()> {
|
||||
fn should_render(&mut self, app: &mut AppState) -> anyhow::Result<ShouldRender> {
|
||||
self.renderer
|
||||
.as_mut()
|
||||
.map_or(Ok(ShouldRender::Unable), |r| r.should_render(app))
|
||||
}
|
||||
fn render(
|
||||
&mut self,
|
||||
app: &mut AppState,
|
||||
tgt: Arc<ImageView>,
|
||||
buf: &mut CommandBuffers,
|
||||
alpha: f32,
|
||||
) -> anyhow::Result<bool> {
|
||||
if let Some(mut selector) = self.selector.take() {
|
||||
let maybe_pw_result = match selector
|
||||
.poll_unpin(&mut Context::from_waker(futures::task::noop_waker_ref()))
|
||||
@@ -53,7 +66,7 @@ impl OverlayRenderer for MirrorRenderer {
|
||||
Poll::Ready(result) => result,
|
||||
Poll::Pending => {
|
||||
self.selector = Some(selector);
|
||||
return Ok(());
|
||||
return Ok(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -76,7 +89,7 @@ impl OverlayRenderer for MirrorRenderer {
|
||||
));
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("Failed to create mirror due to PipeWire error: {:?}", e);
|
||||
log::warn!("Failed to create mirror due to PipeWire error: {e:?}");
|
||||
self.renderer = None;
|
||||
// drop self
|
||||
app.tasks
|
||||
@@ -87,24 +100,25 @@ impl OverlayRenderer for MirrorRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
let mut result = false;
|
||||
if let Some(renderer) = self.renderer.as_mut() {
|
||||
renderer.render(app)?;
|
||||
if let Some(view) = renderer.view() {
|
||||
let extent = view.image().extent();
|
||||
result = renderer.render(app, tgt, buf, alpha)?;
|
||||
if let Some(meta) = renderer.frame_meta() {
|
||||
let extent = meta.extent;
|
||||
if self.last_extent != extent {
|
||||
self.last_extent = extent;
|
||||
// resized
|
||||
app.tasks.enqueue(TaskType::Overlay(
|
||||
OverlaySelector::Name(self.name.clone()),
|
||||
Box::new(move |_app, o| {
|
||||
o.interaction_transform = ui_transform(&[extent[0], extent[1]]);
|
||||
o.interaction_transform = ui_transform([extent[0], extent[1]]);
|
||||
}),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(result)
|
||||
}
|
||||
fn pause(&mut self, app: &mut AppState) -> anyhow::Result<()> {
|
||||
if let Some(renderer) = self.renderer.as_mut() {
|
||||
@@ -118,12 +132,9 @@ impl OverlayRenderer for MirrorRenderer {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn view(&mut self) -> Option<std::sync::Arc<vulkano::image::view::ImageView>> {
|
||||
self.renderer.as_mut().and_then(|r| r.view())
|
||||
}
|
||||
|
||||
fn frame_transform(&mut self) -> Option<FrameTransform> {
|
||||
Some(FrameTransform {
|
||||
fn frame_meta(&mut self) -> Option<FrameMeta> {
|
||||
Some(FrameMeta {
|
||||
extent: self.last_extent,
|
||||
..Default::default()
|
||||
})
|
||||
@@ -134,7 +145,7 @@ pub fn new_mirror(
|
||||
name: Arc<str>,
|
||||
show_hide: bool,
|
||||
session: &AppSession,
|
||||
) -> Option<(OverlayState, Box<dyn OverlayBackend>)> {
|
||||
) -> (OverlayState, Box<dyn OverlayBackend>) {
|
||||
let state = OverlayState {
|
||||
name: name.clone(),
|
||||
show_hide,
|
||||
@@ -147,5 +158,5 @@ pub fn new_mirror(
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
Some((state, backend))
|
||||
(state, backend)
|
||||
}
|
||||
+436
-378
File diff suppressed because it is too large.
Load diff
+29
-26
@@ -1,8 +1,12 @@
|
||||
use std::{f32::consts::PI, ops::Add, sync::Arc, time::Instant};
|
||||
use std::{
|
||||
f32::consts::PI,
|
||||
ops::Add,
|
||||
sync::{Arc, LazyLock},
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use glam::{vec3a, Quat};
|
||||
use idmap_derive::IntegerId;
|
||||
use once_cell::sync::Lazy;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
@@ -18,7 +22,7 @@ use crate::{
|
||||
const FONT_SIZE: isize = 16;
|
||||
const PADDING: (f32, f32) = (25., 7.);
|
||||
const PIXELS_TO_METERS: f32 = 1. / 2000.;
|
||||
static TOAST_NAME: Lazy<Arc<str>> = Lazy::new(|| "toast".into());
|
||||
static TOAST_NAME: LazyLock<Arc<str>> = LazyLock::new(|| "toast".into());
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
pub enum DisplayMethod {
|
||||
@@ -46,8 +50,8 @@ pub struct Toast {
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl Toast {
|
||||
pub fn new(topic: ToastTopic, title: Arc<str>, body: Arc<str>) -> Self {
|
||||
Toast {
|
||||
pub const fn new(topic: ToastTopic, title: Arc<str>, body: Arc<str>) -> Self {
|
||||
Self {
|
||||
title,
|
||||
body,
|
||||
opacity: 1.0,
|
||||
@@ -56,15 +60,15 @@ impl Toast {
|
||||
topic,
|
||||
}
|
||||
}
|
||||
pub fn with_timeout(mut self, timeout: f32) -> Self {
|
||||
pub const fn with_timeout(mut self, timeout: f32) -> Self {
|
||||
self.timeout = timeout;
|
||||
self
|
||||
}
|
||||
pub fn with_opacity(mut self, opacity: f32) -> Self {
|
||||
pub const fn with_opacity(mut self, opacity: f32) -> Self {
|
||||
self.opacity = opacity;
|
||||
self
|
||||
}
|
||||
pub fn with_sound(mut self, sound: bool) -> Self {
|
||||
pub const fn with_sound(mut self, sound: bool) -> Self {
|
||||
self.sound = sound;
|
||||
self
|
||||
}
|
||||
@@ -93,7 +97,7 @@ impl Toast {
|
||||
// frame, only the first one gets created
|
||||
app.tasks.enqueue_at(
|
||||
TaskType::CreateOverlay(
|
||||
selector.clone(),
|
||||
selector,
|
||||
Box::new(move |app| {
|
||||
let mut maybe_toast = new_toast(self, app);
|
||||
if let Some((state, _)) = maybe_toast.as_mut() {
|
||||
@@ -110,7 +114,6 @@ impl Toast {
|
||||
),
|
||||
instant,
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,13 +143,19 @@ fn new_toast(toast: Toast, app: &mut AppState) -> Option<(OverlayState, Box<dyn
|
||||
}
|
||||
};
|
||||
|
||||
let title = if toast.title.len() > 0 {
|
||||
toast.title
|
||||
} else {
|
||||
let title = if toast.title.is_empty() {
|
||||
"Notification".into()
|
||||
} else {
|
||||
toast.title
|
||||
};
|
||||
|
||||
let mut size = if toast.body.len() > 0 {
|
||||
let mut size = if toast.body.is_empty() {
|
||||
let (w, h) = app
|
||||
.fc
|
||||
.get_text_size(&title, FONT_SIZE, app.graphics.clone())
|
||||
.ok()?;
|
||||
(w, h + 20.)
|
||||
} else {
|
||||
let (w0, _) = app
|
||||
.fc
|
||||
.get_text_size(&title, FONT_SIZE, app.graphics.clone())
|
||||
@@ -156,12 +165,6 @@ fn new_toast(toast: Toast, app: &mut AppState) -> Option<(OverlayState, Box<dyn
|
||||
.get_text_size(&toast.body, FONT_SIZE, app.graphics.clone())
|
||||
.ok()?;
|
||||
(w0.max(w1), h1 + 50.)
|
||||
} else {
|
||||
let (w, h) = app
|
||||
.fc
|
||||
.get_text_size(&title, FONT_SIZE, app.graphics.clone())
|
||||
.ok()?;
|
||||
(w, h + 20.)
|
||||
};
|
||||
|
||||
let og_width = size.0;
|
||||
@@ -181,15 +184,15 @@ fn new_toast(toast: Toast, app: &mut AppState) -> Option<(OverlayState, Box<dyn
|
||||
canvas.bg_color = color_parse("#1e2030").unwrap(); // want panic
|
||||
canvas.panel(0., 0., size.0, size.1, 16.);
|
||||
|
||||
if toast.body.len() > 0 {
|
||||
if toast.body.is_empty() {
|
||||
canvas.label_centered(PADDING.0, 0., og_width, size.1, 16., title);
|
||||
} else {
|
||||
canvas.label(PADDING.0, 54., og_width, size.1 - 54., 3., toast.body);
|
||||
|
||||
canvas.fg_color = color_parse("#b8c0e0").unwrap(); // want panic
|
||||
canvas.bg_color = color_parse("#24273a").unwrap(); // want panic
|
||||
canvas.panel(0., 0., size.0, 30., 16.);
|
||||
canvas.label_centered(PADDING.0, 16., og_width, FONT_SIZE as f32 + 2., 16., title);
|
||||
} else {
|
||||
canvas.label_centered(PADDING.0, 0., og_width, size.1, 16., title);
|
||||
}
|
||||
|
||||
let state = OverlayState {
|
||||
@@ -219,13 +222,13 @@ pub fn error_toast<ErrorType>(app: &mut AppState, title: &str, err: ErrorType)
|
||||
where
|
||||
ErrorType: std::fmt::Display + std::fmt::Debug,
|
||||
{
|
||||
log::error!("{}: {:?}", title, err); // More detailed version (use Debug)
|
||||
log::error!("{title}: {err:?}"); // More detailed version (use Debug)
|
||||
|
||||
// Brief version (use Display)
|
||||
msg_err(app, &format!("{}: {}", title, err));
|
||||
msg_err(app, &format!("{title}: {err}"));
|
||||
}
|
||||
|
||||
pub fn error_toast_str(app: &mut AppState, message: &str) {
|
||||
log::error!("{}", message);
|
||||
log::error!("{message}");
|
||||
msg_err(app, message);
|
||||
}
|
||||
@@ -29,7 +29,7 @@ where
|
||||
spawn_scale: config.width,
|
||||
spawn_point: state.session.config.watch_pos,
|
||||
spawn_rotation: state.session.config.watch_rot,
|
||||
interaction_transform: ui_transform(&config.size),
|
||||
interaction_transform: ui_transform(config.size),
|
||||
relative_to,
|
||||
..Default::default()
|
||||
},
|
||||
@@ -44,7 +44,7 @@ pub fn create_watch_canvas(
|
||||
) -> anyhow::Result<Canvas<(), ModularData>> {
|
||||
let config = config.unwrap_or_else(|| load_known_yaml::<ModularUiConfig>(ConfigType::Watch));
|
||||
|
||||
modular_canvas(&config.size, &config.elements, state)
|
||||
modular_canvas(config.size, &config.elements, state)
|
||||
}
|
||||
|
||||
pub fn watch_fade<D>(app: &mut AppState, watch: &mut OverlayData<D>)
|
||||
|
||||
+143
-97
@@ -12,8 +12,8 @@ use crate::{
|
||||
common::{OverlayContainer, OverlaySelector},
|
||||
input::{self, InteractionHandler},
|
||||
overlay::{
|
||||
ui_transform, FrameTransform, OverlayData, OverlayID, OverlayRenderer, OverlayState,
|
||||
SplitOverlayBackend, Z_ORDER_DASHBOARD,
|
||||
ui_transform, FrameMeta, OverlayData, OverlayID, OverlayRenderer, OverlayState,
|
||||
ShouldRender, SplitOverlayBackend, Z_ORDER_DASHBOARD,
|
||||
},
|
||||
task::TaskType,
|
||||
wayvr::{
|
||||
@@ -23,7 +23,7 @@ use crate::{
|
||||
},
|
||||
},
|
||||
config_wayvr,
|
||||
graphics::WlxGraphics,
|
||||
graphics::{CommandBuffers, WlxGraphics, WlxPipeline},
|
||||
gui::modular::button::{WayVRAction, WayVRDisplayClickAction},
|
||||
state::{self, AppState, KeyboardFocus},
|
||||
};
|
||||
@@ -41,14 +41,11 @@ pub struct WayVRContext {
|
||||
}
|
||||
|
||||
impl WayVRContext {
|
||||
pub fn new(
|
||||
wvr: Rc<RefCell<WayVRData>>,
|
||||
display: wayvr::display::DisplayHandle,
|
||||
) -> anyhow::Result<Self> {
|
||||
Ok(Self {
|
||||
wayvr: wvr.clone(),
|
||||
pub const fn new(wvr: Rc<RefCell<WayVRData>>, display: wayvr::display::DisplayHandle) -> Self {
|
||||
Self {
|
||||
wayvr: wvr,
|
||||
display,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +64,7 @@ pub struct WayVRData {
|
||||
impl WayVRData {
|
||||
pub fn new(config: wayvr::Config) -> anyhow::Result<Self> {
|
||||
Ok(Self {
|
||||
display_handle_map: Default::default(),
|
||||
display_handle_map: HashMap::default(),
|
||||
data: WayVR::new(config)?,
|
||||
overlays_to_create: Vec::new(),
|
||||
dashboard_executed: false,
|
||||
@@ -87,7 +84,7 @@ impl WayVRData {
|
||||
.any(|d| d.obj.name == candidate)
|
||||
{
|
||||
if num > 0 {
|
||||
candidate = format!("{} ({})", candidate, num);
|
||||
candidate = format!("{candidate} ({num})");
|
||||
}
|
||||
num += 1;
|
||||
}
|
||||
@@ -102,7 +99,7 @@ pub struct WayVRInteractionHandler {
|
||||
}
|
||||
|
||||
impl WayVRInteractionHandler {
|
||||
pub fn new(context: Rc<RefCell<WayVRContext>>, mouse_transform: Affine2) -> Self {
|
||||
pub const fn new(context: Rc<RefCell<WayVRContext>>, mouse_transform: Affine2) -> Self {
|
||||
Self {
|
||||
context,
|
||||
mouse_transform,
|
||||
@@ -122,8 +119,8 @@ impl InteractionHandler for WayVRInteractionHandler {
|
||||
|
||||
if let Some(disp) = wayvr.state.displays.get(&ctx.display) {
|
||||
let pos = self.mouse_transform.transform_point2(hit.uv);
|
||||
let x = ((pos.x * disp.width as f32) as i32).max(0);
|
||||
let y = ((pos.y * disp.height as f32) as i32).max(0);
|
||||
let x = ((pos.x * f32::from(disp.width)) as i32).max(0);
|
||||
let y = ((pos.y * f32::from(disp.height)) as i32).max(0);
|
||||
|
||||
let ctx = self.context.borrow();
|
||||
wayvr.state.send_mouse_move(ctx.display, x as u32, y as u32);
|
||||
@@ -151,7 +148,7 @@ impl InteractionHandler for WayVRInteractionHandler {
|
||||
if pressed {
|
||||
wayvr.state.send_mouse_down(ctx.display, index);
|
||||
} else {
|
||||
wayvr.state.send_mouse_up(ctx.display, index);
|
||||
wayvr.state.send_mouse_up(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -168,11 +165,12 @@ impl InteractionHandler for WayVRInteractionHandler {
|
||||
.borrow_mut()
|
||||
.data
|
||||
.state
|
||||
.send_mouse_scroll(ctx.display, delta_y, delta_x);
|
||||
.send_mouse_scroll(delta_y, delta_x);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WayVRRenderer {
|
||||
pipeline: Arc<WlxPipeline>,
|
||||
vk_image: Option<Arc<vulkano::image::Image>>,
|
||||
vk_image_view: Option<Arc<vulkano::image::view::ImageView>>,
|
||||
context: Rc<RefCell<WayVRContext>>,
|
||||
@@ -185,8 +183,20 @@ impl WayVRRenderer {
|
||||
wvr: Rc<RefCell<WayVRData>>,
|
||||
display: wayvr::display::DisplayHandle,
|
||||
) -> anyhow::Result<Self> {
|
||||
let Ok(shaders) = app.graphics.shared_shaders.read() else {
|
||||
anyhow::bail!("Failed to lock shared shaders for reading");
|
||||
};
|
||||
|
||||
let pipeline = app.graphics.create_pipeline(
|
||||
shaders.get("vert_common").unwrap().clone(), // want panic
|
||||
shaders.get("frag_srgb").unwrap().clone(), // want panic
|
||||
app.graphics.native_format,
|
||||
None,
|
||||
)?;
|
||||
|
||||
Ok(Self {
|
||||
context: Rc::new(RefCell::new(WayVRContext::new(wvr, display)?)),
|
||||
pipeline,
|
||||
context: Rc::new(RefCell::new(WayVRContext::new(wvr, display))),
|
||||
vk_image: None,
|
||||
vk_image_view: None,
|
||||
graphics: app.graphics.clone(),
|
||||
@@ -207,10 +217,7 @@ fn get_or_create_display_by_name(
|
||||
.session
|
||||
.wayvr_config
|
||||
.get_display(disp_name)
|
||||
.ok_or(anyhow::anyhow!(
|
||||
"Cannot find display named \"{}\"",
|
||||
disp_name
|
||||
))?
|
||||
.ok_or_else(|| anyhow::anyhow!("Cannot find display named \"{}\"", disp_name))?
|
||||
.clone();
|
||||
|
||||
let disp_handle = wayvr.data.state.create_display(
|
||||
@@ -289,6 +296,7 @@ where
|
||||
},
|
||||
)?;
|
||||
|
||||
overlay.state.curvature = Some(0.15);
|
||||
overlay.state.want_visible = true;
|
||||
overlay.state.spawn_scale = 2.0;
|
||||
overlay.state.spawn_point = vec3a(0.0, -0.35, -1.75);
|
||||
@@ -299,25 +307,17 @@ where
|
||||
unreachable!(); /* safe, not possible to trigger */
|
||||
};
|
||||
|
||||
// FIXME: overlay curvature needs to be dispatched for some unknown reason, this value is not set otherwise
|
||||
app.tasks.enqueue(TaskType::Overlay(
|
||||
OverlaySelector::Id(overlay.state.id),
|
||||
Box::new(move |_app, o| {
|
||||
o.curvature = Some(0.15);
|
||||
}),
|
||||
));
|
||||
|
||||
overlays.add(overlay);
|
||||
|
||||
let args_vec = match &conf_dash.args {
|
||||
Some(args) => gen_args_vec(args),
|
||||
None => vec![],
|
||||
};
|
||||
let args_vec = &conf_dash
|
||||
.args
|
||||
.as_ref()
|
||||
.map_or_else(Vec::new, |args| gen_args_vec(args.as_str()));
|
||||
|
||||
let env_vec = match &conf_dash.env {
|
||||
Some(env) => gen_env_vec(env),
|
||||
None => vec![],
|
||||
};
|
||||
let env_vec = &conf_dash
|
||||
.env
|
||||
.as_ref()
|
||||
.map_or_else(Vec::new, |env| gen_env_vec(env));
|
||||
|
||||
let mut userdata = HashMap::new();
|
||||
userdata.insert(String::from("type"), String::from("dashboard"));
|
||||
@@ -326,8 +326,8 @@ where
|
||||
let _process_handle_unused = wayvr.data.state.spawn_process(
|
||||
disp_handle,
|
||||
&conf_dash.exec,
|
||||
&args_vec,
|
||||
&env_vec,
|
||||
args_vec,
|
||||
env_vec,
|
||||
userdata,
|
||||
)?;
|
||||
|
||||
@@ -433,6 +433,7 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub fn tick_events<O>(app: &mut AppState, overlays: &mut OverlayContainer<O>) -> anyhow::Result<()>
|
||||
where
|
||||
O: Default,
|
||||
@@ -477,36 +478,42 @@ where
|
||||
|
||||
for result in res {
|
||||
match result {
|
||||
wayvr::TickTask::NewExternalProcess(req) => {
|
||||
wayvr::TickTask::NewExternalProcess(request) => {
|
||||
let config = &app.session.wayvr_config;
|
||||
|
||||
let disp_name = if let Some(display_name) = req.env.display_name {
|
||||
config
|
||||
.get_display(display_name.as_str())
|
||||
.map(|_| display_name)
|
||||
} else {
|
||||
config
|
||||
.get_default_display()
|
||||
.map(|(display_name, _)| display_name)
|
||||
};
|
||||
let disp_name = request.env.display_name.map_or_else(
|
||||
|| {
|
||||
config
|
||||
.get_default_display()
|
||||
.map(|(display_name, _)| display_name)
|
||||
},
|
||||
|display_name| {
|
||||
config
|
||||
.get_display(display_name.as_str())
|
||||
.map(|_| display_name)
|
||||
},
|
||||
);
|
||||
|
||||
if let Some(disp_name) = disp_name {
|
||||
let mut wayvr = r_wayvr.borrow_mut();
|
||||
|
||||
log::info!("Registering external process with PID {}", req.pid);
|
||||
log::info!("Registering external process with PID {}", request.pid);
|
||||
|
||||
let disp_handle = get_or_create_display_by_name(app, &mut wayvr, &disp_name)?;
|
||||
|
||||
wayvr.data.state.add_external_process(disp_handle, req.pid);
|
||||
wayvr
|
||||
.data
|
||||
.state
|
||||
.add_external_process(disp_handle, request.pid);
|
||||
|
||||
wayvr
|
||||
.data
|
||||
.state
|
||||
.manager
|
||||
.add_client(wayvr::client::WayVRClient {
|
||||
client: req.client,
|
||||
client: request.client,
|
||||
display_handle: disp_handle,
|
||||
pid: req.pid,
|
||||
pid: request.pid,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -558,19 +565,23 @@ impl WayVRRenderer {
|
||||
&mut self,
|
||||
data: &wayvr::egl_data::RenderSoftwarePixelsData,
|
||||
) -> anyhow::Result<()> {
|
||||
let mut upload = self
|
||||
.graphics
|
||||
.create_command_buffer(CommandBufferUsage::OneTimeSubmit)?;
|
||||
let mut upload = self.graphics.create_uploads_command_buffer(
|
||||
self.graphics.transfer_queue.clone(),
|
||||
CommandBufferUsage::OneTimeSubmit,
|
||||
)?;
|
||||
|
||||
let tex = upload.texture2d_raw(
|
||||
data.width as u32,
|
||||
data.height as u32,
|
||||
u32::from(data.width),
|
||||
u32::from(data.height),
|
||||
vulkano::format::Format::R8G8B8A8_UNORM,
|
||||
&data.data,
|
||||
)?;
|
||||
|
||||
// FIXME: can we use _buffers_ here?
|
||||
upload.build_and_execute_now()?;
|
||||
|
||||
//buffers.push(upload.build()?);
|
||||
|
||||
self.vk_image = Some(tex.clone());
|
||||
self.vk_image_view = Some(ImageView::new_default(tex).unwrap());
|
||||
|
||||
@@ -599,8 +610,8 @@ impl WayVRRenderer {
|
||||
|
||||
let frame = DmabufFrame {
|
||||
format: FrameFormat {
|
||||
width: disp.width as u32,
|
||||
height: disp.height as u32,
|
||||
width: u32::from(disp.width),
|
||||
height: u32::from(disp.height),
|
||||
fourcc: FourCC {
|
||||
value: data.mod_info.fourcc,
|
||||
},
|
||||
@@ -609,6 +620,7 @@ impl WayVRRenderer {
|
||||
},
|
||||
num_planes: 1,
|
||||
planes,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
drop(wayvr);
|
||||
@@ -653,27 +665,39 @@ impl OverlayRenderer for WayVRRenderer {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn render(&mut self, _app: &mut state::AppState) -> anyhow::Result<()> {
|
||||
fn should_render(&mut self, _app: &mut AppState) -> anyhow::Result<ShouldRender> {
|
||||
let ctx = self.context.borrow();
|
||||
let mut wayvr = ctx.wayvr.borrow_mut();
|
||||
|
||||
let redrawn = match wayvr.data.tick_display(ctx.display) {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
log::error!("tick_display failed: {}", e);
|
||||
return Ok(()); // do not proceed further
|
||||
log::error!("tick_display failed: {e}");
|
||||
return Ok(ShouldRender::Unable);
|
||||
}
|
||||
};
|
||||
|
||||
if !redrawn {
|
||||
return Ok(());
|
||||
if redrawn {
|
||||
Ok(ShouldRender::Should)
|
||||
} else {
|
||||
Ok(ShouldRender::Can)
|
||||
}
|
||||
}
|
||||
|
||||
fn render(
|
||||
&mut self,
|
||||
app: &mut state::AppState,
|
||||
tgt: Arc<ImageView>,
|
||||
buf: &mut CommandBuffers,
|
||||
alpha: f32,
|
||||
) -> anyhow::Result<bool> {
|
||||
let ctx = self.context.borrow();
|
||||
let wayvr = ctx.wayvr.borrow_mut();
|
||||
|
||||
let data = wayvr
|
||||
.data
|
||||
.state
|
||||
.get_render_data(ctx.display)
|
||||
.ok_or(anyhow::anyhow!("Failed to fetch render data"))?
|
||||
.ok_or_else(|| anyhow::anyhow!("Failed to fetch render data"))?
|
||||
.clone();
|
||||
|
||||
drop(wayvr);
|
||||
@@ -690,18 +714,43 @@ impl OverlayRenderer for WayVRRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
let Some(view) = self.vk_image_view.as_ref() else {
|
||||
return Ok(false);
|
||||
};
|
||||
|
||||
let set0 =
|
||||
self.pipeline
|
||||
.uniform_sampler(0, view.clone(), app.graphics.texture_filtering)?;
|
||||
|
||||
let set1 = self.pipeline.uniform_buffer(1, vec![alpha])?;
|
||||
|
||||
let pass = self
|
||||
.pipeline
|
||||
.create_pass_for_target(tgt.clone(), vec![set0, set1])?;
|
||||
|
||||
let mut cmd_buffer = app
|
||||
.graphics
|
||||
.create_command_buffer(CommandBufferUsage::OneTimeSubmit)?;
|
||||
cmd_buffer.begin_rendering(tgt)?;
|
||||
cmd_buffer.run_ref(&pass)?;
|
||||
cmd_buffer.end_rendering()?;
|
||||
buf.push(cmd_buffer.build()?);
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn view(&mut self) -> Option<Arc<vulkano::image::view::ImageView>> {
|
||||
self.vk_image_view.clone()
|
||||
}
|
||||
|
||||
fn frame_transform(&mut self) -> Option<FrameTransform> {
|
||||
self.vk_image_view.as_ref().map(|view| FrameTransform {
|
||||
extent: view.image().extent(),
|
||||
..Default::default()
|
||||
})
|
||||
fn frame_meta(&mut self) -> Option<FrameMeta> {
|
||||
let ctx = self.context.borrow();
|
||||
let wayvr = ctx.wayvr.borrow_mut();
|
||||
wayvr
|
||||
.data
|
||||
.state
|
||||
.displays
|
||||
.get(&ctx.display)
|
||||
.map(|disp| FrameMeta {
|
||||
extent: [disp.width as _, disp.height as _, 1],
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -717,10 +766,10 @@ pub fn create_wayvr_display_overlay<O>(
|
||||
where
|
||||
O: Default,
|
||||
{
|
||||
let transform = ui_transform(&[display_width as u32, display_height as u32]);
|
||||
let transform = ui_transform([u32::from(display_width), u32::from(display_height)]);
|
||||
|
||||
let state = OverlayState {
|
||||
name: format!("WayVR - {}", name).into(),
|
||||
name: format!("WayVR - {name}").into(),
|
||||
keyboard_focus: Some(KeyboardFocus::WayVR),
|
||||
want_visible: true,
|
||||
interactable: true,
|
||||
@@ -772,16 +821,13 @@ fn action_app_click<O>(
|
||||
where
|
||||
O: Default,
|
||||
{
|
||||
let wayvr = app.get_wayvr()?.clone();
|
||||
let wayvr = app.get_wayvr()?;
|
||||
|
||||
let catalog = app
|
||||
.session
|
||||
.wayvr_config
|
||||
.get_catalog(catalog_name)
|
||||
.ok_or(anyhow::anyhow!(
|
||||
"Failed to get catalog \"{}\"",
|
||||
catalog_name
|
||||
))?
|
||||
.ok_or_else(|| anyhow::anyhow!("Failed to get catalog \"{}\"", catalog_name))?
|
||||
.clone();
|
||||
|
||||
if let Some(app_entry) = catalog.get_app(app_name) {
|
||||
@@ -793,22 +839,22 @@ where
|
||||
&app_entry.target_display.to_lowercase(),
|
||||
)?;
|
||||
|
||||
let args_vec = match &app_entry.args {
|
||||
Some(args) => gen_args_vec(args),
|
||||
None => vec![],
|
||||
};
|
||||
let args_vec = &app_entry
|
||||
.args
|
||||
.as_ref()
|
||||
.map_or_else(Vec::new, |args| gen_args_vec(args.as_str()));
|
||||
|
||||
let env_vec = match &app_entry.env {
|
||||
Some(env) => gen_env_vec(env),
|
||||
None => vec![],
|
||||
};
|
||||
let env_vec = &app_entry
|
||||
.env
|
||||
.as_ref()
|
||||
.map_or_else(Vec::new, |env| gen_env_vec(env));
|
||||
|
||||
// Terminate existing process if required
|
||||
if let Some(process_handle) =
|
||||
wayvr
|
||||
.data
|
||||
.state
|
||||
.process_query(disp_handle, &app_entry.exec, &args_vec, &env_vec)
|
||||
.process_query(disp_handle, &app_entry.exec, args_vec, env_vec)
|
||||
{
|
||||
// Terminate process
|
||||
wayvr.data.terminate_process(process_handle);
|
||||
@@ -817,9 +863,9 @@ where
|
||||
wayvr.data.state.spawn_process(
|
||||
disp_handle,
|
||||
&app_entry.exec,
|
||||
&args_vec,
|
||||
&env_vec,
|
||||
Default::default(),
|
||||
args_vec,
|
||||
env_vec,
|
||||
HashMap::default(),
|
||||
)?;
|
||||
|
||||
show_display::<O>(&mut wayvr, overlays, app_entry.target_display.as_str());
|
||||
|
||||
+7
-3
@@ -32,11 +32,15 @@ keyboard_repeat_rate: 50
|
||||
|
||||
# WayVR-compatible dashboard.
|
||||
# For now, there is only one kind of dashboard with WayVR IPC support (WayVR Dashboard).
|
||||
#
|
||||
# Build instructions: https://github.com/olekolek1000/wayvr-dashboard
|
||||
# exec: Executable path, for example /home/USER/wayvr_dashboard/src-tauri/target/release/wayvr_dashboard
|
||||
# GDK_BACKEND=wayland: Force-use Wayland for GTK apps
|
||||
#
|
||||
# exec: Executable path, for example "/home/USER/wayvr-dashboard/src-tauri/target/release/wayvr-dashboard"
|
||||
# or just "wayvr-dashboard" if you have it installed from your package manager.
|
||||
#
|
||||
# GDK_BACKEND=wayland: Force-use Wayland for GTK apps.
|
||||
dashboard:
|
||||
exec: "wayvr_dashboard"
|
||||
exec: "wayvr-dashboard"
|
||||
args: ""
|
||||
env: ["GDK_BACKEND=wayland"]
|
||||
|
||||
|
||||
+28
-8
@@ -46,6 +46,7 @@ pub mod frag_color {
|
||||
",
|
||||
}
|
||||
}
|
||||
|
||||
//layout (location = 1) in float corner_radius;
|
||||
//out_color = in_color;
|
||||
// Some equation that determines whether to keep the pixel
|
||||
@@ -176,17 +177,14 @@ pub mod frag_screen {
|
||||
layout (location = 0) out vec4 out_color;
|
||||
|
||||
layout (set = 0, binding = 0) uniform sampler2D in_texture;
|
||||
layout (set = 1, binding = 0) uniform AlphaBlock {
|
||||
uniform float alpha;
|
||||
};
|
||||
|
||||
void main()
|
||||
{
|
||||
out_color = texture(in_texture, in_uv);
|
||||
|
||||
// linear to srgb
|
||||
bvec4 cutoff = lessThan(out_color, vec4(0.0031308));
|
||||
vec4 higher = (pow(out_color, vec4(0.4166667)) * vec4(1.055)) - vec4(0.055);
|
||||
vec4 lower = out_color*vec4(12.92);
|
||||
out_color = mix(higher, lower, cutoff);
|
||||
out_color.a = 1.0;
|
||||
out_color.a = alpha;
|
||||
}
|
||||
",
|
||||
}
|
||||
@@ -216,7 +214,7 @@ pub mod frag_srgb {
|
||||
vec4 lower = out_color/vec4(12.92);
|
||||
|
||||
out_color = mix(higher, lower, cutoff);
|
||||
out_color.a = alpha;
|
||||
out_color.a *= alpha;
|
||||
}
|
||||
",
|
||||
}
|
||||
@@ -244,3 +242,25 @@ pub mod frag_swapchain {
|
||||
",
|
||||
}
|
||||
}
|
||||
|
||||
pub mod frag_line {
|
||||
vulkano_shaders::shader! {
|
||||
ty: "fragment",
|
||||
src: r"#version 310 es
|
||||
precision highp float;
|
||||
|
||||
layout (location = 0) in vec2 in_uv;
|
||||
layout (location = 0) out vec4 out_color;
|
||||
|
||||
layout (set = 0, binding = 0) uniform ColorBlock {
|
||||
uniform vec4 in_color;
|
||||
uniform vec2 unused;
|
||||
};
|
||||
|
||||
void main()
|
||||
{
|
||||
out_color = in_color;
|
||||
}
|
||||
",
|
||||
}
|
||||
}
|
||||
+26
-31
@@ -26,8 +26,8 @@ use crate::{
|
||||
hid::HidProvider,
|
||||
overlays::toast::{DisplayMethod, ToastTopic},
|
||||
shaders::{
|
||||
frag_color, frag_glyph, frag_grid, frag_screen, frag_sprite, frag_sprite2, frag_sprite2_hl,
|
||||
frag_swapchain, vert_common,
|
||||
frag_color, frag_glyph, frag_grid, frag_line, frag_screen, frag_sprite, frag_sprite2,
|
||||
frag_sprite2_hl, frag_srgb, frag_swapchain, vert_common,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -74,6 +74,12 @@ impl AppState {
|
||||
let shader = frag_color::load(graphics.device.clone())?;
|
||||
shaders.insert("frag_color", shader);
|
||||
|
||||
let shader = frag_line::load(graphics.device.clone())?;
|
||||
shaders.insert("frag_line", shader);
|
||||
|
||||
let shader = frag_srgb::load(graphics.device.clone())?;
|
||||
shaders.insert("frag_srgb", shader);
|
||||
|
||||
let shader = frag_glyph::load(graphics.device.clone())?;
|
||||
shaders.insert("frag_glyph", shader);
|
||||
|
||||
@@ -112,12 +118,12 @@ impl AppState {
|
||||
#[cfg(feature = "osc")]
|
||||
let osc_sender = crate::backend::osc::OscSender::new(session.config.osc_out_port).ok();
|
||||
|
||||
let toast_sound_wav = AppState::try_load_bytes(
|
||||
let toast_sound_wav = Self::try_load_bytes(
|
||||
&session.config.notification_sound,
|
||||
include_bytes!("res/557297.wav")
|
||||
include_bytes!("res/557297.wav"),
|
||||
);
|
||||
|
||||
Ok(AppState {
|
||||
Ok(Self {
|
||||
fc: FontCache::new(session.config.primary_font.clone())?,
|
||||
session,
|
||||
tasks,
|
||||
@@ -153,39 +159,28 @@ impl AppState {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_load_bytes(path: &str, fallback_data: &'static [u8]) -> &'static [u8]
|
||||
{
|
||||
pub fn try_load_bytes(path: &str, fallback_data: &'static [u8]) -> &'static [u8] {
|
||||
if path.is_empty() {
|
||||
return fallback_data;
|
||||
}
|
||||
|
||||
let real_path = config_io::get_config_root().join(&*path);
|
||||
let real_path = config_io::get_config_root().join(path);
|
||||
|
||||
if std::fs::File::open(real_path.clone()).is_err() {
|
||||
log::warn!(
|
||||
"Could not open file at: {}",
|
||||
path
|
||||
);
|
||||
log::warn!("Could not open file at: {path}");
|
||||
return fallback_data;
|
||||
};
|
||||
}
|
||||
|
||||
return match std::fs::read(real_path.clone()){
|
||||
match std::fs::read(real_path) {
|
||||
// Box is used here to work around `f`'s limited lifetime
|
||||
Ok(f) => {
|
||||
Box::leak(Box::new(f)).as_slice()
|
||||
},
|
||||
Ok(f) => Box::leak(Box::new(f)).as_slice(),
|
||||
Err(e) => {
|
||||
log::warn!(
|
||||
"Failed to read file at: {}",
|
||||
path
|
||||
);
|
||||
log::warn!("{:?}", e);
|
||||
log::warn!("Failed to read file at: {path}");
|
||||
log::warn!("{e:?}");
|
||||
fallback_data
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub struct AppSession {
|
||||
@@ -200,7 +195,7 @@ pub struct AppSession {
|
||||
impl AppSession {
|
||||
pub fn load() -> Self {
|
||||
let config_root_path = config_io::ConfigRoot::Generic.ensure_dir();
|
||||
log::info!("Config root path: {:?}", config_root_path);
|
||||
log::info!("Config root path: {config_root_path:?}");
|
||||
let config = GeneralConfig::load_from_disk();
|
||||
|
||||
let mut toast_topics = IdMap::new();
|
||||
@@ -215,11 +210,11 @@ impl AppSession {
|
||||
#[cfg(feature = "wayvr")]
|
||||
let wayvr_config = config_wayvr::load_wayvr();
|
||||
|
||||
AppSession {
|
||||
Self {
|
||||
config,
|
||||
toast_topics,
|
||||
#[cfg(feature = "wayvr")]
|
||||
wayvr_config,
|
||||
toast_topics,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -230,8 +225,8 @@ pub struct AudioOutput {
|
||||
}
|
||||
|
||||
impl AudioOutput {
|
||||
pub fn new() -> Self {
|
||||
AudioOutput {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
audio_stream: None,
|
||||
first_try: true,
|
||||
}
|
||||
@@ -258,7 +253,7 @@ impl AudioOutput {
|
||||
let source = match Decoder::new_wav(cursor) {
|
||||
Ok(source) => source,
|
||||
Err(e) => {
|
||||
log::error!("Failed to play sound: {:?}", e);
|
||||
log::error!("Failed to play sound: {e:?}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user