gled 2.28.4

gled is an application for creating animations and effects on artnet or dmx installations
use crate::{
    midi::{monitor, runtime::RuntimeBus, state::new_receiver},
    ui::action::UiAction,
};
use midir::MidiOutput;
use std::{
    collections::HashSet,
    thread::{sleep, spawn},
    time::Duration,
};

fn is_own_port(name: &str) -> bool {
    name.contains("gled_read_input") || name.contains("gled_write_output")
}

fn is_midi_through(name: &str) -> bool {
    name.contains("Midi Through")
}

pub fn discover(runtime_bus: RuntimeBus) {
    #[cfg(feature = "profiling")]
    profiling::register_thread!("midi:output:discover");

    let mut was_active = false;
    let mut handled_devices = HashSet::new();
    let mut output = None;

    loop {
        let ports = {
            let output = output.get_or_insert_with(|| {
                MidiOutput::new("gled_write_output").expect("Could not create midi output")
            });
            output
                .ports()
                .into_iter()
                .filter_map(|port| match output.port_name(&port) {
                    Ok(name) => {
                        if name.trim().is_empty() {
                            UiAction::Error("Midi port name is empty".to_string()).enqueue();
                            None
                        } else {
                            Some((port, name))
                        }
                    }
                    Err(err) => {
                        UiAction::Error(format!("Could not get midi port name: {err:?}")).enqueue();
                        None
                    }
                })
                .collect::<Vec<_>>()
        };
        handled_devices.retain(|name| ports.iter().any(|(_, port_name)| port_name == name));

        for (port, name) in ports {
            let id = port.id();
            if handled_devices.contains(&name) {
                continue;
            }

            if is_own_port(&name) {
                continue;
            }
            if is_midi_through(&name) {
                continue;
            }

            tracing::trace!("Discovered midi output device \"{name}\" at \"{id}\"");
            tracing::info!("Connecting to output of \"{name}\" at \"{id}\"");
            monitor::push_event(&name, "output connected", &[]);
            if let Some(output) = output.take() {
                let mut connection = match output.connect(&port, "gled_write_output") {
                    Ok(connection) => connection,
                    Err(err) => {
                        UiAction::Error(format!("Could not connect to midi output: {err:?}"))
                            .enqueue();
                        monitor::push_event(&name, "output connect failed", &[]);
                        continue;
                    }
                };
                let port_name = name.clone();
                let mut runtime = runtime_bus.runtime();
                spawn(move || {
                    let receiver = new_receiver();

                    for state in receiver {
                        runtime.send_output(&port_name, &state, &mut connection);
                    }
                });
                handled_devices.insert(name);
            }
        }

        match (handled_devices.is_empty(), was_active) {
            (true, true) => {
                was_active = false;
                UiAction::MidiOutputActive(false).enqueue();
            }
            (false, false) => {
                was_active = true;
                UiAction::MidiOutputActive(true).enqueue();
            }
            _ => {}
        }

        sleep(Duration::from_secs(1));
    }
}