modularize core

This commit is contained in:
🪞👃🪞 2024-06-30 22:47:17 +03:00
parent a4061535b5
commit 2837ffff4a
43 changed files with 629 additions and 770 deletions

121
src/core/mod.rs Normal file
View file

@ -0,0 +1,121 @@
pub type Usually<T> = Result<T, Box<dyn Error>>;
macro_rules! submod {
($($name:ident)*) => { $(mod $name; pub use self::$name::*;)* };
}
submod!( device handle jack keymap note port process render time );
pub use std::error::Error;
pub use std::io::{stdout, Stdout, Write};
pub use std::thread::{spawn, JoinHandle};
pub use std::time::Duration;
pub use std::collections::BTreeMap;
pub use std::sync::{
Arc,
Mutex, MutexGuard,
atomic::{
Ordering,
AtomicBool,
AtomicUsize
},
mpsc::{self, channel, Sender, Receiver}
};
pub use ::crossterm::{
ExecutableCommand, QueueableCommand,
event::{Event, KeyEvent, KeyCode, KeyModifiers},
terminal::{
self,
Clear, ClearType,
EnterAlternateScreen, LeaveAlternateScreen,
enable_raw_mode, disable_raw_mode
},
};
pub use ::ratatui::{
prelude::{
Buffer, Rect, Style, Color, CrosstermBackend, Layout, Stylize, Direction,
Line, Constraint
},
widgets::{Widget, WidgetRef},
//style::Stylize,
};
pub use ::midly::{
MidiMessage,
live::LiveEvent,
num::u7
};
pub use crate::{key, keymap};
/// Run a device as the root of the app.
pub fn run (device: impl Device + Send + Sync + 'static) -> Result<(), Box<dyn Error>> {
let device = Arc::new(Mutex::new(device));
let exited = Arc::new(AtomicBool::new(false));
// Spawn input (handle) thread
let _input_thread = {
let poll = std::time::Duration::from_millis(100);
let exited = exited.clone();
let device = device.clone();
spawn(move || loop {
// Exit if flag is set
if exited.fetch_and(true, Ordering::Relaxed) {
break
}
// Listen for events and send them to the main thread
if ::crossterm::event::poll(poll).is_ok() {
let event = ::crossterm::event::read().unwrap();
match event {
Event::Key(KeyEvent {
code: KeyCode::Char('c'),
modifiers: KeyModifiers::CONTROL,
..
}) => {
exited.store(true, Ordering::Relaxed);
},
_ => if device.lock().unwrap().handle(&AppEvent::Input(event)).is_err() {
break
}
}
}
})
};
// Set up terminal
stdout().execute(EnterAlternateScreen)?;
enable_raw_mode()?;
let mut terminal = ratatui::Terminal::new(CrosstermBackend::new(stdout()))?;
// Set up panic hook
let better_panic_handler = better_panic::Settings::auto()
.verbosity(better_panic::Verbosity::Full)
.create_panic_handler();
std::panic::set_hook(Box::new(move |info: &std::panic::PanicInfo|{
stdout().execute(LeaveAlternateScreen).unwrap();
crossterm::terminal::disable_raw_mode().unwrap();
better_panic_handler(info);
}));
// Main (render) loop
let sleep = std::time::Duration::from_millis(16);
loop {
terminal.draw(|frame|{
let area = frame.size();
let buffer = frame.buffer_mut();
device.lock().unwrap().render(buffer, area).expect("Failed to render content.");
}).expect("Failed to render frame");
if exited.fetch_and(true, Ordering::Relaxed) {
break
}
std::thread::sleep(sleep);
};
// Cleanup
stdout().execute(LeaveAlternateScreen)?;
crossterm::terminal::disable_raw_mode()?;
Ok(())
}