From 40c08b91fb5b3961e8ce4f7df7856f73c79696fb Mon Sep 17 00:00:00 2001
From: Nicole Tietz-Sokolskaya <me@ntietz.com>
Date: Fri, 20 Dec 2024 17:07:12 -0500
Subject: [PATCH] start ui: implement sending ports to the display thread from
 a background thread

---
 src/bin/main.rs |  8 +-------
 src/parser.rs   |  1 -
 src/ui.rs       | 49 +++++++++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 48 insertions(+), 10 deletions(-)

diff --git a/src/bin/main.rs b/src/bin/main.rs
index 78d6c3e..1abcf2e 100644
--- a/src/bin/main.rs
+++ b/src/bin/main.rs
@@ -15,15 +15,9 @@ fn main() {
 
     midi_keys::ui::run();
 
-    match run() {
-        Ok(_) => {}
-        Err(err) => {
-            println!("Ended with error: {}", err);
-        }
-    }
 }
 
-fn run() -> Result<()> {
+pub fn run() -> Result<()> {
     let midi_in = MidiInput::new("keyboard")?;
 
     let midi_device = match find_first_midi_device(&midi_in) {
diff --git a/src/parser.rs b/src/parser.rs
index 0b7cadf..8d98516 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -1,6 +1,5 @@
 use nom::{
     bytes::complete::{tag, take, take_till},
-    combinator::opt,
     IResult,
 };
 
diff --git a/src/ui.rs b/src/ui.rs
index b97d7a0..ed9ed9a 100644
--- a/src/ui.rs
+++ b/src/ui.rs
@@ -1,8 +1,12 @@
-use std::time::Instant;
+use std::{sync::Arc, thread::JoinHandle, time::Instant};
+
+use egui::mutex::Mutex;
+use midir::{MidiInput, MidiInputPort};
 
 /// Launches the UI and runs it until it's done executing.
 pub fn run() {
     let native_options = eframe::NativeOptions::default();
+
     // TODO: don't ignore result
     let _ = eframe::run_native(
         "Midi Keys",
@@ -13,21 +17,32 @@ pub fn run() {
 
 struct MidiKeysApp {
     previous_frame_time: Instant,
+    midi_input_ports: Arc<Mutex<Vec<MidiInputPort>>>,
+    midi_in: MidiInput,
 }
 
 impl MidiKeysApp {
     fn new(_cc: &eframe::CreationContext<'_>) -> Self {
         // this is where to hook in for customizing eguji, like fonts and visuals.
 
+        let midi_in: MidiInput =
+            MidiInput::new("midi-keys").expect("could not connect to system MIDI");
+        let midi_input_ports = Arc::new(Mutex::new(Vec::new()));
+
+        // TODO: have a way to shut down the midi daemon?
+        let _ = launch_midi_daemon(midi_input_ports.clone());
+
         let previous_frame_time = Instant::now();
         MidiKeysApp {
             previous_frame_time,
+            midi_input_ports,
+            midi_in,
         }
     }
 }
 
 impl eframe::App for MidiKeysApp {
-    fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
+    fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
         let duration = self.previous_frame_time.elapsed().as_secs_f32();
         self.previous_frame_time = Instant::now();
 
@@ -36,6 +51,36 @@ impl eframe::App for MidiKeysApp {
 
             let framerate = format!("{:>8.2}", 1. / duration);
             ui.label(framerate);
+
+            for input_port in self.midi_input_ports.lock().iter() {
+                let port_name = self
+                    .midi_in
+                    .port_name(input_port)
+                    .unwrap_or("unknown".to_string());
+
+                ui.label(port_name);
+            }
         });
     }
 }
+
+pub fn launch_midi_daemon(target_field: Arc<Mutex<Vec<MidiInputPort>>>) -> JoinHandle<()> {
+    let daemon_handle = std::thread::spawn(move || midi_daemon(target_field));
+
+    daemon_handle
+}
+
+pub fn midi_daemon(target_field: Arc<Mutex<Vec<MidiInputPort>>>) {
+    let midi_in: MidiInput = MidiInput::new("midi-keys").expect("could not connect to system MIDI");
+
+    loop {
+        let midi_input_ports = get_connnected_midi_devices(&midi_in);
+        *target_field.lock() = midi_input_ports;
+
+        std::thread::sleep(std::time::Duration::from_millis(100));
+    }
+}
+
+pub fn get_connnected_midi_devices(midi_in: &MidiInput) -> Vec<MidiInputPort> {
+    midi_in.ports()
+}