Compare commits

..
19 Commits
Author SHA1 Message Date
galister 5ca634b552 Release 25.4.1 2025-04-10 05:11:24 +09:00
galister 409b89585e release native binary as well as appimage 2025-04-10 05:07:42 +09:00
galister 92c59850b6 only run clippy on default features 2025-04-08 19:59:23 +09:00
galister bffb4c12cf fix build with non-default features 2025-04-08 19:52:34 +09:00
galister feac111949 CI to cover clippy + uidev 2025-04-08 19:25:11 +09:00
galister f13a473048 use multi-threaded capture if vk queues ≥ 2 2025-04-08 00:49:34 +09:00
galister b2a7d3c4cb use-bigger-hammer style fix for wayvr dmabufs 2025-04-07 07:56:01 +09:00
galister 4cb12a2a01 Revert "WayVR: Vulkano dmabuf attempt"
This reverts commit 45ab38c310.
2025-04-07 07:34:56 +09:00
galister b830c826a0 separate transfer and capture queues 2025-04-07 05:41:02 +09:00
Aleksander 45ab38c310 WayVR: Vulkano dmabuf attempt 2025-04-06 22:03:40 +02:00
galister bc6c510af1 get rid of cstr dependency 2025-04-07 04:46:47 +09:00
galister 3dd31ec468 fix mouse renderer 2025-04-07 03:38:16 +09:00
galister 60850d8d70 get rid of once_cell 2025-04-07 03:31:58 +09:00
galister 7ac3072506 move drm modifiers to WlxGraphics 2025-04-07 03:06:22 +09:00
galister 2952a3cea1 update vulkano 2025-04-07 02:33:40 +09:00
Aleksander bb2f8380fd WayVR: Fix frame_meta returning None 2025-04-06 19:03:29 +02:00
galister 1deae19f92 refactors & clippy recommendations 2025-04-07 00:58:03 +09:00
galister 3365aefc13 update deps 2025-04-06 22:15:22 +09:00
galister cf29682e66 rework rendering backend 2025-04-06 22:12:56 +09:00
65 changed files with 3573 additions and 3223 deletions

No files matched your search

@@ -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
+32
View File
@@ -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
+12 -2
View File
@@ -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 -537
View File
File diff suppressed because it is too large. Load diff
+20 -16
View File
@@ -4,25 +4,30 @@ debug = true
[package]
name = "wlx-overlay-s"
version = "25.4.0"
version = "25.4.1"
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.0", 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
View File
@@ -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
View File
@@ -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;
+44 -49
View File
@@ -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 {
+5 -5
View File
@@ -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()?,
})
+8 -9
View File
@@ -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(
+7 -7
View File
@@ -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");
+22 -21
View File
@@ -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()
},
@@ -131,15 +134,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 +180,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()
})
+6 -6
View File
@@ -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
View File
@@ -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));
+29 -21
View File
@@ -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,20 @@ 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], meta.format)?;
self.data.image_view = Some(ImageView::new_default(image)?);
Ok(true)
}
pub(super) fn after_input(
&mut self,
overlay: &mut OverlayManager,
@@ -200,7 +211,7 @@ impl OverlayData<OpenVrOverlayData> {
let effective = self.state.transform
* self
.backend
.frame_transform()
.frame_meta()
.map(|f| f.transform)
.unwrap_or_default();
@@ -221,21 +232,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 +253,7 @@ impl OverlayData<OpenVrOverlayData> {
}
}
let raw_image = image.handle().as_raw();
let format = image.format();
let mut texture = VRVulkanTextureData_t {
@@ -257,8 +265,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{}, {:?}",
+11 -10
View File
@@ -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());
}
+4 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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, SWAPCHAIN_FORMAT},
};
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
SWAPCHAIN_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>,
}
+138 -111
View File
@@ -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},
@@ -43,30 +44,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 +73,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,12 +99,12 @@ 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()
});
@@ -114,15 +113,10 @@ pub fn openxr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
&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 +130,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 +168,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 +198,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 +223,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 +250,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 +264,46 @@ 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 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 +312,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 +354,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 +473,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 +497,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 +523,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 +534,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);
}
}
}
+43 -45
View File
@@ -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)
+15 -22
View File
@@ -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
View File
@@ -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, SWAPCHAIN_FORMAT},
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
SWAPCHAIN_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
SWAPCHAIN_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
View File
@@ -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, SWAPCHAIN_FORMAT};
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: SWAPCHAIN_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: SWAPCHAIN_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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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,
+9 -13
View File
@@ -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;
+3 -5
View File
@@ -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();
}
+27 -27
View File
@@ -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,
+15 -15
View File
@@ -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;
}
+1 -1
View File
@@ -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(),
})),
}
}
+7 -7
View File
@@ -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
View File
@@ -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)?;
+22 -21
View File
@@ -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,
+32 -47
View File
@@ -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:?}");
}
}
}
+4 -4
View File
@@ -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,
+39 -44
View File
@@ -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
}
@@ -294,7 +291,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 +312,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 +369,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 +379,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 +411,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 +428,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
View File
@@ -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
View File
@@ -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
View File
@@ -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,
+172
View File
@@ -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 -536
View File
File diff suppressed because it is too large. Load diff
+3 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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, SWAPCHAIN_FORMAT},
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
SWAPCHAIN_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,
SWAPCHAIN_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
SWAPCHAIN_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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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() {
+3 -4
View File
@@ -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()
})
}
+5 -5
View File
@@ -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
View File
@@ -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
View File
@@ -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)
}
+431 -375
View File
File diff suppressed because it is too large. Load diff
+29 -26
View File
@@ -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);
}
+2 -2
View File
@@ -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>)
+142 -97
View File
@@ -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, SWAPCHAIN_FORMAT},
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
SWAPCHAIN_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,
},
@@ -653,27 +664,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 +713,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 +765,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 +820,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 +838,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 +862,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());
+28 -8
View File
@@ -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
View File
@@ -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;
}
};