fix features, add mers feature, improve merscfg, add run-mers mode

This commit is contained in:
Mark 2024-01-16 20:17:11 +01:00
parent 5ffd71f520
commit d1f2db3e45
5 changed files with 111 additions and 39 deletions

View File

@ -12,9 +12,20 @@ musicdb-lib = { version = "0.1.0", path = "../musicdb-lib" }
regex = "1.9.3" regex = "1.9.3"
speedy2d = { version = "1.12.0", optional = true } speedy2d = { version = "1.12.0", optional = true }
toml = "0.7.6" toml = "0.7.6"
mers_lib = { version = "0.3.1", optional = true } mers_lib = { version = "0.3.2", optional = true }
musicdb-mers = { version = "0.1.0", path = "../musicdb-mers", optional = true }
[features] [features]
default = ["speedy2d"] default = ["gui"]
# gui:
# enables the gui mode
# merscfg:
# allows using mers to configure the gui
# mers:
# enables the run-mers mode
# playback:
# enables Symcplayer modes, where the client mirrors the server's playback
gui = ["speedy2d"]
merscfg = ["mers_lib", "musicdb-mers", "speedy2d"]
mers = ["mers_lib", "musicdb-mers"]
playback = ["musicdb-lib/playback"] playback = ["musicdb-lib/playback"]
merscfg = ["mers_lib"]

View File

@ -39,6 +39,8 @@ use crate::{
pub enum GuiEvent { pub enum GuiEvent {
Refresh, Refresh,
#[cfg(feature = "merscfg")]
RefreshMers,
UpdatedQueue, UpdatedQueue,
UpdatedLibrary, UpdatedLibrary,
Exit, Exit,
@ -213,7 +215,7 @@ pub fn main(
let sender = window.create_user_event_sender(); let sender = window.create_user_event_sender();
window.run_loop(Gui::new( window.run_loop(Gui::new(
font, font,
database, Arc::clone(&database),
connection, connection,
Arc::new(Mutex::new(get_con)), Arc::new(Mutex::new(get_con)),
event_sender_arc, event_sender_arc,
@ -258,7 +260,7 @@ pub fn main(
), ),
], ],
#[cfg(feature = "merscfg")] #[cfg(feature = "merscfg")]
merscfg: crate::merscfg::MersCfg::new(config_dir.join("dynamic_config.mers")), merscfg: crate::merscfg::MersCfg::new(config_dir.join("dynamic_config.mers"), database),
}, },
)); ));
} }
@ -316,7 +318,8 @@ impl Gui {
scroll_pixels_multiplier: f64, scroll_pixels_multiplier: f64,
scroll_lines_multiplier: f64, scroll_lines_multiplier: f64,
scroll_pages_multiplier: f64, scroll_pages_multiplier: f64,
gui_config: GuiConfig, #[cfg(not(feature = "merscfg"))] gui_config: GuiConfig,
#[cfg(feature = "merscfg")] mut gui_config: GuiConfig,
) -> Self { ) -> Self {
let (notif_overlay, notif_sender) = NotifOverlay::new(); let (notif_overlay, notif_sender) = NotifOverlay::new();
let notif_sender_two = notif_sender.clone(); let notif_sender_two = notif_sender.clone();
@ -1195,13 +1198,14 @@ impl WindowHandler<GuiEvent> for Gui {
Rectangle::new(Vec2::ZERO, self.size.into_f32()), Rectangle::new(Vec2::ZERO, self.size.into_f32()),
Color::BLACK, Color::BLACK,
); );
let mut cfg = self.gui_config.take().unwrap();
// before the db is locked!
#[cfg(feature = "merscfg")]
MersCfg::run(&mut cfg, self, |m| &m.func_before_draw);
let dblock = Arc::clone(&self.database); let dblock = Arc::clone(&self.database);
let mut dblock = dblock.lock().unwrap(); let mut dblock = dblock.lock().unwrap();
let mut covers = self.covers.take().unwrap(); let mut covers = self.covers.take().unwrap();
let mut custom_images = self.custom_images.take().unwrap(); let mut custom_images = self.custom_images.take().unwrap();
let mut cfg = self.gui_config.take().unwrap();
#[cfg(feature = "merscfg")]
MersCfg::run(&mut cfg, self, Some(&mut dblock), |m| &m.func_before_draw);
let mut info = DrawInfo { let mut info = DrawInfo {
time: draw_start_time, time: draw_start_time,
actions: Vec::with_capacity(0), actions: Vec::with_capacity(0),
@ -1450,15 +1454,17 @@ impl WindowHandler<GuiEvent> for Gui {
fn on_user_event(&mut self, helper: &mut WindowHelper<GuiEvent>, user_event: GuiEvent) { fn on_user_event(&mut self, helper: &mut WindowHelper<GuiEvent>, user_event: GuiEvent) {
match user_event { match user_event {
GuiEvent::Refresh => helper.request_redraw(), GuiEvent::Refresh => helper.request_redraw(),
#[cfg(feature = "merscfg")]
GuiEvent::RefreshMers => {
if let Some(mut cfg) = self.gui_config.take() {
MersCfg::run(&mut cfg, self, |cfg| &crate::merscfg::OptFunc(None));
self.gui_config = Some(cfg);
}
}
GuiEvent::UpdatedLibrary => { GuiEvent::UpdatedLibrary => {
#[cfg(feature = "merscfg")] #[cfg(feature = "merscfg")]
if let Some(mut gc) = self.gui_config.take() { if let Some(mut gc) = self.gui_config.take() {
MersCfg::run( MersCfg::run(&mut gc, self, |m| &m.func_library_updated);
&mut gc,
self,
self.database.clone().lock().ok().as_mut().map(|v| &mut **v),
|m| &m.func_library_updated,
);
self.gui_config = Some(gc); self.gui_config = Some(gc);
} else { } else {
eprintln!("WARN: Skipping call to merscfg's library_updated because gui_config is not available"); eprintln!("WARN: Skipping call to merscfg's library_updated because gui_config is not available");
@ -1469,12 +1475,7 @@ impl WindowHandler<GuiEvent> for Gui {
GuiEvent::UpdatedQueue => { GuiEvent::UpdatedQueue => {
#[cfg(feature = "merscfg")] #[cfg(feature = "merscfg")]
if let Some(mut gc) = self.gui_config.take() { if let Some(mut gc) = self.gui_config.take() {
MersCfg::run( MersCfg::run(&mut gc, self, |m| &m.func_queue_updated);
&mut gc,
self,
self.database.clone().lock().ok().as_mut().map(|v| &mut **v),
|m| &m.func_queue_updated,
);
self.gui_config = Some(gc); self.gui_config = Some(gc);
} else { } else {
eprintln!("WARN: Skipping call to merscfg's queue_updated because gui_config is not available"); eprintln!("WARN: Skipping call to merscfg's queue_updated because gui_config is not available");

View File

@ -4,7 +4,6 @@ use std::{
path::PathBuf, path::PathBuf,
sync::{Arc, Mutex}, sync::{Arc, Mutex},
thread, thread,
time::Duration,
}; };
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
@ -18,8 +17,9 @@ use musicdb_lib::{
CoverId, SongId, CoverId, SongId,
}, },
load::ToFromBytes, load::ToFromBytes,
server::{get, Command}, server::Command,
}; };
#[cfg(feature = "speedy2d")]
use speedy2d::color::Color; use speedy2d::color::Color;
#[cfg(feature = "speedy2d")] #[cfg(feature = "speedy2d")]
mod gui; mod gui;
@ -27,6 +27,7 @@ mod gui;
mod gui_anim; mod gui_anim;
#[cfg(feature = "speedy2d")] #[cfg(feature = "speedy2d")]
mod gui_base; mod gui_base;
#[cfg(feature = "speedy2d")]
mod gui_edit_song; mod gui_edit_song;
#[cfg(feature = "speedy2d")] #[cfg(feature = "speedy2d")]
mod gui_idle_display; mod gui_idle_display;
@ -75,6 +76,8 @@ enum Mode {
/// play in sync with the server, and fetch the songs from it too. slower than the local variant for obvious reasons /// play in sync with the server, and fetch the songs from it too. slower than the local variant for obvious reasons
#[cfg(feature = "playback")] #[cfg(feature = "playback")]
SyncplayerNetwork, SyncplayerNetwork,
#[cfg(feature = "mers")]
RunMers { path: PathBuf },
} }
fn get_config_file_path() -> PathBuf { fn get_config_file_path() -> PathBuf {
@ -85,6 +88,10 @@ fn get_config_file_path() -> PathBuf {
} }
fn main() { fn main() {
#[cfg(not(feature = "speedy2d"))]
#[cfg(not(feature = "mers"))]
#[cfg(not(feature = "playback"))]
compile_error!("None of the optional features are enabled. Without at least one of these, the application is useless! See Cargo.toml for info.");
// parse args // parse args
let args = Args::parse(); let args = Args::parse();
// start // start
@ -132,7 +139,8 @@ fn main() {
if let Some(player) = &mut player { if let Some(player) = &mut player {
let mut db = database.lock().unwrap(); let mut db = database.lock().unwrap();
if db.is_client_init() { if db.is_client_init() {
player.update(&mut db); // command_sender does nothing. if a song finishes, we don't want to move to the next song, we want to wait for the server to send the NextSong event.
player.update(&mut db, &Arc::new(|_| {}));
} }
} }
let update = Command::from_bytes(&mut con).unwrap(); let update = Command::from_bytes(&mut con).unwrap();
@ -154,7 +162,7 @@ fn main() {
{ {
let occasional_refresh_sender = Arc::clone(&sender); let occasional_refresh_sender = Arc::clone(&sender);
thread::spawn(move || loop { thread::spawn(move || loop {
std::thread::sleep(Duration::from_secs(1)); std::thread::sleep(std::time::Duration::from_secs(1));
if let Some(v) = &*occasional_refresh_sender.lock().unwrap() { if let Some(v) = &*occasional_refresh_sender.lock().unwrap() {
v.send_event(GuiEvent::Refresh).unwrap(); v.send_event(GuiEvent::Refresh).unwrap();
} }
@ -162,7 +170,7 @@ fn main() {
gui::main( gui::main(
database, database,
con, con,
get::Client::new(BufReader::new( musicdb_lib::server::get::Client::new(BufReader::new(
TcpStream::connect(addr).expect("opening get client connection"), TcpStream::connect(addr).expect("opening get client connection"),
)) ))
.expect("initializing get client connection"), .expect("initializing get client connection"),
@ -174,6 +182,38 @@ fn main() {
Mode::SyncplayerLocal { .. } | Mode::SyncplayerNetwork => { Mode::SyncplayerLocal { .. } | Mode::SyncplayerNetwork => {
con_thread.join().unwrap(); con_thread.join().unwrap();
} }
#[cfg(feature = "mers")]
Mode::RunMers { path } => {
let mut src = mers_lib::prelude_compile::Source::new_from_file(path).unwrap();
let srca = Arc::new(src.clone());
let con = Mutex::new(con);
let (mut i1, mut i2, mut i3) = musicdb_mers::add(
mers_lib::prelude_compile::Config::new().bundle_std(),
&database,
&Arc::new(move |cmd: Command| cmd.to_bytes(&mut *con.lock().unwrap()).unwrap()),
)
.infos();
let program = mers_lib::prelude_compile::parse(&mut src, &srca)
.unwrap()
.compile(&mut i1, mers_lib::prelude_compile::CompInfo::default())
.unwrap();
match program.check(&mut i3, None) {
Ok(_) => {}
Err(e) => {
eprintln!("{e}");
std::process::exit(60);
}
};
// wait until db is synced
let dur = std::time::Duration::from_secs_f32(0.1);
loop {
std::thread::sleep(dur);
if database.lock().unwrap().is_client_init() {
break;
}
}
program.run(&mut i2);
}
} }
} }
@ -198,6 +238,7 @@ fn get_cover(song: SongId, database: &Database) -> Option<CoverId> {
} }
} }
#[cfg(feature = "speedy2d")]
pub(crate) fn color_scale(c: Color, r: f32, g: f32, b: f32, new_alpha: Option<f32>) -> Color { pub(crate) fn color_scale(c: Color, r: f32, g: f32, b: f32, new_alpha: Option<f32>) -> Color {
Color::from_rgba(c.r() * r, c.g() * g, c.b() * b, new_alpha.unwrap_or(c.a())) Color::from_rgba(c.r() * r, c.g() * g, c.b() * b, new_alpha.unwrap_or(c.a()))
} }

View File

@ -24,7 +24,7 @@ use crate::{
textcfg::TextBuilder, textcfg::TextBuilder,
}; };
pub struct OptFunc(Option<mers_lib::data::function::Function>); pub struct OptFunc(pub Option<mers_lib::data::function::Function>);
impl OptFunc { impl OptFunc {
pub fn none() -> Self { pub fn none() -> Self {
Self(None) Self(None)
@ -65,6 +65,7 @@ impl OptFunc {
/// - `set_idle_screen_side_text_2_format` /// - `set_idle_screen_side_text_2_format`
pub struct MersCfg { pub struct MersCfg {
pub source_file: PathBuf, pub source_file: PathBuf,
pub database: Arc<Mutex<Database>>,
// - - handler functions - - // - - handler functions - -
pub func_before_draw: OptFunc, pub func_before_draw: OptFunc,
pub func_library_updated: OptFunc, pub func_library_updated: OptFunc,
@ -75,6 +76,10 @@ pub struct MersCfg {
pub var_window_size_in_pixels: Arc<RwLock<Data>>, pub var_window_size_in_pixels: Arc<RwLock<Data>>,
pub var_idle_screen_cover_aspect_ratio: Arc<RwLock<Data>>, pub var_idle_screen_cover_aspect_ratio: Arc<RwLock<Data>>,
// - - results from running functions - - // - - results from running functions - -
pub channel_gui_actions: (
std::sync::mpsc::Sender<Command>,
std::sync::mpsc::Receiver<Command>,
),
pub updated_playing_status: Arc<AtomicU8>, pub updated_playing_status: Arc<AtomicU8>,
pub updated_idle_status: Arc<AtomicU8>, pub updated_idle_status: Arc<AtomicU8>,
pub updated_idle_screen_cover_pos: Arc<Updatable<Option<Rectangle>>>, pub updated_idle_screen_cover_pos: Arc<Updatable<Option<Rectangle>>>,
@ -90,9 +95,10 @@ pub struct MersCfg {
} }
impl MersCfg { impl MersCfg {
pub fn new(path: PathBuf) -> Self { pub fn new(path: PathBuf, database: Arc<Mutex<Database>>) -> Self {
Self { Self {
source_file: path, source_file: path,
database,
func_before_draw: OptFunc::none(), func_before_draw: OptFunc::none(),
func_library_updated: OptFunc::none(), func_library_updated: OptFunc::none(),
@ -110,6 +116,7 @@ impl MersCfg {
mers_lib::data::float::Float(0.0), mers_lib::data::float::Float(0.0),
))), ))),
channel_gui_actions: std::sync::mpsc::channel(),
updated_playing_status: Arc::new(AtomicU8::new(0)), updated_playing_status: Arc::new(AtomicU8::new(0)),
updated_idle_status: Arc::new(AtomicU8::new(0)), updated_idle_status: Arc::new(AtomicU8::new(0)),
updated_idle_screen_cover_pos: Arc::new(Updatable::new()), updated_idle_screen_cover_pos: Arc::new(Updatable::new()),
@ -127,12 +134,19 @@ impl MersCfg {
fn custom_globals( fn custom_globals(
&self, &self,
cfg: mers_lib::prelude_extend_config::Config, cfg: mers_lib::prelude_extend_config::Config,
db: &Arc<Mutex<Database>>,
event_sender: Arc<UserEventSender<GuiEvent>>, event_sender: Arc<UserEventSender<GuiEvent>>,
notif_sender: Sender< notif_sender: Sender<
Box<dyn FnOnce(&NotifOverlay) -> (Box<dyn GuiElem>, NotifInfo) + Send>, Box<dyn FnOnce(&NotifOverlay) -> (Box<dyn GuiElem>, NotifInfo) + Send>,
>, >,
) -> mers_lib::prelude_extend_config::Config { ) -> mers_lib::prelude_extend_config::Config {
cfg.add_var_arc( let cmd_es = event_sender.clone();
let cmd_ga = self.channel_gui_actions.0.clone();
musicdb_mers::add(cfg, db, &Arc::new(move |cmd| {
cmd_ga.send(cmd).unwrap();
cmd_es.send_event(GuiEvent::RefreshMers).unwrap();
}))
.add_var_arc(
"is_playing".to_owned(), "is_playing".to_owned(),
Arc::clone(&self.var_is_playing), Arc::clone(&self.var_is_playing),
self.var_is_playing.read().unwrap().get().as_type(), self.var_is_playing.read().unwrap().get().as_type(),
@ -545,14 +559,11 @@ impl MersCfg {
// ])))) // ]))))
} }
pub fn run( pub fn run(gui_cfg: &mut GuiConfig, gui: &mut Gui, run: impl FnOnce(&Self) -> &OptFunc) {
gui_cfg: &mut GuiConfig, {
gui: &mut Gui, let mut db = gui_cfg.merscfg.database.lock().unwrap();
mut db: Option<&mut Database>, let db = &mut db;
run: impl FnOnce(&Self) -> &OptFunc,
) {
// prepare vars // prepare vars
if let Some(db) = &mut db {
*gui_cfg.merscfg.var_is_playing.write().unwrap() = *gui_cfg.merscfg.var_is_playing.write().unwrap() =
mers_lib::data::Data::new(mers_lib::data::bool::Bool(db.playing)); mers_lib::data::Data::new(mers_lib::data::bool::Bool(db.playing));
} }
@ -572,6 +583,14 @@ impl MersCfg {
// run // run
run(&gui_cfg.merscfg).run(); run(&gui_cfg.merscfg).run();
loop {
if let Ok(a) = gui_cfg.merscfg.channel_gui_actions.1.try_recv() {
gui.exec_gui_action(GuiAction::SendToServer(a));
} else {
break;
}
}
// apply updates // apply updates
match gui_cfg match gui_cfg
@ -701,6 +720,7 @@ impl MersCfg {
let (mut i1, mut i2, mut i3) = self let (mut i1, mut i2, mut i3) = self
.custom_globals( .custom_globals(
mers_lib::prelude_extend_config::Config::new().bundle_std(), mers_lib::prelude_extend_config::Config::new().bundle_std(),
&self.database,
event_sender, event_sender,
notif_sender, notif_sender,
) )

View File

@ -1,6 +1,5 @@
use std::{ use std::{
collections::{BTreeSet, HashMap, HashSet}, collections::{BTreeSet, HashMap},
convert::identity,
fs::{self, File}, fs::{self, File},
io::{BufReader, Read, Write}, io::{BufReader, Read, Write},
path::PathBuf, path::PathBuf,