From 866e88ba2aa0c15b7d8a0ee53cf4622e8d65eaa1 Mon Sep 17 00:00:00 2001 From: Stephen Seo Date: Wed, 15 Dec 2021 14:30:02 +0900 Subject: [PATCH] WIP several fixes info output not implemented yet --- src/display.rs | 111 ++++++++++++++++++++++++++++++++-- src/main.rs | 40 ++++++++++--- src/mpd_handler.rs | 144 +++++++++++++++++++++++++++++++++------------ 3 files changed, 244 insertions(+), 51 deletions(-) diff --git a/src/display.rs b/src/display.rs index bbf7669..be9aa47 100644 --- a/src/display.rs +++ b/src/display.rs @@ -1,11 +1,16 @@ -use crate::mpd_handler::MPDHandler; +use crate::debug_log::log; +use crate::mpd_handler::{InfoFromShared, MPDHandler}; use crate::Opt; use ggez::event::{self, EventHandler}; -use ggez::graphics::{self, Color, DrawParam, Drawable, Rect, Text, TextFragment}; -use ggez::timer::{check_update_time, fps}; +use ggez::graphics::{self, Color, DrawParam, Drawable, Text, TextFragment}; use ggez::Context; use ggez::GameError; +use std::sync::atomic::AtomicBool; use std::sync::{Arc, RwLock}; +use std::thread; +use std::time::{Duration, Instant}; + +const POLL_TIME: Duration = Duration::from_millis(333); pub struct MPDDisplay { opts: Opt, @@ -13,23 +18,84 @@ pub struct MPDDisplay { is_valid: bool, is_initialized: bool, notice_text: Text, + poll_instant: Instant, + shared: Option, + password_entered: bool, + dirty_flag: Option>, } impl MPDDisplay { - pub fn new(ctx: &mut Context, opts: Opt) -> Self { + pub fn new(_ctx: &mut Context, opts: Opt) -> Self { Self { opts, mpd_handler: None, is_valid: true, is_initialized: false, notice_text: Text::new(""), + poll_instant: Instant::now() - POLL_TIME, + shared: None, + password_entered: false, + dirty_flag: None, + } + } + + fn init_mpd_handler(&mut self) -> () { + self.mpd_handler = MPDHandler::new( + self.opts.host, + self.opts.port, + self.opts.password.clone().map_or(String::new(), |s| s), + ) + .map_or_else(|_| None, |v| Some(v)); + if self.mpd_handler.is_some() { + self.is_initialized = true; + loop { + self.dirty_flag = + MPDHandler::get_dirty_flag(self.mpd_handler.as_ref().unwrap().clone()) + .map_or(None, |f| Some(f)); + if self.dirty_flag.is_some() { + break; + } else { + thread::sleep(POLL_TIME); + } + } + log("Successfully initialized MPDHandler"); + } else { + self.is_valid = false; + log("Failed to initialize MPDHandler"); } } } impl EventHandler for MPDDisplay { - fn update(&mut self, ctx: &mut ggez::Context) -> Result<(), GameError> { - self.notice_text = Text::new(TextFragment::new(format!("fps is {}", fps(ctx)))); + fn update(&mut self, _ctx: &mut ggez::Context) -> Result<(), GameError> { + if !self.is_valid { + return Err(GameError::EventLoopError( + "Failed to initialize MPDHandler".into(), + )); + } + + if !self.is_initialized { + if self.opts.enable_prompt_password { + if self.notice_text.contents().is_empty() { + self.notice_text = Text::new(TextFragment::new("password: ")); + } else if self.password_entered { + self.init_mpd_handler(); + } + } else { + self.init_mpd_handler(); + } + } + + if self.is_valid && self.is_initialized && self.poll_instant.elapsed() > POLL_TIME { + self.poll_instant = Instant::now(); + self.shared = MPDHandler::get_current_song_info(self.mpd_handler.clone().unwrap()) + .map_or(None, |f| Some(f)); + if let Some(shared) = &self.shared { + if self.notice_text.contents() != shared.error_text { + self.notice_text = Text::new(TextFragment::new(shared.error_text.clone())); + } + } + } Ok(()) } @@ -41,4 +107,37 @@ impl EventHandler for MPDDisplay { graphics::present(ctx) } + + fn text_input_event(&mut self, _ctx: &mut Context, character: char) { + if !self.is_initialized && self.opts.enable_prompt_password { + if self.opts.password.is_none() { + let s = String::from(character); + self.opts.password = Some(s); + self.notice_text.add('*'); + } else { + self.opts.password.as_mut().unwrap().push(character); + self.notice_text.add('*'); + } + } + } + + fn key_down_event( + &mut self, + _ctx: &mut Context, + keycode: event::KeyCode, + _keymods: event::KeyMods, + _repeat: bool, + ) { + if !self.is_initialized && self.opts.enable_prompt_password { + if keycode == event::KeyCode::Back { + let s: String = self.notice_text.contents(); + + if s.ends_with("*") { + self.notice_text = Text::new(TextFragment::new(s[0..(s.len() - 1)].to_owned())); + } + } else if keycode == event::KeyCode::Return { + self.password_entered = true; + } + } + } } diff --git a/src/main.rs b/src/main.rs index eb84537..46c7401 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,10 +3,10 @@ mod display; mod mpd_handler; use ggez::conf::{WindowMode, WindowSetup}; -use ggez::event::winit_event::KeyboardInput; +use ggez::event::winit_event::{KeyboardInput, ModifiersState}; use ggez::event::{self, ControlFlow, EventHandler}; use ggez::graphics::{self, Rect}; -use ggez::ContextBuilder; +use ggez::{ContextBuilder, GameError}; use std::net::Ipv4Addr; use std::thread; use std::time::{Duration, Instant}; @@ -50,6 +50,8 @@ fn main() -> Result<(), String> { let mut display = display::MPDDisplay::new(&mut ctx, opt); + let mut modifiers_state: ModifiersState = ModifiersState::default(); + event_loop.run(move |mut event, _window_target, control_flow| { if !ctx.continuing { *control_flow = ControlFlow::Exit; @@ -64,6 +66,9 @@ fn main() -> Result<(), String> { match event { event::winit_event::Event::WindowEvent { event, .. } => match event { event::winit_event::WindowEvent::CloseRequested => event::quit(ctx), + event::winit_event::WindowEvent::ModifiersChanged(state) => { + modifiers_state = state; + } event::winit_event::WindowEvent::KeyboardInput { device_id: _, input: @@ -72,12 +77,16 @@ fn main() -> Result<(), String> { .. }, is_synthetic: _, - } => match keycode { - event::KeyCode::Escape | event::KeyCode::Q => { - *control_flow = ControlFlow::Exit; + } => { + match keycode { + event::KeyCode::Escape => { + *control_flow = ControlFlow::Exit; + return; + } + _ => (), } - _ => (), - }, + display.key_down_event(ctx, keycode, modifiers_state.into(), false); + } event::winit_event::WindowEvent::Resized(phys_size) => { graphics::set_screen_coordinates( ctx, @@ -90,13 +99,26 @@ fn main() -> Result<(), String> { ) .expect("Failed to handle resizing window"); } + event::winit_event::WindowEvent::ReceivedCharacter(ch) => { + display.text_input_event(ctx, ch); + } x => log(format!("Other window event fired: {:?}", x)), }, event::winit_event::Event::MainEventsCleared => { ctx.timer_context.tick(); - display.update(ctx).expect("Update failed"); - display.draw(ctx).expect("Draw failed"); + 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; + } + game_result = display.draw(ctx); + if game_result.is_err() { + println!("ERROR draw: {}", game_result.unwrap_err()); + *control_flow = ControlFlow::Exit; + return; + } ctx.mouse_context.reset_delta(); diff --git a/src/mpd_handler.rs b/src/mpd_handler.rs index b60baaf..6c80337 100644 --- a/src/mpd_handler.rs +++ b/src/mpd_handler.rs @@ -1,4 +1,5 @@ -use std::io::{Read, Write}; +use crate::debug_log::log; +use std::io::{self, Read, Write}; use std::net::{IpAddr, Ipv4Addr, SocketAddr, TcpStream}; use std::str::FromStr; use std::sync::atomic::{AtomicBool, Ordering}; @@ -7,7 +8,7 @@ use std::thread; use std::time::{Duration, Instant}; const SLEEP_DURATION: Duration = Duration::from_millis(100); -const POLL_DURATION: Duration = Duration::from_secs(10); +const POLL_DURATION: Duration = Duration::from_secs(5); const BUF_SIZE: usize = 1024 * 4; #[derive(Debug, PartialEq, Copy, Clone)] @@ -22,12 +23,12 @@ enum PollState { #[derive(Debug, Clone)] pub struct InfoFromShared { - filename: String, - title: String, - artist: String, - length: f64, - pos: f64, - error_text: String, + pub filename: String, + pub title: String, + pub artist: String, + pub length: f64, + pub pos: f64, + pub error_text: String, } pub struct MPDHandler { @@ -155,6 +156,13 @@ fn read_line( let count = buf.len(); let mut result = String::new(); + if count == 0 { + return Err(( + String::from("Empty string passed to read_line"), + String::new(), + )); + } + let mut buf_to_read: Vec = Vec::with_capacity(saved.len() + buf.len()); if !saved.is_empty() { @@ -274,15 +282,20 @@ impl MPDHandler { let s_clone = s.clone(); let thread = Arc::new(Mutex::new(thread::spawn(|| Self::handler_loop(s_clone)))); - s.write() - .map_err(|_| String::from("Failed to store thread handle in MPDHandler"))? - .self_thread = Some(thread); + loop { + if let Ok(mut write_handle) = s.try_write() { + write_handle.self_thread = Some(thread); + break; + } else { + thread::sleep(Duration::from_millis(1)); + } + } Ok(s) } pub fn get_art_data(h: Arc>) -> Result, ()> { - if let Ok(read_lock) = h.read() { + if let Ok(read_lock) = h.try_read() { if read_lock.art_data.len() == read_lock.art_data_size { return Ok(read_lock.art_data.clone()); } @@ -292,7 +305,7 @@ impl MPDHandler { } pub fn can_get_art_data(h: Arc>) -> bool { - if let Ok(read_lock) = h.read() { + if let Ok(read_lock) = h.try_read() { return read_lock.can_get_album_art || read_lock.can_get_album_art_in_dir; } @@ -300,7 +313,7 @@ impl MPDHandler { } pub fn get_current_song_info(h: Arc>) -> Result { - if let Ok(read_lock) = h.read() { + if let Ok(read_lock) = h.try_read() { return Ok(InfoFromShared { filename: read_lock.current_song_filename.clone(), title: read_lock.current_song_title.clone(), @@ -314,22 +327,54 @@ impl MPDHandler { Err(()) } + pub fn get_dirty_flag(h: Arc>) -> Result, ()> { + if let Ok(read_lock) = h.try_read() { + return Ok(read_lock.dirty_flag.clone()); + } + + Err(()) + } + pub fn is_dirty(h: Arc>) -> Result { - if let Ok(write_lock) = h.write() { + if let Ok(write_lock) = h.try_write() { return Ok(write_lock.dirty_flag.swap(false, Ordering::Relaxed)); } Err(()) } + pub fn force_get_current_song(h: Arc>) -> () { + loop { + if let Ok(mut write_lock) = h.try_write() { + write_lock.force_get_current_song = true; + break; + } else { + thread::sleep(Duration::from_millis(10)); + } + } + } + fn handler_loop(h: Arc>) -> Result<(), String> { let mut buf: [u8; BUF_SIZE] = [0; BUF_SIZE]; let mut saved: Vec = Vec::new(); let mut saved_str: String = String::new(); + + loop { + if let Ok(write_handle) = h.try_write() { + write_handle + .stream + .set_nonblocking(true) + .map_err(|_| String::from("Failed to set non-blocking on TCP stream"))?; + break; + } else { + thread::sleep(POLL_DURATION); + } + } + 'main: loop { if !Self::is_reading_picture(h.clone()) { thread::sleep(SLEEP_DURATION); - if let Ok(write_handle) = h.write() { + if let Ok(write_handle) = h.try_write() { if write_handle.self_thread.is_none() { // main thread failed to store handle to this thread println!("MPDHandle thread stopping due to failed handle storage"); @@ -346,15 +391,18 @@ impl MPDHandler { println!("WARNING: write_block error: {}", err_string); } - if let Ok(read_handle) = h.read() { + if let Ok(read_handle) = h.try_read() { if read_handle.stop_flag.load(Ordering::Relaxed) { break 'main; } } + + io::stdout().flush().unwrap(); } + log("MPDHandler thread entering exit loop"); 'exit: loop { - if let Ok(mut write_handle) = h.write() { + if let Ok(mut write_handle) = h.try_write() { write_handle.self_thread = None; break 'exit; } @@ -371,29 +419,42 @@ impl MPDHandler { saved_str: &mut String, ) -> Result<(), String> { let mut write_handle = h - .write() + .try_write() .map_err(|_| String::from("Failed to get MPDHandler write lock (read_block)"))?; - let read_result = write_handle - .stream - .read(buf) - .map_err(|io_err| format!("Failed to read from TCP stream: {}", io_err))?; - if read_result == 0 { - return Err(String::from("Got zero bytes from TCP stream")); + let mut read_amount: usize = 0; + let read_result = write_handle.stream.read(buf); + if let Err(io_err) = read_result { + if io_err.kind() != io::ErrorKind::WouldBlock { + return Err(format!("TCP stream error: {}", io_err)); + } else { + return Ok(()); + } + } else if let Ok(read_amount_result) = read_result { + if read_amount_result == 0 { + return Err(String::from("Got zero bytes from TCP stream")); + } + read_amount = read_amount_result; } - let mut buf_vec: Vec = Vec::from(&buf[0..read_result]); + let mut buf_vec: Vec = Vec::from(&buf[0..read_amount]); 'handle_buf: loop { if write_handle.current_binary_size > 0 { - if write_handle.current_binary_size <= buf_vec.len() { + if write_handle.current_binary_size < buf_vec.len() { let count = write_handle.current_binary_size; write_handle.art_data.extend_from_slice(&buf_vec[0..count]); - buf_vec = buf_vec.split_off(write_handle.current_binary_size + 1); + buf_vec = buf_vec.split_off(count + 1); write_handle.current_binary_size = 0; write_handle.poll_state = PollState::None; write_handle.dirty_flag.store(true, Ordering::Relaxed); + log("Got complete album art chunk"); } else { write_handle.art_data.extend_from_slice(&buf_vec); write_handle.current_binary_size -= buf_vec.len(); + log(format!( + "Album art recv progress: {}/{}", + write_handle.art_data.len(), + write_handle.art_data_size + )); break 'handle_buf; } } @@ -413,6 +474,10 @@ impl MPDHandler { } // write_handle.is_init if line.starts_with("OK") { + log(format!( + "Got OK when poll state is {:?}", + write_handle.poll_state + )); match write_handle.poll_state { PollState::Password => write_handle.is_authenticated = true, PollState::ReadPicture => { @@ -518,10 +583,15 @@ impl MPDHandler { } else if line.starts_with("Artist: ") { write_handle.current_song_artist = line.split_off(8); } else { - println!("WARNING: Got unrecognized line: {}", line); + log(format!("WARNING: Got unrecognized/ignored line: {}", line)); } } else if let Err((msg, read_line_in_progress)) = read_line_result { - println!("ERROR read_line: {}", msg); + log(format!( + "WARNING read_line: {}, saved size == {}, in_progress size == {}", + msg, + saved.len(), + read_line_in_progress.len() + )); *saved_str = read_line_in_progress; break 'handle_buf; } else { @@ -534,7 +604,7 @@ impl MPDHandler { fn handler_write_block(h: Arc>) -> Result<(), String> { let mut write_handle = h - .write() + .try_write() .map_err(|_| String::from("Failed to get MPDHandler write lock (write_block)"))?; if write_handle.poll_state == PollState::None { if !write_handle.did_check_overtime @@ -615,11 +685,13 @@ impl MPDHandler { } fn is_reading_picture(h: Arc>) -> bool { - if let Ok(read_handle) = h.read() { - read_handle.poll_state == PollState::ReadPicture - || read_handle.poll_state == PollState::ReadPictureInDir - } else { - false + loop { + if let Ok(read_handle) = h.try_read() { + return read_handle.poll_state == PollState::ReadPicture + || read_handle.poll_state == PollState::ReadPictureInDir; + } else { + thread::sleep(Duration::from_millis(5)); + } } } }