From 2f010bb42b72253d356e8ae122e64c2a8ee0ddfe Mon Sep 17 00:00:00 2001 From: galister <22305755+galister@users.noreply.github.com> Date: Thu, 8 Jan 2026 02:19:00 +0900 Subject: [PATCH] settings tab buttons; autorestart --- dash-frontend/assets/gui/tab/settings.xml | 6 +- dash-frontend/assets/lang/en.json | 14 +++- dash-frontend/src/tab/settings.rs | 83 +++++++++++++++++++- wlx-common/src/config_io.rs | 54 +++++++++++++ wlx-common/src/dash_interface.rs | 1 + wlx-common/src/dash_interface_emulated.rs | 2 + wlx-common/src/lib.rs | 1 + wlx-overlay-s/src/assets/gui/watch.xml | 2 +- wlx-overlay-s/src/backend/openvr/input.rs | 2 +- wlx-overlay-s/src/backend/openvr/manifest.rs | 2 +- wlx-overlay-s/src/backend/openxr/input.rs | 2 +- wlx-overlay-s/src/backend/openxr/skybox.rs | 2 +- wlx-overlay-s/src/config.rs | 2 +- wlx-overlay-s/src/config_io.rs | 54 ------------- wlx-overlay-s/src/config_wayvr.rs | 3 +- wlx-overlay-s/src/gui/panel/button.rs | 15 +--- wlx-overlay-s/src/main.rs | 38 +++++++-- wlx-overlay-s/src/overlays/dashboard.rs | 8 ++ wlx-overlay-s/src/overlays/screen/pw.rs | 7 +- wlx-overlay-s/src/state.rs | 2 +- 20 files changed, 212 insertions(+), 88 deletions(-) create mode 100644 wlx-common/src/config_io.rs delete mode 100644 wlx-overlay-s/src/config_io.rs diff --git a/dash-frontend/assets/gui/tab/settings.xml b/dash-frontend/assets/gui/tab/settings.xml index f9be516..cb29c7e 100644 --- a/dash-frontend/assets/gui/tab/settings.xml +++ b/dash-frontend/assets/gui/tab/settings.xml @@ -26,8 +26,12 @@ + +
- \ No newline at end of file + diff --git a/dash-frontend/assets/lang/en.json b/dash-frontend/assets/lang/en.json index 8ece3e3..0516089 100644 --- a/dash-frontend/assets/lang/en.json +++ b/dash-frontend/assets/lang/en.json @@ -57,7 +57,19 @@ "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_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_STARTED": "Application started", diff --git a/dash-frontend/src/tab/settings.rs b/dash-frontend/src/tab/settings.rs index 4260137..0c52817 100644 --- a/dash-frontend/src/tab/settings.rs +++ b/dash-frontend/src/tab/settings.rs @@ -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 wgui::{ assets::AssetPath, - components::{checkbox::ComponentCheckbox, slider::ComponentSlider}, + components::{button::ComponentButton, checkbox::ComponentCheckbox, slider::ComponentSlider}, layout::{Layout, WidgetID}, + log::LogErr, parser::{Fetchable, ParseDocumentParams, ParserState}, task::Tasks, }; -use wlx_common::config::GeneralConfig; +use wlx_common::{config::GeneralConfig, config_io::ConfigRoot}; use crate::{ frontend::{Frontend, FrontendTask}, @@ -19,6 +20,10 @@ enum Task { UpdateBool(SettingType, bool), UpdateFloat(SettingType, f32), UpdateInt(SettingType, i32), + ClearPipewireTokens, + ClearSavedState, + DeleteAllConfigs, + RestartSoftware, } pub struct TabSettings { @@ -54,6 +59,23 @@ impl Tab for TabSettings { *setting.mut_i32(config) = n; 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 { @@ -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> = 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::(&id)?; + btn.on_click(Box::new({ + let tasks = $mp.tasks.clone(); + move |_common, _e| { + tasks.push($task); + Ok(()) + } + })); + }; +} + struct MacroParams<'a> { layout: &'a mut Layout, parser_state: &'a mut ParserState, @@ -428,6 +475,36 @@ impl TabSettings { checkbox!(mp, c, SettingType::DoubleCursorFix); 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 { tasks: mp.tasks, state: parser_state, diff --git a/wlx-common/src/config_io.rs b/wlx-common/src/config_io.rs new file mode 100644 index 0000000..c70d72c --- /dev/null +++ b/wlx-common/src/config_io.rs @@ -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 = 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 { + let path = get_config_file_path(filename); + log::info!("Loading config: {}", path.to_string_lossy()); + + std::fs::read_to_string(path).ok() +} diff --git a/wlx-common/src/dash_interface.rs b/wlx-common/src/dash_interface.rs index c4c5d60..e966c93 100644 --- a/wlx-common/src/dash_interface.rs +++ b/wlx-common/src/dash_interface.rs @@ -17,6 +17,7 @@ pub trait DashInterface { 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 config_changed(&mut self, data: &mut T); + fn restart(&mut self, data: &mut T); } pub type BoxDashInterface = Box>; diff --git a/wlx-common/src/dash_interface_emulated.rs b/wlx-common/src/dash_interface_emulated.rs index 64d3fe7..2919abb 100644 --- a/wlx-common/src/dash_interface_emulated.rs +++ b/wlx-common/src/dash_interface_emulated.rs @@ -180,4 +180,6 @@ impl DashInterface<()> for DashInterfaceEmulated { } fn config_changed(&mut self, _: &mut ()) {} + + fn restart(&mut self, data: &mut ()) {} } diff --git a/wlx-common/src/lib.rs b/wlx-common/src/lib.rs index 0a5cdd2..a96fbba 100644 --- a/wlx-common/src/lib.rs +++ b/wlx-common/src/lib.rs @@ -3,6 +3,7 @@ pub mod audio; pub mod cache_dir; pub mod common; pub mod config; +pub mod config_io; pub mod dash_interface; pub mod dash_interface_emulated; pub mod desktop_finder; diff --git a/wlx-overlay-s/src/assets/gui/watch.xml b/wlx-overlay-s/src/assets/gui/watch.xml index 4d68bdd..c7e306f 100644 --- a/wlx-overlay-s/src/assets/gui/watch.xml +++ b/wlx-overlay-s/src/assets/gui/watch.xml @@ -113,7 +113,7 @@ - +
diff --git a/wlx-overlay-s/src/backend/openvr/input.rs b/wlx-overlay-s/src/backend/openvr/input.rs index 8343825..979e341 100644 --- a/wlx-overlay-s/src/backend/openvr/input.rs +++ b/wlx-overlay-s/src/backend/openvr/input.rs @@ -10,10 +10,10 @@ use ovr_overlay::{ }, system::SystemManager, }; +use wlx_common::config_io; use crate::{ backend::input::{Haptics, TrackedDevice, TrackedDeviceRole}, - config_io, state::AppState, }; diff --git a/wlx-overlay-s/src/backend/openvr/manifest.rs b/wlx-overlay-s/src/backend/openvr/manifest.rs index 33bfc91..ddbbbc9 100644 --- a/wlx-overlay-s/src/backend/openvr/manifest.rs +++ b/wlx-overlay-s/src/backend/openvr/manifest.rs @@ -4,7 +4,7 @@ use anyhow::{Context, bail}; use json::{array, object}; use ovr_overlay::applications::ApplicationsManager; -use crate::config_io; +use wlx_common::config_io; const APP_KEY: &str = "galister.wlxoverlay-s"; diff --git a/wlx-overlay-s/src/backend/openxr/input.rs b/wlx-overlay-s/src/backend/openxr/input.rs index 4e96ce3..9c9e392 100644 --- a/wlx-overlay-s/src/backend/openxr/input.rs +++ b/wlx-overlay-s/src/backend/openxr/input.rs @@ -8,10 +8,10 @@ use glam::{Affine3A, Quat, Vec3, bool}; use libmonado as mnd; use openxr::{self as xr, Quaternionf, Vector2f, Vector3f}; use serde::{Deserialize, Serialize}; +use wlx_common::config_io; use crate::{ backend::input::{Haptics, Pointer, TrackedDevice, TrackedDeviceRole}, - config_io, state::{AppSession, AppState}, }; diff --git a/wlx-overlay-s/src/backend/openxr/skybox.rs b/wlx-overlay-s/src/backend/openxr/skybox.rs index 9e14eaa..839bb17 100644 --- a/wlx-overlay-s/src/backend/openxr/skybox.rs +++ b/wlx-overlay-s/src/backend/openxr/skybox.rs @@ -11,10 +11,10 @@ use vulkano::{ pipeline::graphics::color_blend::AttachmentBlend, }; use wgui::gfx::{cmd::WGfxClearMode, pipeline::WPipelineCreateInfo}; +use wlx_common::config_io; use crate::{ backend::openxr::{helpers::translation_rotation_to_posef, swapchain::SwapchainOpts}, - config_io, graphics::{ExtentExt, GpuFutures, dds::WlxCommandBufferDds}, state::AppState, }; diff --git a/wlx-overlay-s/src/config.rs b/wlx-overlay-s/src/config.rs index 00a9848..f42f316 100644 --- a/wlx-overlay-s/src/config.rs +++ b/wlx-overlay-s/src/config.rs @@ -1,4 +1,3 @@ -use crate::config_io; use config::{Config, File}; use log::error; use serde::{Deserialize, Serialize}; @@ -6,6 +5,7 @@ use std::path::PathBuf; use wlx_common::{ astr_containers::AStrMap, config::{GeneralConfig, SerializedWindowSet, SerializedWindowStates}, + config_io, overlays::BackendAttribValue, }; diff --git a/wlx-overlay-s/src/config_io.rs b/wlx-overlay-s/src/config_io.rs deleted file mode 100644 index 9ee03f4..0000000 --- a/wlx-overlay-s/src/config_io.rs +++ /dev/null @@ -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 = 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 { - let path = get_config_file_path(filename); - log::info!("Loading config: {}", path.to_string_lossy()); - - std::fs::read_to_string(path).ok() -} diff --git a/wlx-overlay-s/src/config_wayvr.rs b/wlx-overlay-s/src/config_wayvr.rs index da00be4..219b4c9 100644 --- a/wlx-overlay-s/src/config_wayvr.rs +++ b/wlx-overlay-s/src/config_wayvr.rs @@ -9,7 +9,7 @@ use std::{ use anyhow::Context; use serde::{Deserialize, Serialize}; 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::{ backend::{ @@ -17,7 +17,6 @@ use crate::{ wayvr::{self, WvrServerState}, }, config::load_config_with_conf_d, - config_io, graphics::WGfxExtras, ipc::{event_queue::SyncEventQueue, signal::WayVRSignal}, }; diff --git a/wlx-overlay-s/src/gui/panel/button.rs b/wlx-overlay-s/src/gui/panel/button.rs index d9e4a48..9e6c80a 100644 --- a/wlx-overlay-s/src/gui/panel/button.rs +++ b/wlx-overlay-s/src/gui/panel/button.rs @@ -25,9 +25,8 @@ use wgui::{ use wlx_common::overlays::ToastTopic; use crate::{ - RUNNING, + RESTART, RUNNING, backend::{ - XrBackend, task::{OverlayTask, PlayspaceTask, TaskType, ToggleMode}, wayvr::process::KillSignal, }, @@ -572,16 +571,8 @@ pub(super) fn setup_custom_button( if !test_button(data) || !test_duration(&button, app) { return Ok(EventResult::Pass); } - - let runtime = match app.xr_backend { - XrBackend::OpenVR => "--openvr", - XrBackend::OpenXR => "--openxr", - }; - - Command::new("/proc/self/exe") - .arg(runtime) // ensure same runtime - .arg("--replace") // SIGTERM the previous process - .arg("--show"); + RUNNING.store(false, Ordering::Relaxed); + RESTART.store(true, Ordering::Relaxed); Ok(EventResult::Consumed) }), diff --git a/wlx-overlay-s/src/main.rs b/wlx-overlay-s/src/main.rs index 8b4e5e3..34b4654 100644 --- a/wlx-overlay-s/src/main.rs +++ b/wlx-overlay-s/src/main.rs @@ -20,7 +20,6 @@ mod app_misc; mod backend; mod config; -mod config_io; mod graphics; mod gui; mod ipc; @@ -34,7 +33,9 @@ mod windowing; mod config_wayvr; use std::{ + os::unix::process::CommandExt, path::PathBuf, + process::Command, sync::atomic::{AtomicBool, AtomicUsize, Ordering}, }; @@ -45,10 +46,11 @@ use sysinfo::Pid; use tracing::level_filters::LevelFilter; 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 RUNNING: AtomicBool = AtomicBool::new(true); +pub static RESTART: AtomicBool = AtomicBool::new(false); pub static KEYMAP_CHANGE: AtomicBool = AtomicBool::new(false); /// The lightweight desktop overlay for OpenVR and OpenXR @@ -121,13 +123,29 @@ fn main() -> Result<(), Box> { 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(()) } #[allow(unused_mut, clippy::similar_names)] -fn auto_run(args: Args) { +fn auto_run(args: Args, used_backend: &mut Option) { let mut tried_xr = false; let mut tried_vr = false; @@ -136,9 +154,13 @@ fn auto_run(args: Args) { use crate::backend::{BackendError, openxr::openxr_run}; tried_xr = true; match openxr_run(args.show, args.headless) { - Ok(()) => return, + Ok(()) => { + used_backend.replace(XrBackend::OpenXR); + return; + } Err(BackendError::NotSupported) => (), Err(e) => { + used_backend.replace(XrBackend::OpenXR); log::error!("{e:?}"); return; } @@ -150,9 +172,13 @@ fn auto_run(args: Args) { use crate::backend::{BackendError, openvr::openvr_run}; tried_vr = true; match openvr_run(args.show, args.headless) { - Ok(()) => return, + Ok(()) => { + used_backend.replace(XrBackend::OpenVR); + return; + } Err(BackendError::NotSupported) => (), Err(e) => { + used_backend.replace(XrBackend::OpenVR); log::error!("{e:?}"); return; } diff --git a/wlx-overlay-s/src/overlays/dashboard.rs b/wlx-overlay-s/src/overlays/dashboard.rs index f1d7914..770902e 100644 --- a/wlx-overlay-s/src/overlays/dashboard.rs +++ b/wlx-overlay-s/src/overlays/dashboard.rs @@ -1,3 +1,5 @@ +use std::sync::atomic::Ordering; + use dash_frontend::frontend::{self, FrontendTask, FrontendUpdateParams}; use glam::{Affine2, Affine3A, Vec2, vec2, vec3}; use wayvr_ipc::{ @@ -23,6 +25,7 @@ use wlx_common::{ }; use crate::{ + RESTART, RUNNING, backend::{ input::{Haptics, HoverResult, PointerHit, PointerMode}, task::{OverlayTask, PlayspaceTask, TaskType, ToggleMode}, @@ -418,4 +421,9 @@ impl DashInterface for DashInterfaceLive { data.tasks .enqueue(TaskType::Overlay(OverlayTask::SettingsChanged)); } + + fn restart(&mut self, _data: &mut AppState) { + RUNNING.store(false, Ordering::Relaxed); + RESTART.store(true, Ordering::Relaxed); + } } diff --git a/wlx-overlay-s/src/overlays/screen/pw.rs b/wlx-overlay-s/src/overlays/screen/pw.rs index c7a16fa..89193d1 100644 --- a/wlx-overlay-s/src/overlays/screen/pw.rs +++ b/wlx-overlay-s/src/overlays/screen/pw.rs @@ -6,9 +6,12 @@ use wlx_capture::{ pipewire::{PipewireCapture, PipewireSelectScreenResult}, 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::{ backend::ScreenBackend, diff --git a/wlx-overlay-s/src/state.rs b/wlx-overlay-s/src/state.rs index 1d9089b..e228697 100644 --- a/wlx-overlay-s/src/state.rs +++ b/wlx-overlay-s/src/state.rs @@ -9,6 +9,7 @@ use wgui::{ use wlx_common::{ audio, config::GeneralConfig, + config_io::{self, get_config_file_path}, desktop_finder::DesktopFinder, overlays::{ToastDisplayMethod, ToastTopic}, }; @@ -24,7 +25,6 @@ use crate::subsystem::osc::OscSender; use crate::{ backend::{XrBackend, input::InputState, task::TaskContainer}, config::load_general_config, - config_io::{self, get_config_file_path}, graphics::WGfxExtras, gui, ipc::{event_queue::SyncEventQueue, ipc_server, signal::WayVRSignal},