settings tab buttons; autorestart

This commit is contained in:
galister
2026-01-08 02:19:00 +09:00
parent cd8480100f
commit 2f010bb42b
20 changed files with 212 additions and 88 deletions

View File

@@ -26,8 +26,12 @@
<RadioBox text="${text}" translation="${translation}" value="${value}" tooltip="${tooltip}" /> <RadioBox text="${text}" translation="${translation}" value="${value}" tooltip="${tooltip}" />
</template> </template>
<template name="DangerButton">
<Button id="${id}" color="#AA3333" height="32" width="100%" sprite_src_builtin="${icon}" translation="${translation}" tooltip="${translation}_HELP" />
</template>
<elements> <elements>
<TabTitle translation="SETTINGS" icon="dashboard/settings.svg" /> <TabTitle translation="SETTINGS" icon="dashboard/settings.svg" />
<div flex_wrap="wrap" justify_content="stretch" gap="4" id="settings_root" /> <div flex_wrap="wrap" justify_content="stretch" gap="4" id="settings_root" />
</elements> </elements>
</layout> </layout>

View File

@@ -57,7 +57,19 @@
"BLOCK_GAME_INPUT_IGNORE_WATCH_HELP": "Do not block input when watch is hovered", "BLOCK_GAME_INPUT_IGNORE_WATCH_HELP": "Do not block input when watch is hovered",
"USE_SKYBOX_HELP": "Show a skybox if there's no scene app or passthrough", "USE_SKYBOX_HELP": "Show a skybox if there's no scene app or passthrough",
"USE_PASSTHROUGH_HELP": "Allow passthrough if the XR runtime supports it", "USE_PASSTHROUGH_HELP": "Allow passthrough if the XR runtime supports it",
"SCREEN_RENDER_DOWN_HELP": "Helps with aliasing on high-res screens" "SCREEN_RENDER_DOWN_HELP": "Helps with aliasing on high-res screens",
"TROUBLESHOOTING": "Troubleshooting",
"CLEAR_SAVED_STATE": "Clear saved state",
"CLEAR_PIPEWIRE_TOKENS": "Clear PipeWire tokens",
"DELETE_ALL_CONFIGS": "Wipe configuration",
"RESTART_SOFTWARE": "Restart software",
"CLEAR_SAVED_STATE_HELP": "Reset sets & overlay positions",
"CLEAR_PIPEWIRE_TOKENS_HELP": "Prompt for screen selection on next start",
"DELETE_ALL_CONFIGS_HELP": "Remove all configuration files from conf.d",
"RESTART_SOFTWARE_HELP": "Apply settings that require a restart"
}, },
"APPLICATION_LAUNCHER": "Application launcher", "APPLICATION_LAUNCHER": "Application launcher",
"APPLICATION_STARTED": "Application started", "APPLICATION_STARTED": "Application started",

View File

@@ -1,14 +1,15 @@
use std::{collections::HashMap, marker::PhantomData, rc::Rc}; use std::{collections::HashMap, marker::PhantomData, os::unix::process::CommandExt, process::Command, rc::Rc};
use strum::AsRefStr; use strum::AsRefStr;
use wgui::{ use wgui::{
assets::AssetPath, assets::AssetPath,
components::{checkbox::ComponentCheckbox, slider::ComponentSlider}, components::{button::ComponentButton, checkbox::ComponentCheckbox, slider::ComponentSlider},
layout::{Layout, WidgetID}, layout::{Layout, WidgetID},
log::LogErr,
parser::{Fetchable, ParseDocumentParams, ParserState}, parser::{Fetchable, ParseDocumentParams, ParserState},
task::Tasks, task::Tasks,
}; };
use wlx_common::config::GeneralConfig; use wlx_common::{config::GeneralConfig, config_io::ConfigRoot};
use crate::{ use crate::{
frontend::{Frontend, FrontendTask}, frontend::{Frontend, FrontendTask},
@@ -19,6 +20,10 @@ enum Task {
UpdateBool(SettingType, bool), UpdateBool(SettingType, bool),
UpdateFloat(SettingType, f32), UpdateFloat(SettingType, f32),
UpdateInt(SettingType, i32), UpdateInt(SettingType, i32),
ClearPipewireTokens,
ClearSavedState,
DeleteAllConfigs,
RestartSoftware,
} }
pub struct TabSettings<T> { pub struct TabSettings<T> {
@@ -54,6 +59,23 @@ impl<T> Tab<T> for TabSettings<T> {
*setting.mut_i32(config) = n; *setting.mut_i32(config) = n;
changed = true; changed = true;
} }
Task::ClearPipewireTokens => {
let _ = std::fs::remove_file(ConfigRoot::Generic.get_conf_d_path().join("pw_tokens.yaml"))
.log_err("Could not remove pw_tokens.yaml");
}
Task::ClearSavedState => {
let _ = std::fs::remove_file(ConfigRoot::Generic.get_conf_d_path().join("zz-saved-state.json5"))
.log_err("Could not remove zz-saved-state.json5");
}
Task::DeleteAllConfigs => {
let path = ConfigRoot::Generic.get_conf_d_path();
std::fs::remove_dir_all(&path)?;
std::fs::create_dir(&path)?;
}
Task::RestartSoftware => {
frontend.interface.restart(data);
return Ok(());
}
} }
} }
if changed { if changed {
@@ -359,6 +381,31 @@ macro_rules! slider_i32 {
}; };
} }
macro_rules! button {
($mp:expr, $root:expr, $translation:expr, $icon:expr, $task:expr) => {
let id = $mp.idx.to_string();
$mp.idx += 1;
let mut params: HashMap<Rc<str>, Rc<str>> = HashMap::new();
params.insert(Rc::from("id"), Rc::from(id.as_ref()));
params.insert(Rc::from("translation"), Rc::from($translation));
params.insert(Rc::from("icon"), Rc::from($icon));
$mp
.parser_state
.instantiate_template($mp.doc_params, "DangerButton", $mp.layout, $root, params)?;
let btn = $mp.parser_state.fetch_component_as::<ComponentButton>(&id)?;
btn.on_click(Box::new({
let tasks = $mp.tasks.clone();
move |_common, _e| {
tasks.push($task);
Ok(())
}
}));
};
}
struct MacroParams<'a> { struct MacroParams<'a> {
layout: &'a mut Layout, layout: &'a mut Layout,
parser_state: &'a mut ParserState, parser_state: &'a mut ParserState,
@@ -428,6 +475,36 @@ impl<T> TabSettings<T> {
checkbox!(mp, c, SettingType::DoubleCursorFix); checkbox!(mp, c, SettingType::DoubleCursorFix);
checkbox!(mp, c, SettingType::ScreenRenderDown); checkbox!(mp, c, SettingType::ScreenRenderDown);
let c = category!(mp, root, "APP_SETTINGS.TROUBLESHOOTING", "dashboard/blocks.svg")?;
button!(
mp,
c,
"APP_SETTINGS.CLEAR_SAVED_STATE",
"dashboard/remove_circle.svg",
Task::ClearSavedState
);
button!(
mp,
c,
"APP_SETTINGS.CLEAR_PIPEWIRE_TOKENS",
"dashboard/remove_circle.svg",
Task::ClearPipewireTokens
);
button!(
mp,
c,
"APP_SETTINGS.DELETE_ALL_CONFIGS",
"dashboard/remove_circle.svg",
Task::DeleteAllConfigs
);
button!(
mp,
c,
"APP_SETTINGS.RESTART_SOFTWARE",
"dashboard/refresh.svg",
Task::RestartSoftware
);
Ok(Self { Ok(Self {
tasks: mp.tasks, tasks: mp.tasks,
state: parser_state, state: parser_state,

View File

@@ -0,0 +1,54 @@
use log::error;
use std::{path::PathBuf, sync::LazyLock};
pub enum ConfigRoot {
Generic,
#[allow(dead_code)]
WayVR,
}
const FALLBACK_CONFIG_PATH: &str = "/tmp/wlxoverlay";
static CONFIG_ROOT_PATH: LazyLock<PathBuf> = LazyLock::new(|| {
if let Some(mut dir) = xdg::BaseDirectories::new().get_config_home() {
dir.push("wlxoverlay");
return dir;
}
//Return fallback config path
error!("Err: Failed to find config path, using {FALLBACK_CONFIG_PATH}");
PathBuf::from(FALLBACK_CONFIG_PATH)
});
pub fn get_config_root() -> PathBuf {
CONFIG_ROOT_PATH.clone()
}
impl ConfigRoot {
pub fn get_conf_d_path(&self) -> PathBuf {
get_config_root().join(match self {
Self::Generic => "conf.d",
Self::WayVR => "wayvr.conf.d",
})
}
// Make sure config directory is present and return root config path
pub fn ensure_dir(&self) -> PathBuf {
let path = get_config_root();
let _ = std::fs::create_dir(&path);
let path_conf_d = self.get_conf_d_path();
let _ = std::fs::create_dir(path_conf_d);
path
}
}
pub fn get_config_file_path(filename: &str) -> PathBuf {
get_config_root().join(filename)
}
pub fn load(filename: &str) -> Option<String> {
let path = get_config_file_path(filename);
log::info!("Loading config: {}", path.to_string_lossy());
std::fs::read_to_string(path).ok()
}

View File

@@ -17,6 +17,7 @@ pub trait DashInterface<T> {
fn desktop_finder<'a>(&'a mut self, data: &'a mut T) -> &'a mut DesktopFinder; fn desktop_finder<'a>(&'a mut self, data: &'a mut T) -> &'a mut DesktopFinder;
fn general_config<'a>(&'a mut self, data: &'a mut T) -> &'a mut GeneralConfig; fn general_config<'a>(&'a mut self, data: &'a mut T) -> &'a mut GeneralConfig;
fn config_changed(&mut self, data: &mut T); fn config_changed(&mut self, data: &mut T);
fn restart(&mut self, data: &mut T);
} }
pub type BoxDashInterface<T> = Box<dyn DashInterface<T>>; pub type BoxDashInterface<T> = Box<dyn DashInterface<T>>;

View File

@@ -180,4 +180,6 @@ impl DashInterface<()> for DashInterfaceEmulated {
} }
fn config_changed(&mut self, _: &mut ()) {} fn config_changed(&mut self, _: &mut ()) {}
fn restart(&mut self, data: &mut ()) {}
} }

View File

@@ -3,6 +3,7 @@ pub mod audio;
pub mod cache_dir; pub mod cache_dir;
pub mod common; pub mod common;
pub mod config; pub mod config;
pub mod config_io;
pub mod dash_interface; pub mod dash_interface;
pub mod dash_interface_emulated; pub mod dash_interface_emulated;
pub mod desktop_finder; pub mod desktop_finder;

View File

@@ -113,7 +113,7 @@
<Hmd idx="0" /> <Hmd idx="0" />
<LeftHand idx="1" /> <LeftHand idx="1" />
<RightHand idx="2" /> <RightHand idx="2" />
<Track idx="3" /> <Tracker idx="3" />
</div> </div>
<!-- All other elements inside the container --> <!-- All other elements inside the container -->

View File

@@ -10,10 +10,10 @@ use ovr_overlay::{
}, },
system::SystemManager, system::SystemManager,
}; };
use wlx_common::config_io;
use crate::{ use crate::{
backend::input::{Haptics, TrackedDevice, TrackedDeviceRole}, backend::input::{Haptics, TrackedDevice, TrackedDeviceRole},
config_io,
state::AppState, state::AppState,
}; };

View File

@@ -4,7 +4,7 @@ use anyhow::{Context, bail};
use json::{array, object}; use json::{array, object};
use ovr_overlay::applications::ApplicationsManager; use ovr_overlay::applications::ApplicationsManager;
use crate::config_io; use wlx_common::config_io;
const APP_KEY: &str = "galister.wlxoverlay-s"; const APP_KEY: &str = "galister.wlxoverlay-s";

View File

@@ -8,10 +8,10 @@ use glam::{Affine3A, Quat, Vec3, bool};
use libmonado as mnd; use libmonado as mnd;
use openxr::{self as xr, Quaternionf, Vector2f, Vector3f}; use openxr::{self as xr, Quaternionf, Vector2f, Vector3f};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use wlx_common::config_io;
use crate::{ use crate::{
backend::input::{Haptics, Pointer, TrackedDevice, TrackedDeviceRole}, backend::input::{Haptics, Pointer, TrackedDevice, TrackedDeviceRole},
config_io,
state::{AppSession, AppState}, state::{AppSession, AppState},
}; };

View File

@@ -11,10 +11,10 @@ use vulkano::{
pipeline::graphics::color_blend::AttachmentBlend, pipeline::graphics::color_blend::AttachmentBlend,
}; };
use wgui::gfx::{cmd::WGfxClearMode, pipeline::WPipelineCreateInfo}; use wgui::gfx::{cmd::WGfxClearMode, pipeline::WPipelineCreateInfo};
use wlx_common::config_io;
use crate::{ use crate::{
backend::openxr::{helpers::translation_rotation_to_posef, swapchain::SwapchainOpts}, backend::openxr::{helpers::translation_rotation_to_posef, swapchain::SwapchainOpts},
config_io,
graphics::{ExtentExt, GpuFutures, dds::WlxCommandBufferDds}, graphics::{ExtentExt, GpuFutures, dds::WlxCommandBufferDds},
state::AppState, state::AppState,
}; };

View File

@@ -1,4 +1,3 @@
use crate::config_io;
use config::{Config, File}; use config::{Config, File};
use log::error; use log::error;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -6,6 +5,7 @@ use std::path::PathBuf;
use wlx_common::{ use wlx_common::{
astr_containers::AStrMap, astr_containers::AStrMap,
config::{GeneralConfig, SerializedWindowSet, SerializedWindowStates}, config::{GeneralConfig, SerializedWindowSet, SerializedWindowStates},
config_io,
overlays::BackendAttribValue, overlays::BackendAttribValue,
}; };

View File

@@ -1,54 +0,0 @@
use log::error;
use std::{path::PathBuf, sync::LazyLock};
pub enum ConfigRoot {
Generic,
#[allow(dead_code)]
WayVR,
}
const FALLBACK_CONFIG_PATH: &str = "/tmp/wlxoverlay";
static CONFIG_ROOT_PATH: LazyLock<PathBuf> = LazyLock::new(|| {
if let Some(mut dir) = xdg::BaseDirectories::new().get_config_home() {
dir.push("wlxoverlay");
return dir;
}
//Return fallback config path
error!("Err: Failed to find config path, using {FALLBACK_CONFIG_PATH}");
PathBuf::from(FALLBACK_CONFIG_PATH)
});
pub fn get_config_root() -> PathBuf {
CONFIG_ROOT_PATH.clone()
}
impl ConfigRoot {
pub fn get_conf_d_path(&self) -> PathBuf {
get_config_root().join(match self {
Self::Generic => "conf.d",
Self::WayVR => "wayvr.conf.d",
})
}
// Make sure config directory is present and return root config path
pub fn ensure_dir(&self) -> PathBuf {
let path = get_config_root();
let _ = std::fs::create_dir(&path);
let path_conf_d = self.get_conf_d_path();
let _ = std::fs::create_dir(path_conf_d);
path
}
}
pub fn get_config_file_path(filename: &str) -> PathBuf {
get_config_root().join(filename)
}
pub fn load(filename: &str) -> Option<String> {
let path = get_config_file_path(filename);
log::info!("Loading config: {}", path.to_string_lossy());
std::fs::read_to_string(path).ok()
}

View File

@@ -9,7 +9,7 @@ use std::{
use anyhow::Context; use anyhow::Context;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use wgui::gfx::WGfx; use wgui::gfx::WGfx;
use wlx_common::{common::LeftRight, config::GeneralConfig, windowing::Positioning}; use wlx_common::{common::LeftRight, config::GeneralConfig, config_io, windowing::Positioning};
use crate::{ use crate::{
backend::{ backend::{
@@ -17,7 +17,6 @@ use crate::{
wayvr::{self, WvrServerState}, wayvr::{self, WvrServerState},
}, },
config::load_config_with_conf_d, config::load_config_with_conf_d,
config_io,
graphics::WGfxExtras, graphics::WGfxExtras,
ipc::{event_queue::SyncEventQueue, signal::WayVRSignal}, ipc::{event_queue::SyncEventQueue, signal::WayVRSignal},
}; };

View File

@@ -25,9 +25,8 @@ use wgui::{
use wlx_common::overlays::ToastTopic; use wlx_common::overlays::ToastTopic;
use crate::{ use crate::{
RUNNING, RESTART, RUNNING,
backend::{ backend::{
XrBackend,
task::{OverlayTask, PlayspaceTask, TaskType, ToggleMode}, task::{OverlayTask, PlayspaceTask, TaskType, ToggleMode},
wayvr::process::KillSignal, wayvr::process::KillSignal,
}, },
@@ -572,16 +571,8 @@ pub(super) fn setup_custom_button<S: 'static>(
if !test_button(data) || !test_duration(&button, app) { if !test_button(data) || !test_duration(&button, app) {
return Ok(EventResult::Pass); return Ok(EventResult::Pass);
} }
RUNNING.store(false, Ordering::Relaxed);
let runtime = match app.xr_backend { RESTART.store(true, Ordering::Relaxed);
XrBackend::OpenVR => "--openvr",
XrBackend::OpenXR => "--openxr",
};
Command::new("/proc/self/exe")
.arg(runtime) // ensure same runtime
.arg("--replace") // SIGTERM the previous process
.arg("--show");
Ok(EventResult::Consumed) Ok(EventResult::Consumed)
}), }),

View File

@@ -20,7 +20,6 @@
mod app_misc; mod app_misc;
mod backend; mod backend;
mod config; mod config;
mod config_io;
mod graphics; mod graphics;
mod gui; mod gui;
mod ipc; mod ipc;
@@ -34,7 +33,9 @@ mod windowing;
mod config_wayvr; mod config_wayvr;
use std::{ use std::{
os::unix::process::CommandExt,
path::PathBuf, path::PathBuf,
process::Command,
sync::atomic::{AtomicBool, AtomicUsize, Ordering}, sync::atomic::{AtomicBool, AtomicUsize, Ordering},
}; };
@@ -45,10 +46,11 @@ use sysinfo::Pid;
use tracing::level_filters::LevelFilter; use tracing::level_filters::LevelFilter;
use tracing_subscriber::{EnvFilter, layer::SubscriberExt, util::SubscriberInitExt}; use tracing_subscriber::{EnvFilter, layer::SubscriberExt, util::SubscriberInitExt};
use crate::subsystem::dbus::DbusConnector; use crate::{backend::XrBackend, subsystem::dbus::DbusConnector};
pub static FRAME_COUNTER: AtomicUsize = AtomicUsize::new(0); pub static FRAME_COUNTER: AtomicUsize = AtomicUsize::new(0);
pub static RUNNING: AtomicBool = AtomicBool::new(true); pub static RUNNING: AtomicBool = AtomicBool::new(true);
pub static RESTART: AtomicBool = AtomicBool::new(false);
pub static KEYMAP_CHANGE: AtomicBool = AtomicBool::new(false); pub static KEYMAP_CHANGE: AtomicBool = AtomicBool::new(false);
/// The lightweight desktop overlay for OpenVR and OpenXR /// The lightweight desktop overlay for OpenVR and OpenXR
@@ -121,13 +123,29 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
setup_signal_hooks()?; setup_signal_hooks()?;
auto_run(args); let mut used_backend = None;
auto_run(args, &mut used_backend);
if RESTART.load(Ordering::Relaxed) {
log::warn!("Restarting...");
let exe = std::env::current_exe()?;
let mut args = vec!["--replace", "--show"];
match used_backend {
Some(XrBackend::OpenXR) => args.push("--openxr"),
Some(XrBackend::OpenVR) => args.push("--openvr"),
_ => {}
};
let _ = Command::new(exe).args(args).spawn();
}
Ok(()) Ok(())
} }
#[allow(unused_mut, clippy::similar_names)] #[allow(unused_mut, clippy::similar_names)]
fn auto_run(args: Args) { fn auto_run(args: Args, used_backend: &mut Option<XrBackend>) {
let mut tried_xr = false; let mut tried_xr = false;
let mut tried_vr = false; let mut tried_vr = false;
@@ -136,9 +154,13 @@ fn auto_run(args: Args) {
use crate::backend::{BackendError, openxr::openxr_run}; use crate::backend::{BackendError, openxr::openxr_run};
tried_xr = true; tried_xr = true;
match openxr_run(args.show, args.headless) { match openxr_run(args.show, args.headless) {
Ok(()) => return, Ok(()) => {
used_backend.replace(XrBackend::OpenXR);
return;
}
Err(BackendError::NotSupported) => (), Err(BackendError::NotSupported) => (),
Err(e) => { Err(e) => {
used_backend.replace(XrBackend::OpenXR);
log::error!("{e:?}"); log::error!("{e:?}");
return; return;
} }
@@ -150,9 +172,13 @@ fn auto_run(args: Args) {
use crate::backend::{BackendError, openvr::openvr_run}; use crate::backend::{BackendError, openvr::openvr_run};
tried_vr = true; tried_vr = true;
match openvr_run(args.show, args.headless) { match openvr_run(args.show, args.headless) {
Ok(()) => return, Ok(()) => {
used_backend.replace(XrBackend::OpenVR);
return;
}
Err(BackendError::NotSupported) => (), Err(BackendError::NotSupported) => (),
Err(e) => { Err(e) => {
used_backend.replace(XrBackend::OpenVR);
log::error!("{e:?}"); log::error!("{e:?}");
return; return;
} }

View File

@@ -1,3 +1,5 @@
use std::sync::atomic::Ordering;
use dash_frontend::frontend::{self, FrontendTask, FrontendUpdateParams}; use dash_frontend::frontend::{self, FrontendTask, FrontendUpdateParams};
use glam::{Affine2, Affine3A, Vec2, vec2, vec3}; use glam::{Affine2, Affine3A, Vec2, vec2, vec3};
use wayvr_ipc::{ use wayvr_ipc::{
@@ -23,6 +25,7 @@ use wlx_common::{
}; };
use crate::{ use crate::{
RESTART, RUNNING,
backend::{ backend::{
input::{Haptics, HoverResult, PointerHit, PointerMode}, input::{Haptics, HoverResult, PointerHit, PointerMode},
task::{OverlayTask, PlayspaceTask, TaskType, ToggleMode}, task::{OverlayTask, PlayspaceTask, TaskType, ToggleMode},
@@ -418,4 +421,9 @@ impl DashInterface<AppState> for DashInterfaceLive {
data.tasks data.tasks
.enqueue(TaskType::Overlay(OverlayTask::SettingsChanged)); .enqueue(TaskType::Overlay(OverlayTask::SettingsChanged));
} }
fn restart(&mut self, _data: &mut AppState) {
RUNNING.store(false, Ordering::Relaxed);
RESTART.store(true, Ordering::Relaxed);
}
} }

View File

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

View File

@@ -9,6 +9,7 @@ use wgui::{
use wlx_common::{ use wlx_common::{
audio, audio,
config::GeneralConfig, config::GeneralConfig,
config_io::{self, get_config_file_path},
desktop_finder::DesktopFinder, desktop_finder::DesktopFinder,
overlays::{ToastDisplayMethod, ToastTopic}, overlays::{ToastDisplayMethod, ToastTopic},
}; };
@@ -24,7 +25,6 @@ use crate::subsystem::osc::OscSender;
use crate::{ use crate::{
backend::{XrBackend, input::InputState, task::TaskContainer}, backend::{XrBackend, input::InputState, task::TaskContainer},
config::load_general_config, config::load_general_config,
config_io::{self, get_config_file_path},
graphics::WGfxExtras, graphics::WGfxExtras,
gui, gui,
ipc::{event_queue::SyncEventQueue, ipc_server, signal::WayVRSignal}, ipc::{event_queue::SyncEventQueue, ipc_server, signal::WayVRSignal},