diff --git a/src/main.rs b/src/main.rs index 715838d..ed63cc5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,7 +11,7 @@ use bevy::prelude::Mat3; // for glam::f32::Mat3, which is distinct from nannou:: use nannou_egui::{self, egui, Egui}; use nannou_laser::DacId; use nannou_laser::{self as laser}; -use serde_json::Result; +// use serde_json::Result; use laserspace::trap::filters::{MappedPoint, PointFilters}; use laserspace::trap::laser::{shape_rect, LaserPoints, LaserSpace, StreamSource, STREAM_SOURCES, TMP_DESK_CLUBMAX, Corner}; use laserspace::trap::tracks::{CoordinateSpace, RenderableLayers, renderable}; @@ -66,7 +66,7 @@ struct GuiModel { laser_model: LaserModel, // A copy of the laser settings so that we can control them with the GUI. laser_settings: LaserSettings, - per_laser_config: DacConfigMap, + per_laser_config: OutputConfigMap, // For receiving newly detected DACs. dac_rx: mpsc::Receiver, // The UI for control over laser parameters and settings. @@ -78,7 +78,7 @@ struct GuiModel { // dimming_factor: f32, lost_alpha: f32, connected: bool, - selected_stream: Option, + selected_stream: Option, canvas_scale: f32, canvas_translate: Vec2, canvas_dragging_corner: Option, @@ -235,32 +235,62 @@ fn zmq_receive(model: &mut GuiModel) { } -type DacConfigMap = HashMap; +type OutputConfigMap = HashMap; - - -#[derive(Debug, Serialize, Deserialize)] -#[serde(remote = "DacId")] -pub enum DacIdSerializable { +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub enum OutputId{ EtherDream { mac_address: [u8; 6] }, Helios { id: u32 }, + Display { monitor: u32, enabled: bool } } + +impl From for OutputId { + fn from(dac_id: DacId) -> OutputId { + match dac_id { + DacId::EtherDream { mac_address } => OutputId::EtherDream { mac_address }, + DacId::Helios {id } => OutputId::Helios { id }, + } + } +} + +impl TryInto for OutputId { + type Error = OutputId; + + fn try_into(self) -> Result { + match self { + OutputId::EtherDream { mac_address } => Ok(DacId::EtherDream { mac_address }), + OutputId::Helios { id } => Ok(DacId::Helios { id }), + // Return the original OutputId in the Err variant + // if it doesn't match any DacId variant + other => Err(other), + } + } +} + +// #[derive(Debug, Serialize, Deserialize)] +// #[serde(remote = "DacId")] +// pub enum DacIdSerializable { +// EtherDream { mac_address: [u8; 6] }, +// Helios { id: u32 }, +// } + + #[derive(Debug, Serialize, Deserialize)] -pub struct SavedDacConfig{ - #[serde(with = "DacIdSerializable")] - dac_id: DacId, +pub struct SavedOutputConfig{ + // #[serde(with = "DacIdSerializable")] + dac_id: OutputId, config: DacConfig, } #[derive(Debug, Serialize, Deserialize)] pub struct SavedConfig { - dacs: Vec + dacs: Vec } -impl Into for SavedConfig { - fn into(self) -> DacConfigMap { +impl Into for SavedConfig { + fn into(self) -> OutputConfigMap { let mut configs = HashMap::new(); for dac in self.dacs { configs.insert(dac.dac_id, dac.config); @@ -269,11 +299,11 @@ impl Into for SavedConfig { } } -impl From<&mut DacConfigMap> for SavedConfig { - fn from(per_laser_config: &mut DacConfigMap) -> SavedConfig { +impl From<&mut OutputConfigMap> for SavedConfig { + fn from(per_output_config: &mut OutputConfigMap) -> SavedConfig { let mut dacs = Vec::new(); - for (dac_id, config) in per_laser_config.into_iter() { - dacs.push(SavedDacConfig{ + for (dac_id, config) in per_output_config.into_iter() { + dacs.push(SavedOutputConfig{ dac_id: dac_id.clone(), config: config.clone() }); } @@ -305,7 +335,7 @@ fn save_config_file>(path: P, config: SavedConfig) -> std::result // Some hardcoded config. Not spending time on reading/writing config atm. -fn get_dac_configs(config_path: &PathBuf) -> DacConfigMap{ +fn get_dac_configs(config_path: &PathBuf) -> OutputConfigMap{ match read_config_from_file(config_path) { Err(err) => { eprintln!("Could not load config {}", err); @@ -501,16 +531,17 @@ fn double_send_settings_at_connect(laser_settings: &LaserSettings, stream: &lase stream.set_radians_per_point(laser_settings.radians_per_point).ok(); } -fn update(_app: &App, model: &mut GuiModel, update: Update) { +fn update(app: &App, model: &mut GuiModel, update: Update) { // First, check for new laser DACs. for dac in model.dac_rx.try_recv() { println!("Detected DAC {:?}!", dac.id()); - if !model.per_laser_config.contains_key(&dac.id()) { + let output_id: OutputId = dac.id().into(); + if !model.per_laser_config.contains_key(&output_id) { println!("Found unknown DAC, register with defaults"); - model.per_laser_config.insert(dac.id(), DacConfig::default()); + model.per_laser_config.insert(output_id.clone(), DacConfig::default()); } - let config = &model.per_laser_config[&dac.id()]; + let config = &model.per_laser_config[&output_id]; let stream = model .laser_api @@ -557,9 +588,10 @@ fn update(_app: &App, model: &mut GuiModel, update: Update) { eprintln!("Stream closed due to an error: {}", err); } // TODO: keeps looping on disconnect. - println!("attempting to restart stream with DAC {:?}", dac.id()); let dac_id = dac.id(); - let config = &model.per_laser_config[&dac.id()]; + println!("attempting to restart stream with DAC {:?}", &dac_id); + let output_id: OutputId = dac_id.clone().into(); + let config = &model.per_laser_config[&output_id]; match model .laser_api .new_frame_stream(model.laser_model.with_config(config), laser_frame_producer) @@ -728,48 +760,92 @@ fn update(_app: &App, model: &mut GuiModel, update: Update) { ui.separator(); ui.heading("Laser specific settings"); - if per_laser_config.is_empty() { - ui.label("No dacs available"); - } else { - ui.horizontal_wrapped(|ui| { - ui.selectable_value( - selected_stream, - None, - "⊗" - ); + ui.horizontal_wrapped(|ui| { + ui.selectable_value( + selected_stream, + None, + "⊗" + ); + if per_laser_config.is_empty() { + ui.label("No dacs available"); + } else { - for (dac_id, _config) in per_laser_config.iter() { - let is_available = laser_streams.contains_key(&dac_id); + for (output_id, _config) in per_laser_config.iter() { + let is_available = match &output_id.clone().try_into() { + Ok(dac_id) => laser_streams.contains_key(dac_id), + Err(_output_id) => { + if let OutputId::Display { enabled, .. } = output_id { + *enabled + } else { + false + } + }, + }; + ui.style_mut().visuals.override_text_color = if is_available {Some(egui::Color32::GREEN)} else {None}; ui.style_mut().visuals.widgets.inactive.bg_stroke = if is_available {egui::Stroke::new(2.0, egui::Color32::GREEN)} else {egui::Stroke::NONE}; - let name = if let Some(config) = per_laser_config.get(&dac_id) { config.name.clone() } else { "DAC".into() }; + let name = if let Some(config) = per_laser_config.get(&output_id) { config.name.clone() } else { "DAC".into() }; let indicator = if is_available{" 🔌"}else{""}; // egui::widgets::SelectableLabel cannot have border unless hovered/highlighted // TODO: alternatively underscore with ui.painter().rect/hline(ui.selectable_value().rect.max/min, ....) ui.selectable_value( selected_stream, - Some(dac_id.clone()), + Some(output_id.clone()), format!("{name}{indicator}") ); } // reset ui.style_mut().visuals.override_text_color = None; - }); - } + } + + if ui.button("+").clicked() { + let output_id = OutputId::Display { monitor: 0, enabled: false }; + per_laser_config.insert(output_id, DacConfig::default()); + } + }); - if let Some(selected_stream_value) = selected_stream { + if let Some(selected_output) = selected_stream { ui.separator(); - ui.add(egui::Label::new(format!("{:?}", selected_stream_value))); + ui.add(egui::Label::new(format!("{:?}", selected_output))); - let selected_config: &mut DacConfig = per_laser_config.get_mut(&selected_stream_value).unwrap(); + let selected_config: &mut DacConfig = per_laser_config.get_mut(&selected_output).unwrap(); ui.add(egui::TextEdit::singleline(&mut selected_config.name)); - let selected_laser_stream = laser_streams.get(&selected_stream_value); + let selected_laser_stream = match selected_output.clone().try_into() { + Ok(dac_id) => laser_streams.get(&dac_id), + Err(_output_id) => None, + } ; // let stream_config: &mut StreamConfig = laser_streams.get_mut(&selected_stream_value).expect("Selected stream not found in configs"); + + if let OutputId::Display { monitor, enabled: fullscreen } = selected_output { + let monitors = app.available_monitors(); + // monitors.get(0).unwrap().name() + + // fullscreen = FullScreen::Borderless(Some(monitor_handle)); + // app.new_window().fullscreen_with(fullscreen) + + let mut selected_monitor_name = String::from("haha"); + + egui::ComboBox::from_label("Monitor") + .selected_text(format!("MONITOR -- {selected_monitor_name:?}")) + .show_ui(ui, |ui| { + for monitor_option in monitors { + if let Some(monitor_name) = monitor_option.name() { + + if ui.selectable_value(&mut selected_monitor_name, monitor_name.clone(), format!("{:?}", &monitor_name)).clicked() { + if let Some(stream) = selected_laser_stream { + // TODO: make this for monitor: switch it + } + }; + } + + } + }); + } let source = &mut selected_config.source; @@ -791,7 +867,7 @@ fn update(_app: &App, model: &mut GuiModel, update: Update) { ui.horizontal_wrapped(|ui| { - for layer_nr in 1..=8 { + for layer_nr in 0..8 { let mask = 1 << layer_nr; let mut enabled_bool = mask & selected_config.layers_enabled != 0; @@ -1284,7 +1360,11 @@ fn laser_mouse_moved(app: &App, model: &mut GuiModel, pos: Point2) { // 3. update config in laser stream threat let homography = config.filters.homography.clone(); - let selected_laser_stream = model.laser_streams.get(&dac_id); + let selected_laser_stream = match dac_id.clone().try_into() { + Ok(dac_id) => model.laser_streams.get(&dac_id), + Err(_output_id) => None, + } ; + if let Some(stream) = selected_laser_stream { stream.send(move |laser_model: &mut LaserModel| { laser_model.config.filters.homography = homography @@ -1305,7 +1385,11 @@ fn laser_mouse_moved(app: &App, model: &mut GuiModel, pos: Point2) { // 3. update config in laser stream threat let mask = config.filters.clip.mask.clone(); - let selected_laser_stream = model.laser_streams.get(&dac_id); + let selected_laser_stream = match dac_id.clone().try_into() { + Ok(dac_id) => model.laser_streams.get(&dac_id), + Err(_output_id) => None, + } ; + if let Some(stream) = selected_laser_stream { stream.send(move |laser_model: &mut LaserModel| { laser_model.config.filters.clip.mask = mask; @@ -1431,7 +1515,11 @@ fn map_mouse_moved(_app: &App, model: &mut GuiModel, pos: Point2) { } // 3. propagate to laser stream threat - let selected_laser_stream = model.laser_streams.get(&dac_id); + let selected_laser_stream = match dac_id.clone().try_into() { + Ok(dac_id) => model.laser_streams.get(&dac_id), + Err(_output_id) => None, + } ; + let homography = config.filters.homography.clone(); if let Some(stream) = selected_laser_stream { @@ -1540,7 +1628,11 @@ fn map_mouse_pressed(app: &App, model: &mut GuiModel, button: MouseButton) { } // propagate to laser dac streamer - let selected_laser_stream = model.laser_streams.get(&dac_id); + let selected_laser_stream = match dac_id.clone().try_into() { + Ok(dac_id) => model.laser_streams.get(&dac_id), + Err(_output_id) => None, + } ; + let homography = config.filters.homography.clone(); if let Some(stream) = selected_laser_stream {