Fixes/refactorings/improvements
Add some "error handling" for authentication fail, album art fetch fail, or getting status fail (if either of the three fails, then mpd_info_screen will not try to authenticate/fetch-info again). Some refactoring like using a struct instead of a tuple. Improve drawing of text by caching the picked font size that is only changed when the song changes or when the window dimensions change.
This commit is contained in:
parent
6850dced25
commit
bf62fde38d
1 changed files with 114 additions and 44 deletions
148
src/main.rs
148
src/main.rs
|
@ -16,6 +16,8 @@ const TITLE_Y_OFFSET: f32 = 20.0f32;
|
||||||
const TIMER_X_OFFSET: f32 = 20.0f32;
|
const TIMER_X_OFFSET: f32 = 20.0f32;
|
||||||
const TIMER_Y_OFFSET: f32 = 20.0f32;
|
const TIMER_Y_OFFSET: f32 = 20.0f32;
|
||||||
const TIME_MAX_DIFF: f64 = 2.0f64;
|
const TIME_MAX_DIFF: f64 = 2.0f64;
|
||||||
|
const INITIAL_FONT_SIZE: u16 = 96;
|
||||||
|
const SCREEN_DIFF_MARGIN: f32 = 1.0;
|
||||||
|
|
||||||
#[derive(StructOpt, Debug)]
|
#[derive(StructOpt, Debug)]
|
||||||
#[structopt(name = "mpd_info_screen")]
|
#[structopt(name = "mpd_info_screen")]
|
||||||
|
@ -38,6 +40,9 @@ struct Shared {
|
||||||
stream: TcpStream,
|
stream: TcpStream,
|
||||||
password: String,
|
password: String,
|
||||||
dirty: bool,
|
dirty: bool,
|
||||||
|
can_authenticate: bool,
|
||||||
|
can_get_album_art: bool,
|
||||||
|
can_get_status: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Shared {
|
impl Shared {
|
||||||
|
@ -53,10 +58,30 @@ impl Shared {
|
||||||
stream,
|
stream,
|
||||||
password: String::new(),
|
password: String::new(),
|
||||||
dirty: true,
|
dirty: true,
|
||||||
|
can_authenticate: true,
|
||||||
|
can_get_album_art: true,
|
||||||
|
can_get_status: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||||
|
enum PollState {
|
||||||
|
None,
|
||||||
|
Password,
|
||||||
|
CurrentSong,
|
||||||
|
Status,
|
||||||
|
ReadPicture,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct InfoFromShared {
|
||||||
|
title: String,
|
||||||
|
length: f64,
|
||||||
|
pos: f64,
|
||||||
|
instant_rec: Instant,
|
||||||
|
}
|
||||||
|
|
||||||
fn get_connection(host: Ipv4Addr, port: u16) -> Result<TcpStream, String> {
|
fn get_connection(host: Ipv4Addr, port: u16) -> Result<TcpStream, String> {
|
||||||
let stream = TcpStream::connect_timeout(
|
let stream = TcpStream::connect_timeout(
|
||||||
&SocketAddr::new(IpAddr::V4(host), port),
|
&SocketAddr::new(IpAddr::V4(host), port),
|
||||||
|
@ -232,6 +257,7 @@ fn info_loop(shared_data: Arc<Mutex<Shared>>) -> Result<(), String> {
|
||||||
let mut song_pos_get_time: Instant = Instant::now() - Duration::from_secs(10);
|
let mut song_pos_get_time: Instant = Instant::now() - Duration::from_secs(10);
|
||||||
let mut song_length_get_time: Instant = Instant::now() - Duration::from_secs(10);
|
let mut song_length_get_time: Instant = Instant::now() - Duration::from_secs(10);
|
||||||
let mut current_binary_size: usize = 0;
|
let mut current_binary_size: usize = 0;
|
||||||
|
let mut poll_state = PollState::None;
|
||||||
'main: loop {
|
'main: loop {
|
||||||
if !shared_data
|
if !shared_data
|
||||||
.lock()
|
.lock()
|
||||||
|
@ -258,6 +284,7 @@ fn info_loop(shared_data: Arc<Mutex<Shared>>) -> Result<(), String> {
|
||||||
read_vec = read_vec.split_off(current_binary_size + 1);
|
read_vec = read_vec.split_off(current_binary_size + 1);
|
||||||
count = read_vec.len();
|
count = read_vec.len();
|
||||||
current_binary_size = 0;
|
current_binary_size = 0;
|
||||||
|
poll_state = PollState::None;
|
||||||
//println!(
|
//println!(
|
||||||
// "art_data len is {} after fully reading",
|
// "art_data len is {} after fully reading",
|
||||||
// lock.art_data.len()
|
// lock.art_data.len()
|
||||||
|
@ -279,6 +306,7 @@ fn info_loop(shared_data: Arc<Mutex<Shared>>) -> Result<(), String> {
|
||||||
if line.starts_with("OK MPD ") {
|
if line.starts_with("OK MPD ") {
|
||||||
init = false;
|
init = false;
|
||||||
println!("Got initial \"OK\" from MPD");
|
println!("Got initial \"OK\" from MPD");
|
||||||
|
poll_state = PollState::None;
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
return Err(String::from(
|
return Err(String::from(
|
||||||
|
@ -288,9 +316,19 @@ fn info_loop(shared_data: Arc<Mutex<Shared>>) -> Result<(), String> {
|
||||||
} else {
|
} else {
|
||||||
//println!("Got response: {}", line);
|
//println!("Got response: {}", line);
|
||||||
if line.starts_with("OK") {
|
if line.starts_with("OK") {
|
||||||
|
poll_state = PollState::None;
|
||||||
break;
|
break;
|
||||||
} else if line.starts_with("ACK") {
|
} else if line.starts_with("ACK") {
|
||||||
println!("ERROR: {}", line);
|
println!("ERROR: {}", line);
|
||||||
|
match poll_state {
|
||||||
|
PollState::Password => lock.can_authenticate = false,
|
||||||
|
PollState::CurrentSong | PollState::Status => {
|
||||||
|
lock.can_get_status = false
|
||||||
|
}
|
||||||
|
PollState::ReadPicture => lock.can_get_album_art = false,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
poll_state = PollState::None;
|
||||||
} else if line.starts_with("file: ") {
|
} else if line.starts_with("file: ") {
|
||||||
let song_file = line.split_off(6);
|
let song_file = line.split_off(6);
|
||||||
if song_file != lock.current_song {
|
if song_file != lock.current_song {
|
||||||
|
@ -347,31 +385,38 @@ fn info_loop(shared_data: Arc<Mutex<Shared>>) -> Result<(), String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// write block
|
// write block
|
||||||
{
|
if poll_state == PollState::None {
|
||||||
let lock_result = shared_data.try_lock();
|
let lock_result = shared_data.try_lock();
|
||||||
if let Ok(mut lock) = lock_result {
|
if let Ok(mut lock) = lock_result {
|
||||||
if !authenticated && !lock.password.is_empty() {
|
if !authenticated && !lock.password.is_empty() && lock.can_authenticate {
|
||||||
let p = lock.password.clone();
|
let p = lock.password.clone();
|
||||||
let write_result = lock.stream.write(format!("password {}\n", p).as_bytes());
|
let write_result = lock.stream.write(format!("password {}\n", p).as_bytes());
|
||||||
if write_result.is_ok() {
|
if write_result.is_ok() {
|
||||||
authenticated = true;
|
authenticated = true;
|
||||||
|
poll_state = PollState::Password;
|
||||||
} else if let Err(e) = write_result {
|
} else if let Err(e) = write_result {
|
||||||
println!("Got error requesting authentication: {}", e);
|
println!("Got error requesting authentication: {}", e);
|
||||||
}
|
}
|
||||||
} else if song_title_get_time.elapsed() > POLL_DURATION {
|
} else if song_title_get_time.elapsed() > POLL_DURATION && lock.can_get_status {
|
||||||
let write_result = lock.stream.write(b"currentsong\n");
|
let write_result = lock.stream.write(b"currentsong\n");
|
||||||
if let Err(e) = write_result {
|
if let Err(e) = write_result {
|
||||||
println!("Got error requesting currentsong info: {}", e);
|
println!("Got error requesting currentsong info: {}", e);
|
||||||
|
} else {
|
||||||
|
poll_state = PollState::CurrentSong;
|
||||||
}
|
}
|
||||||
} else if song_length_get_time.elapsed() > POLL_DURATION
|
} else if (song_length_get_time.elapsed() > POLL_DURATION
|
||||||
|| song_pos_get_time.elapsed() > POLL_DURATION
|
|| song_pos_get_time.elapsed() > POLL_DURATION)
|
||||||
|
&& lock.can_get_status
|
||||||
{
|
{
|
||||||
let write_result = lock.stream.write(b"status\n");
|
let write_result = lock.stream.write(b"status\n");
|
||||||
if let Err(e) = write_result {
|
if let Err(e) = write_result {
|
||||||
println!("Got error requesting status: {}", e);
|
println!("Got error requesting status: {}", e);
|
||||||
|
} else {
|
||||||
|
poll_state = PollState::Status;
|
||||||
}
|
}
|
||||||
} else if (lock.art_data.is_empty() || lock.art_data.len() != lock.art_data_size)
|
} else if (lock.art_data.is_empty() || lock.art_data.len() != lock.art_data_size)
|
||||||
&& !lock.current_song.is_empty()
|
&& !lock.current_song.is_empty()
|
||||||
|
&& lock.can_get_album_art
|
||||||
{
|
{
|
||||||
let title = lock.current_song.clone();
|
let title = lock.current_song.clone();
|
||||||
let art_data_length = lock.art_data.len();
|
let art_data_length = lock.art_data.len();
|
||||||
|
@ -380,11 +425,16 @@ fn info_loop(shared_data: Arc<Mutex<Shared>>) -> Result<(), String> {
|
||||||
);
|
);
|
||||||
if let Err(e) = write_result {
|
if let Err(e) = write_result {
|
||||||
println!("Got error requesting albumart: {}", e);
|
println!("Got error requesting albumart: {}", e);
|
||||||
|
} else {
|
||||||
|
poll_state = PollState::ReadPicture;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
println!("Failed to acquire lock for writing to stream");
|
println!("Failed to acquire lock for writing to stream");
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// TODO DEBUG
|
||||||
|
//println!("poll_state == {:?}, skipping write...", poll_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
thread::sleep(Duration::from_millis(50));
|
thread::sleep(Duration::from_millis(50));
|
||||||
|
@ -395,7 +445,7 @@ fn info_loop(shared_data: Arc<Mutex<Shared>>) -> Result<(), String> {
|
||||||
fn get_info_from_shared(
|
fn get_info_from_shared(
|
||||||
shared: Arc<Mutex<Shared>>,
|
shared: Arc<Mutex<Shared>>,
|
||||||
force_check: bool,
|
force_check: bool,
|
||||||
) -> Result<(String, f64, f64, Instant), String> {
|
) -> Result<InfoFromShared, String> {
|
||||||
let mut title: String = String::new();
|
let mut title: String = String::new();
|
||||||
let mut length: f64 = 0.0;
|
let mut length: f64 = 0.0;
|
||||||
let mut pos: f64 = 0.0;
|
let mut pos: f64 = 0.0;
|
||||||
|
@ -413,7 +463,12 @@ fn get_info_from_shared(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((title, length, pos, instant_rec))
|
Ok(InfoFromShared {
|
||||||
|
title,
|
||||||
|
length,
|
||||||
|
pos,
|
||||||
|
instant_rec,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn window_conf() -> Conf {
|
fn window_conf() -> Conf {
|
||||||
|
@ -480,6 +535,10 @@ async fn main() -> Result<(), String> {
|
||||||
let mut track_timer: f64 = 0.0;
|
let mut track_timer: f64 = 0.0;
|
||||||
let mut title: String = String::new();
|
let mut title: String = String::new();
|
||||||
let mut art_texture: Option<Texture2D> = None;
|
let mut art_texture: Option<Texture2D> = None;
|
||||||
|
let mut filename_font_size: Option<u16> = None;
|
||||||
|
let mut text_dim: TextDimensions = measure_text("undefined", None, 24, 1.0);
|
||||||
|
let mut prev_width = screen_width();
|
||||||
|
let mut prev_height = screen_height();
|
||||||
|
|
||||||
'macroquad_main: loop {
|
'macroquad_main: loop {
|
||||||
let dt: f64 = get_frame_time() as f64;
|
let dt: f64 = get_frame_time() as f64;
|
||||||
|
@ -487,6 +546,12 @@ async fn main() -> Result<(), String> {
|
||||||
|
|
||||||
if is_key_pressed(KeyCode::Escape) || is_key_pressed(KeyCode::Q) {
|
if is_key_pressed(KeyCode::Escape) || is_key_pressed(KeyCode::Q) {
|
||||||
break 'macroquad_main;
|
break 'macroquad_main;
|
||||||
|
} else if (prev_width - screen_width()).abs() > SCREEN_DIFF_MARGIN
|
||||||
|
|| (prev_height - screen_height()).abs() > SCREEN_DIFF_MARGIN
|
||||||
|
{
|
||||||
|
prev_width = screen_width();
|
||||||
|
prev_height = screen_height();
|
||||||
|
filename_font_size = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
timer -= dt;
|
timer -= dt;
|
||||||
|
@ -494,15 +559,16 @@ async fn main() -> Result<(), String> {
|
||||||
if timer < 0.0 || track_timer < 0.0 {
|
if timer < 0.0 || track_timer < 0.0 {
|
||||||
timer = wait_time;
|
timer = wait_time;
|
||||||
let info_result = get_info_from_shared(shared_data.clone(), true);
|
let info_result = get_info_from_shared(shared_data.clone(), true);
|
||||||
if let Ok((track_title, duration, pos, instant_rec)) = info_result {
|
if let Ok(info) = info_result {
|
||||||
if track_title != title {
|
if info.title != title {
|
||||||
title = track_title;
|
title = info.title;
|
||||||
art_texture = None;
|
art_texture = None;
|
||||||
|
filename_font_size = None;
|
||||||
}
|
}
|
||||||
let duration_since = instant_rec.elapsed();
|
let duration_since = info.instant_rec.elapsed();
|
||||||
let recorded_time = duration - pos - duration_since.as_secs_f64();
|
let recorded_time = info.length - info.pos - duration_since.as_secs_f64();
|
||||||
if (recorded_time - track_timer).abs() > TIME_MAX_DIFF {
|
if (recorded_time - track_timer).abs() > TIME_MAX_DIFF {
|
||||||
track_timer = duration - pos;
|
track_timer = info.length - info.pos - duration_since.as_secs_f64();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -532,15 +598,15 @@ async fn main() -> Result<(), String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(texture) = art_texture {
|
if let Some(texture) = art_texture {
|
||||||
if texture.width() > screen_width() || texture.height() > screen_height() {
|
if texture.width() > prev_width || texture.height() > prev_height {
|
||||||
let ratio: f32 = texture.width() / texture.height();
|
let ratio: f32 = texture.width() / texture.height();
|
||||||
// try filling to height
|
// try filling to height
|
||||||
let mut height = screen_height();
|
let mut height = prev_height;
|
||||||
let mut width = screen_height() * ratio;
|
let mut width = prev_height * ratio;
|
||||||
if width > screen_width() {
|
if width > prev_width {
|
||||||
// try filling to width instead
|
// try filling to width instead
|
||||||
width = screen_width();
|
width = prev_width;
|
||||||
height = screen_width() / ratio;
|
height = prev_width / ratio;
|
||||||
}
|
}
|
||||||
|
|
||||||
let draw_params: DrawTextureParams = DrawTextureParams {
|
let draw_params: DrawTextureParams = DrawTextureParams {
|
||||||
|
@ -549,40 +615,48 @@ async fn main() -> Result<(), String> {
|
||||||
};
|
};
|
||||||
draw_texture_ex(
|
draw_texture_ex(
|
||||||
texture,
|
texture,
|
||||||
(screen_width() - width) / 2.0f32,
|
(prev_width - width) / 2.0f32,
|
||||||
(screen_height() - height) / 2.0f32,
|
(prev_height - height) / 2.0f32,
|
||||||
WHITE,
|
WHITE,
|
||||||
draw_params,
|
draw_params,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
draw_texture(
|
draw_texture(
|
||||||
texture,
|
texture,
|
||||||
(screen_width() - texture.width()) / 2.0f32,
|
(prev_width - texture.width()) / 2.0f32,
|
||||||
(screen_height() - texture.height()) / 2.0f32,
|
(prev_height - texture.height()) / 2.0f32,
|
||||||
WHITE,
|
WHITE,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !title.is_empty() {
|
if !title.is_empty() {
|
||||||
let mut text_size = 64;
|
if filename_font_size.is_none() {
|
||||||
let mut text_dim: TextDimensions;
|
filename_font_size = Some(INITIAL_FONT_SIZE);
|
||||||
loop {
|
loop {
|
||||||
text_dim = measure_text(&title, None, text_size, 1.0f32);
|
text_dim =
|
||||||
if text_dim.width + TITLE_X_OFFSET > screen_width() {
|
measure_text(&title, None, *filename_font_size.as_ref().unwrap(), 1.0f32);
|
||||||
text_size -= 4;
|
if text_dim.width + TITLE_X_OFFSET > prev_width {
|
||||||
|
filename_font_size = filename_font_size.map(|s| s - 4);
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if text_size <= 4 {
|
if *filename_font_size.as_ref().unwrap() <= 4 {
|
||||||
text_size = 4;
|
filename_font_size = Some(4);
|
||||||
|
text_dim = measure_text(
|
||||||
|
&title,
|
||||||
|
None,
|
||||||
|
*filename_font_size.as_ref().unwrap(),
|
||||||
|
1.0f32,
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
draw_rectangle(
|
draw_rectangle(
|
||||||
TITLE_X_OFFSET,
|
TITLE_X_OFFSET,
|
||||||
screen_height() - TITLE_Y_OFFSET - text_dim.height * 2.0,
|
prev_height - TITLE_Y_OFFSET - text_dim.height * 2.0,
|
||||||
text_dim.width,
|
text_dim.width,
|
||||||
text_dim.height,
|
text_dim.height,
|
||||||
Color::new(0.0, 0.0, 0.0, 0.4),
|
Color::new(0.0, 0.0, 0.0, 0.4),
|
||||||
|
@ -590,8 +664,8 @@ async fn main() -> Result<(), String> {
|
||||||
draw_text(
|
draw_text(
|
||||||
&title,
|
&title,
|
||||||
TITLE_X_OFFSET,
|
TITLE_X_OFFSET,
|
||||||
screen_height() - TITLE_Y_OFFSET - text_dim.height,
|
prev_height - TITLE_Y_OFFSET - text_dim.height,
|
||||||
text_size as f32,
|
*filename_font_size.as_ref().unwrap() as f32,
|
||||||
WHITE,
|
WHITE,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -599,7 +673,7 @@ async fn main() -> Result<(), String> {
|
||||||
let timer_dim = measure_text(&timer_string, None, 64, 1.0f32);
|
let timer_dim = measure_text(&timer_string, None, 64, 1.0f32);
|
||||||
draw_rectangle(
|
draw_rectangle(
|
||||||
TIMER_X_OFFSET,
|
TIMER_X_OFFSET,
|
||||||
screen_height()
|
prev_height
|
||||||
- TITLE_Y_OFFSET
|
- TITLE_Y_OFFSET
|
||||||
- text_dim.height
|
- text_dim.height
|
||||||
- TIMER_Y_OFFSET
|
- TIMER_Y_OFFSET
|
||||||
|
@ -611,11 +685,7 @@ async fn main() -> Result<(), String> {
|
||||||
draw_text(
|
draw_text(
|
||||||
&timer_string,
|
&timer_string,
|
||||||
TIMER_X_OFFSET,
|
TIMER_X_OFFSET,
|
||||||
screen_height()
|
prev_height - TITLE_Y_OFFSET - text_dim.height - TIMER_Y_OFFSET - timer_dim.height,
|
||||||
- TITLE_Y_OFFSET
|
|
||||||
- text_dim.height
|
|
||||||
- TIMER_Y_OFFSET
|
|
||||||
- timer_dim.height,
|
|
||||||
64.0f32,
|
64.0f32,
|
||||||
WHITE,
|
WHITE,
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in a new issue