Skip to content

Commit

Permalink
Allow setting config path via env variable
Browse files Browse the repository at this point in the history
  • Loading branch information
4JX committed Apr 12, 2024
1 parent c7ea0c0 commit 5322239
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 40 deletions.
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,9 @@ sudo udevadm control --reload-rules && sudo udevadm trigger

### With GUI

Execute the file by double-clicking on it or running it from a console _without_ arguments.
Execute the file by double-clicking on it. You may pass extra startup options via the CLI by also specifying the `--gui` flag.

Configuration for this mode is saved by default on the folder the program was executed in a file called `settings.json`, you can override this location by setting the `LEGION_KEYBOARD_CONFIG` environment variable.

### Via the command line

Expand Down Expand Up @@ -166,8 +168,8 @@ legion-kb-rgb set -e SmoothWave -s 4 -b 2 -d Left

This program has been tested to work on:

- Legion 5 (Pro) 2020, 2021, 2022, 2023
- Ideapad Gaming 3 2021, 2022, 2023
- Legion 5 (Pro) 2020, 2021, 2022, 2023, 2024
- Ideapad Gaming 3 2021, 2022, 2023, 2024

### "How about X model"

Expand Down
51 changes: 23 additions & 28 deletions app/src/gui/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{mem, path::Path, process, thread, time::Duration};
use std::{mem, process, thread, time::Duration};

use crossbeam_channel::{Receiver, Sender};
use device_query::{DeviceQuery, Keycode};
Expand All @@ -19,8 +19,6 @@ use crate::{
effects::{self, custom_effect::CustomEffect, EffectManager, ManagerCreationError},
enums::Effects,
persist::Settings,
profile::Profile,
util::StorageTrait,
};

use self::{effect_options::EffectOptions, menu_bar::MenuBarState, profile_list::ProfileList, style::Theme};
Expand All @@ -32,6 +30,8 @@ mod profile_list;
mod style;

pub struct App {
settings: Settings,

instance_not_unique: bool,
hide_window: bool,
window_open_rx: Option<crossbeam_channel::Receiver<GuiMessage>>,
Expand All @@ -40,7 +40,6 @@ pub struct App {
tray: Option<TrayItem>,

manager: Option<EffectManager>,
profile: Profile,
profile_changed: bool,
custom_effect: CustomEffectState,

Expand Down Expand Up @@ -77,33 +76,35 @@ impl App {

let manager = manager_result.ok();

let settings: Settings = Settings::load_or_default(Path::new("./settings.json"));
let settings: Settings = Settings::load();
let profiles = settings.profiles.clone();

// Default app state
let mut app = Self {
settings,

instance_not_unique,
hide_window,
window_open_rx: Some(rx),
tray: None,

manager,
profile: Profile::default(),
// Default to true for an instant update on launch
profile_changed: true,
custom_effect: CustomEffectState::default(),

menu_bar: MenuBarState::new(tx),
profile_list: ProfileList::new(settings.profiles),
profile_list: ProfileList::new(profiles),
effect_options: EffectOptions::default(),
global_rgb: [0; 3],
theme: Theme::default(),
};

// Update the state according to the option chosen by the user
match output {
OutputType::Profile(profile) => app.profile = profile,
OutputType::Profile(profile) => app.settings.current_profile = profile,
OutputType::Custom(effect) => app.custom_effect = CustomEffectState::Queued(effect),
OutputType::NoArgs => app.profile = settings.ui_state,
OutputType::NoArgs => {}
OutputType::Exit => unreachable!("Exiting the app supersedes starting the GUI"),
}

Expand Down Expand Up @@ -205,7 +206,7 @@ impl eframe::App for App {
frame.set_visible(!self.hide_window);

TopBottomPanel::top("top-panel").show(ctx, |ui| {
self.menu_bar.show(ctx, ui, &mut self.profile, &mut self.custom_effect, &mut self.profile_changed);
self.menu_bar.show(ctx, ui, &mut self.settings.current_profile, &mut self.custom_effect, &mut self.profile_changed);
});

CentralPanel::default()
Expand All @@ -216,23 +217,23 @@ impl eframe::App for App {
ui.with_layout(Layout::left_to_right(Align::Center).with_cross_justify(true), |ui| {
ui.vertical(|ui| {
let res = ui.scope(|ui| {
ui.set_enabled(self.profile.effect.takes_color_array() && matches!(self.custom_effect, CustomEffectState::None));
ui.set_enabled(self.settings.current_profile.effect.takes_color_array() && matches!(self.custom_effect, CustomEffectState::None));

ui.style_mut().spacing.item_spacing.y = self.theme.spacing.medium;

let response = ui.horizontal(|ui| {
ui.style_mut().spacing.interact_size = Vec2::splat(60.0);

for i in 0..4 {
self.profile_changed |= ui.color_edit_button_srgb(&mut self.profile.rgb_zones[i].rgb).changed();
self.profile_changed |= ui.color_edit_button_srgb(&mut self.settings.current_profile.rgb_zones[i].rgb).changed();
}
});

ui.style_mut().spacing.interact_size = Vec2::new(response.response.rect.width(), 30.0);

if ui.color_edit_button_srgb(&mut self.global_rgb).changed() {
for i in 0..4 {
self.profile.rgb_zones[i].rgb = self.global_rgb;
self.settings.current_profile.rgb_zones[i].rgb = self.global_rgb;
}

self.profile_changed = true;
Expand All @@ -245,11 +246,11 @@ impl eframe::App for App {

ui.scope(|ui| {
ui.set_enabled(matches!(self.custom_effect, CustomEffectState::None));
self.effect_options.show(ui, &mut self.profile, &mut self.profile_changed, &self.theme.spacing);
self.effect_options.show(ui, &mut self.settings.current_profile, &mut self.profile_changed, &self.theme.spacing);
});

self.profile_list
.show(ctx, ui, &mut self.profile, &self.theme.spacing, &mut self.profile_changed, &mut self.custom_effect);
.show(ctx, ui, &mut self.settings.current_profile, &self.theme.spacing, &mut self.profile_changed, &mut self.custom_effect);
});

ui.vertical_centered_justified(|ui| {
Expand All @@ -270,7 +271,7 @@ impl eframe::App for App {
ui.with_layout(Layout::top_down_justified(Align::Min), |ui| {
for val in Effects::iter() {
let text: &'static str = val.into();
if ui.selectable_value(&mut self.profile.effect, val, text).clicked() {
if ui.selectable_value(&mut self.settings.current_profile.effect, val, text).clicked() {
self.profile_changed = true;
self.custom_effect = CustomEffectState::None;
};
Expand All @@ -285,7 +286,7 @@ impl eframe::App for App {
if self.profile_changed {
if let Some(manager) = self.manager.as_mut() {
if matches!(self.custom_effect, CustomEffectState::None) {
manager.set_profile(self.profile.clone());
manager.set_profile(self.settings.current_profile.clone());
} else if matches!(self.custom_effect, CustomEffectState::Queued(_)) {
let state = mem::replace(&mut self.custom_effect, CustomEffectState::Playing);
if let CustomEffectState::Queued(effect) = state {
Expand All @@ -308,15 +309,9 @@ impl eframe::App for App {
}

fn on_exit(&mut self, _gl: Option<&eframe::glow::Context>) {
let path = Path::new("./settings.json");

let mut settings = Settings::load_or_default(path);

settings.profiles = std::mem::take(&mut self.profile_list.profiles);

settings.ui_state = std::mem::take(&mut self.profile);
self.settings.profiles = std::mem::take(&mut self.profile_list.profiles);

settings.save(path).unwrap();
self.settings.save();
}
}

Expand Down Expand Up @@ -354,13 +349,13 @@ impl App {
fn cycle_profiles(&mut self) {
let len = self.profile_list.profiles.len();

let current_profile_name = &self.profile.name;
let current_profile_name = &self.settings.current_profile.name;

if let Some((i, _)) = self.profile_list.profiles.iter().enumerate().find(|(_, profile)| &profile.name == current_profile_name) {
if i == len - 1 && len > 0 {
self.profile = self.profile_list.profiles[0].clone();
self.settings.current_profile = self.profile_list.profiles[0].clone();
} else {
self.profile = self.profile_list.profiles[i + 1].clone();
self.settings.current_profile = self.profile_list.profiles[i + 1].clone();
}

self.profile_changed = true;
Expand Down
41 changes: 32 additions & 9 deletions app/src/persist.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,49 @@
use std::{fs, path::Path};
use std::{
env,
fs::{self, File},
io::Write,
path::PathBuf,
};

use crate::profile::Profile;
use serde::{Deserialize, Serialize};

use crate::{profile::Profile, util::StorageTrait};

#[derive(Debug, Deserialize, Serialize, Default)]
pub struct Settings {
pub profiles: Vec<Profile>,
pub ui_state: Profile,
// Up to 0.19.5
#[serde(alias = "ui_state")]
pub current_profile: Profile,
}

impl Settings {
/// Load the settings from the specified path or generate default ones if an error occurs
pub fn load_or_default(path: &Path) -> Self {
/// Load the settings from the configured path or generate default ones if an error occurs
pub fn load() -> Self {
let mut persist: Self = Self::default();

if let Ok(string) = fs::read_to_string(path) {
if let Ok(string) = fs::read_to_string(Self::get_location()) {
persist = serde_json::from_str(&string).unwrap_or_default();
}

persist
}
}

impl<'a> StorageTrait<'a> for Settings {}
/// Save the settings to the configured path
pub fn save(&mut self) {
let mut file = File::create(Self::get_location()).unwrap();

let stringified_json = serde_json::to_string(&self).unwrap();

file.write_all(stringified_json.as_bytes()).unwrap();
}

fn get_location() -> PathBuf {
let default = PathBuf::from("./settings.json");

if let Ok(maybe_path) = env::var("LEGION_KEYBOARD_CONFIG") {
PathBuf::try_from(maybe_path).unwrap_or(default)
} else {
default
}
}
}

0 comments on commit 5322239

Please sign in to comment.