feat: persist screen visibility & curvature

This commit is contained in:
galister
2024-04-05 01:59:03 +09:00
parent e3ae9a9890
commit 1bee936db1
6 changed files with 254 additions and 118 deletions

View File

@@ -15,8 +15,9 @@ use thiserror::Error;
use wlx_capture::wayland::{OutputChangeEvent, WlxClient}; use wlx_capture::wayland::{OutputChangeEvent, WlxClient};
use crate::{ use crate::{
config::{AStrMapExt, AStrSetExt},
overlays::{ overlays::{
keyboard::create_keyboard, keyboard::{create_keyboard, KEYBOARD_NAME},
screen::{create_screen_interaction, create_screen_renderer_wl, load_pw_token_config}, screen::{create_screen_interaction, create_screen_renderer_wl, load_pw_token_config},
watch::{create_watch, create_watch_canvas, WATCH_NAME}, watch::{create_watch, create_watch_canvas, WATCH_NAME},
}, },
@@ -66,15 +67,21 @@ where
let mut show_screens = app.session.config.show_screens.clone(); let mut show_screens = app.session.config.show_screens.clone();
if show_screens.is_empty() { if show_screens.is_empty() {
if let Some((_, s, _)) = data.screens.first() { if let Some((_, s, _)) = data.screens.first() {
show_screens.push(s.name.clone()); show_screens.arc_ins(s.name.clone());
} }
} }
for (meta, mut state, backend) in data.screens { for (meta, mut state, backend) in data.screens {
if show_screens.contains(&state.name) { if show_screens.arc_get(state.name.as_ref()) {
state.show_hide = true; state.show_hide = true;
state.want_visible = false; state.want_visible = false;
} }
state.curvature = app
.session
.config
.curve_values
.arc_get(state.name.as_ref())
.copied();
overlays.insert( overlays.insert(
state.id, state.id,
OverlayData::<T> { OverlayData::<T> {
@@ -93,6 +100,12 @@ where
let mut keyboard = create_keyboard(app)?; let mut keyboard = create_keyboard(app)?;
keyboard.state.show_hide = true; keyboard.state.show_hide = true;
keyboard.state.want_visible = false; keyboard.state.want_visible = false;
keyboard.state.curvature = app
.session
.config
.curve_values
.arc_get(KEYBOARD_NAME)
.copied();
overlays.insert(keyboard.state.id, keyboard); overlays.insert(keyboard.state.id, keyboard);
Ok(Self { overlays, wl }) Ok(Self { overlays, wl })
@@ -283,7 +296,10 @@ where
self.overlays.values_mut().for_each(|o| { self.overlays.values_mut().for_each(|o| {
if o.state.show_hide { if o.state.show_hide {
o.state.want_visible = !any_shown; o.state.want_visible = !any_shown;
if o.state.want_visible && app.session.config.realign_on_showhide && o.state.recenter { if o.state.want_visible
&& app.session.config.realign_on_showhide
&& o.state.recenter
{
o.state.reset(app, false); o.state.reset(app, false);
} }
} }

View File

@@ -6,7 +6,7 @@ use glam::{Affine3A, Vec2, Vec3A};
use ovr_overlay::TrackedDeviceIndex; use ovr_overlay::TrackedDeviceIndex;
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use crate::config::GeneralConfig; use crate::config::{save_state, AStrMapExt, GeneralConfig};
use crate::state::AppState; use crate::state::AppState;
use super::{ use super::{
@@ -246,6 +246,7 @@ struct RayHit {
pub struct GrabData { pub struct GrabData {
pub offset: Vec3A, pub offset: Vec3A,
pub grabbed_id: usize, pub grabbed_id: usize,
pub old_curvature: Option<f32>,
} }
#[repr(u8)] #[repr(u8)]
@@ -288,7 +289,7 @@ where
let mut pointer = &mut app.input_state.pointers[idx]; let mut pointer = &mut app.input_state.pointers[idx];
if let Some(grab_data) = pointer.interaction.grabbed { if let Some(grab_data) = pointer.interaction.grabbed {
if let Some(grabbed) = overlays.mut_by_id(grab_data.grabbed_id) { if let Some(grabbed) = overlays.mut_by_id(grab_data.grabbed_id) {
pointer.handle_grabbed(grabbed, hmd, &app.session.config); pointer.handle_grabbed(grabbed, hmd, &mut app.session.config);
} else { } else {
log::warn!("Grabbed overlay {} does not exist", grab_data.grabbed_id); log::warn!("Grabbed overlay {} does not exist", grab_data.grabbed_id);
pointer.interaction.grabbed = None; pointer.interaction.grabbed = None;
@@ -469,12 +470,17 @@ impl Pointer {
self.interaction.grabbed = Some(GrabData { self.interaction.grabbed = Some(GrabData {
offset, offset,
grabbed_id: overlay.state.id, grabbed_id: overlay.state.id,
old_curvature: overlay.state.curvature,
}); });
log::info!("Hand {}: grabbed {}", self.idx, overlay.state.name); log::info!("Hand {}: grabbed {}", self.idx, overlay.state.name);
} }
fn handle_grabbed<O>(&mut self, overlay: &mut OverlayData<O>, hmd: &Affine3A, config: &GeneralConfig) fn handle_grabbed<O>(
where &mut self,
overlay: &mut OverlayData<O>,
hmd: &Affine3A,
config: &mut GeneralConfig,
) where
O: Default, O: Default,
{ {
if self.now.grab { if self.now.grab {
@@ -509,6 +515,26 @@ impl Pointer {
hmd.inverse() hmd.inverse()
.transform_point3a(overlay.state.transform.translation), .transform_point3a(overlay.state.transform.translation),
); );
if let Some(grab_data) = self.interaction.grabbed.as_ref() {
let mut state_dirty = false;
if overlay.state.curvature != grab_data.old_curvature {
if let Some(val) = overlay.state.curvature {
config.curve_values.arc_ins(overlay.state.name.clone(), val);
} else {
let ref_name = overlay.state.name.as_ref();
config.curve_values.arc_rm(ref_name);
}
state_dirty = true;
}
if state_dirty {
match save_state(config) {
Ok(_) => log::debug!("Saved state"),
Err(e) => log::error!("Failed to save state: {:?}", e),
}
}
}
self.interaction.grabbed = None; self.interaction.grabbed = None;
log::info!("Hand {}: dropped {}", self.idx, overlay.state.name); log::info!("Hand {}: dropped {}", self.idx, overlay.state.name);
} }

View File

@@ -1,3 +1,4 @@
use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
use crate::config_io; use crate::config_io;
@@ -15,6 +16,68 @@ use log::error;
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
pub type AStrMap<V> = Vec<(Arc<str>, V)>;
pub trait AStrMapExt<V> {
fn arc_ins(&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_ins(&mut self, key: Arc<str>, value: V) -> bool {
if self.iter().any(|(k, _)| k.as_ref().eq(key.as_ref())) {
return false;
}
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_ins(&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_ins(&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
.map(|i| {
self.remove(i);
true
})
.unwrap_or(false)
}
}
pub type PwTokenMap = AStrMap<String>;
pub fn def_watch_pos() -> [f32; 3] { pub fn def_watch_pos() -> [f32; 3] {
[-0.03, -0.01, 0.125] [-0.03, -0.01, 0.125]
} }
@@ -27,8 +90,8 @@ pub fn def_left() -> LeftRight {
LeftRight::Left LeftRight::Left
} }
pub fn def_pw_tokens() -> Vec<(String, String)> { pub fn def_pw_tokens() -> PwTokenMap {
Vec::new() AStrMap::new()
} }
fn def_click_freeze_time_ms() -> u32 { fn def_click_freeze_time_ms() -> u32 {
@@ -59,8 +122,12 @@ fn def_osc_port() -> u16 {
9000 9000
} }
fn def_screens() -> Vec<Arc<str>> { fn def_screens() -> AStrSet {
vec![] AStrSet::new()
}
fn def_curve_values() -> AStrMap<f32> {
AStrMap::new()
} }
fn def_auto() -> Arc<str> { fn def_auto() -> Arc<str> {
@@ -113,7 +180,7 @@ pub struct GeneralConfig {
pub long_press_duration: f32, pub long_press_duration: f32,
#[serde(default = "def_pw_tokens")] #[serde(default = "def_pw_tokens")]
pub pw_tokens: Vec<(String, String)>, pub pw_tokens: PwTokenMap,
#[serde(default = "def_osc_port")] #[serde(default = "def_osc_port")]
pub osc_out_port: u16, pub osc_out_port: u16,
@@ -125,7 +192,10 @@ pub struct GeneralConfig {
pub double_cursor_fix: bool, pub double_cursor_fix: bool,
#[serde(default = "def_screens")] #[serde(default = "def_screens")]
pub show_screens: Vec<Arc<str>>, pub show_screens: AStrSet,
#[serde(default = "def_curve_values")]
pub curve_values: AStrMap<f32>,
#[serde(default = "def_auto")] #[serde(default = "def_auto")]
pub capture_method: Arc<str>, pub capture_method: Arc<str>,
@@ -270,3 +340,67 @@ pub fn load_general() -> GeneralConfig {
} }
}; };
} }
// Config that is saved from the settings panel
#[derive(Serialize)]
pub struct AutoSettings {
pub watch_pos: [f32; 3],
pub watch_rot: [f32; 4],
pub watch_hand: LeftRight,
pub watch_view_angle_min: f32,
pub watch_view_angle_max: f32,
pub notifications_enabled: bool,
pub notifications_sound_enabled: bool,
pub realign_on_showhide: bool,
pub allow_sliding: bool,
}
fn get_settings_path() -> PathBuf {
let mut path = config_io::get_conf_d_path();
path.push("zz-saved-config.json5");
path
}
pub fn save_settings(config: &GeneralConfig) -> anyhow::Result<()> {
let conf = AutoSettings {
watch_pos: config.watch_pos,
watch_rot: config.watch_rot,
watch_hand: config.watch_hand,
watch_view_angle_min: config.watch_view_angle_min,
watch_view_angle_max: config.watch_view_angle_max,
notifications_enabled: config.notifications_enabled,
notifications_sound_enabled: config.notifications_sound_enabled,
realign_on_showhide: config.realign_on_showhide,
allow_sliding: config.allow_sliding,
};
let json = serde_json::to_string_pretty(&conf).unwrap(); // want panic
std::fs::write(get_settings_path(), json)?;
Ok(())
}
// Config that is saved after manipulating overlays
#[derive(Serialize)]
pub struct AutoState {
pub show_screens: AStrSet,
pub curve_values: AStrMap<f32>,
}
fn get_state_path() -> PathBuf {
let mut path = config_io::get_conf_d_path();
path.push("zz-saved-state.json5");
path
}
pub fn save_state(config: &GeneralConfig) -> anyhow::Result<()> {
let conf = AutoState {
show_screens: config.show_screens.clone(),
curve_values: config.curve_values.clone(),
};
let json = serde_json::to_string_pretty(&conf).unwrap(); // want panic
std::fs::write(get_state_path(), json)?;
Ok(())
}

View File

@@ -1,14 +1,13 @@
use std::{ use std::{
f32::consts::PI, f32::consts::PI,
ops::Add, ops::Add,
path::PathBuf,
process::{self, Child}, process::{self, Child},
sync::Arc, sync::Arc,
time::{Duration, Instant}, time::{Duration, Instant},
}; };
use glam::{Quat, Vec3A, Vec4}; use glam::{Quat, Vec3A, Vec4};
use serde::{Deserialize, Serialize}; use serde::Deserialize;
use crate::{ use crate::{
backend::{ backend::{
@@ -16,13 +15,12 @@ use crate::{
input::PointerMode, input::PointerMode,
overlay::RelativeTo, overlay::RelativeTo,
}, },
config::{def_half, def_left, def_point7, def_true, def_watch_pos, def_watch_rot}, config::{save_settings, save_state, AStrSetExt},
config_io,
overlays::{ overlays::{
toast::{Toast, ToastTopic}, toast::{Toast, ToastTopic},
watch::WATCH_NAME, watch::WATCH_NAME,
}, },
state::{AppState, LeftRight}, state::AppState,
}; };
use super::{ExecArgs, ModularControl, ModularData}; use super::{ExecArgs, ModularControl, ModularData};
@@ -44,6 +42,7 @@ pub enum Axis {
#[derive(Deserialize, Clone)] #[derive(Deserialize, Clone)]
pub enum HighlightTest { pub enum HighlightTest {
AllowSliding,
AutoRealign, AutoRealign,
NotificationSounds, NotificationSounds,
Notifications, Notifications,
@@ -51,6 +50,7 @@ pub enum HighlightTest {
#[derive(Deserialize, Clone)] #[derive(Deserialize, Clone)]
pub enum SystemAction { pub enum SystemAction {
ToggleAllowSliding,
ToggleAutoRealign, ToggleAutoRealign,
ToggleNotificationSounds, ToggleNotificationSounds,
ToggleNotifications, ToggleNotifications,
@@ -289,6 +289,7 @@ fn modular_button_highlight(
if let Some(test) = &data.highlight { if let Some(test) = &data.highlight {
let lit = match test { let lit = match test {
HighlightTest::AllowSliding => app.session.config.allow_sliding,
HighlightTest::AutoRealign => app.session.config.realign_on_showhide, HighlightTest::AutoRealign => app.session.config.realign_on_showhide,
HighlightTest::NotificationSounds => app.session.config.notifications_sound_enabled, HighlightTest::NotificationSounds => app.session.config.notifications_sound_enabled,
HighlightTest::Notifications => app.session.config.notifications_enabled, HighlightTest::Notifications => app.session.config.notifications_enabled,
@@ -330,19 +331,30 @@ fn handle_action(action: &ButtonAction, press: &mut PressData, app: &mut AppStat
} }
} }
const ENABLED_DISABLED: [&str; 2] = ["enabled", "disabled"];
fn run_system(action: &SystemAction, app: &mut AppState) { fn run_system(action: &SystemAction, app: &mut AppState) {
match action { match action {
SystemAction::ToggleAllowSliding => {
app.session.config.allow_sliding = !app.session.config.allow_sliding;
Toast::new(
ToastTopic::System,
format!(
"Sliding is {}.",
ENABLED_DISABLED[app.session.config.allow_sliding as usize]
)
.into(),
"".into(),
)
.submit(app);
}
SystemAction::ToggleAutoRealign => { SystemAction::ToggleAutoRealign => {
app.session.config.realign_on_showhide = !app.session.config.realign_on_showhide; app.session.config.realign_on_showhide = !app.session.config.realign_on_showhide;
Toast::new( Toast::new(
ToastTopic::System, ToastTopic::System,
format!( format!(
"Auto realign is {}.", "Auto realign is {}.",
if app.session.config.realign_on_showhide { ENABLED_DISABLED[app.session.config.realign_on_showhide as usize]
"enabled"
} else {
"disabled"
}
) )
.into(), .into(),
"".into(), "".into(),
@@ -379,11 +391,7 @@ fn run_system(action: &SystemAction, app: &mut AppState) {
ToastTopic::System, ToastTopic::System,
format!( format!(
"Notifications are {}.", "Notifications are {}.",
if app.session.config.notifications_enabled { ENABLED_DISABLED[app.session.config.notifications_enabled as usize]
"enabled"
} else {
"disabled"
}
) )
.into(), .into(),
"".into(), "".into(),
@@ -397,11 +405,7 @@ fn run_system(action: &SystemAction, app: &mut AppState) {
ToastTopic::System, ToastTopic::System,
format!( format!(
"Notification sounds are {}.", "Notification sounds are {}.",
if app.session.config.notifications_sound_enabled { ENABLED_DISABLED[app.session.config.notifications_sound_enabled as usize]
"enabled"
} else {
"disabled"
}
) )
.into(), .into(),
"".into(), "".into(),
@@ -409,7 +413,7 @@ fn run_system(action: &SystemAction, app: &mut AppState) {
.submit(app); .submit(app);
} }
SystemAction::PersistConfig => { SystemAction::PersistConfig => {
if let Err(e) = save_settings(app) { if let Err(e) = save_settings(&app.session.config) {
log::error!("Failed to save config: {:?}", e); log::error!("Failed to save config: {:?}", e);
} }
} }
@@ -585,6 +589,20 @@ fn run_overlay(overlay: &OverlaySelector, action: &OverlayAction, app: &mut AppS
o.show_hide = o.want_visible; o.show_hide = o.want_visible;
o.reset(app, false); o.reset(app, false);
} }
let mut state_dirty = false;
if !o.want_visible {
state_dirty |= app.session.config.show_screens.arc_rm(o.name.as_ref());
} else if o.want_visible {
state_dirty |= app.session.config.show_screens.arc_ins(o.name.clone());
}
if state_dirty {
match save_state(&app.session.config) {
Ok(_) => log::debug!("Saved state"),
Err(e) => log::error!("Failed to save state: {:?}", e),
}
}
}), }),
)); ));
} }
@@ -698,53 +716,3 @@ const THUMP_AUDIO_WAV: &[u8] = include_bytes!("../../res/380885.wav");
fn audio_thump(app: &mut AppState) { fn audio_thump(app: &mut AppState) {
app.audio.play(THUMP_AUDIO_WAV); app.audio.play(THUMP_AUDIO_WAV);
} }
#[derive(Deserialize, Serialize)]
pub struct AutoSettings {
#[serde(default = "def_watch_pos")]
pub watch_pos: [f32; 3],
#[serde(default = "def_watch_rot")]
pub watch_rot: [f32; 4],
#[serde(default = "def_left")]
pub watch_hand: LeftRight,
#[serde(default = "def_half")]
pub watch_view_angle_min: f32,
#[serde(default = "def_point7")]
pub watch_view_angle_max: f32,
#[serde(default = "def_true")]
pub notifications_enabled: bool,
#[serde(default = "def_true")]
pub notifications_sound_enabled: bool,
#[serde(default = "def_true")]
pub realign_on_showhide: bool,
}
fn get_config_path() -> PathBuf {
let mut path = config_io::get_conf_d_path();
path.push("zz-saved-config.json5");
path
}
pub fn save_settings(app: &mut AppState) -> anyhow::Result<()> {
let conf = AutoSettings {
watch_pos: app.session.config.watch_pos,
watch_rot: app.session.config.watch_rot,
watch_hand: app.session.config.watch_hand,
watch_view_angle_min: app.session.config.watch_view_angle_min,
watch_view_angle_max: app.session.config.watch_view_angle_max,
notifications_enabled: app.session.config.notifications_enabled,
notifications_sound_enabled: app.session.config.notifications_sound_enabled,
realign_on_showhide: app.session.config.realign_on_showhide,
};
let json = serde_json::to_string_pretty(&conf).unwrap(); // want panic
std::fs::write(get_config_path(), json)?;
Ok(())
}

View File

@@ -23,7 +23,7 @@ use wlx_capture::{
use { use {
crate::config_io, crate::config_io,
glam::Vec3, glam::Vec3,
std::{collections::HashMap, error::Error, f32::consts::PI, ops::Deref, path::PathBuf}, std::{error::Error, f32::consts::PI, ops::Deref, path::PathBuf},
wlx_capture::{ wlx_capture::{
pipewire::{pipewire_select_screen, PipewireCapture}, pipewire::{pipewire_select_screen, PipewireCapture},
wayland::{wayland_client::protocol::wl_output, WlxClient, WlxOutput}, wayland::{wayland_client::protocol::wl_output, WlxClient, WlxOutput},
@@ -42,7 +42,7 @@ use crate::{
input::{Haptics, InteractionHandler, PointerHit, PointerMode}, input::{Haptics, InteractionHandler, PointerHit, PointerMode},
overlay::{OverlayRenderer, OverlayState, SplitOverlayBackend}, overlay::{OverlayRenderer, OverlayState, SplitOverlayBackend},
}, },
config::def_pw_tokens, config::{def_pw_tokens, AStrMapExt, PwTokenMap},
graphics::{fourcc_to_vk, WlxCommandBuffer, WlxPipeline, WlxPipelineLegacy}, graphics::{fourcc_to_vk, WlxCommandBuffer, WlxPipeline, WlxPipelineLegacy},
hid::{MOUSE_LEFT, MOUSE_MIDDLE, MOUSE_RIGHT}, hid::{MOUSE_LEFT, MOUSE_MIDDLE, MOUSE_RIGHT},
state::{AppSession, AppState, ScreenMeta}, state::{AppSession, AppState, ScreenMeta},
@@ -505,7 +505,7 @@ pub fn create_screen_renderer_wl(
output: &WlxOutput, output: &WlxOutput,
has_wlr_dmabuf: bool, has_wlr_dmabuf: bool,
has_wlr_screencopy: bool, has_wlr_screencopy: bool,
pw_token_store: &mut HashMap<String, String>, pw_token_store: &mut PwTokenMap,
session: &AppSession, session: &AppSession,
) -> Option<ScreenRenderer> { ) -> Option<ScreenRenderer> {
let mut capture: Option<ScreenRenderer> = None; let mut capture: Option<ScreenRenderer> = None;
@@ -527,7 +527,7 @@ pub fn create_screen_renderer_wl(
let display_name = output.name.deref(); let display_name = output.name.deref();
// Find existing token by display // Find existing token by display
let token = pw_token_store.get(display_name).map(|s| s.as_str()); let token = pw_token_store.arc_get(display_name).map(|s| s.as_str());
if let Some(t) = token { if let Some(t) = token {
log::info!( log::info!(
@@ -542,10 +542,7 @@ pub fn create_screen_renderer_wl(
capture = Some(renderer); capture = Some(renderer);
if let Some(token) = restore_token { if let Some(token) = restore_token {
if pw_token_store if pw_token_store.arc_ins(display_name.into(), token.clone()) {
.insert(String::from(display_name), token.clone())
.is_none()
{
log::info!("Adding Pipewire token {}", token); log::info!("Adding Pipewire token {}", token);
} }
} }
@@ -607,11 +604,6 @@ fn create_screen_state(
OverlayState { OverlayState {
name: name.clone(), name: name.clone(),
show_hide: session
.config
.show_screens
.iter()
.any(|s| s.as_ref() == name.as_ref()),
grabbable: true, grabbable: true,
recenter: true, recenter: true,
interactable: true, interactable: true,
@@ -626,7 +618,7 @@ fn create_screen_state(
#[derive(Deserialize, Serialize, Default)] #[derive(Deserialize, Serialize, Default)]
pub struct TokenConf { pub struct TokenConf {
#[serde(default = "def_pw_tokens")] #[serde(default = "def_pw_tokens")]
pub pw_tokens: Vec<(String, String)>, pub pw_tokens: PwTokenMap,
} }
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
@@ -637,31 +629,18 @@ fn get_pw_token_path() -> PathBuf {
} }
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
pub fn save_pw_token_config(tokens: &HashMap<String, String>) -> Result<(), Box<dyn Error>> { pub fn save_pw_token_config(tokens: &PwTokenMap) -> Result<(), Box<dyn Error>> {
let mut conf = TokenConf::default(); let yaml = serde_yaml::to_string(tokens)?;
for (name, token) in tokens {
conf.pw_tokens.push((name.clone(), token.clone()));
}
let yaml = serde_yaml::to_string(&conf)?;
std::fs::write(get_pw_token_path(), yaml)?; std::fs::write(get_pw_token_path(), yaml)?;
Ok(()) Ok(())
} }
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
pub fn load_pw_token_config() -> Result<HashMap<String, String>, Box<dyn Error>> { pub fn load_pw_token_config() -> Result<PwTokenMap, Box<dyn Error>> {
let mut map: HashMap<String, String> = HashMap::new();
let yaml = std::fs::read_to_string(get_pw_token_path())?; let yaml = std::fs::read_to_string(get_pw_token_path())?;
let conf: TokenConf = serde_yaml::from_str(yaml.as_str())?; let conf: TokenConf = serde_yaml::from_str(yaml.as_str())?;
Ok(conf.pw_tokens)
for (name, token) in conf.pw_tokens {
map.insert(name, token);
}
Ok(map)
} }
pub(crate) struct ScreenCreateData { pub(crate) struct ScreenCreateData {
@@ -681,13 +660,15 @@ pub fn create_screens_wayland(
wl: &mut WlxClient, wl: &mut WlxClient,
app: &mut AppState, app: &mut AppState,
) -> anyhow::Result<ScreenCreateData> { ) -> anyhow::Result<ScreenCreateData> {
use crate::config::AStrMap;
let mut screens = vec![]; let mut screens = vec![];
// Load existing Pipewire tokens from file // Load existing Pipewire tokens from file
let mut pw_tokens: HashMap<String, String> = if let Ok(conf) = load_pw_token_config() { let mut pw_tokens: PwTokenMap = if let Ok(conf) = load_pw_token_config() {
conf conf
} else { } else {
HashMap::new() AStrMap::new()
}; };
let pw_tokens_copy = pw_tokens.clone(); let pw_tokens_copy = pw_tokens.clone();

View File

@@ -565,6 +565,17 @@ elements:
action: ToggleAutoRealign action: ToggleAutoRealign
highlight: AutoRealign highlight: AutoRealign
- type: Button
rect: [30, 555, 220, 30]
font_size: 12
fg_color: "#ffffff"
bg_color: "#401010"
text: "Grab+Scroll Slide"
click_down:
- type: System
action: ToggleAllowSliding
highlight: AllowSliding
####### Footer Section ####### ####### Footer Section #######
- type: Panel - type: Panel