From ebffc93d69a84f045d603d590320da55d18b124a Mon Sep 17 00:00:00 2001 From: Benjamin Klum Date: Tue, 7 Jan 2025 11:03:07 +0100 Subject: [PATCH] #1391 Inform app when instances change --- main/src/application/unit_model.rs | 20 +++--- main/src/infrastructure/data/unit_data.rs | 6 +- .../infrastructure/plugin/backbone_shell.rs | 2 + main/src/infrastructure/proto/ext.rs | 2 +- main/src/infrastructure/proto/hub.rs | 4 ++ main/src/infrastructure/server/http/send.rs | 20 +++--- .../ui/companion_app_presenter.rs | 16 ++++- main/src/infrastructure/ui/header_panel.rs | 2 + main/src/infrastructure/ui/unit_panel.rs | 64 ++++++++++--------- 9 files changed, 78 insertions(+), 58 deletions(-) diff --git a/main/src/application/unit_model.rs b/main/src/application/unit_model.rs index f904b43e7..a1f8d17e6 100644 --- a/main/src/application/unit_model.rs +++ b/main/src/application/unit_model.rs @@ -64,7 +64,6 @@ pub trait SessionUi { fn handle_internal_info_event(&self, event: &InternalInfoEvent); fn handle_external_info_event(&self, event: InstanceInfoEvent); fn handle_everything_changed(&self, unit_model: &UnitModel); - fn handle_unit_name_changed(&self); fn handle_global_control_and_feedback_state_changed(&self); fn handle_affected( &self, @@ -88,7 +87,7 @@ pub struct UnitModel { /// Persisted and can be user-customized. Should be /// unique but if not it's not a big deal, then it won't crash but the user can't be sure which /// session will be picked. Most relevant for HTTP/WS API. - pub unit_key: Prop, + unit_key: String, pub name: Option, pub let_matched_events_through: Prop, pub let_unmatched_events_through: Prop, @@ -255,7 +254,7 @@ impl UnitModel { .map(|au| au.controller_id.clone()) .unwrap_or_else(|| nanoid::nanoid!(8)); let mut model = Self { - unit_key: prop(initial_unit_key), + unit_key: initial_unit_key, instance_id, unit_id, let_matched_events_through: prop(session_defaults::LET_MATCHED_EVENTS_THROUGH), @@ -344,7 +343,7 @@ impl UnitModel { } pub fn name_or_key(&self) -> &str { - self.name.as_deref().unwrap_or(self.unit_key.get_ref()) + self.name.as_deref().unwrap_or(&self.unit_key) } pub fn auto_unit(&self) -> Option<&AutoUnitData> { @@ -402,7 +401,7 @@ impl UnitModel { } pub fn unit_key(&self) -> &str { - self.unit_key.get_ref() + &self.unit_key } pub fn instance_track_descriptor(&self) -> &TrackDescriptor { @@ -1109,6 +1108,10 @@ impl UnitModel { self.name = unit_name; Some(One(SessionProp::UnitName)) } + C::SetUnitKey(unit_key) => { + self.unit_key = unit_key; + Some(One(SessionProp::UnitKey)) + } C::SetInstanceTrack(api_desc) => { let virtual_track = domain::TrackDescriptor::from_api(api_desc.clone()).unwrap_or_default(); @@ -1277,9 +1280,6 @@ impl UnitModel { use SessionProp::*; let mut session = session.borrow_mut(); match &affected { - One(UnitName) => { - session.ui().handle_unit_name_changed(); - } One(WantsKeyboardInput | StreamDeckDeviceId) => { session.sync_settings(); } @@ -2444,7 +2444,7 @@ impl UnitModel { - Controller mapping subscription count: {}\n\ ", self.unit_id, - self.unit_key.get_ref(), + &self.unit_key, self.mappings[CompartmentKind::Main].len(), self.mapping_subscriptions[CompartmentKind::Main].len(), self.groups.len(), @@ -3029,6 +3029,7 @@ pub fn reaper_supports_global_midi_filter() -> bool { #[allow(dead_code)] pub enum SessionCommand { SetUnitName(Option), + SetUnitKey(String), SetInstanceTrack(TrackDescriptor), SetInstanceFx(FxDescriptor), SetWantsKeyboardInput(bool), @@ -3039,6 +3040,7 @@ pub enum SessionCommand { pub enum SessionProp { UnitName, + UnitKey, InstanceTrack, InstanceFx, WantsKeyboardInput, diff --git a/main/src/infrastructure/data/unit_data.rs b/main/src/infrastructure/data/unit_data.rs index 7110901f0..efeab0a01 100644 --- a/main/src/infrastructure/data/unit_data.rs +++ b/main/src/infrastructure/data/unit_data.rs @@ -690,9 +690,6 @@ impl UnitData { )?; // Mutation let migration_descriptor = MigrationDescriptor::new(self.version.as_ref()); - if let Some(id) = &self.id { - session.unit_key.set_without_notification(id.clone()) - }; session.lives_on_upper_floor.set(self.lives_on_upper_floor); session .send_feedback_only_if_armed @@ -848,6 +845,9 @@ impl UnitData { session.tags.set_without_notification(self.tags.clone()); session.set_instance_preset_link_config(self.instance_preset_link_config.clone()); session.set_use_unit_preset_links_only(self.use_instance_preset_links_only); + if let Some(id) = &self.id { + let _ = session.change(SessionCommand::SetUnitKey(id.clone())); + }; let _ = session.change(SessionCommand::SetUnitName(self.name.clone())); let _ = session.change(SessionCommand::SetInstanceTrack( self.instance_track.clone(), diff --git a/main/src/infrastructure/plugin/backbone_shell.rs b/main/src/infrastructure/plugin/backbone_shell.rs index ac5fc6790..01c05bd5d 100644 --- a/main/src/infrastructure/plugin/backbone_shell.rs +++ b/main/src/infrastructure/plugin/backbone_shell.rs @@ -1459,6 +1459,7 @@ impl BackboneShell { self.control_surface_main_task_sender.0.send_complaining( RealearnControlSurfaceMainTask::AddInstance(instance_id, instance), ); + self.proto_hub.notify_instances_changed(); } pub fn unregister_instance(&self, instance_id: InstanceId) { @@ -1474,6 +1475,7 @@ impl BackboneShell { }); self.audio_hook_task_sender .send_complaining(NormalAudioHookTask::RemoveRealTimeInstance(instance_id)); + self.proto_hub.notify_instances_changed(); } pub fn register_unit( diff --git a/main/src/infrastructure/proto/ext.rs b/main/src/infrastructure/proto/ext.rs index 3b2dc211f..9e4edd143 100644 --- a/main/src/infrastructure/proto/ext.rs +++ b/main/src/infrastructure/proto/ext.rs @@ -50,7 +50,7 @@ impl occasional_instance_update::Update { let unit_model = unit_model.borrow(); Unit { id: unit_model.unit_id().into(), - key: unit_model.unit_key.get_ref().clone(), + key: unit_model.unit_key().to_string(), name: unit_model.name().map(|n| n.to_string()), } }); diff --git a/main/src/infrastructure/proto/hub.rs b/main/src/infrastructure/proto/hub.rs index 1987a294e..40d88188c 100644 --- a/main/src/infrastructure/proto/hub.rs +++ b/main/src/infrastructure/proto/hub.rs @@ -61,6 +61,10 @@ impl ProtoHub { }); } + pub fn notify_instances_changed(&self) { + self.send_occasional_global_updates(|| [occasional_global_update::Update::instances()]); + } + pub fn notify_midi_input_devices_changed(&self) { self.send_occasional_global_updates(|| { [occasional_global_update::Update::midi_input_devices()] diff --git a/main/src/infrastructure/server/http/send.rs b/main/src/infrastructure/server/http/send.rs index db2e54a9b..262bfb66a 100644 --- a/main/src/infrastructure/server/http/send.rs +++ b/main/src/infrastructure/server/http/send.rs @@ -201,16 +201,12 @@ pub fn keep_informing_clients_about_session_events(shared_session: &SharedUnitMo .do_async(|session, _| { let _ = send_updated_active_controller(&session.borrow()); }); - when( - session - .everything_changed() - .merge(session.unit_key.changed()), - ) - .with(Rc::downgrade(shared_session)) - .do_async(|session, _| { - send_sessions_to_subscribed_clients(); - let session = session.borrow(); - let _ = send_updated_active_controller(&session); - let _ = send_updated_controller_routing(&session); - }); + when(session.everything_changed()) + .with(Rc::downgrade(shared_session)) + .do_async(|session, _| { + send_sessions_to_subscribed_clients(); + let session = session.borrow(); + let _ = send_updated_active_controller(&session); + let _ = send_updated_controller_routing(&session); + }); } diff --git a/main/src/infrastructure/ui/companion_app_presenter.rs b/main/src/infrastructure/ui/companion_app_presenter.rs index 0abfe5ed0..47ba4db55 100644 --- a/main/src/infrastructure/ui/companion_app_presenter.rs +++ b/main/src/infrastructure/ui/companion_app_presenter.rs @@ -3,7 +3,7 @@ #![allow(clippy::useless_let_if_seq)] use askama::Template; -use crate::application::{SharedUnitModel, WeakUnitModel}; +use crate::application::{Affected, SessionProp, SharedUnitModel, WeakUnitModel}; use crate::base::when; use crate::infrastructure::plugin::BackboneShell; @@ -37,6 +37,19 @@ impl CompanionAppPresenter { shared } + pub fn handle_affected(&self, affected: &Affected, _initiator: Option) { + use crate::application::Affected::One; + use crate::application::SessionProp::UnitKey; + match affected { + One(UnitKey) => { + if self.app_info_requested.get() { + self.update_app_info(); + } + } + _ => {} + } + } + pub fn show_app_info(&self) { self.app_info_requested.set(true); let index_file = self.update_app_info(); @@ -114,7 +127,6 @@ impl CompanionAppPresenter { when( app.changed() .merge(app.server().borrow().changed()) - .merge(self.session().borrow().unit_key.changed()) .take_until(self.party_is_over()), ) .with(Rc::downgrade(self)) diff --git a/main/src/infrastructure/ui/header_panel.rs b/main/src/infrastructure/ui/header_panel.rs index 328bd9d38..279d305dc 100644 --- a/main/src/infrastructure/ui/header_panel.rs +++ b/main/src/infrastructure/ui/header_panel.rs @@ -189,6 +189,8 @@ impl HeaderPanel { } pub fn handle_affected(&self, affected: &Affected, initiator: Option) { + self.companion_app_presenter + .handle_affected(affected, initiator); if !self.is_open() { return; } diff --git a/main/src/infrastructure/ui/unit_panel.rs b/main/src/infrastructure/ui/unit_panel.rs index 77cb58728..e451f2ab2 100644 --- a/main/src/infrastructure/ui/unit_panel.rs +++ b/main/src/infrastructure/ui/unit_panel.rs @@ -8,6 +8,7 @@ use reaper_high::Reaper; use std::cell::RefCell; use std::fmt; +use crate::application::SessionProp::UnitName; use crate::application::{ get_virtual_fx_label, get_virtual_track_label, Affected, CompartmentProp, SessionCommand, SessionProp, SessionUi, UnitModel, VirtualFxType, WeakUnitModel, @@ -21,7 +22,8 @@ use crate::domain::{ }; use crate::infrastructure::plugin::{update_auto_units_async, BackboneShell}; use crate::infrastructure::server::http::{ - send_projection_feedback_to_subscribed_clients, send_updated_controller_routing, + send_projection_feedback_to_subscribed_clients, send_sessions_to_subscribed_clients, + send_updated_controller_routing, }; use crate::infrastructure::ui::instance_panel::InstancePanel; use crate::infrastructure::ui::util::{header_panel_height, parse_tags_from_csv}; @@ -41,7 +43,7 @@ type _MainPanel = UnitPanel; pub struct UnitPanel { instance_id: InstanceId, view: ViewContext, - session: WeakUnitModel, + unit_model: WeakUnitModel, instance_panel: WeakView, header_panel: SharedView, mapping_rows_panel: SharedView, @@ -63,7 +65,7 @@ impl UnitPanel { instance_id, view: Default::default(), state: state.clone(), - session: session.clone(), + unit_model: session.clone(), instance_panel: instance_panel.clone(), header_panel: HeaderPanel::new( session.clone(), @@ -197,7 +199,7 @@ impl UnitPanel { } fn do_with_session(&self, f: impl FnOnce(&UnitModel) -> R) -> Result { - match self.session.upgrade() { + match self.unit_model.upgrade() { None => Err("session not available anymore"), Some(session) => Ok(f(&session.borrow())), } @@ -207,7 +209,7 @@ impl UnitPanel { &self, f: impl FnOnce(&mut UnitModel) -> R, ) -> Result { - match self.session.upgrade() { + match self.unit_model.upgrade() { None => Err("session not available anymore"), Some(session) => Ok(f(&mut session.borrow_mut())), } @@ -263,12 +265,6 @@ impl UnitPanel { self.when(session.everything_changed(), |view| { view.invalidate_all_controls(); }); - self.when( - session.tags.changed().merge(session.unit_key.changed()), - |view| { - view.invalidate_status_1_text(); - }, - ); let instance_state = session.unit().borrow(); self.when( instance_state.global_control_and_feedback_state_changed(), @@ -339,24 +335,27 @@ impl UnitPanel { .notify_about_instance_info_event(self.instance_id, event); } - fn handle_unit_name_changed(&self) { - if let Ok(shell) = self.instance_panel().shell() { - // At the time when this method is called, the unit is still borrowed, so the proto hub can't create - // an overview over all units. - // TODO-medium It would be better if we would send around tiny events here and collect - // them from the event loop. - let _ = - Global::task_support().do_later_in_main_thread_from_main_thread_asap(move || { + fn handle_affected_own(self: SharedView, affected: Affected) { + use Affected::*; + use SessionProp::*; + // Handle even if closed + match affected { + One(UnitName) => { + if let Ok(shell) = self.instance_panel().shell() { BackboneShell::get() .proto_hub() .notify_instance_units_changed(&shell); - }); + } + } + One(UnitKey) => { + // Actually, the instances are only affected if the main unit key is changed, but + // so what. + BackboneShell::get().proto_hub().notify_instances_changed(); + send_sessions_to_subscribed_clients(); + } + _ => {} } - } - - fn handle_affected_own(self: SharedView, affected: Affected) { - use Affected::*; - use SessionProp::*; + // Handle only if open if !self.is_open() { return; } @@ -367,6 +366,9 @@ impl UnitPanel { One(UnitName) => { let _ = self.invalidate_unit_button(); } + One(UnitKey) => { + self.invalidate_status_1_text(); + } Multiple => { self.invalidate_all_controls(); } @@ -453,7 +455,7 @@ impl UnitPanel { session.change_with_notification( SessionCommand::SetUnitName(unit_name), None, - self.session.clone(), + self.unit_model.clone(), ); })?; // Take care of unit key @@ -468,7 +470,11 @@ impl UnitPanel { return Ok(()); } self.do_with_session_mut(|session| { - session.unit_key.set(new_unit_key); + session.change_with_notification( + SessionCommand::SetUnitKey(new_unit_key), + None, + self.unit_model.clone(), + ); })?; } Ok(()) @@ -596,10 +602,6 @@ impl SessionUi for Weak { .notify_everything_in_unit_has_changed(unit_model.instance_id(), unit_model.unit_id()); } - fn handle_unit_name_changed(&self) { - upgrade_panel(self).handle_unit_name_changed(); - } - fn handle_global_control_and_feedback_state_changed(&self) { update_auto_units_async(); }