-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,
is_valid: bool,
is_initialized: bool,
notice_text: Text,
+ poll_instant: Instant,
+ shared: Option<InfoFromShared>,
+ password_entered: bool,
+ dirty_flag: Option<Arc<AtomicBool>>,
}
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(())
}
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;
+ }
+ }
+ }
}
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};
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;
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:
..
},
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,
)
.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();
-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};
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)]
#[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 {
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<u8> = Vec::with_capacity(saved.len() + buf.len());
if !saved.is_empty() {
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<RwLock<Self>>) -> Result<Vec<u8>, ()> {
- 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());
}
}
pub fn can_get_art_data(h: Arc<RwLock<Self>>) -> 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;
}
}
pub fn get_current_song_info(h: Arc<RwLock<Self>>) -> Result<InfoFromShared, ()> {
- 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(),
Err(())
}
+ pub fn get_dirty_flag(h: Arc<RwLock<Self>>) -> Result<Arc<AtomicBool>, ()> {
+ if let Ok(read_lock) = h.try_read() {
+ return Ok(read_lock.dirty_flag.clone());
+ }
+
+ Err(())
+ }
+
pub fn is_dirty(h: Arc<RwLock<Self>>) -> Result<bool, ()> {
- 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<RwLock<Self>>) -> () {
+ 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<RwLock<Self>>) -> Result<(), String> {
let mut buf: [u8; BUF_SIZE] = [0; BUF_SIZE];
let mut saved: Vec<u8> = 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");
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;
}
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<u8> = Vec::from(&buf[0..read_result]);
+ let mut buf_vec: Vec<u8> = 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;
}
}
} // 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 => {
} 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 {
fn handler_write_block(h: Arc<RwLock<MPDHandler>>) -> 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
}
fn is_reading_picture(h: Arc<RwLock<MPDHandler>>) -> 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));
+ }
}
}
}