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"
speedy2d = { version = "1.12.0", optional = true }
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]
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"]
merscfg = ["mers_lib"]

View File

@ -39,6 +39,8 @@ use crate::{
pub enum GuiEvent {
Refresh,
#[cfg(feature = "merscfg")]
RefreshMers,
UpdatedQueue,
UpdatedLibrary,
Exit,
@ -213,7 +215,7 @@ pub fn main(
let sender = window.create_user_event_sender();
window.run_loop(Gui::new(
font,
database,
Arc::clone(&database),
connection,
Arc::new(Mutex::new(get_con)),
event_sender_arc,
@ -258,7 +260,7 @@ pub fn main(
),
],
#[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_lines_multiplier: f64,
scroll_pages_multiplier: f64,
gui_config: GuiConfig,
#[cfg(not(feature = "merscfg"))] gui_config: GuiConfig,
#[cfg(feature = "merscfg")] mut gui_config: GuiConfig,
) -> Self {
let (notif_overlay, notif_sender) = NotifOverlay::new();
let notif_sender_two = notif_sender.clone();
@ -1195,13 +1198,14 @@ impl WindowHandler<GuiEvent> for Gui {
Rectangle::new(Vec2::ZERO, self.size.into_f32()),
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 mut dblock = dblock.lock().unwrap();
let mut covers = self.covers.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 {
time: draw_start_time,
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) {
match user_event {
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 => {
#[cfg(feature = "merscfg")]
if let Some(mut gc) = self.gui_config.take() {
MersCfg::run(
&mut gc,
self,
self.database.clone().lock().ok().as_mut().map(|v| &mut **v),
|m| &m.func_library_updated,
);
MersCfg::run(&mut gc, self, |m| &m.func_library_updated);
self.gui_config = Some(gc);
} else {
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 => {
#[cfg(feature = "merscfg")]
if let Some(mut gc) = self.gui_config.take() {
MersCfg::run(
&mut gc,
self,
self.database.clone().lock().ok().as_mut().map(|v| &mut **v),
|m| &m.func_queue_updated,
);
MersCfg::run(&mut gc, self, |m| &m.func_queue_updated);
self.gui_config = Some(gc);
} else {
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,
sync::{Arc, Mutex},
thread,
time::Duration,
};
use clap::{Parser, Subcommand};
@ -18,8 +17,9 @@ use musicdb_lib::{
CoverId, SongId,
},
load::ToFromBytes,
server::{get, Command},
server::Command,
};
#[cfg(feature = "speedy2d")]
use speedy2d::color::Color;
#[cfg(feature = "speedy2d")]
mod gui;
@ -27,6 +27,7 @@ mod gui;
mod gui_anim;
#[cfg(feature = "speedy2d")]
mod gui_base;
#[cfg(feature = "speedy2d")]
mod gui_edit_song;
#[cfg(feature = "speedy2d")]
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
#[cfg(feature = "playback")]
SyncplayerNetwork,
#[cfg(feature = "mers")]
RunMers { path: PathBuf },
}
fn get_config_file_path() -> PathBuf {
@ -85,6 +88,10 @@ fn get_config_file_path() -> PathBuf {
}
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
let args = Args::parse();
// start
@ -132,7 +139,8 @@ fn main() {
if let Some(player) = &mut player {
let mut db = database.lock().unwrap();
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();
@ -154,7 +162,7 @@ fn main() {
{
let occasional_refresh_sender = Arc::clone(&sender);
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() {
v.send_event(GuiEvent::Refresh).unwrap();
}
@ -162,7 +170,7 @@ fn main() {
gui::main(
database,
con,
get::Client::new(BufReader::new(
musicdb_lib::server::get::Client::new(BufReader::new(
TcpStream::connect(addr).expect("opening get client connection"),
))
.expect("initializing get client connection"),
@ -174,6 +182,38 @@ fn main() {
Mode::SyncplayerLocal { .. } | Mode::SyncplayerNetwork => {
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 {
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,
};
pub struct OptFunc(Option<mers_lib::data::function::Function>);
pub struct OptFunc(pub Option<mers_lib::data::function::Function>);
impl OptFunc {
pub fn none() -> Self {
Self(None)
@ -65,6 +65,7 @@ impl OptFunc {
/// - `set_idle_screen_side_text_2_format`
pub struct MersCfg {
pub source_file: PathBuf,
pub database: Arc<Mutex<Database>>,
// - - handler functions - -
pub func_before_draw: OptFunc,
pub func_library_updated: OptFunc,
@ -75,6 +76,10 @@ pub struct MersCfg {
pub var_window_size_in_pixels: Arc<RwLock<Data>>,
pub var_idle_screen_cover_aspect_ratio: Arc<RwLock<Data>>,
// - - 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_idle_status: Arc<AtomicU8>,
pub updated_idle_screen_cover_pos: Arc<Updatable<Option<Rectangle>>>,
@ -90,9 +95,10 @@ pub struct MersCfg {
}
impl MersCfg {
pub fn new(path: PathBuf) -> Self {
pub fn new(path: PathBuf, database: Arc<Mutex<Database>>) -> Self {
Self {
source_file: path,
database,
func_before_draw: OptFunc::none(),
func_library_updated: OptFunc::none(),
@ -110,6 +116,7 @@ impl MersCfg {
mers_lib::data::float::Float(0.0),
))),
channel_gui_actions: std::sync::mpsc::channel(),
updated_playing_status: Arc::new(AtomicU8::new(0)),
updated_idle_status: Arc::new(AtomicU8::new(0)),
updated_idle_screen_cover_pos: Arc::new(Updatable::new()),
@ -127,12 +134,19 @@ impl MersCfg {
fn custom_globals(
&self,
cfg: mers_lib::prelude_extend_config::Config,
db: &Arc<Mutex<Database>>,
event_sender: Arc<UserEventSender<GuiEvent>>,
notif_sender: Sender<
Box<dyn FnOnce(&NotifOverlay) -> (Box<dyn GuiElem>, NotifInfo) + Send>,
>,
) -> 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(),
Arc::clone(&self.var_is_playing),
self.var_is_playing.read().unwrap().get().as_type(),
@ -545,14 +559,11 @@ impl MersCfg {
// ]))))
}
pub fn run(
gui_cfg: &mut GuiConfig,
gui: &mut Gui,
mut db: Option<&mut Database>,
run: impl FnOnce(&Self) -> &OptFunc,
) {
// prepare vars
if let Some(db) = &mut db {
pub fn run(gui_cfg: &mut GuiConfig, gui: &mut Gui, run: impl FnOnce(&Self) -> &OptFunc) {
{
let mut db = gui_cfg.merscfg.database.lock().unwrap();
let db = &mut db;
// prepare vars
*gui_cfg.merscfg.var_is_playing.write().unwrap() =
mers_lib::data::Data::new(mers_lib::data::bool::Bool(db.playing));
}
@ -572,6 +583,14 @@ impl 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
match gui_cfg
@ -701,6 +720,7 @@ impl MersCfg {
let (mut i1, mut i2, mut i3) = self
.custom_globals(
mers_lib::prelude_extend_config::Config::new().bundle_std(),
&self.database,
event_sender,
notif_sender,
)

View File

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