From 8d11ae87c03653b3df14b7c4caf853e3cbdf0d6a Mon Sep 17 00:00:00 2001 From: unspeaker Date: Thu, 4 Jul 2024 15:38:46 +0300 Subject: [PATCH] chore: further cleanup --- src/core.rs | 136 +++++++++++++++++++++++++++++++++++++++------ src/core/handle.rs | 38 +++++++++++++ src/core/keymap.rs | 39 ------------- src/core/layout.rs | 0 src/core/run.rs | 105 ---------------------------------- 5 files changed, 158 insertions(+), 160 deletions(-) delete mode 100644 src/core/keymap.rs delete mode 100644 src/core/layout.rs delete mode 100644 src/core/run.rs diff --git a/src/core.rs b/src/core.rs index e5b40260..1f121d60 100644 --- a/src/core.rs +++ b/src/core.rs @@ -1,9 +1,31 @@ +// Stdlib dependencies: +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}; +pub use std::sync::atomic::{Ordering, AtomicBool, AtomicUsize}; +pub use std::sync::mpsc::{channel, Sender, Receiver}; + +// Non-stdlib dependencies: +pub use microxdg::XdgApp; +pub use ratatui::prelude::*; +pub use midly::{MidiMessage, live::LiveEvent, num::u7}; +pub use crossterm::{ExecutableCommand, QueueableCommand}; +pub use crossterm::event::{Event, KeyEvent, KeyCode, KeyModifiers}; +use better_panic::{Settings, Verbosity}; +use crossterm::terminal::{ + EnterAlternateScreen, LeaveAlternateScreen, + enable_raw_mode, disable_raw_mode +}; + /// Define and reexport submodules. #[macro_export] macro_rules! submod { ($($name:ident)*) => { $(mod $name; pub use self::$name::*;)* }; } -submod!( handle keymap midi render run time ); +submod!( handle midi render time ); /// Standard result type. pub type Usually = Result>; @@ -36,19 +58,101 @@ pub use crate::{submod, render, handle, process, phrase, keymap, key, ports}; // Reexport JACK proto-lib: pub use crate::jack::*; -// Various stdlib dependencies: -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}; -pub use std::sync::atomic::{Ordering, AtomicBool, AtomicUsize}; -pub use std::sync::mpsc::{channel, Sender, Receiver}; +impl Run for T {} -// Various non-stdlib dependencies: -pub use microxdg::XdgApp; -pub use ratatui::prelude::*; -pub use midly::{MidiMessage, live::LiveEvent, num::u7}; -pub use crossterm::{ExecutableCommand, QueueableCommand}; -pub use crossterm::event::{Event, KeyEvent, KeyCode, KeyModifiers}; +pub trait Run: Render + Handle + Send + Sized + 'static { + fn run (self, init: Option>)->Usually<()>>) -> Usually<()> { + let app = Arc::new(Mutex::new(self)); + let exited = Arc::new(AtomicBool::new(false)); + let _input_thread = input_thread(&exited, &app); + terminal_setup()?; + panic_hook_setup(); + let main_thread = main_thread(&exited, &app)?; + if let Some(init) = init { + init(app)?; + } + main_thread.join().unwrap(); + terminal_teardown()?; + Ok(()) + } +} + +/// Set up panic hook +pub fn panic_hook_setup () { + let better_panic_handler = Settings::auto().verbosity(Verbosity::Full).create_panic_handler(); + std::panic::set_hook(Box::new(move |info: &std::panic::PanicInfo|{ + stdout().execute(LeaveAlternateScreen).unwrap(); + disable_raw_mode().unwrap(); + better_panic_handler(info); + })); +} + +/// Set up terminal +pub fn terminal_setup () -> Usually<()> { + stdout().execute(EnterAlternateScreen)?; + enable_raw_mode()?; + Ok(()) +} +/// Cleanup +pub fn terminal_teardown () -> Usually<()> { + stdout().execute(LeaveAlternateScreen)?; + disable_raw_mode()?; + Ok(()) +} + +/// Main thread render loop +pub fn main_thread ( + exited: &Arc, + device: &Arc> +) -> Usually> { + let exited = exited.clone(); + let device = device.clone(); + let mut terminal = ratatui::Terminal::new(CrosstermBackend::new(stdout()))?; + let sleep = std::time::Duration::from_millis(16); + Ok(spawn(move || 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); + + })) +} +/// Spawn thread that listens for user input +pub fn input_thread ( + exited: &Arc, + device: &Arc> +) -> JoinHandle<()> { + 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 + } + } + } + }) +} diff --git a/src/core/handle.rs b/src/core/handle.rs index 8ba6cd6d..a8151804 100644 --- a/src/core/handle.rs +++ b/src/core/handle.rs @@ -45,3 +45,41 @@ pub enum AppEvent { /// JACK notification Jack(JackEvent) } + +pub type KeyHandler = &'static dyn Fn(&mut T)->Usually; + +pub type KeyBinding = ( + KeyCode, KeyModifiers, &'static str, &'static str, KeyHandler +); + +pub type KeyMap = [KeyBinding]; + +pub fn handle_keymap ( + state: &mut T, event: &AppEvent, keymap: &KeyMap, +) -> Usually { + match event { + AppEvent::Input(Event::Key(event)) => { + for (code, modifiers, _, _, command) in keymap.iter() { + if *code == event.code && modifiers.bits() == event.modifiers.bits() { + return command(state) + } + } + }, + _ => {} + }; + Ok(false) +} + +#[macro_export] macro_rules! key { + ($k:ident $(($char:literal))?, $m:ident, $n: literal, $d: literal, $f: expr) => { + (KeyCode::$k $(($char))?, KeyModifiers::$m, $n, $d, &$f) + }; +} + +#[macro_export] macro_rules! keymap { + ($T:ty { $([$k:ident $(($char:literal))?, $m:ident, $n: literal, $d: literal, $f: expr]),* $(,)? }) => { + &[ + $((KeyCode::$k $(($char))?, KeyModifiers::$m, $n, $d, &$f as KeyHandler<$T>)),* + ] as &'static [KeyBinding<$T>] + } +} diff --git a/src/core/keymap.rs b/src/core/keymap.rs deleted file mode 100644 index d9ea4919..00000000 --- a/src/core/keymap.rs +++ /dev/null @@ -1,39 +0,0 @@ -use crate::core::*; - -pub type KeyHandler = &'static dyn Fn(&mut T)->Usually; - -pub type KeyBinding = ( - KeyCode, KeyModifiers, &'static str, &'static str, KeyHandler -); - -pub type KeyMap = [KeyBinding]; - -pub fn handle_keymap ( - state: &mut T, event: &AppEvent, keymap: &KeyMap, -) -> Usually { - match event { - AppEvent::Input(Event::Key(event)) => { - for (code, modifiers, _, _, command) in keymap.iter() { - if *code == event.code && modifiers.bits() == event.modifiers.bits() { - return command(state) - } - } - }, - _ => {} - }; - Ok(false) -} - -#[macro_export] macro_rules! key { - ($k:ident $(($char:literal))?, $m:ident, $n: literal, $d: literal, $f: expr) => { - (KeyCode::$k $(($char))?, KeyModifiers::$m, $n, $d, &$f) - }; -} - -#[macro_export] macro_rules! keymap { - ($T:ty { $([$k:ident $(($char:literal))?, $m:ident, $n: literal, $d: literal, $f: expr]),* $(,)? }) => { - &[ - $((KeyCode::$k $(($char))?, KeyModifiers::$m, $n, $d, &$f as KeyHandler<$T>)),* - ] as &'static [KeyBinding<$T>] - } -} diff --git a/src/core/layout.rs b/src/core/layout.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/src/core/run.rs b/src/core/run.rs deleted file mode 100644 index 0a9ad3a7..00000000 --- a/src/core/run.rs +++ /dev/null @@ -1,105 +0,0 @@ -use crate::core::*; -use better_panic::{Settings, Verbosity}; -use crossterm::terminal::{ - EnterAlternateScreen, LeaveAlternateScreen, - enable_raw_mode, disable_raw_mode -}; - -impl Run for T {} - -pub trait Run: Render + Handle + Send + Sized + 'static { - fn run (self, init: Option>)->Usually<()>>) -> Usually<()> { - let app = Arc::new(Mutex::new(self)); - let exited = Arc::new(AtomicBool::new(false)); - let _input_thread = input_thread(&exited, &app); - terminal_setup()?; - panic_hook_setup(); - let main_thread = main_thread(&exited, &app)?; - if let Some(init) = init { - init(app)?; - } - main_thread.join().unwrap(); - terminal_teardown()?; - Ok(()) - } -} - -/// Set up panic hook -pub fn panic_hook_setup () { - let better_panic_handler = Settings::auto().verbosity(Verbosity::Full).create_panic_handler(); - std::panic::set_hook(Box::new(move |info: &std::panic::PanicInfo|{ - stdout().execute(LeaveAlternateScreen).unwrap(); - disable_raw_mode().unwrap(); - better_panic_handler(info); - })); -} - -/// Set up terminal -pub fn terminal_setup () -> Usually<()> { - stdout().execute(EnterAlternateScreen)?; - enable_raw_mode()?; - Ok(()) -} -/// Cleanup -pub fn terminal_teardown () -> Usually<()> { - stdout().execute(LeaveAlternateScreen)?; - disable_raw_mode()?; - Ok(()) -} - -/// Main thread render loop -pub fn main_thread ( - exited: &Arc, - device: &Arc> -) -> Usually> { - let exited = exited.clone(); - let device = device.clone(); - let mut terminal = ratatui::Terminal::new(CrosstermBackend::new(stdout()))?; - let sleep = std::time::Duration::from_millis(16); - Ok(spawn(move || 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); - - })) -} -/// Spawn thread that listens for user input -pub fn input_thread ( - exited: &Arc, - device: &Arc> -) -> JoinHandle<()> { - 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 - } - } - } - }) -}