diff --git a/Linux/Cargo.lock b/Linux/Cargo.lock index 91f63fc..949937d 100644 --- a/Linux/Cargo.lock +++ b/Linux/Cargo.lock @@ -10,6 +10,7 @@ dependencies = [ "log", "min-rs", "platform-dirs", + "regex", "relm4", "relm4-icons", "rust-i18n", diff --git a/Linux/Cargo.toml b/Linux/Cargo.toml index aedb7aa..7c11c1e 100644 --- a/Linux/Cargo.toml +++ b/Linux/Cargo.toml @@ -8,6 +8,7 @@ build = "build.rs" env_logger = "0.11.3" log = "0.4.21" platform-dirs = "0.3.0" +regex = "1.10.5" relm4 = "0.8.1" relm4-icons = "0.8.3" rust-i18n = "3.0.1" diff --git a/Linux/locales/actions/pipewire.yaml b/Linux/locales/actions/pipewire.yaml new file mode 100644 index 0000000..b27e1d5 --- /dev/null +++ b/Linux/locales/actions/pipewire.yaml @@ -0,0 +1,7 @@ +_version: 2 + +actions: + pipewire: + set_volume: + name: + en: Set volume \ No newline at end of file diff --git a/Linux/locales/devices/emulator.yaml b/Linux/locales/devices/emulator.yaml new file mode 100644 index 0000000..34a0e1f --- /dev/null +++ b/Linux/locales/devices/emulator.yaml @@ -0,0 +1,6 @@ +_version: 2 + +devices: + emulator: + name: + en: Emulator \ No newline at end of file diff --git a/Linux/locales/devices/serial_min.yaml b/Linux/locales/devices/serial_min.yaml new file mode 100644 index 0000000..a1d1455 --- /dev/null +++ b/Linux/locales/devices/serial_min.yaml @@ -0,0 +1,6 @@ +_version: 2 + +devices: + serial_min: + name: + en: Serial device using MIN protocol \ No newline at end of file diff --git a/Linux/src/actions/mod.rs b/Linux/src/actions/mod.rs index f172a7a..85cdecb 100644 --- a/Linux/src/actions/mod.rs +++ b/Linux/src/actions/mod.rs @@ -1,8 +1,32 @@ -pub mod registry; pub mod pipewire; +use crate::registry::MkRegistry; +use crate::registry::RegistryItem; +use crate::util::unique_id::UniqueId; -pub fn register() + +pub struct MkAction { - pipewire::register(); + pub unique_id: UniqueId +} + + +impl RegistryItem for MkAction +{ + fn unique_id(&self) -> UniqueId + { + self.unique_id.clone() + } + + fn name(&self) -> String + { + t!(format!("actions.{}.name", self.unique_id.as_str()).as_str()).to_string() + } +} + + + +pub fn register(registry: &mut MkRegistry) +{ + pipewire::register(registry); } \ No newline at end of file diff --git a/Linux/src/actions/pipewire/mod.rs b/Linux/src/actions/pipewire/mod.rs index 2ba9245..b59729e 100644 --- a/Linux/src/actions/pipewire/mod.rs +++ b/Linux/src/actions/pipewire/mod.rs @@ -1,10 +1,14 @@ -use super::registry::{MkAction, register_action}; - +use crate::registry::MkRegistry; +use crate::util::unique_id::UniqueId; +use super::MkAction; pub mod set_volume; -pub fn register() +pub fn register(registry: &mut MkRegistry) { - register_action(MkAction::new("Set volume")); + registry.register(MkAction + { + unique_id: UniqueId::new("pipewire.set_volume") + }); } \ No newline at end of file diff --git a/Linux/src/actions/registry.rs b/Linux/src/actions/registry.rs deleted file mode 100644 index 9320575..0000000 --- a/Linux/src/actions/registry.rs +++ /dev/null @@ -1,26 +0,0 @@ -use log::info; - - -pub struct MkAction -{ - // TODO prepare for i18n by making this a translation key? - name: &'static str -} - - -impl MkAction -{ - pub fn new(name: &'static str) -> Self - { - Self - { - name - } - } -} - - -pub fn register_action(action: MkAction) -{ - info!("Registered action: {}", action.name); -} \ No newline at end of file diff --git a/Linux/src/config.rs b/Linux/src/config.rs index f314c2b..9fc3edc 100644 --- a/Linux/src/config.rs +++ b/Linux/src/config.rs @@ -2,10 +2,11 @@ use std::path::{Path, PathBuf}; use std::io::{Error, Read, Write}; use platform_dirs::AppDirs; - +#[derive(Debug)] pub struct Config { - root: PathBuf + root: PathBuf, + pub device_id: Option } @@ -17,7 +18,8 @@ impl Config Self { - root: appdirs.data_dir + root: appdirs.data_dir, + device_id: None } } diff --git a/Linux/src/devices/emulator/mod.rs b/Linux/src/devices/emulator/mod.rs index a051db6..0f39949 100644 --- a/Linux/src/devices/emulator/mod.rs +++ b/Linux/src/devices/emulator/mod.rs @@ -1,17 +1,18 @@ -//use emulatorwindow::EmulatorWindow; -//use relm4::prelude::*; -//use relm4::gtk::prelude::GtkApplicationExt; - - -use super::registry::{MkDevice, register_device}; +use crate::registry::MkRegistry; +use crate::util::unique_id::UniqueId; +use super::MkDevice; pub mod emulatorwindow; -pub fn register() + + +pub fn register(registry: &mut MkRegistry) { - register_device(MkDevice::new("Emulator")); + registry.register(MkDevice { + unique_id: UniqueId::new("emulator") + }); } /* diff --git a/Linux/src/devices/mod.rs b/Linux/src/devices/mod.rs index c457b1c..0cd95d7 100644 --- a/Linux/src/devices/mod.rs +++ b/Linux/src/devices/mod.rs @@ -1,10 +1,35 @@ -pub mod registry; pub mod emulator; pub mod serial_min; -pub fn register() +use crate::registry::MkRegistry; +use crate::registry::RegistryItem; +use crate::util::unique_id::UniqueId; + + +pub struct MkDevice { - emulator::register(); - serial_min::register(); + pub unique_id: UniqueId +} + + +impl RegistryItem for MkDevice +{ + fn unique_id(&self) -> UniqueId + { + self.unique_id.clone() + } + + fn name(&self) -> String + { + t!(format!("devices.{}.name", self.unique_id.as_str()).as_str()).to_string() + } +} + + + +pub fn register(registry: &mut MkRegistry) +{ + emulator::register(registry); + serial_min::register(registry); } \ No newline at end of file diff --git a/Linux/src/devices/registry.rs b/Linux/src/devices/registry.rs deleted file mode 100644 index db5535a..0000000 --- a/Linux/src/devices/registry.rs +++ /dev/null @@ -1,26 +0,0 @@ -use log::info; - - -pub struct MkDevice -{ - // TODO prepare for i18n by making this a translation key? - name: &'static str -} - - -impl MkDevice -{ - pub fn new(name: &'static str) -> Self - { - Self - { - name - } - } -} - - -pub fn register_device(action: MkDevice) -{ - info!("Registered device: {}", action.name); -} \ No newline at end of file diff --git a/Linux/src/devices/serial_min/mod.rs b/Linux/src/devices/serial_min/mod.rs index 70a1b0a..61f3eb2 100644 --- a/Linux/src/devices/serial_min/mod.rs +++ b/Linux/src/devices/serial_min/mod.rs @@ -1,7 +1,11 @@ -use super::registry::{MkDevice, register_device}; +use crate::registry::MkRegistry; +use crate::util::unique_id::UniqueId; +use super::MkDevice; -pub fn register() +pub fn register(registry: &mut MkRegistry) { - register_device(MkDevice::new("Serial device using MIN protocol")); + registry.register(MkDevice { + unique_id: UniqueId::new("serial_min") + }); } \ No newline at end of file diff --git a/Linux/src/main.rs b/Linux/src/main.rs index 60342ed..82339a6 100644 --- a/Linux/src/main.rs +++ b/Linux/src/main.rs @@ -1,4 +1,7 @@ +use std::rc::Rc; use env_logger::Env; +use mainwindow::MainWindowViewModel; +use orchestrator::Orchestrator; use relm4::prelude::*; #[macro_use] @@ -7,10 +10,13 @@ extern crate rust_i18n; i18n!("locales"); +pub mod util; +pub mod registry; pub mod devices; pub mod actions; pub mod config; +pub mod orchestrator; pub mod mainwindow; fn main() @@ -19,20 +25,13 @@ fn main() // .format_timestamp(None) .init(); - devices::register(); - actions::register(); - relm4_icons::initialize_icons(); - load_config(); + let orchestrator = Rc::new(Orchestrator::new()); let app = RelmApp::new("com.github.mvrens.massiveknob"); - app.run::(()); -} - - -fn load_config() -{ - //let config = config::Config::new(); - //config.get_reader(name) + app.run::(MainWindowViewModel + { + orchestrator: Rc::clone(&orchestrator) + }); } \ No newline at end of file diff --git a/Linux/src/mainwindow.rs b/Linux/src/mainwindow.rs index 47e6d9a..2d776d0 100644 --- a/Linux/src/mainwindow.rs +++ b/Linux/src/mainwindow.rs @@ -1,11 +1,31 @@ +use std::rc::Rc; use gtk::prelude::*; use relm4::prelude::*; +use crate::orchestrator::Orchestrator; + pub struct MainWindow { } +pub struct MainWindowViewModel +{ + pub orchestrator: Rc +} + + +impl std::fmt::Debug for MainWindowViewModel +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result + { + f.debug_struct("MainWindowViewModel") + // Skip orchestrator + .finish() + } +} + + #[derive(Debug)] pub enum MainWindowMsg { @@ -19,7 +39,7 @@ pub struct MainWindowWidgets impl SimpleComponent for MainWindow { - type Init = (); + type Init = MainWindowViewModel; type Input = MainWindowMsg; type Output = (); type Root = gtk::Window; diff --git a/Linux/src/orchestrator/mod.rs b/Linux/src/orchestrator/mod.rs new file mode 100644 index 0000000..e5deacc --- /dev/null +++ b/Linux/src/orchestrator/mod.rs @@ -0,0 +1,53 @@ +use crate::actions; +use crate::actions::MkAction; +use crate::config::Config; +use crate::devices; +use crate::devices::MkDevice; +use crate::registry::MkRegistry; +use crate::util::unique_id::UniqueId; + + +pub struct Orchestrator +{ + config: Config, + + device_registry: MkRegistry, + action_registry: MkRegistry +} + + +impl Orchestrator +{ + pub fn new() -> Self + { + let config = Config::new(); + //config.get_reader(name) + + let mut device_registry = MkRegistry::new(); + let mut action_registry = MkRegistry::new(); + + devices::register(&mut device_registry); + actions::register(&mut action_registry); + + Self + { + config, + device_registry, + action_registry + } + } + + + pub fn current_device(&self) -> Option<&MkDevice> + { + let Some(device_id) = &self.config.device_id else { return None }; + self.device_registry.by_id(UniqueId::new(device_id.as_str())) + } + + + pub fn set_current_device_id(&self, id: &str) + { + // TODO if changed, unload old device, activate new + todo!("Store in config"); + } +} \ No newline at end of file diff --git a/Linux/src/registry/mod.rs b/Linux/src/registry/mod.rs new file mode 100644 index 0000000..ad465ec --- /dev/null +++ b/Linux/src/registry/mod.rs @@ -0,0 +1,50 @@ +use std::collections::HashMap; +use log::info; + +use crate::util::unique_id::UniqueId; + + +pub trait RegistryItem +{ + /// The unique ID of the item. This should remain stable across releases for + /// the purpose of storing it in the user's configuration. + fn unique_id(&self) -> UniqueId; + + /// The name of the item for display purposes. + fn name(&self) -> String; +} + + + +pub struct MkRegistry where T: RegistryItem +{ + items: HashMap +} + + + +impl<'a, T> MkRegistry where T: RegistryItem +{ + pub fn new() -> Self + { + Self + { + items: HashMap::new() + } + } + + + pub fn register(&mut self, device: T) + { + let device_id = device.unique_id(); + + info!("Registered device: [{}] {}", device_id.as_str(), device.name()); + self.items.insert(String::from(device_id.as_str()), device); + } + + + pub fn by_id(&self, id: UniqueId) -> Option<&T> + { + self.items.get(id.as_str()) + } +} \ No newline at end of file diff --git a/Linux/src/util/mod.rs b/Linux/src/util/mod.rs new file mode 100644 index 0000000..358541a --- /dev/null +++ b/Linux/src/util/mod.rs @@ -0,0 +1 @@ +pub mod unique_id; \ No newline at end of file diff --git a/Linux/src/util/unique_id.rs b/Linux/src/util/unique_id.rs new file mode 100644 index 0000000..4df9136 --- /dev/null +++ b/Linux/src/util/unique_id.rs @@ -0,0 +1,38 @@ +use regex::Regex; + + +pub struct UniqueId +{ + inner: String, +} + + +impl UniqueId +{ + pub fn new(id: &str) -> Self + { + assert!(is_valid_unique_id(id), "Id '{id}' has invalid characters"); + UniqueId { inner: id.to_string() } + } + + pub fn as_str(&self) -> &str + { + self.inner.as_str() + } +} + + +fn is_valid_unique_id(id: &str) -> bool +{ + let re = Regex::new(r"^[a-zA-Z0-9\.\-_]+$").unwrap(); + re.is_match(id) +} + + +impl Clone for UniqueId +{ + fn clone(&self) -> Self + { + Self { inner: self.inner.clone() } + } +} \ No newline at end of file