diff --git a/.gitignore b/.gitignore index 11de2f0..7b4f656 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ /target **/*.rs.bk +*.glade~ + Cargo.lock \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..d1aff5a --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,82 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Debug executable 'service'", + "cargo": { + "args": [ + "build", + "--bin=service", + "--package=macroboard" + ], + "filter": { + "name": "service", + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in executable 'service'", + "cargo": { + "args": [ + "test", + "--no-run", + "--bin=service", + "--package=macroboard" + ], + "filter": { + "name": "service", + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug executable 'gui'", + "cargo": { + "args": [ + "build", + "--bin=gui", + "--package=macroboard" + ], + "filter": { + "name": "gui", + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in executable 'gui'", + "cargo": { + "args": [ + "test", + "--no-run", + "--bin=gui", + "--package=macroboard" + ], + "filter": { + "name": "gui", + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..ef5d6f8 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,19 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "Run Gui", + "type": "shell", + "command": "cargo run --bin gui", + "problemMatcher": [] + }, + { + "label": "Run Service", + "type": "shell", + "command": "cargo run --bin service", + "problemMatcher": [] + } + ] +} \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index beb018a..438cb1f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,8 +4,17 @@ version = "0.1.0" authors = ["hodasemi "] edition = "2018" +[[bin]] +name = "service" +path = "src/service.rs" + +[[bin]] +name = "gui" +path = "src/gui.rs" + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] serial = "*" +gtk = { version = "*", features = ["v3_24"] } utilities = { git = "http://dimov.cloud:80/hodasemi/Context.git" } \ No newline at end of file diff --git a/macro_ui.glade b/macro_ui.glade new file mode 100644 index 0000000..227b1b0 --- /dev/null +++ b/macro_ui.glade @@ -0,0 +1,270 @@ + + + + + + False + center-always + + + + + + True + False + 10 + 10 + 10 + 5 + True + True + 5 + 5 + + + True + False + 10 + False + False + Button 1 + + + 0 + 0 + + + + + True + False + 10 + False + False + Button 2 + + + 0 + 1 + + + + + True + False + 10 + False + False + Button 3 + + + 0 + 2 + + + + + True + False + 10 + False + False + Button 4 + + + 0 + 3 + + + + + True + False + 10 + False + False + Button 5 + + + 0 + 4 + + + + + True + False + 10 + False + False + Button 6 + + + 0 + 5 + + + + + True + False + 10 + False + False + Button 7 + + + 0 + 6 + + + + + True + False + 10 + False + False + Button 8 + + + 0 + 7 + + + + + True + True + False + 0.5 + False + Command not set + + + 1 + 0 + + + + + True + True + False + 0.5 + False + Command not set + + + 1 + 1 + + + + + True + True + False + 0.5 + False + Command not set + + + 1 + 2 + + + + + True + True + False + 0.5 + False + Command not set + + + 1 + 3 + + + + + True + True + False + 0.5 + False + Command not set + + + 1 + 4 + + + + + True + True + False + 0.5 + False + Command not set + + + 1 + 5 + + + + + True + True + False + 0.5 + False + Command not set + + + 1 + 6 + + + + + True + True + False + 0.5 + False + Command not set + + + 1 + 7 + + + + + Save + True + True + True + True + 20 + 20 + 5 + 5 + + + 1 + 8 + + + + + + + + + diff --git a/src/gui.rs b/src/gui.rs new file mode 100644 index 0000000..899f4e5 --- /dev/null +++ b/src/gui.rs @@ -0,0 +1,113 @@ +use gtk; +use gtk::prelude::*; +use gtk::{Builder, Window}; + +use utilities::prelude::*; + +use std::fs; +use std::path::Path; + +mod shared; + +use shared::config::{Config, COMMAND_COUNT}; + +fn main() -> VerboseResult<()> { + gtk::init().map_err(|_| "failed to initialize GTK")?; + + let builder = Builder::new_from_string(include_str!("../macro_ui.glade")); + + setup_gui(builder)?; + + gtk::main(); + + Ok(()) +} + +fn setup_gui(builder: Builder) -> VerboseResult<()> { + let config = load_config()?; + + apply_config(&builder, &config)?; + setup_save(&builder)?; + + let window: Window = builder + .get_object("MainWindow") + .ok_or("failed getting main window")?; + + window.show_all(); + + // close event + window.connect_delete_event(|_, _| { + gtk::main_quit(); + Inhibit(false) + }); + + Ok(()) +} + +fn load_config() -> VerboseResult { + let home_dir = std::env::var("HOME").map_err(|_| "failed getting home directory")?; + + if !Path::new(&format!("{}/.config", home_dir)).exists() { + fs::create_dir(format!("{}/.config", home_dir))?; + } + + if !Path::new(&format!("{}/.config/MacroBoard", home_dir)).exists() { + fs::create_dir(format!("{}/.config/MacroBoard", home_dir))?; + } + + Config::open(&format!("{}/.config/MacroBoard/commands.cfg", home_dir)) +} + +fn setup_save(builder: &Builder) -> VerboseResult<()> { + let mut text_fields = Vec::with_capacity(COMMAND_COUNT); + + for i in 0..COMMAND_COUNT { + let command_text_field: gtk::Entry = builder + .get_object(&format!("ButtonCommand{}", i + 1)) + .ok_or(format!("Failed getting Entry for command {}", i + 1))?; + + text_fields.push(command_text_field); + } + + let save_button: gtk::Button = builder + .get_object("SaveButton") + .ok_or("Failed getting save button")?; + + save_button.connect_clicked(move |_| { + let mut config = Config::default(); + + for i in 0..COMMAND_COUNT { + let command = text_fields[i].get_buffer().get_text(); + + config.commands[i] = Some(command); + } + + let home_dir = match std::env::var("HOME") { + Ok(var) => var, + Err(_) => { + println!("failed getting home directory"); + return; + } + }; + + if let Err(err) = config.save(&format!("{}/.config/MacroBoard/commands.cfg", home_dir)) { + println!("{}", err); + } + }); + + Ok(()) +} + +fn apply_config(builder: &Builder, config: &Config) -> VerboseResult<()> { + for i in 0..COMMAND_COUNT { + if let Some(command) = &config.commands[i] { + let command_text_field: gtk::Entry = builder + .get_object(&format!("ButtonCommand{}", i + 1)) + .ok_or(format!("Failed getting Entry for command {}", i + 1))?; + + command_text_field.get_buffer().set_text(command); + } + } + + Ok(()) +} diff --git a/src/main.rs b/src/service.rs similarity index 100% rename from src/main.rs rename to src/service.rs diff --git a/src/shared/config.rs b/src/shared/config.rs new file mode 100644 index 0000000..92d32e0 --- /dev/null +++ b/src/shared/config.rs @@ -0,0 +1,68 @@ +use utilities::prelude::*; + +pub const COMMAND_COUNT: usize = 8; + +const COMMANDS_TAG: &str = "Commands"; +const COMMANDS: [&str; COMMAND_COUNT] = [ + "command_1", + "command_2", + "command_3", + "command_4", + "command_5", + "command_6", + "command_7", + "command_8", +]; + +pub struct Config { + pub commands: [Option; COMMAND_COUNT], +} + +impl Config { + pub fn open(path: &str) -> VerboseResult { + let mut config = Config::default(); + + let config_loader = match ConfigHandler::read_config(path) { + Ok(config_loader) => config_loader, + Err(_) => return Ok(config), + }; + + if let Some(commands) = config_loader.get(COMMANDS_TAG) { + for i in 0..COMMAND_COUNT { + if let Some(command) = commands.get(COMMANDS[i]) { + config.commands[i] = Some(command.to_value()?); + } + } + } + + Ok(config) + } + + pub fn save(&self, path: &str) -> VerboseResult<()> { + let fields = (0..COMMAND_COUNT) + .collect::>() + .iter() + .map(|i| { + ( + COMMANDS[*i], + match &self.commands[*i] { + Some(command) => Value::from_value(command), + None => Value::empty(), + }, + ) + }) + .collect(); + + let data = &[(COMMANDS_TAG, fields)]; + + ConfigHandler::write_config(path, data) + } +} + +impl Default for Config { + fn default() -> Config { + Config { + commands: Default::default(), + } + } +} diff --git a/src/shared/mod.rs b/src/shared/mod.rs new file mode 100644 index 0000000..ef68c36 --- /dev/null +++ b/src/shared/mod.rs @@ -0,0 +1 @@ +pub mod config;