add wlx-common lib, move GeneralConfig into it

This commit is contained in:
Aleksander
2025-11-25 00:21:51 +01:00
parent a6da79bf3d
commit 8485639e00
35 changed files with 601 additions and 566 deletions

12
Cargo.lock generated
View File

@@ -6847,6 +6847,17 @@ dependencies = [
"wayland-protocols",
]
[[package]]
name = "wlx-common"
version = "0.1.0"
dependencies = [
"chrono",
"glam",
"idmap",
"idmap-derive",
"serde",
]
[[package]]
name = "wlx-overlay-s"
version = "25.4.2"
@@ -6902,6 +6913,7 @@ dependencies = [
"wgui",
"winit",
"wlx-capture",
"wlx-common",
"xcb",
"xdg 3.0.0",
"xkbcommon 0.9.0",

View File

@@ -10,13 +10,21 @@ inherits = "release"
debug = true
[workspace]
members = ["uidev", "wgui", "wlx-overlay-s", "wlx-capture", "dash-frontend"]
members = [
"uidev",
"wgui",
"wlx-common",
"wlx-overlay-s",
"wlx-capture",
"dash-frontend",
]
resolver = "3"
[workspace.dependencies]
anyhow = "1.0.100"
glam = "0.30.7"
idmap = "0.2.2"
idmap-derive = "0.2.2"
log = "0.4.28"
regex = "1.11.3"
rust-embed = "8.7.2"

8
wlx-common/.editorconfig Normal file
View File

@@ -0,0 +1,8 @@
root = true
[*.rs]
indent_style = tab
indent_size = 2
charset = utf-8
trim_trailing_whitespace = false
insert_final_newline = false

12
wlx-common/Cargo.toml Normal file
View File

@@ -0,0 +1,12 @@
[package]
name = "wlx-common"
version = "0.1.0"
edition = "2024"
[dependencies]
serde = { version = "1.0.228", features = ["derive"] }
glam = { workspace = true }
chrono = "0.4.42"
idmap = { workspace = true, features = ["serde"] }
idmap-derive = { workspace = true }

3
wlx-common/rustfmt.toml Normal file
View File

@@ -0,0 +1,3 @@
tab_spaces = 2
hard_tabs = true
max_width = 120

View File

@@ -0,0 +1,59 @@
use std::sync::Arc;
pub type AStrMap<V> = Vec<(Arc<str>, V)>;
pub trait AStrMapExt<V> {
fn arc_set(&mut self, key: Arc<str>, value: V) -> bool;
fn arc_get(&self, key: &str) -> Option<&V>;
fn arc_rm(&mut self, key: &str) -> Option<V>;
}
impl<V> AStrMapExt<V> for AStrMap<V> {
fn arc_set(&mut self, key: Arc<str>, value: V) -> bool {
let index = self.iter().position(|(k, _)| k.as_ref().eq(key.as_ref()));
index.map(|i| self.remove(i).1);
self.push((key, value));
true
}
fn arc_get(&self, key: &str) -> Option<&V> {
self
.iter()
.find_map(|(k, v)| if k.as_ref().eq(key) { Some(v) } else { None })
}
fn arc_rm(&mut self, key: &str) -> Option<V> {
let index = self.iter().position(|(k, _)| k.as_ref().eq(key));
index.map(|i| self.remove(i).1)
}
}
pub type AStrSet = Vec<Arc<str>>;
pub trait AStrSetExt {
fn arc_set(&mut self, value: Arc<str>) -> bool;
fn arc_get(&self, value: &str) -> bool;
fn arc_rm(&mut self, value: &str) -> bool;
}
impl AStrSetExt for AStrSet {
fn arc_set(&mut self, value: Arc<str>) -> bool {
if self.iter().any(|v| v.as_ref().eq(value.as_ref())) {
return false;
}
self.push(value);
true
}
fn arc_get(&self, value: &str) -> bool {
self.iter().any(|v| v.as_ref().eq(value))
}
fn arc_rm(&mut self, value: &str) -> bool {
let index = self.iter().position(|v| v.as_ref().eq(value));
index.is_some_and(|i| {
self.remove(i);
true
})
}
}

9
wlx-common/src/common.rs Normal file
View File

@@ -0,0 +1,9 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default)]
#[repr(u8)]
pub enum LeftRight {
#[default]
Left,
Right,
}

285
wlx-common/src/config.rs Normal file
View File

@@ -0,0 +1,285 @@
use std::{collections::HashMap, sync::Arc};
use chrono::Offset;
use glam::{Affine3A, Quat, Vec3, vec3};
use idmap::IdMap;
use serde::{Deserialize, Serialize};
use crate::{
astr_containers::{AStrMap, AStrSet},
common::LeftRight,
overlays::{ToastDisplayMethod, ToastTopic},
windowing::OverlayWindowState,
};
pub type PwTokenMap = AStrMap<String>;
#[derive(Clone, Serialize, Deserialize)]
pub struct SerializedWindowSet {
pub name: Arc<str>,
pub overlays: HashMap<Arc<str>, OverlayWindowState>,
}
pub const fn def_watch_pos() -> Vec3 {
vec3(-0.03, -0.01, 0.125)
}
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 const fn def_left() -> LeftRight {
LeftRight::Left
}
pub const fn def_pw_tokens() -> PwTokenMap {
AStrMap::new()
}
const fn def_mouse_move_interval_ms() -> u32 {
10 // 100fps
}
const fn def_click_freeze_time_ms() -> u32 {
300
}
pub const fn def_true() -> bool {
true
}
const fn def_false() -> bool {
false
}
const fn def_one() -> f32 {
1.0
}
pub const fn def_half() -> f32 {
0.5
}
pub const fn def_point7() -> f32 {
0.7
}
pub const fn def_point3() -> f32 {
0.3
}
const fn def_osc_port() -> u16 {
9000
}
const fn def_empty_vec_string() -> Vec<String> {
Vec::new()
}
const fn def_sets() -> Vec<SerializedWindowSet> {
Vec::new()
}
const fn def_zero_u32() -> u32 {
0
}
fn def_timezones() -> Vec<String> {
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()],
APAC..=i32::MAX => vec!["Europe/Paris".into(), "America/New_York".into()],
}
}
const fn def_screens() -> AStrSet {
AStrSet::new()
}
const fn def_curve_values() -> AStrMap<f32> {
AStrMap::new()
}
const fn def_transforms() -> AStrMap<Affine3A> {
AStrMap::new()
}
fn def_auto() -> Arc<str> {
"auto".into()
}
fn def_empty() -> Arc<str> {
"".into()
}
fn def_toast_topics() -> IdMap<ToastTopic, ToastDisplayMethod> {
IdMap::new()
}
fn def_font() -> Arc<str> {
"LiberationSans:style=Bold".into()
}
const fn def_max_height() -> u16 {
1440
}
#[derive(Deserialize, Serialize)]
pub struct GeneralConfig {
#[serde(default = "def_watch_pos")]
pub watch_pos: Vec3,
#[serde(default = "def_watch_rot")]
pub watch_rot: Quat,
#[serde(default = "def_left")]
pub watch_hand: LeftRight,
#[serde(default = "def_click_freeze_time_ms")]
pub click_freeze_time_ms: u32,
#[serde(default = "def_false")]
pub invert_scroll_direction_x: bool,
#[serde(default = "def_false")]
pub invert_scroll_direction_y: bool,
#[serde(default = "def_one")]
pub scroll_speed: f32,
#[serde(default = "def_mouse_move_interval_ms")]
pub mouse_move_interval_ms: u32,
#[serde(default = "def_true")]
pub notifications_enabled: bool,
#[serde(default = "def_true")]
pub notifications_sound_enabled: bool,
#[serde(default = "def_toast_topics")]
pub notification_topics: IdMap<ToastTopic, ToastDisplayMethod>,
#[serde(default = "def_empty")]
pub notification_sound: Arc<str>,
#[serde(default = "def_true")]
pub keyboard_sound_enabled: bool,
#[serde(default = "def_one")]
pub keyboard_scale: f32,
#[serde(default = "def_one")]
pub desktop_view_scale: f32,
#[serde(default = "def_half")]
pub watch_view_angle_min: f32,
#[serde(default = "def_point7")]
pub watch_view_angle_max: f32,
#[serde(default = "def_one")]
pub long_press_duration: f32,
#[serde(default = "def_osc_port")]
pub osc_out_port: u16,
#[serde(default = "def_false")]
pub upright_screen_fix: bool,
#[serde(default = "def_false")]
pub double_cursor_fix: bool,
#[serde(default = "def_screens")]
pub show_screens: AStrSet,
#[serde(default = "def_curve_values")]
pub curve_values: AStrMap<f32>,
#[serde(default = "def_transforms")]
pub transform_values: AStrMap<Affine3A>,
#[serde(default = "def_auto")]
pub capture_method: Arc<str>,
#[serde(default = "def_point7")]
pub xr_grab_sensitivity: f32,
#[serde(default = "def_point7")]
pub xr_click_sensitivity: f32,
#[serde(default = "def_point7")]
pub xr_alt_click_sensitivity: f32,
#[serde(default = "def_half")]
pub xr_grab_sensitivity_release: f32,
#[serde(default = "def_half")]
pub xr_click_sensitivity_release: f32,
#[serde(default = "def_half")]
pub xr_alt_click_sensitivity_release: f32,
#[serde(default = "def_true")]
pub allow_sliding: bool,
#[serde(default = "def_true")]
pub realign_on_showhide: bool,
#[serde(default = "def_false")]
pub focus_follows_mouse_mode: bool,
#[serde(default = "def_false")]
pub block_game_input: bool,
#[serde(default = "def_true")]
pub block_game_input_ignore_watch: bool,
#[serde(default = "def_font")]
pub primary_font: Arc<str>,
#[serde(default = "def_one")]
pub space_drag_multiplier: f32,
#[serde(default = "def_empty")]
pub skybox_texture: Arc<str>,
#[serde(default = "def_true")]
pub use_skybox: bool,
#[serde(default = "def_true")]
pub use_passthrough: bool,
#[serde(default = "def_max_height")]
pub screen_max_height: u16,
#[serde(default = "def_false")]
pub screen_render_down: bool,
#[serde(default = "def_point3")]
pub pointer_lerp_factor: f32,
#[serde(default = "def_false")]
pub space_rotate_unlocked: bool,
#[serde(default = "def_empty_vec_string")]
pub alt_click_down: Vec<String>,
#[serde(default = "def_empty_vec_string")]
pub alt_click_up: Vec<String>,
#[serde(default = "def_timezones")]
pub timezones: Vec<String>,
#[serde(default = "def_false")]
pub clock_12h: bool,
#[serde(default = "def_sets")]
pub sets: Vec<SerializedWindowSet>,
#[serde(default = "def_zero_u32")]
pub last_set: u32,
}

5
wlx-common/src/lib.rs Normal file
View File

@@ -0,0 +1,5 @@
pub mod astr_containers;
pub mod common;
pub mod config;
pub mod overlays;
pub mod windowing;

View File

@@ -0,0 +1,17 @@
use idmap_derive::IntegerId;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, IntegerId, Serialize, Deserialize)]
pub enum ToastTopic {
System,
DesktopNotification,
XSNotification,
IpdChange,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum ToastDisplayMethod {
Hide,
Center,
Watch,
}

View File

@@ -0,0 +1,60 @@
use glam::Affine3A;
use serde::{Deserialize, Serialize};
use crate::common::LeftRight;
#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)]
pub enum Positioning {
/// Stays in place, recenters relative to HMD
#[default]
Floating,
/// Stays in place, recenters relative to anchor. Follows anchor during anchor grab.
Anchored,
/// Same as anchor but paused due to interaction
AnchoredPaused,
/// Stays in place, no recentering
Static,
/// Following HMD
FollowHead { lerp: f32 },
/// Normally follows HMD, but paused due to interaction
FollowHeadPaused { lerp: f32 },
/// Following hand
FollowHand { hand: LeftRight, lerp: f32 },
/// Normally follows hand, but paused due to interaction
FollowHandPaused { hand: LeftRight, lerp: f32 },
}
impl Positioning {
pub const fn moves_with_space(self) -> bool {
matches!(self, Self::Floating | Self::Anchored | Self::Static)
}
}
// Contains the window state for a given set
#[derive(Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct OverlayWindowState {
pub transform: Affine3A,
pub alpha: f32,
pub grabbable: bool,
pub interactable: bool,
pub positioning: Positioning,
pub curvature: Option<f32>,
pub additive: bool,
pub saved_transform: Option<Affine3A>,
}
impl Default for OverlayWindowState {
fn default() -> Self {
Self {
grabbable: false,
interactable: false,
alpha: 1.0,
positioning: Positioning::Floating,
curvature: None,
transform: Affine3A::IDENTITY,
additive: false,
saved_transform: None,
}
}
}

View File

@@ -31,7 +31,7 @@ dbus = { version = "0.9.9" }
futures = "0.3.31"
glam = { workspace = true, features = ["mint", "serde"] }
idmap = { workspace = true, features = ["serde"] }
idmap-derive = "0.2.2"
idmap-derive = { workspace = true }
input-linux = "0.7.1"
json = { version = "0.12.4", optional = true }
json5 = "0.4.1"
@@ -61,6 +61,7 @@ strum = { version = "0.27.2", features = ["derive"] }
sysinfo = { version = "0.37" }
thiserror = "2.0"
wlx-capture = { path = "../wlx-capture" }
wlx-common = { path = "../wlx-common" }
libmonado = { version = "1.3.2", optional = true }
winit = { version = "0.30.12", optional = true }
xdg = "3.0"

View File

@@ -4,14 +4,15 @@ use std::{collections::VecDeque, time::Instant};
use glam::{Affine3A, Vec2, Vec3A, Vec3Swizzles};
use smallvec::{smallvec, SmallVec};
use smallvec::{SmallVec, smallvec};
use wlx_common::windowing::{OverlayWindowState, Positioning};
use crate::overlays::anchor::ANCHOR_NAME;
use crate::state::{AppSession, AppState};
use crate::subsystem::hid::WheelDelta;
use crate::subsystem::input::KeyboardFocus;
use crate::windowing::manager::OverlayWindowManager;
use crate::windowing::window::{realign, OverlayWindowData, OverlayWindowState, Positioning};
use crate::windowing::window::{self, OverlayWindowData, realign};
use crate::windowing::{OverlayID, OverlaySelector};
use super::task::TaskType;
@@ -705,7 +706,7 @@ where
x => x,
};
overlay_state.save_transform(app);
window::save_transform(overlay_state, app);
}
// Hide anchor

View File

@@ -2,37 +2,38 @@ use std::{
collections::VecDeque,
ops::Add,
sync::{
atomic::{AtomicBool, AtomicUsize, Ordering},
Arc,
atomic::{AtomicBool, AtomicUsize, Ordering},
},
time::{Duration, Instant},
};
use anyhow::{anyhow, Result};
use anyhow::{Result, anyhow};
use ovr_overlay::{
sys::{ETrackedDeviceProperty, EVRApplicationType, EVREventType},
TrackedDeviceIndex,
sys::{ETrackedDeviceProperty, EVRApplicationType, EVREventType},
};
use vulkano::{device::physical::PhysicalDevice, Handle, VulkanObject};
use vulkano::{Handle, VulkanObject, device::physical::PhysicalDevice};
use wlx_common::overlays::ToastTopic;
use crate::{
backend::{
BackendError,
input::interact,
openvr::{
helpers::adjust_gain,
input::{set_action_manifest, OpenVrInputSource},
input::{OpenVrInputSource, set_action_manifest},
lines::LinePool,
manifest::{install_manifest, uninstall_manifest},
overlay::OpenVrOverlayData,
},
task::{SystemTask, TaskType},
BackendError,
},
config::save_state,
graphics::{init_openvr_graphics, GpuFutures},
graphics::{GpuFutures, init_openvr_graphics},
overlays::{
toast::{Toast, ToastTopic},
watch::{watch_fade, WATCH_NAME},
toast::Toast,
watch::{WATCH_NAME, watch_fade},
},
state::AppState,
subsystem::notifications::NotificationManager,

View File

@@ -2,8 +2,8 @@ use std::{
collections::VecDeque,
ops::Add,
sync::{
atomic::{AtomicBool, AtomicUsize, Ordering},
Arc,
atomic::{AtomicBool, AtomicUsize, Ordering},
},
time::{Duration, Instant},
};
@@ -14,19 +14,20 @@ use libmonado::Monado;
use openxr as xr;
use skybox::create_skybox;
use vulkano::{Handle, VulkanObject};
use wlx_common::overlays::ToastTopic;
use crate::{
backend::{
BackendError,
input::interact,
openxr::{lines::LinePool, overlay::OpenXrOverlayData},
task::{SystemTask, TaskType},
BackendError,
},
config::save_state,
graphics::{init_openxr_graphics, GpuFutures},
graphics::{GpuFutures, init_openxr_graphics},
overlays::{
toast::{Toast, ToastTopic},
watch::{watch_fade, WATCH_NAME},
toast::Toast,
watch::{WATCH_NAME, watch_fade},
},
state::AppState,
subsystem::notifications::NotificationManager,

View File

@@ -1,360 +1,13 @@
use std::path::PathBuf;
use std::sync::Arc;
use crate::config_io;
use crate::overlays::toast::{DisplayMethod, ToastTopic};
use crate::state::LeftRight;
use crate::windowing::set::SerializedWindowSet;
use chrono::Offset;
use config::{Config, File};
use glam::{Affine3A, Quat, Vec3, vec3};
use idmap::IdMap;
use glam::{Quat, Vec3};
use log::error;
use serde::{Deserialize, Serialize};
pub type AStrMap<V> = Vec<(Arc<str>, V)>;
pub trait AStrMapExt<V> {
fn arc_set(&mut self, key: Arc<str>, value: V) -> bool;
fn arc_get(&self, key: &str) -> Option<&V>;
fn arc_rm(&mut self, key: &str) -> Option<V>;
}
impl<V> AStrMapExt<V> for AStrMap<V> {
fn arc_set(&mut self, key: Arc<str>, value: V) -> bool {
let index = self.iter().position(|(k, _)| k.as_ref().eq(key.as_ref()));
index.map(|i| self.remove(i).1);
self.push((key, value));
true
}
fn arc_get(&self, key: &str) -> Option<&V> {
self.iter()
.find_map(|(k, v)| if k.as_ref().eq(key) { Some(v) } else { None })
}
fn arc_rm(&mut self, key: &str) -> Option<V> {
let index = self.iter().position(|(k, _)| k.as_ref().eq(key));
index.map(|i| self.remove(i).1)
}
}
pub type AStrSet = Vec<Arc<str>>;
pub trait AStrSetExt {
fn arc_set(&mut self, value: Arc<str>) -> bool;
fn arc_get(&self, value: &str) -> bool;
fn arc_rm(&mut self, value: &str) -> bool;
}
impl AStrSetExt for AStrSet {
fn arc_set(&mut self, value: Arc<str>) -> bool {
if self.iter().any(|v| v.as_ref().eq(value.as_ref())) {
return false;
}
self.push(value);
true
}
fn arc_get(&self, value: &str) -> bool {
self.iter().any(|v| v.as_ref().eq(value))
}
fn arc_rm(&mut self, value: &str) -> bool {
let index = self.iter().position(|v| v.as_ref().eq(value));
index.is_some_and(|i| {
self.remove(i);
true
})
}
}
pub type PwTokenMap = AStrMap<String>;
pub const fn def_watch_pos() -> Vec3 {
vec3(-0.03, -0.01, 0.125)
}
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 const fn def_left() -> LeftRight {
LeftRight::Left
}
pub const fn def_pw_tokens() -> PwTokenMap {
AStrMap::new()
}
const fn def_mouse_move_interval_ms() -> u32 {
10 // 100fps
}
const fn def_click_freeze_time_ms() -> u32 {
300
}
pub const fn def_true() -> bool {
true
}
const fn def_false() -> bool {
false
}
const fn def_one() -> f32 {
1.0
}
pub const fn def_half() -> f32 {
0.5
}
pub const fn def_point7() -> f32 {
0.7
}
pub const fn def_point3() -> f32 {
0.3
}
const fn def_osc_port() -> u16 {
9000
}
const fn def_empty_vec_string() -> Vec<String> {
Vec::new()
}
const fn def_sets() -> Vec<SerializedWindowSet> {
Vec::new()
}
const fn def_zero_u32() -> u32 {
0
}
fn def_timezones() -> Vec<String> {
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()],
APAC..=i32::MAX => vec!["Europe/Paris".into(), "America/New_York".into()],
}
}
const fn def_screens() -> AStrSet {
AStrSet::new()
}
const fn def_curve_values() -> AStrMap<f32> {
AStrMap::new()
}
const fn def_transforms() -> AStrMap<Affine3A> {
AStrMap::new()
}
fn def_auto() -> Arc<str> {
"auto".into()
}
fn def_empty() -> Arc<str> {
"".into()
}
fn def_toast_topics() -> IdMap<ToastTopic, DisplayMethod> {
IdMap::new()
}
fn def_font() -> Arc<str> {
"LiberationSans:style=Bold".into()
}
const fn def_max_height() -> u16 {
1440
}
#[derive(Deserialize, Serialize)]
pub struct GeneralConfig {
#[serde(default = "def_watch_pos")]
pub watch_pos: Vec3,
#[serde(default = "def_watch_rot")]
pub watch_rot: Quat,
#[serde(default = "def_left")]
pub watch_hand: LeftRight,
#[serde(default = "def_click_freeze_time_ms")]
pub click_freeze_time_ms: u32,
#[serde(default = "def_false")]
pub invert_scroll_direction_x: bool,
#[serde(default = "def_false")]
pub invert_scroll_direction_y: bool,
#[serde(default = "def_one")]
pub scroll_speed: f32,
#[serde(default = "def_mouse_move_interval_ms")]
pub mouse_move_interval_ms: u32,
#[serde(default = "def_true")]
pub notifications_enabled: bool,
#[serde(default = "def_true")]
pub notifications_sound_enabled: bool,
#[serde(default = "def_toast_topics")]
pub notification_topics: IdMap<ToastTopic, DisplayMethod>,
#[serde(default = "def_empty")]
pub notification_sound: Arc<str>,
#[serde(default = "def_true")]
pub keyboard_sound_enabled: bool,
#[serde(default = "def_one")]
pub keyboard_scale: f32,
#[serde(default = "def_one")]
pub desktop_view_scale: f32,
#[serde(default = "def_half")]
pub watch_view_angle_min: f32,
#[serde(default = "def_point7")]
pub watch_view_angle_max: f32,
#[serde(default = "def_one")]
pub long_press_duration: f32,
#[serde(default = "def_osc_port")]
pub osc_out_port: u16,
#[serde(default = "def_false")]
pub upright_screen_fix: bool,
#[serde(default = "def_false")]
pub double_cursor_fix: bool,
#[serde(default = "def_screens")]
pub show_screens: AStrSet,
#[serde(default = "def_curve_values")]
pub curve_values: AStrMap<f32>,
#[serde(default = "def_transforms")]
pub transform_values: AStrMap<Affine3A>,
#[serde(default = "def_auto")]
pub capture_method: Arc<str>,
#[serde(default = "def_point7")]
pub xr_grab_sensitivity: f32,
#[serde(default = "def_point7")]
pub xr_click_sensitivity: f32,
#[serde(default = "def_point7")]
pub xr_alt_click_sensitivity: f32,
#[serde(default = "def_half")]
pub xr_grab_sensitivity_release: f32,
#[serde(default = "def_half")]
pub xr_click_sensitivity_release: f32,
#[serde(default = "def_half")]
pub xr_alt_click_sensitivity_release: f32,
#[serde(default = "def_true")]
pub allow_sliding: bool,
#[serde(default = "def_true")]
pub realign_on_showhide: bool,
#[serde(default = "def_false")]
pub focus_follows_mouse_mode: bool,
#[serde(default = "def_false")]
pub block_game_input: bool,
#[serde(default = "def_true")]
pub block_game_input_ignore_watch: bool,
#[serde(default = "def_font")]
pub primary_font: Arc<str>,
#[serde(default = "def_one")]
pub space_drag_multiplier: f32,
#[serde(default = "def_empty")]
pub skybox_texture: Arc<str>,
#[serde(default = "def_true")]
pub use_skybox: bool,
#[serde(default = "def_true")]
pub use_passthrough: bool,
#[serde(default = "def_max_height")]
pub screen_max_height: u16,
#[serde(default = "def_false")]
pub screen_render_down: bool,
#[serde(default = "def_point3")]
pub pointer_lerp_factor: f32,
#[serde(default = "def_false")]
pub space_rotate_unlocked: bool,
#[serde(default = "def_empty_vec_string")]
pub alt_click_down: Vec<String>,
#[serde(default = "def_empty_vec_string")]
pub alt_click_up: Vec<String>,
#[serde(default = "def_timezones")]
pub timezones: Vec<String>,
#[serde(default = "def_false")]
pub clock_12h: bool,
#[serde(default = "def_sets")]
pub sets: Vec<SerializedWindowSet>,
#[serde(default = "def_zero_u32")]
pub last_set: u32,
}
impl GeneralConfig {
fn sanitize_range(name: &str, val: f32, from: f32, to: f32) {
assert!(
!(!val.is_normal() || val < from || val > to),
"GeneralConfig: {name} needs to be between {from} and {to}"
);
}
pub fn load_from_disk() -> Self {
let config = load_general();
config.post_load();
config
}
fn post_load(&self) {
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);
Self::sanitize_range("scroll_speed", self.scroll_speed, 0.01, 10.0);
}
}
use std::path::PathBuf;
use wlx_common::{
common::LeftRight,
config::{GeneralConfig, SerializedWindowSet},
};
const FALLBACKS: [&str; 2] = [
include_str!("res/keyboard.yaml"),
@@ -446,12 +99,10 @@ where
}
}
pub fn load_general() -> GeneralConfig {
pub fn load_general_config() -> GeneralConfig {
load_config_with_conf_d::<GeneralConfig>("config.yaml", config_io::ConfigRoot::Generic)
}
// Config that is saved from the settings panel
#[derive(Serialize)]
pub struct AutoSettings {
pub watch_pos: Vec3,

View File

@@ -9,6 +9,7 @@ use std::{
};
use serde::{Deserialize, Serialize};
use wlx_common::{common::LeftRight, config::GeneralConfig, windowing::Positioning};
use crate::{
backend::{
@@ -17,9 +18,7 @@ use crate::{
},
config::load_config_with_conf_d,
config_io,
overlays::wayvr::{executable_exists_in_path, WayVRData},
state::LeftRight,
windowing::window::Positioning,
overlays::wayvr::{WayVRData, executable_exists_in_path},
};
// Flat version of RelativeTo
@@ -180,7 +179,7 @@ impl WayVRConfig {
}
pub fn get_wayvr_config(
config_general: &crate::config::GeneralConfig,
config_general: &GeneralConfig,
config_wayvr: &Self,
) -> anyhow::Result<wayvr::Config> {
Ok(wayvr::Config {
@@ -199,7 +198,7 @@ impl WayVRConfig {
pub fn post_load(
&self,
config: &crate::config::GeneralConfig,
config: &GeneralConfig,
tasks: &mut TaskContainer,
) -> anyhow::Result<Option<Rc<RefCell<WayVRData>>>> {
let primary_count = self

View File

@@ -1,10 +1,11 @@
use glam::{Affine3A, Quat, Vec3};
use std::sync::{Arc, LazyLock};
use wlx_common::windowing::{OverlayWindowState, Positioning};
use crate::gui::panel::GuiPanel;
use crate::state::AppState;
use crate::windowing::window::{OverlayWindowConfig, OverlayWindowState, Positioning};
use crate::windowing::Z_ORDER_ANCHOR;
use crate::windowing::window::OverlayWindowConfig;
pub static ANCHOR_NAME: LazyLock<Arc<str>> = LazyLock::new(|| Arc::from("anchor"));

View File

@@ -1,12 +1,9 @@
use std::sync::Arc;
use glam::{Affine3A, Quat, Vec3, vec3};
use wlx_common::windowing::OverlayWindowState;
use crate::{
gui::panel::GuiPanel,
state::AppState,
windowing::window::{OverlayWindowConfig, OverlayWindowState},
};
use crate::{gui::panel::GuiPanel, state::AppState, windowing::window::OverlayWindowConfig};
const SETTINGS_NAME: &str = "settings";

View File

@@ -5,11 +5,9 @@ use wgui::{
parser::Fetchable, renderer_vk::text::custom_glyph::CustomGlyphData,
widget::sprite::WidgetSprite,
};
use wlx_common::{common::LeftRight, windowing::Positioning};
use crate::{
backend::task::OverlayTask, overlays::edit::EditModeWrapPanel, state::LeftRight,
windowing::window::Positioning,
};
use crate::{backend::task::OverlayTask, overlays::edit::EditModeWrapPanel, windowing::window};
static POS_NAMES: [&str; 6] = ["static", "anchored", "floating", "hmd", "hand_l", "hand_r"];
@@ -98,7 +96,7 @@ impl PositioningHandler {
Box::new(move |app, owc| {
let state = owc.active_state.as_mut().unwrap(); //want panic
state.positioning = pos;
state.save_transform(app);
window::save_transform(state, app);
})
}

View File

@@ -17,12 +17,13 @@ use wgui::{
util::WLength,
},
};
use wlx_common::windowing::{OverlayWindowState, Positioning};
use crate::{
gui::panel::GuiPanel,
state::AppState,
subsystem::hid::{ALT, CTRL, META, SHIFT, SUPER, XkbKeymap},
windowing::window::{OverlayWindowConfig, OverlayWindowState, Positioning},
windowing::window::OverlayWindowConfig,
};
use super::{

View File

@@ -5,7 +5,8 @@ use std::{
use futures::{Future, FutureExt};
use glam::{Affine2, Affine3A, Vec3};
use wlx_capture::pipewire::{pipewire_select_screen, PipewireCapture, PipewireSelectScreenResult};
use wlx_capture::pipewire::{PipewireCapture, PipewireSelectScreenResult, pipewire_select_screen};
use wlx_common::windowing::OverlayWindowState;
use crate::{
backend::{
@@ -15,12 +16,12 @@ use crate::{
state::{AppSession, AppState},
subsystem::hid::WheelDelta,
windowing::{
backend::{
ui_transform, FrameMeta, OverlayBackend, OverlayEventData, RenderResources,
ShouldRender,
},
window::{OverlayWindowConfig, OverlayWindowState},
OverlaySelector,
backend::{
FrameMeta, OverlayBackend, OverlayEventData, RenderResources, ShouldRender,
ui_transform,
},
window::OverlayWindowConfig,
},
};

View File

@@ -7,25 +7,26 @@ use vulkano::{
command_buffer::CommandBufferUsage,
device::Queue,
format::Format,
image::{sampler::Filter, view::ImageView, Image},
image::{Image, sampler::Filter, view::ImageView},
pipeline::graphics::color_blend::AttachmentBlend,
};
use wgui::gfx::{
WGfx,
cmd::WGfxClearMode,
pass::WGfxPass,
pipeline::{WGfxPipeline, WPipelineCreateInfo},
WGfx,
};
use wlx_capture::{
frame::{self as wlx_frame, DrmFormat, FrameFormat, MouseMeta, Transform, WlxFrame},
WlxCapture,
frame::{self as wlx_frame, DrmFormat, FrameFormat, MouseMeta, Transform, WlxFrame},
};
use wlx_common::config::GeneralConfig;
use crate::{
config::GeneralConfig,
graphics::{
dmabuf::{fourcc_to_vk, WGfxDmabuf},
upload_quad_vertices, Vert2Uv,
Vert2Uv,
dmabuf::{WGfxDmabuf, fourcc_to_vk},
upload_quad_vertices,
},
state::AppState,
windowing::backend::{FrameMeta, RenderResources},

View File

@@ -1,15 +1,13 @@
use std::{f32::consts::PI, sync::Arc};
use glam::{vec3, Affine3A, Quat, Vec3};
use glam::{Affine3A, Quat, Vec3, vec3};
use wlx_capture::frame::Transform;
use wlx_common::windowing::{OverlayWindowState, Positioning};
use crate::{
state::{AppSession, AppState, ScreenMeta},
subsystem::{hid::XkbKeymap, input::KeyboardFocus},
windowing::{
backend::OverlayBackend,
window::{OverlayWindowConfig, OverlayWindowState, Positioning},
},
windowing::{backend::OverlayBackend, window::OverlayWindowConfig},
};
pub mod backend;

View File

@@ -6,12 +6,9 @@ use wlx_capture::{
pipewire::{PipewireCapture, PipewireSelectScreenResult},
wayland::WlxOutput,
};
use wlx_common::config::{PwTokenMap, def_pw_tokens};
use crate::{
config::{PwTokenMap, def_pw_tokens},
config_io,
state::AppState,
};
use crate::{config_io, state::AppState};
use super::{
backend::ScreenBackend,

View File

@@ -5,9 +5,9 @@ use wlx_capture::{
wlr_dmabuf::WlrDmabufCapture,
wlr_screencopy::WlrScreencopyCapture,
};
use wlx_common::{astr_containers::AStrMapExt, config::PwTokenMap};
use crate::{
config::{AStrMapExt, PwTokenMap},
overlays::screen::create_screen_from_backend,
state::{AppState, ScreenMeta},
};

View File

@@ -35,9 +35,9 @@ impl ScreenBackend {
pub fn create_screens_x11pw(app: &mut AppState) -> anyhow::Result<ScreenCreateData> {
use glam::vec2;
use wlx_capture::{pipewire::PipewireCapture, xshm::xshm_get_monitors};
use wlx_common::{astr_containers::AStrMapExt, config::PwTokenMap};
use crate::{
config::{AStrMapExt, PwTokenMap},
overlays::screen::{
create_screen_from_backend,
pw::{load_pw_token_config, save_pw_token_config, select_pw_screen},

View File

@@ -6,8 +6,6 @@ use std::{
};
use glam::{Affine3A, Quat, Vec3, vec3};
use idmap_derive::IntegerId;
use serde::{Deserialize, Serialize};
use wgui::{
i18n::Translation,
parser::parse_color_hex,
@@ -22,15 +20,17 @@ use wgui::{
util::WLength,
},
};
use wlx_common::{
common::LeftRight,
overlays::{ToastDisplayMethod, ToastTopic},
windowing::{OverlayWindowState, Positioning},
};
use crate::{
backend::task::TaskType,
gui::panel::GuiPanel,
state::{AppState, LeftRight},
windowing::{
OverlaySelector, Z_ORDER_TOAST,
window::{OverlayWindowConfig, OverlayWindowState, Positioning},
},
state::AppState,
windowing::{OverlaySelector, Z_ORDER_TOAST, window::OverlayWindowConfig},
};
const FONT_SIZE: isize = 16;
@@ -38,21 +38,6 @@ const PADDING: (f32, f32) = (25., 7.);
const PIXELS_TO_METERS: f32 = 1. / 2000.;
static TOAST_NAME: LazyLock<Arc<str>> = LazyLock::new(|| "toast".into());
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum DisplayMethod {
Hide,
Center,
Watch,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, IntegerId, Serialize, Deserialize)]
pub enum ToastTopic {
System,
DesktopNotification,
XSNotification,
IpdChange,
}
pub struct Toast {
pub title: String,
pub body: String,
@@ -134,16 +119,16 @@ fn new_toast(toast: Toast, app: &mut AppState) -> Option<OverlayWindowConfig> {
.toast_topics
.get(toast.topic)
.copied()
.unwrap_or(DisplayMethod::Hide);
.unwrap_or(ToastDisplayMethod::Hide);
let (spawn_point, spawn_rotation, positioning) = match current_method {
DisplayMethod::Hide => return None,
DisplayMethod::Center => (
ToastDisplayMethod::Hide => return None,
ToastDisplayMethod::Center => (
vec3(0., -0.2, -0.5),
Quat::IDENTITY,
Positioning::FollowHead { lerp: 0.1 },
),
DisplayMethod::Watch => {
ToastDisplayMethod::Watch => {
let mut watch_pos = app.session.config.watch_pos + vec3(-0.005, -0.05, 0.02);
let mut watch_rot = app.session.config.watch_rot;
let relative_to = match app.session.config.watch_hand {

View File

@@ -6,6 +6,7 @@ use wgui::{
event::{CallbackDataCommon, EventAlterables},
parser::Fetchable,
};
use wlx_common::windowing::{OverlayWindowState, Positioning};
use crate::{
gui::{
@@ -14,9 +15,9 @@ use crate::{
},
state::AppState,
windowing::{
backend::OverlayEventData,
window::{OverlayWindowConfig, OverlayWindowData, OverlayWindowState, Positioning},
Z_ORDER_WATCH,
backend::OverlayEventData,
window::{OverlayWindowConfig, OverlayWindowData},
},
};

View File

@@ -1,42 +1,42 @@
use glam::{vec3, Affine2, Affine3A, Quat, Vec3};
use glam::{Affine2, Affine3A, Quat, Vec3, vec3};
use smallvec::smallvec;
use std::{cell::RefCell, collections::HashMap, rc::Rc, sync::Arc};
use vulkano::{
buffer::{BufferUsage, Subbuffer},
command_buffer::CommandBufferUsage,
format::Format,
image::{view::ImageView, Image, ImageTiling, SubresourceLayout},
image::{Image, ImageTiling, SubresourceLayout, view::ImageView},
};
use wayvr_ipc::packet_server::{self, PacketServer, WvrStateChanged};
use wgui::gfx::{
WGfx,
pass::WGfxPass,
pipeline::{WGfxPipeline, WPipelineCreateInfo},
WGfx,
};
use wlx_capture::frame::{DmabufFrame, FourCC, FrameFormat, FramePlane};
use wlx_common::windowing::OverlayWindowState;
use crate::{
backend::{
input::{self, HoverResult},
task::TaskType,
wayvr::{
self, display,
self, WayVR, WayVRAction, WayVRDisplayClickAction, display,
server_ipc::{gen_args_vec, gen_env_vec},
WayVR, WayVRAction, WayVRDisplayClickAction,
},
},
config_wayvr,
graphics::{dmabuf::WGfxDmabuf, Vert2Uv},
graphics::{Vert2Uv, dmabuf::WGfxDmabuf},
state::{self, AppState},
subsystem::{hid::WheelDelta, input::KeyboardFocus},
windowing::{
OverlayID, OverlaySelector, Z_ORDER_DASHBOARD,
backend::{
ui_transform, FrameMeta, OverlayBackend, OverlayEventData, RenderResources,
ShouldRender,
FrameMeta, OverlayBackend, OverlayEventData, RenderResources, ShouldRender,
ui_transform,
},
manager::OverlayWindowManager,
window::{OverlayWindowConfig, OverlayWindowData, OverlayWindowState},
OverlayID, OverlaySelector, Z_ORDER_DASHBOARD,
window::{OverlayWindowConfig, OverlayWindowData},
},
};

View File

@@ -1,12 +1,15 @@
use glam::Affine3A;
use idmap::IdMap;
use serde::{Deserialize, Serialize};
use smallvec::{smallvec, SmallVec};
use smallvec::{SmallVec, smallvec};
use std::sync::Arc;
use wgui::{
font_config::WguiFontConfig, gfx::WGfx, globals::WguiGlobals,
renderer_vk::context::SharedContext as WSharedContext,
};
use wlx_common::{
config::GeneralConfig,
overlays::{ToastDisplayMethod, ToastTopic},
};
#[cfg(feature = "wayvr")]
use {
@@ -20,11 +23,10 @@ use crate::subsystem::osc::OscSender;
use crate::{
backend::{input::InputState, task::TaskContainer},
config::GeneralConfig,
config::load_general_config,
config_io,
graphics::WGfxExtras,
gui,
overlays::toast::{DisplayMethod, ToastTopic},
subsystem::{audio::AudioOutput, input::HidWrapper},
};
@@ -156,19 +158,19 @@ pub struct AppSession {
#[cfg(feature = "wayvr")]
pub wayvr_config: WayVRConfig,
pub toast_topics: IdMap<ToastTopic, DisplayMethod>,
pub toast_topics: IdMap<ToastTopic, ToastDisplayMethod>,
}
impl AppSession {
pub fn load() -> Self {
let config_root_path = config_io::ConfigRoot::Generic.ensure_dir();
log::info!("Config root path: {}", config_root_path.display());
let config = GeneralConfig::load_from_disk();
let config = load_general_config();
let mut toast_topics = IdMap::new();
toast_topics.insert(ToastTopic::System, DisplayMethod::Center);
toast_topics.insert(ToastTopic::DesktopNotification, DisplayMethod::Center);
toast_topics.insert(ToastTopic::XSNotification, DisplayMethod::Center);
toast_topics.insert(ToastTopic::System, ToastDisplayMethod::Center);
toast_topics.insert(ToastTopic::DesktopNotification, ToastDisplayMethod::Center);
toast_topics.insert(ToastTopic::XSNotification, ToastDisplayMethod::Center);
config.notification_topics.iter().for_each(|(k, v)| {
toast_topics.insert(*k, *v);
@@ -190,11 +192,3 @@ pub struct ScreenMeta {
pub name: Arc<str>,
pub native_handle: u32,
}
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default)]
#[repr(u8)]
pub enum LeftRight {
#[default]
Left,
Right,
}

View File

@@ -17,11 +17,9 @@ use std::{
},
time::Duration,
};
use wlx_common::overlays::ToastTopic;
use crate::{
overlays::toast::{Toast, ToastTopic},
state::AppState,
};
use crate::{overlays::toast::Toast, state::AppState};
pub struct NotificationManager {
rx_toast: mpsc::Receiver<Toast>,

View File

@@ -2,6 +2,7 @@ use std::collections::HashMap;
use glam::{Affine3A, Vec3, Vec3A};
use slotmap::{HopSlotMap, Key, SecondaryMap};
use wlx_common::config::SerializedWindowSet;
use crate::{
overlays::{
@@ -10,11 +11,8 @@ use crate::{
},
state::AppState,
windowing::{
backend::OverlayEventData,
set::{OverlayWindowSet, SerializedWindowSet},
snap_upright,
OverlayID, OverlaySelector, backend::OverlayEventData, set::OverlayWindowSet, snap_upright,
window::OverlayWindowData,
OverlayID, OverlaySelector,
},
};

View File

@@ -1,18 +1,11 @@
use std::{collections::HashMap, sync::Arc};
use serde::{Deserialize, Serialize};
use slotmap::SecondaryMap;
use std::sync::Arc;
use wlx_common::windowing::OverlayWindowState;
use crate::windowing::{window::OverlayWindowState, OverlayID};
use crate::windowing::OverlayID;
#[derive(Default)]
pub struct OverlayWindowSet {
pub(super) name: Arc<str>,
pub(super) overlays: SecondaryMap<OverlayID, OverlayWindowState>,
}
#[derive(Clone, Serialize, Deserialize)]
pub struct SerializedWindowSet {
pub name: Arc<str>,
pub overlays: HashMap<Arc<str>, OverlayWindowState>,
}

View File

@@ -1,9 +1,9 @@
use glam::{Affine3A, Mat3A, Quat, Vec3, Vec3A};
use serde::{Deserialize, Serialize};
use std::{f32::consts::PI, sync::Arc};
use wlx_common::windowing::{OverlayWindowState, Positioning};
use crate::{
state::{AppState, LeftRight},
state::AppState,
subsystem::input::KeyboardFocus,
windowing::{
backend::{FrameMeta, OverlayBackend, RenderResources, ShouldRender},
@@ -11,33 +11,6 @@ use crate::{
},
};
#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)]
pub enum Positioning {
/// Stays in place, recenters relative to HMD
#[default]
Floating,
/// Stays in place, recenters relative to anchor. Follows anchor during anchor grab.
Anchored,
/// Same as anchor but paused due to interaction
AnchoredPaused,
/// Stays in place, no recentering
Static,
/// Following HMD
FollowHead { lerp: f32 },
/// Normally follows HMD, but paused due to interaction
FollowHeadPaused { lerp: f32 },
/// Following hand
FollowHand { hand: LeftRight, lerp: f32 },
/// Normally follows hand, but paused due to interaction
FollowHandPaused { hand: LeftRight, lerp: f32 },
}
impl Positioning {
pub const fn moves_with_space(self) -> bool {
matches!(self, Self::Floating | Self::Anchored | Self::Static)
}
}
pub struct OverlayWindowData<T> {
pub config: OverlayWindowConfig,
pub data: T,
@@ -261,53 +234,20 @@ pub fn realign(transform: &mut Affine3A, hmd: &Affine3A) {
transform.matrix3 = Mat3A::from_cols(col_x, col_y, col_z).mul_scalar(scale) * rot;
}
// Contains the window state for a given set
#[derive(Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct OverlayWindowState {
pub transform: Affine3A,
pub alpha: f32,
pub grabbable: bool,
pub interactable: bool,
pub positioning: Positioning,
pub curvature: Option<f32>,
pub additive: bool,
pub saved_transform: Option<Affine3A>,
}
impl Default for OverlayWindowState {
fn default() -> Self {
Self {
grabbable: false,
interactable: false,
alpha: 1.0,
positioning: Positioning::Floating,
curvature: None,
transform: Affine3A::IDENTITY,
additive: false,
saved_transform: None,
pub fn save_transform(state: &mut OverlayWindowState, app: &mut AppState) -> bool {
let parent_transform = match state.positioning {
Positioning::Floating => snap_upright(app.input_state.hmd, Vec3A::Y),
Positioning::FollowHead { .. } | Positioning::FollowHeadPaused { .. } => {
app.input_state.hmd
}
}
}
impl OverlayWindowState {
pub fn save_transform(&mut self, app: &mut AppState) -> bool {
let parent_transform = match self.positioning {
Positioning::Floating => snap_upright(app.input_state.hmd, Vec3A::Y),
Positioning::FollowHead { .. } | Positioning::FollowHeadPaused { .. } => {
app.input_state.hmd
}
Positioning::FollowHand { hand, .. } | Positioning::FollowHandPaused { hand, .. } => {
app.input_state.pointers[hand as usize].pose
}
Positioning::Anchored | Positioning::AnchoredPaused => {
snap_upright(app.anchor, Vec3A::Y)
}
Positioning::Static => return false,
};
self.saved_transform = Some(parent_transform.inverse() * self.transform);
true
}
Positioning::FollowHand { hand, .. } | Positioning::FollowHandPaused { hand, .. } => {
app.input_state.pointers[hand as usize].pose
}
Positioning::Anchored | Positioning::AnchoredPaused => snap_upright(app.anchor, Vec3A::Y),
Positioning::Static => return false,
};
state.saved_transform = Some(parent_transform.inverse() * state.transform);
true
}