mod debug_log; mod display; mod mpd_handler; #[cfg(feature = "unicode_support")] mod unicode_support; use ggez::conf::{WindowMode, WindowSetup}; use ggez::event::winit_event::{ElementState, KeyboardInput, ModifiersState}; use ggez::event::{self, ControlFlow, EventHandler}; use ggez::input::keyboard::{self, KeyInput}; use ggez::{ContextBuilder, GameError}; use std::fs::File; use std::io::Read; use std::net::Ipv4Addr; use std::path::PathBuf; use std::thread; use std::time::{Duration, Instant}; use clap::Parser; use debug_log::log; #[derive(Parser, Debug, Clone)] #[command(author, version, about, long_about = None)] pub struct Opt { host: Ipv4Addr, #[arg(default_value = "6600")] port: u16, #[arg(short = 'p')] password: Option, #[arg(long = "disable-show-title", help = "disable title display")] disable_show_title: bool, #[arg(long = "disable-show-artist", help = "disable artist display")] disable_show_artist: bool, #[arg(long = "disable-show-album", help = "disable album display")] disable_show_album: bool, #[arg(long = "disable-show-filename", help = "disable filename display")] disable_show_filename: bool, #[arg(long = "pprompt", help = "input password via prompt")] enable_prompt_password: bool, #[arg(long = "pfile", help = "read password from file")] password_file: Option, #[arg( long = "no-scale-fill", help = "don't scale-fill the album art to the window" )] do_not_fill_scale_album_art: bool, #[arg( short = 'l', long = "log-level", default_value = "error", )] log_level: debug_log::LogLevel, #[arg( short, long, help = "sets the opacity of the text background (0-255)", default_value = "190" )] text_bg_opacity: u8, } fn main() -> Result<(), String> { let mut opt = Opt::parse(); println!("Got host addr == {}, port == {}", opt.host, opt.port); // Read password from file if exists, error otherwise. if let Some(psswd_file_path) = opt.password_file.as_ref() { let mut file = File::open(psswd_file_path).expect("pfile/password_file should exist"); let mut content: String = String::new(); file.read_to_string(&mut content) .expect("Should be able to read from pfile/password_file"); if content.ends_with("\r\n") { content.truncate(content.len() - 2); } else if content.ends_with('\n') { content.truncate(content.len() - 1); } opt.password = Some(content); } let (mut ctx, event_loop) = ContextBuilder::new("mpd_info_screen", "Stephen Seo") .window_setup(WindowSetup { title: "mpd info screen".into(), ..Default::default() }) .window_mode(WindowMode { resizable: true, ..Default::default() }) .build() .expect("Failed to create ggez context"); // mount "/" read-only so that fonts can be loaded via absolute paths ctx.fs.mount(&PathBuf::from("/"), true); let mut display = display::MPDDisplay::new(&mut ctx, opt.clone()); let mut modifiers_state: ModifiersState = ModifiersState::default(); event_loop.run(move |mut event, _window_target, control_flow| { if !ctx.continuing { *control_flow = ControlFlow::Exit; return; } *control_flow = ControlFlow::WaitUntil(Instant::now() + Duration::from_millis(100)); let ctx = &mut ctx; event::process_event(ctx, &mut event); match event { event::winit_event::Event::WindowEvent { event, .. } => match event { event::winit_event::WindowEvent::CloseRequested => ctx.request_quit(), event::winit_event::WindowEvent::ModifiersChanged(state) => { modifiers_state = state; } event::winit_event::WindowEvent::KeyboardInput { device_id: _, input: KeyboardInput { virtual_keycode: Some(keycode), state, .. }, is_synthetic: _, } => { if keycode == keyboard::KeyCode::Escape { *control_flow = ControlFlow::Exit; return; } let ki = KeyInput { scancode: 0, keycode: Some(keycode), mods: From::from(modifiers_state), }; if state == ElementState::Pressed { display.key_down_event(ctx, ki, false).ok(); } else { display.key_up_event(ctx, ki).ok(); } } event::winit_event::WindowEvent::Resized(phys_size) => { display .resize_event(ctx, phys_size.width as f32, phys_size.height as f32) .ok(); } event::winit_event::WindowEvent::ReceivedCharacter(ch) => { display.text_input_event(ctx, ch).ok(); } x => log( format!("Other window event fired: {x:?}"), debug_log::LogState::Verbose, opt.log_level, ), }, event::winit_event::Event::MainEventsCleared => { ctx.time.tick(); let mut game_result: Result<(), GameError> = display.update(ctx); if game_result.is_err() { println!("Error update: {}", game_result.unwrap_err()); *control_flow = ControlFlow::Exit; return; } ctx.gfx.begin_frame().unwrap(); game_result = display.draw(ctx); if game_result.is_err() { println!("Error draw: {}", game_result.unwrap_err()); *control_flow = ControlFlow::Exit; return; } ctx.gfx.end_frame().unwrap(); ctx.mouse.reset_delta(); // sleep to force ~5 fps thread::sleep(Duration::from_millis(200)); ggez::timer::yield_now(); } x => log( format!("Device event fired: {x:?}"), debug_log::LogState::Verbose, opt.log_level, ), } }); }