mirror of
https://codeberg.org/unspeaker/tengri.git
synced 2026-09-18 05:16:44 +02:00
This commit is contained in:
parent
92f992e2e6
commit
8e7286e409
6 changed files with 123 additions and 47 deletions
99
src/lib.rs
99
src/lib.rs
|
|
@ -68,9 +68,32 @@ mod deps; pub use self::deps::*;
|
|||
}
|
||||
|
||||
/// Implement [`Debug`] in bulk.
|
||||
#[macro_export] macro_rules! impl_debug (($($S:ty|$self:ident,$w:ident|$body:block)*)=>{
|
||||
$(impl std::fmt::Debug for $S { fn fmt (&$self, $w: &mut std::fmt::Formatter) -> std::fmt::Result $body })*
|
||||
});
|
||||
#[macro_export] macro_rules! impl_debug (
|
||||
(<$($T:ident $(: $U:ident)?),+> $S:ty|$self:ident,$w:ident|$body:block)=>{
|
||||
impl <$($T$(:$U)?),+> std::fmt::Debug for $S {
|
||||
fn fmt (&$self, $w: &mut std::fmt::Formatter) -> std::fmt::Result $body
|
||||
}
|
||||
};
|
||||
($S:ty|$self:ident,$w:ident|$body:block)=>{
|
||||
impl std::fmt::Debug for $S {
|
||||
fn fmt (&$self, $w: &mut std::fmt::Formatter) -> std::fmt::Result $body
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
/// Implement [`Display`] in bulk.
|
||||
#[macro_export] macro_rules! impl_display (
|
||||
(<$($T:ident $(: $U:ident)?),+> $S:ty|$self:ident,$w:ident|$body:block)=>{
|
||||
impl <$($T$(:$U)?),+> std::fmt::Display for $S {
|
||||
fn fmt (&$self, $w: &mut std::fmt::Formatter) -> std::fmt::Result $body
|
||||
}
|
||||
};
|
||||
($S:ty|$self:ident,$w:ident|$body:block)=>{
|
||||
impl std::fmt::Display for $S {
|
||||
fn fmt (&$self, $w: &mut std::fmt::Formatter) -> std::fmt::Result $body
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
/// Implement [`From`] in bulk.
|
||||
#[macro_export] macro_rules! impl_from (
|
||||
|
|
@ -280,7 +303,7 @@ pub trait AsMutOpt<T> { fn as_mut_opt (&mut self) -> Option<&mut T>; }
|
|||
}
|
||||
/// Run something with the client.
|
||||
pub fn with_client <T> (&self, op: impl FnOnce(&Client)->T) -> T {
|
||||
match &*self.0.read().unwrap() {
|
||||
match &*self.0.try_read().unwrap() {
|
||||
Inert => panic!("jack client not activated"),
|
||||
Inactive(client) => op(client),
|
||||
Activating => panic!("jack client has not finished activation"),
|
||||
|
|
@ -293,14 +316,14 @@ pub trait AsMutOpt<T> { fn as_mut_opt (&mut self) -> Option<&mut T>; }
|
|||
let client_state = self.0.clone();
|
||||
let app: Arc<RwLock<T>> = Arc::new(RwLock::new(init(self)?));
|
||||
let mut state = Activating;
|
||||
std::mem::swap(&mut*client_state.write().unwrap(), &mut state);
|
||||
std::mem::swap(&mut*client_state.try_write().unwrap(), &mut state);
|
||||
if let Inactive(client) = state {
|
||||
// This is the misc notifications handler. It's a struct that wraps a [Box]
|
||||
// which performs type erasure on a callback that takes [JackEvent], which is
|
||||
// one of the available misc notifications.
|
||||
let notify = JackNotify(Box::new({
|
||||
let app = app.clone();
|
||||
move|event|(&mut*app.write().unwrap()).handle(event)
|
||||
move|event|(&mut*app.try_write().unwrap()).handle(event)
|
||||
}) as BoxedJackEventHandler);
|
||||
// This is the main processing handler. It's a struct that wraps a [Box]
|
||||
// which performs type erasure on a callback that takes [Client] and [ProcessScope]
|
||||
|
|
@ -308,14 +331,14 @@ pub trait AsMutOpt<T> { fn as_mut_opt (&mut self) -> Option<&mut T>; }
|
|||
// implements audio and MIDI input and output on a realtime basis.
|
||||
let process = ::jack::contrib::ClosureProcessHandler::new(Box::new({
|
||||
let app = app.clone();
|
||||
move|c: &_, s: &_|if let Ok(mut app) = app.write() {
|
||||
move|c: &_, s: &_|if let Some(mut app) = app.try_write() {
|
||||
app.process(c, s)
|
||||
} else {
|
||||
Control::Quit
|
||||
}
|
||||
}) as BoxedAudioHandler);
|
||||
// Launch a client with the two handlers.
|
||||
*client_state.write().unwrap() = Active(
|
||||
*client_state.try_write().unwrap() = Active(
|
||||
client.activate_async(notify, process)?
|
||||
);
|
||||
} else {
|
||||
|
|
@ -445,7 +468,7 @@ pub trait AsMutOpt<T> { fn as_mut_opt (&mut self) -> Option<&mut T>; }
|
|||
fn callback (
|
||||
state: &Arc<RwLock<Self>>, client: &Client, scope: &ProcessScope
|
||||
) -> Control where Self: Sized {
|
||||
if let Ok(mut state) = state.write() {
|
||||
if let Some(mut state) = state.try_write() {
|
||||
state.process(client, scope)
|
||||
} else {
|
||||
Control::Quit
|
||||
|
|
@ -612,10 +635,10 @@ pub trait AsMutOpt<T> { fn as_mut_opt (&mut self) -> Option<&mut T>; }
|
|||
for connect in self.connections().iter() {
|
||||
match &connect.name {
|
||||
Some(Exact(name)) => {
|
||||
*connect.status.write().unwrap() = self.connect_exact(name)?;
|
||||
*connect.status.try_write().unwrap() = self.connect_exact(name)?;
|
||||
},
|
||||
Some(RegExp(re)) => {
|
||||
*connect.status.write().unwrap() = self.connect_regexp(re, connect.scope)?;
|
||||
*connect.status.try_write().unwrap() = self.connect_regexp(re, connect.scope)?;
|
||||
},
|
||||
_ => {},
|
||||
};
|
||||
|
|
@ -922,7 +945,7 @@ pub trait AsMutOpt<T> { fn as_mut_opt (&mut self) -> Option<&mut T>; }
|
|||
self.output_buffer[sample].push(self.note_buffer.clone());
|
||||
// Update the list of currently held notes.
|
||||
if let LiveEvent::Midi { ref message, .. } = event {
|
||||
update_keys(&mut*self.held.write().unwrap(), message);
|
||||
update_keys(&mut*self.held.try_write().unwrap(), message);
|
||||
}
|
||||
}
|
||||
/// Write a chunk of MIDI data from the output buffer to the output port.
|
||||
|
|
@ -1079,7 +1102,7 @@ pub trait AsMutOpt<T> { fn as_mut_opt (&mut self) -> Option<&mut T>; }
|
|||
|
||||
pub fn info (&self) -> Arc<str> {
|
||||
format!(" ({}) {} {}", {
|
||||
let status = self.status.read().unwrap();
|
||||
let status = self.status.try_read().unwrap();
|
||||
let mut ok = 0;
|
||||
for (_, _, state) in status.iter() {
|
||||
if *state == Connected {
|
||||
|
|
@ -1167,6 +1190,8 @@ pub trait AsMutOpt<T> { fn as_mut_opt (&mut self) -> Option<&mut T>; }
|
|||
use crate::time::PerfModel;
|
||||
|
||||
#[derive(Debug)] pub struct Task {
|
||||
/// Human-friendly name
|
||||
pub name: Arc<str>,
|
||||
/// Exit flag.
|
||||
pub exit: Arc<AtomicBool>,
|
||||
/// Performance counter.
|
||||
|
|
@ -1177,30 +1202,37 @@ pub trait AsMutOpt<T> { fn as_mut_opt (&mut self) -> Option<&mut T>; }
|
|||
|
||||
impl Task {
|
||||
/// Spawn a TUI thread that runs `callt least one, then repeats until `exit`.
|
||||
pub fn new <F> (exit: Arc<AtomicBool>, mut call: F) -> Result<Self, std::io::Error>
|
||||
where F: FnMut(&PerfModel)->() + Send + Sync + 'static
|
||||
{
|
||||
pub fn new <F: FnMut(&PerfModel)->() + Send + Sync + 'static> (
|
||||
name: Option<impl AsRef<str>>,
|
||||
exit: Arc<AtomicBool>,
|
||||
mut call: F
|
||||
) -> Result<Self, std::io::Error> {
|
||||
let perf = Arc::new(PerfModel::default());
|
||||
let name: Arc<str> = name.map(|x|x.as_ref().into()).unwrap_or_else(||"tengri".into());
|
||||
Ok(Self {
|
||||
exit: exit.clone(),
|
||||
perf: perf.clone(),
|
||||
join: Builder::new().name("tengri task".into()).spawn(move || {
|
||||
join: Builder::new().name(name.as_ref().into()).spawn(move || {
|
||||
#[cfg(feature = "prof")] profiling::register_thread!();
|
||||
while !exit.fetch_and(true, Relaxed) {
|
||||
let _ = perf.cycle(&mut call);
|
||||
}
|
||||
})?.into()
|
||||
})?.into(),
|
||||
name
|
||||
})
|
||||
}
|
||||
|
||||
/// Spawn a thread that runs `call` least one, then repeats
|
||||
/// until `exit`, sleeping for `time` msec after every iteration.
|
||||
pub fn new_sleep <F> (
|
||||
exit: Arc<AtomicBool>, time: Duration, mut call: F
|
||||
name: Option<impl AsRef<str>>,
|
||||
exit: Arc<AtomicBool>,
|
||||
time: Duration,
|
||||
mut call: F
|
||||
) -> Result<Self, std::io::Error>
|
||||
where F: FnMut(&PerfModel)->() + Send + Sync + 'static
|
||||
{
|
||||
Self::new(exit, move |perf| {
|
||||
Self::new(name, exit, move |perf| {
|
||||
let _ = call(perf);
|
||||
sleep(time);
|
||||
})
|
||||
|
|
@ -1209,11 +1241,14 @@ pub trait AsMutOpt<T> { fn as_mut_opt (&mut self) -> Option<&mut T>; }
|
|||
/// Spawn a thread that uses [crossterm::event::poll]
|
||||
/// to run `call` every `time` msec.
|
||||
#[cfg(feature = "term")] pub fn new_poll <F> (
|
||||
exit: Arc<AtomicBool>, time: Duration, mut call: F
|
||||
name: Option<impl AsRef<str>>,
|
||||
exit: Arc<AtomicBool>,
|
||||
time: Duration,
|
||||
mut call: F
|
||||
) -> Result<Self, std::io::Error>
|
||||
where F: FnMut(&PerfModel)->() + Send + Sync + 'static
|
||||
{
|
||||
Self::new(exit, move |perf| {
|
||||
Self::new(name, exit, move |perf| {
|
||||
if poll(time).is_ok() {
|
||||
let _ = call(perf);
|
||||
}
|
||||
|
|
@ -1366,7 +1401,19 @@ pub trait AsMutOpt<T> { fn as_mut_opt (&mut self) -> Option<&mut T>; }
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, S: Screen, D: Draw<S>> Draw<S> for Option<D> {
|
||||
impl<S: Screen, D: Draw<S>> Draw<S> for Arc<D> {
|
||||
fn draw (&self, to: &mut S) -> Drawn<S::Unit> {
|
||||
(**self).draw(to)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Screen, D: Draw<S>> Draw<S> for Box<D> {
|
||||
fn draw (&self, to: &mut S) -> Drawn<S::Unit> {
|
||||
(**self).draw(to)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Screen, D: Draw<S>> Draw<S> for Option<D> {
|
||||
fn draw (&self, to: &mut S) -> Drawn<S::Unit> {
|
||||
self.as_ref().map(|it|it.draw(to)).transpose().map(Option::unwrap_or_default)
|
||||
}
|
||||
|
|
@ -1378,12 +1425,6 @@ pub trait AsMutOpt<T> { fn as_mut_opt (&mut self) -> Option<&mut T>; }
|
|||
//}
|
||||
//}
|
||||
|
||||
//impl<T: Screen, D: Draw<T>> Draw<T> for Arc<D> {
|
||||
//fn draw (&self, __: &mut T) -> Perhaps<XYWH<T::Unit>> {
|
||||
//todo!()
|
||||
//}
|
||||
//}
|
||||
|
||||
impl<'a, T: Screen, V: Draw<T>> Draw<T> for &V {
|
||||
fn draw (&self, to: &mut T) -> Drawn<T::Unit> {
|
||||
(*self).draw(to)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue