Skip to content

Commit

Permalink
Cleanup CLI code
Browse files Browse the repository at this point in the history
  • Loading branch information
4JX committed Nov 17, 2024
1 parent 37d7eae commit 135702c
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 129 deletions.
201 changes: 99 additions & 102 deletions app/src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{convert::TryInto, path::PathBuf, process, str::FromStr};
use std::{convert::TryInto, path::PathBuf, str::FromStr};

use clap::{arg, command, Parser, Subcommand};
use error_stack::{Result, ResultExt};
Expand Down Expand Up @@ -110,25 +110,15 @@ fn parse_colors(arg: &str) -> std::result::Result<[u8; 12], String> {

pub enum CliOutput {
/// Start the UI
Gui { hide_window: bool, output: OutputType },
Gui { hide_window: bool, output_type: OutputType },

/// CLI arguments were passed
Cli(OutputType),
}

impl CliOutput {
fn maybe_gui(start_gui: bool, hide_window: bool, output: OutputType) -> Self {
if start_gui {
Self::Gui { hide_window, output }
} else {
Self::Cli(output)
}
}
}

pub enum GuiCommand {
/// Start the UI
Start { hide_window: bool, output: OutputType },
Start { hide_window: bool, output_type: OutputType },

/// Close the program as the CLI was invoked
Exit,
Expand All @@ -148,115 +138,122 @@ pub enum OutputType {
pub struct CliError;

pub fn try_cli() -> Result<GuiCommand, CliError> {
let output = parse_cli()?;
let output_type = parse_cli()?;

match output {
CliOutput::Gui { hide_window, output } => {
match output_type {
CliOutput::Gui { hide_window, output_type } => {
if hide_window {
println!("Window hiding is currently not supported. See https://github.com/4JX/L5P-Keyboard-RGB/issues/181");
}
Ok(GuiCommand::Start { hide_window, output })
Ok(GuiCommand::Start { hide_window, output_type })
}
CliOutput::Cli(output) => {
let manager_result = effects::EffectManager::new(effects::OperationMode::Cli);

let instance_not_unique = if let Err(err) = &manager_result {
&ManagerCreationError::InstanceAlreadyRunning == err.current_context()
} else {
false
};

// Don't interrupt other instances if trying to interact with the keyboard
if let OutputType::Profile(..) | OutputType::Custom(..) = output {
if instance_not_unique {
println!("Another instance of the program is already running, please close it before starting a new one.");
process::exit(0);
}
}
CliOutput::Cli(output_type) => handle_cli_output(output_type),
}
}

let mut effect_manager = manager_result.unwrap();
fn handle_cli_output(output_type: OutputType) -> Result<GuiCommand, CliError> {
let manager_result = effects::EffectManager::new(effects::OperationMode::Cli);
let instance_not_unique = manager_result
.as_ref()
.err()
.map_or(false, |err| &ManagerCreationError::InstanceAlreadyRunning == err.current_context());

match output {
OutputType::Profile(profile) => {
effect_manager.set_profile(profile);
effect_manager.shutdown();
Ok(GuiCommand::Exit)
}
OutputType::Custom(effect) => {
effect_manager.custom_effect(effect);
effect_manager.shutdown();
Ok(GuiCommand::Exit)
}
OutputType::Exit => Ok(GuiCommand::Exit),
OutputType::NoArgs => unreachable!("No arguments were provided but the app is in CLI mode"),
}
}
if matches!(output_type, OutputType::Profile(..) | OutputType::Custom(..)) && instance_not_unique {
println!("Another instance of the program is already running, please close it before starting a new one.");
return Ok(GuiCommand::Exit);
}

let mut effect_manager = manager_result.change_context(CliError)?;

let command_result = match output_type {
OutputType::Profile(profile) => {
effect_manager.set_profile(profile);
Ok(GuiCommand::Exit)
}
OutputType::Custom(effect) => {
effect_manager.custom_effect(effect);
Ok(GuiCommand::Exit)
}
OutputType::Exit => Ok(GuiCommand::Exit),
OutputType::NoArgs => unreachable!("No arguments were provided but the app is in CLI mode"),
};

effect_manager.shutdown();
command_result
}

fn parse_cli() -> Result<CliOutput, CliError> {
let cli = Cli::parse();

let Some(subcommand) = cli.command else {
let exec_name = std::env::current_exe().unwrap().file_name().unwrap().to_string_lossy().into_owned();
println!("No subcommands found, starting in GUI mode. To view the possible subcommands type \"{exec_name} --help\".",);
return Ok(CliOutput::maybe_gui(true, cli.hide_window, OutputType::NoArgs));
};

match subcommand {
Commands::Set {
effect,
colors,
brightness,
speed,
direction,
save,
} => {
let direction = direction.unwrap_or_default();

let rgb_array: [u8; 12] = if effect.takes_color_array() {
colors.unwrap_or_else(|| {
println!("This effect requires specifying the colors to use.");
process::exit(0);
})
} else {
[0; 12]
};

let profile = Profile {
name: "Profile".to_string(),
rgb_zones: profile::arr_to_zones(rgb_array),
if let Some(subcommand) = cli.command {
match subcommand {
Commands::Set {
effect,
direction,
speed,
colors,
brightness,
};
speed,
direction,
save,
} => {
let direction = direction.unwrap_or_default();
let rgb_array = if effect.takes_color_array() {
colors.unwrap_or_else(|| {
println!("This effect requires specifying the colors to use.");
std::process::exit(0);
})
} else {
[0; 12]
};

let profile = Profile {
name: "Profile".to_string(),
rgb_zones: profile::arr_to_zones(rgb_array),
effect,
direction,
speed,
brightness,
};

if let Some(filename) = save {
profile.save_profile(&filename).expect("Failed to save.");
}

if let Some(filename) = save {
profile.save_profile(&filename).expect("Failed to save.");
return Ok(CliOutput::Gui {
hide_window: cli.hide_window,
output_type: OutputType::Profile(profile),
});
}

Ok(CliOutput::maybe_gui(cli.gui, cli.hide_window, OutputType::Profile(profile)))
}
Commands::List => {
println!("List of available effects:");
for (i, effect) in Effects::iter().enumerate() {
println!("{}. {effect}", i + 1);
Commands::List => {
println!("List of available effects:");
for (i, effect) in Effects::iter().enumerate() {
println!("{}. {effect}", i + 1);
}
return Ok(CliOutput::Cli(OutputType::Exit));
}

Ok(CliOutput::maybe_gui(false, cli.hide_window, OutputType::Exit))
}

Commands::LoadProfile { path } => {
let profile = Profile::load_profile(&path).change_context(CliError)?;

Ok(CliOutput::maybe_gui(cli.gui, cli.hide_window, OutputType::Profile(profile)))
}

Commands::CustomEffect { path } => {
let effect = CustomEffect::from_file(&path).change_context(CliError)?;
Commands::LoadProfile { path } => {
let profile = Profile::load_profile(&path).change_context(CliError)?;
return Ok(CliOutput::Gui {
hide_window: cli.hide_window,
output_type: OutputType::Profile(profile),
});
}

Ok(CliOutput::maybe_gui(cli.gui, cli.hide_window, OutputType::Custom(effect)))
Commands::CustomEffect { path } => {
let effect = CustomEffect::from_file(&path).change_context(CliError)?;
return Ok(CliOutput::Gui {
hide_window: cli.hide_window,
output_type: OutputType::Custom(effect),
});
}
}
}

// If no subcommands were found, start in GUI mode
let exec_name = std::env::current_exe().unwrap().file_name().unwrap().to_string_lossy().into_owned();
println!("No subcommands found, starting in GUI mode. To view the possible subcommands type \"{exec_name} --help\".");
Ok(CliOutput::Gui {
hide_window: cli.hide_window,
output_type: OutputType::NoArgs,
})
}
50 changes: 23 additions & 27 deletions app/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,32 +22,32 @@ use color_eyre::{eyre::eyre, Result};
use eframe::{egui::IconData, epaint::Vec2};
use gui::App;

const APP_ICON: &'static [u8; 14987] = include_bytes!("../res/trayIcon.ico");
const APP_ICON: &[u8; 14987] = include_bytes!("../res/trayIcon.ico");
const WINDOW_SIZE: Vec2 = Vec2::new(500., 400.);

fn main() {
#[cfg(target_os = "windows")]
{
setup_panic().unwrap();

// This just enables output if the program is already being ran from the CLI
console::attach();
let res = init();
console::free();

if res.is_err() {
std::process::exit(2);
}
run_windows();
}

#[cfg(target_os = "linux")]
{
color_eyre::install().unwrap();

init().unwrap();
}
}

#[cfg(target_os = "windows")]
fn run_windows() {
console::attach();
if init().is_err() {
std::process::exit(2);
}
console::free();
}

#[cfg(target_os = "windows")]
fn setup_panic() -> Result<()> {
// A somewhat unwrapped version of color_eyre::install() to add a "wait for enter" after printing the text
Expand All @@ -61,14 +61,12 @@ fn setup_panic() -> Result<()> {

std::panic::set_hook(Box::new(move |panic_info| {
if !console::alloc() {
// No point trying to print without a console...
return;
return; // No console to print to
}

eprintln!("{}", panic_hook.panic_report(panic_info));
println!("Press Enter to continue...");
let _ = std::io::stdin().read_line(&mut String::new());

std::process::exit(1);
}));

Expand All @@ -79,9 +77,8 @@ fn init() -> Result<()> {
let cli_output = cli::try_cli().map_err(|err| eyre!("{:?}", err))?;

match cli_output {
GuiCommand::Start { hide_window, output } => {
start_ui(output, hide_window);

GuiCommand::Start { hide_window, output_type } => {
start_ui(output_type, hide_window);
Ok(())
}
GuiCommand::Exit => Ok(()),
Expand All @@ -107,18 +104,16 @@ fn start_ui(output_type: OutputType, hide_window: bool) {
// Since egui uses winit under the hood and doesn't use gtk on Linux, and we need gtk for
// the tray icon to show up, we need to spawn a thread
// where we initialize gtk and create the tray_icon
{
std::thread::spawn(move || {
#[cfg(target_os = "linux")]
gtk::init().unwrap();
std::thread::spawn(move || {
#[cfg(target_os = "linux")]
gtk::init().unwrap();

let tray_icon = tray::build_tray(true);
has_tray_c.store(tray_icon.is_some(), Ordering::SeqCst);
let tray_icon = tray::build_tray(true);
has_tray_c.store(tray_icon.is_some(), Ordering::SeqCst);

#[cfg(target_os = "linux")]
gtk::main();
});
}
#[cfg(target_os = "linux")]
gtk::main();
});

let app = App::new(output_type, has_tray.clone(), visible.clone());

Expand All @@ -128,6 +123,7 @@ fn start_ui(output_type: OutputType, hide_window: bool) {
#[must_use]
fn load_icon_data(image_data: &[u8]) -> IconData {
let image = image::load_from_memory(image_data).unwrap();

let image_buffer = image.to_rgba8();
let pixels = image_buffer.into_flat_samples().samples;

Expand Down

0 comments on commit 135702c

Please sign in to comment.