1
0
mirror of synced 2024-11-05 09:49:16 +00:00

Implemented creating device instances

This commit is contained in:
Mark van Renswoude 2024-07-24 16:20:03 +02:00
parent c93bdab059
commit 50eb38aae1
12 changed files with 318 additions and 130 deletions

View File

@ -1,3 +1,4 @@
{ {
"nixEnvSelector.nixFile": "${workspaceFolder}/shell.nix" "nixEnvSelector.nixFile": "${workspaceFolder}/shell.nix",
"files.trimTrailingWhitespace": true
} }

View File

@ -1,17 +1,22 @@
pub mod pipewire; pub mod pipewire;
use crate::registry::MkRegistry; use crate::registry::Registry;
use crate::registry::RegistryItem; use crate::registry::RegistryItem;
use crate::util::unique_id::UniqueId; use crate::util::unique_id::UniqueId;
pub struct MkAction pub struct ActionRegistryItem
{ {
pub unique_id: UniqueId pub unique_id: UniqueId
} }
impl RegistryItem for MkAction pub trait Action
{
}
impl RegistryItem for ActionRegistryItem
{ {
fn unique_id(&self) -> UniqueId fn unique_id(&self) -> UniqueId
{ {
@ -26,7 +31,7 @@ impl RegistryItem for MkAction
pub fn register(registry: &mut MkRegistry<MkAction>) pub fn register(registry: &mut Registry<ActionRegistryItem>)
{ {
pipewire::register(registry); pipewire::register(registry);
} }

View File

@ -1,13 +1,13 @@
use crate::registry::MkRegistry; use crate::registry::Registry;
use crate::util::unique_id::UniqueId; use crate::util::unique_id::UniqueId;
use super::MkAction; use super::ActionRegistryItem;
pub mod set_volume; pub mod set_volume;
pub fn register(registry: &mut MkRegistry<MkAction>) pub fn register(registry: &mut Registry<ActionRegistryItem>)
{ {
registry.register(MkAction registry.register(ActionRegistryItem
{ {
unique_id: UniqueId::new("pipewire.set_volume") unique_id: UniqueId::new("pipewire.set_volume")
}); });

View File

@ -49,15 +49,20 @@ impl ConfigManager
pub fn get_writer(&self, name: &ConfigName) -> Result<impl Write, Error> pub fn get_writer(&self, name: &ConfigName) -> Result<impl Write, Error>
{ {
let path = Path::join(&self.root, name.as_str()); let path = Path::join(&self.root, name.as_str());
if !path.exists() let parent = Path::parent(&path);
if let Some(parent) = parent
{ {
match std::fs::create_dir_all(path.clone()) if !parent.exists()
{ {
Ok(_v) => (), match std::fs::create_dir_all(parent)
Err(e) => return Err(e) {
Ok(_v) => (),
Err(e) => return Err(e)
}
} }
} }
std::fs::File::create(path) std::fs::File::create(path)
} }
} }

View File

@ -1,24 +1,58 @@
use crate::registry::MkRegistry; use emulatorwindow::EmulatorWindow;
use relm4::{component::Connector, ComponentController};
use relm4::Component;
use relm4::gtk::prelude::*;
use crate::registry::Registry;
use crate::util::unique_id::UniqueId; use crate::util::unique_id::UniqueId;
use super::MkDevice; use super::{Device, DeviceRegistryItem};
pub mod emulatorwindow; pub mod emulatorwindow;
pub struct EmulatorWindowDevice
pub fn register(registry: &mut MkRegistry<MkDevice>)
{ {
registry.register(MkDevice { window: Option<Connector<EmulatorWindow>>
unique_id: UniqueId::new("emulator")
});
} }
/*
let app = relm4::main_application();
let builder = EmulatorWindow::builder();
app.add_window(&builder.root);
builder.launch(()).detach_runtime(); impl EmulatorWindowDevice
*/ {
fn new() -> Self
{
EmulatorWindowDevice
{
window: None
}
}
}
impl Device for EmulatorWindowDevice
{
fn activate(&mut self)
{
if self.window.is_some() { return }
let builder = EmulatorWindow::builder();
self.window = Some(builder.launch({}));
}
fn deactivate(&mut self)
{
let Some(window) = self.window.take() else { return };
window.widget().close();
}
}
pub fn register(registry: &mut Registry<DeviceRegistryItem>)
{
registry.register(DeviceRegistryItem {
unique_id: UniqueId::new("emulator"),
factory: || Box::new(EmulatorWindowDevice::new())
});
}

View File

@ -2,18 +2,26 @@ pub mod emulator;
pub mod serial_min; pub mod serial_min;
use crate::registry::MkRegistry; use crate::registry::Registry;
use crate::registry::RegistryItem; use crate::registry::RegistryItem;
use crate::util::unique_id::UniqueId; use crate::util::unique_id::UniqueId;
pub struct MkDevice pub struct DeviceRegistryItem
{ {
pub unique_id: UniqueId pub unique_id: UniqueId,
pub factory: fn() -> Box<dyn Device>
} }
impl RegistryItem for MkDevice pub trait Device
{
fn activate(&mut self);
fn deactivate(&mut self);
}
impl RegistryItem for DeviceRegistryItem
{ {
fn unique_id(&self) -> UniqueId fn unique_id(&self) -> UniqueId
{ {
@ -28,7 +36,7 @@ impl RegistryItem for MkDevice
pub fn register(registry: &mut MkRegistry<MkDevice>) pub fn register(registry: &mut Registry<DeviceRegistryItem>)
{ {
emulator::register(registry); emulator::register(registry);
serial_min::register(registry); serial_min::register(registry);

View File

@ -1,11 +1,33 @@
use crate::registry::MkRegistry; use crate::registry::Registry;
use crate::util::unique_id::UniqueId; use crate::util::unique_id::UniqueId;
use super::MkDevice; use super::{Device, DeviceRegistryItem};
pub fn register(registry: &mut MkRegistry<MkDevice>) pub struct SerialMinDevice
{ {
registry.register(MkDevice {
unique_id: UniqueId::new("serial_min") }
impl Device for SerialMinDevice
{
fn activate(&mut self)
{
//todo!()
}
fn deactivate(&mut self)
{
//todo!()
}
}
pub fn register(registry: &mut Registry<DeviceRegistryItem>)
{
registry.register(DeviceRegistryItem {
unique_id: UniqueId::new("serial_min"),
factory: || Box::new(SerialMinDevice {})
}); });
} }

View File

@ -1,6 +1,7 @@
use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
use env_logger::Env; use env_logger::Env;
use mainwindow::MainWindowViewModel; use mainwindow::MainWindowInit;
use orchestrator::Orchestrator; use orchestrator::Orchestrator;
use relm4::prelude::*; use relm4::prelude::*;
@ -27,11 +28,13 @@ fn main()
relm4_icons::initialize_icons(); relm4_icons::initialize_icons();
let orchestrator = Rc::new(Orchestrator::new()); let orchestrator = Rc::new(RefCell::new(Orchestrator::new()));
let app = RelmApp::new("com.github.mvrens.massiveknob"); let app = RelmApp::new("com.github.mvrens.massiveknob");
app.run::<mainwindow::MainWindow>(MainWindowViewModel app.run::<mainwindow::MainWindow>(MainWindowInit
{ {
orchestrator: Rc::clone(&orchestrator) orchestrator: Rc::clone(&orchestrator)
}); });
orchestrator.borrow_mut().finalize();
} }

View File

@ -1,25 +1,31 @@
use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
use gtk::glib::clone;
use gtk::prelude::*; use gtk::prelude::*;
use relm4::prelude::*; use relm4::prelude::*;
use crate::{devices::MkDevice, orchestrator::Orchestrator, registry::RegistryItem, util::unique_id::UniqueId}; use crate::orchestrator::Orchestrator;
use crate::registry::RegistryItem;
use crate::util::unique_id::UniqueId;
pub struct MainWindow pub struct MainWindow
{ {
orchestrator: Rc<RefCell<Orchestrator>>,
devices_sorted: Vec<SortedDevice>
} }
pub struct MainWindowViewModel pub struct MainWindowInit
{ {
pub orchestrator: Rc<Orchestrator> pub orchestrator: Rc<RefCell<Orchestrator>>
} }
impl std::fmt::Debug for MainWindowViewModel impl std::fmt::Debug for MainWindowInit
{ {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
{ {
f.debug_struct("MainWindowViewModel") f.debug_struct("MainWindowInit")
// Skip orchestrator // Skip orchestrator
.finish() .finish()
} }
@ -29,25 +35,25 @@ impl std::fmt::Debug for MainWindowViewModel
#[derive(Debug)] #[derive(Debug)]
pub enum MainWindowMsg pub enum MainWindowMsg
{ {
DeviceChanged(usize)
} }
pub struct MainWindowWidgets pub struct MainWindowWidgets
{ {
device: MainWindowDeviceWidgets _device: MainWindowDeviceWidgets
} }
pub struct MainWindowDeviceWidgets pub struct MainWindowDeviceWidgets
{ {
devices_sorted: Vec<SortedDevice>, _devices_combobox: gtk::ComboBoxText
devices_combobox: gtk::ComboBoxText
} }
impl SimpleComponent for MainWindow impl SimpleComponent for MainWindow
{ {
type Init = MainWindowViewModel; type Init = MainWindowInit;
type Input = MainWindowMsg; type Input = MainWindowMsg;
type Output = (); type Output = ();
type Root = gtk::Window; type Root = gtk::Window;
@ -61,67 +67,19 @@ impl SimpleComponent for MainWindow
.title(t!("mainwindow.title")) .title(t!("mainwindow.title"))
.default_width(500) .default_width(500)
.default_height(500) .default_height(500)
.build() .build()
} }
fn init(data: Self::Init, window: Self::Root, _sender: ComponentSender<Self>, ) -> ComponentParts<Self> fn init(data: Self::Init, window: Self::Root, sender: ComponentSender<Self>) -> ComponentParts<Self>
{ {
let orchestrator = data.orchestrator.as_ref();
let model = MainWindow {};
let widgets = Self::init_ui(&window, &orchestrator);
ComponentParts { model, widgets }
}
fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>)
{
match msg
{ {
let mut init_orchestrator = data.orchestrator.borrow_mut();
init_orchestrator.initialize();
} }
}
}
let orchestrator = data.orchestrator.borrow();
impl MainWindow
{
fn init_ui(window: &gtk::Window, orchestrator: &Orchestrator) -> MainWindowWidgets
{
let tabs = gtk::Notebook::builder().build();
window.set_child(Some(&tabs));
MainWindowWidgets
{
device: Self::init_device_tab(&tabs, &orchestrator)
//Self::new_box_tab(&tabs, "mainwindow.tab.analoginputs");
//Self::new_box_tab(&tabs, "mainwindow.tab.digitalinputs");
//Self::add_box_tab(&tabs, "mainwindow.tab.analogoutputs");
//Self::add_box_tab(&tabs, "mainwindow.tab.digitaloutputs");
}
}
fn init_device_tab(tabs: &gtk::Notebook, orchestrator: &Orchestrator) -> MainWindowDeviceWidgets
{
let tab = Self::new_box_tab(&tabs, "mainwindow.tab.device");
let label = gtk::Label::builder()
.label(t!("mainwindow.deviceType.label"))
.halign(gtk::Align::Start)
.build();
tab.append(&label);
let devices_combobox = gtk::ComboBoxText::builder()
.build();
tab.append(&devices_combobox);
let mut devices_sorted: Vec<SortedDevice> = orchestrator.devices() let mut devices_sorted: Vec<SortedDevice> = orchestrator.devices()
.map(|device| SortedDevice .map(|device| SortedDevice
@ -133,14 +91,114 @@ impl MainWindow
devices_sorted.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase())); devices_sorted.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase()));
let current_device_id = orchestrator.current_device_id();
let model = MainWindow
{
orchestrator: data.orchestrator.clone(),
devices_sorted
};
let widgets = MainWindowBuilder::new(&window, &model, &sender).build();
ComponentParts { model, widgets }
}
for (index, device) in devices_sorted.iter().enumerate() fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>)
{
match msg
{
MainWindowMsg::DeviceChanged(index) =>
{
let mut orchestrator = self.orchestrator.borrow_mut();
let device = &self.devices_sorted[index];
orchestrator.set_current_device_id(device.unique_id.clone());
}
}
}
}
struct MainWindowBuilder<'a>
{
window: &'a gtk::Window,
model: &'a MainWindow,
sender: &'a ComponentSender<MainWindow>
}
impl<'a> MainWindowBuilder<'a>
{
fn new(window: &'a gtk::Window, model: &'a MainWindow, sender: &'a ComponentSender<MainWindow>) -> Self
{
Self
{
window,
model,
sender
}
}
fn build(&self) -> MainWindowWidgets
{
let tabs = gtk::Notebook::builder().build();
self.window.set_child(Some(&tabs));
MainWindowWidgets
{
_device: self.init_device_tab(&tabs)
//Self::new_box_tab(&tabs, "mainwindow.tab.analoginputs");
//Self::new_box_tab(&tabs, "mainwindow.tab.digitalinputs");
//Self::add_box_tab(&tabs, "mainwindow.tab.analogoutputs");
//Self::add_box_tab(&tabs, "mainwindow.tab.digitaloutputs");
}
}
fn init_device_tab(&self, tabs: &gtk::Notebook) -> MainWindowDeviceWidgets
{
let sender = self.sender;
let tab = Self::new_box_tab(&tabs, "mainwindow.tab.device");
let label = gtk::Label::builder()
.label(t!("mainwindow.deviceType.label"))
.halign(gtk::Align::Start)
.build();
tab.append(&label);
let devices_combobox = gtk::ComboBoxText::builder()
.build();
let devices_combobox_cloned = devices_combobox.clone();
devices_combobox.connect_changed(clone!(
@strong sender => move |_|
{
if let Some(active_index) = devices_combobox_cloned.active()
{
if let Ok(active_index_usize) = usize::try_from(active_index)
{
sender.input(MainWindowMsg::DeviceChanged(active_index_usize));
}
}
}));
tab.append(&devices_combobox);
let current_device_id = self.model.orchestrator.borrow().current_device_id();
for (index, device) in self.model.devices_sorted.iter().enumerate()
{ {
devices_combobox.append_text(device.name.as_str()); devices_combobox.append_text(device.name.as_str());
if let Some(device_id) = current_device_id if let Some(device_id) = current_device_id.clone()
{ {
if device_id == device.unique_id if device_id == device.unique_id
{ {
@ -152,12 +210,11 @@ impl MainWindow
MainWindowDeviceWidgets MainWindowDeviceWidgets
{ {
devices_sorted, _devices_combobox: devices_combobox
devices_combobox
} }
} }
fn new_box_tab(notebook: &gtk::Notebook, title_key: &str) -> gtk::Box fn new_box_tab(notebook: &gtk::Notebook, title_key: &str) -> gtk::Box
{ {
let tab = gtk::Box::builder() let tab = gtk::Box::builder()

View File

@ -1,10 +1,10 @@
use crate::actions; use crate::actions;
use crate::actions::MkAction; use crate::actions::ActionRegistryItem;
use crate::config::json::JsonConfigManager; use crate::config::json::JsonConfigManager;
use crate::config::{ConfigManager, ConfigName}; use crate::config::{ConfigManager, ConfigName};
use crate::devices; use crate::devices::{self, Device};
use crate::devices::MkDevice; use crate::devices::DeviceRegistryItem;
use crate::registry::MkRegistry; use crate::registry::Registry;
use crate::util::unique_id::UniqueId; use crate::util::unique_id::UniqueId;
@ -15,11 +15,14 @@ pub struct Orchestrator
{ {
config_manager: ConfigManager, config_manager: ConfigManager,
device_registry: MkRegistry<MkDevice>, device_registry: Registry<DeviceRegistryItem>,
action_registry: MkRegistry<MkAction>, action_registry: Registry<ActionRegistryItem>,
settings_name: ConfigName, settings_name: ConfigName,
settings: settings::Settings settings: settings::Settings,
current_device_instance: Option<Box<dyn Device>>
} }
@ -36,8 +39,8 @@ impl Orchestrator
Some(v) => v Some(v) => v
}; };
let mut device_registry = MkRegistry::new(); let mut device_registry = Registry::new();
let mut action_registry = MkRegistry::new(); let mut action_registry = Registry::new();
devices::register(&mut device_registry); devices::register(&mut device_registry);
actions::register(&mut action_registry); actions::register(&mut action_registry);
@ -50,12 +53,27 @@ impl Orchestrator
action_registry, action_registry,
settings_name, settings_name,
settings settings,
current_device_instance: None
} }
} }
pub fn devices(&self) -> impl Iterator<Item = &MkDevice> pub fn initialize(&mut self)
{
self.set_current_device(self.current_device_id());
}
pub fn finalize(&mut self)
{
self.set_current_device(None);
}
pub fn devices(&self) -> impl Iterator<Item = &DeviceRegistryItem>
{ {
self.device_registry.iter() self.device_registry.iter()
} }
@ -67,22 +85,23 @@ impl Orchestrator
Some(UniqueId::new(device_id.as_str())) Some(UniqueId::new(device_id.as_str()))
} }
pub fn current_device(&self) -> Option<&MkDevice>
pub fn current_device(&self) -> Option<&DeviceRegistryItem>
{ {
let Some(device_id) = self.current_device_id() else { return None }; let Some(device_id) = self.current_device_id() else { return None };
self.device_registry.by_id(device_id) self.device_registry.by_id(device_id)
} }
pub fn set_current_device_id(&mut self, id: &str) pub fn set_current_device_id(&mut self, id: UniqueId)
{ {
let new_id = Some(String::from(id)); let new_id = Some(String::from(id.as_str()));
if new_id == self.settings.device_id { return; } if new_id == self.settings.device_id { return; }
self.settings.device_id = new_id; self.settings.device_id = new_id;
self.store_settings(); self.store_settings();
// TODO unload old device, activate new self.set_current_device(Some(id));
} }
@ -93,4 +112,36 @@ impl Orchestrator
log::error!("Error writing settings: {e}"); log::error!("Error writing settings: {e}");
} }
} }
fn set_current_device(&mut self, id: Option<UniqueId>)
{
let prev_device_instance;
let new_device = match id
{
None => None,
Some(v) => self.device_registry.by_id(v)
};
// Replace the device instance
if let Some(new_device) = new_device
{
let mut new_device_instance = (new_device.factory)();
new_device_instance.activate();
prev_device_instance = self.current_device_instance.replace(new_device_instance);
}
else
{
prev_device_instance = self.current_device_instance.take();
}
// Deactivate the previous instance
if let Some(mut prev_device_instance) = prev_device_instance
{
prev_device_instance.deactivate();
}
}
} }

View File

@ -16,14 +16,14 @@ pub trait RegistryItem
pub struct MkRegistry<T> where T: RegistryItem pub struct Registry<T> where T: RegistryItem
{ {
items: HashMap<String, T> items: HashMap<String, T>
} }
impl<T> MkRegistry<T> where T: RegistryItem impl<T> Registry<T> where T: RegistryItem
{ {
pub fn new() -> Self pub fn new() -> Self
{ {
@ -35,8 +35,8 @@ impl<T> MkRegistry<T> where T: RegistryItem
pub fn register(&mut self, device: T) pub fn register(&mut self, device: T)
{ {
let device_id = device.unique_id(); let device_id = device.unique_id();
log::debug!("Registered device: [{}] {}", device_id.as_str(), device.name()); log::debug!("Registered device: [{}] {}", device_id.as_str(), device.name());
self.items.insert(String::from(device_id.as_str()), device); self.items.insert(String::from(device_id.as_str()), device);

View File

@ -4,8 +4,10 @@ use super::validated_string::{ValidatedString, ValidatedStringPattern};
pub type UniqueId = ValidatedString<UniqueIdPattern>; pub type UniqueId = ValidatedString<UniqueIdPattern>;
#[derive(PartialEq, Eq)]
pub struct UniqueIdPattern; pub struct UniqueIdPattern;
impl ValidatedStringPattern for UniqueIdPattern impl ValidatedStringPattern for UniqueIdPattern
{ {
fn pattern() -> &'static str { r"^[a-zA-Z0-9\.\-_]+$" } fn pattern() -> &'static str { r"^[a-zA-Z0-9\.\-_]+$" }