From 1dd1f49beccbd40c70ab7a1863f897f6e9addde7 Mon Sep 17 00:00:00 2001 From: Stephen Seo Date: Wed, 15 Dec 2021 18:31:51 +0900 Subject: [PATCH] Impl info text Doesn't yet handle flags to disable some info text. --- src/display.rs | 303 ++++++++++++++++++++++++++++++++++++++++++++- src/mpd_handler.rs | 23 ---- 2 files changed, 297 insertions(+), 29 deletions(-) diff --git a/src/display.rs b/src/display.rs index a99950a..55aee22 100644 --- a/src/display.rs +++ b/src/display.rs @@ -3,19 +3,50 @@ use crate::mpd_handler::{InfoFromShared, MPDHandler}; use crate::Opt; use ggez::event::{self, EventHandler}; use ggez::graphics::{ - self, Color, DrawParam, Drawable, Image, Rect, Text, TextFragment, Transform, + self, Color, DrawMode, DrawParam, Drawable, Font, Image, Mesh, MeshBuilder, PxScale, Rect, + Text, TextFragment, Transform, }; +use ggez::timer; use ggez::Context; use ggez::GameError; use image::io::Reader as ImageReader; use image::GenericImageView; use std::io::Cursor; use std::sync::atomic::AtomicBool; -use std::sync::{Arc, RwLock}; +use std::sync::{atomic::Ordering, Arc, RwLock}; use std::thread; use std::time::{Duration, Instant}; const POLL_TIME: Duration = Duration::from_millis(333); +const INIT_FONT_SIZE_X: f32 = 24.0; +const INIT_FONT_SIZE_Y: f32 = 34.0; +const TEXT_X_OFFSET: f32 = 0.3; +const TEXT_OFFSET_Y_SPACING: f32 = 0.4; +const TEXT_HEIGHT_LIMIT: f32 = 55.0; +const ARTIST_HEIGHT_LIMIT: f32 = 40.0; + +fn seconds_to_time(seconds: f64) -> String { + let seconds_int: u64 = seconds.floor() as u64; + let minutes = seconds_int / 60; + let new_seconds: f64 = seconds - (minutes * 60) as f64; + let mut result: String; + if minutes > 0 { + result = minutes.to_string(); + result.push(':'); + if new_seconds < 10.0 { + result.push('0'); + } + } else { + result = String::new(); + } + result.push_str(&new_seconds.to_string()); + let idx_result = result.find('.'); + if let Some(idx) = idx_result { + result.truncate(idx); + } + + result +} pub struct MPDDisplay { opts: Opt, @@ -30,8 +61,19 @@ pub struct MPDDisplay { album_art: Option, album_art_draw_transform: Option, filename_text: Text, + filename_transform: Transform, + filename_y: f32, artist_text: Text, + artist_transform: Transform, + artist_y: f32, title_text: Text, + title_transform: Transform, + title_y: f32, + timer_text: Text, + timer_transform: Transform, + timer_y: f32, + timer: f64, + length: f64, } impl MPDDisplay { @@ -49,8 +91,19 @@ impl MPDDisplay { album_art: None, album_art_draw_transform: None, filename_text: Text::new(""), + filename_transform: Transform::default(), + filename_y: 1.0, artist_text: Text::new(""), + artist_transform: Transform::default(), + artist_y: 1.0, title_text: Text::new(""), + title_transform: Transform::default(), + title_y: 1.0, + timer_text: Text::new("0"), + timer_transform: Transform::default(), + timer_y: 1.0, + timer: 0.0, + length: 0.0, } } @@ -129,6 +182,122 @@ impl MPDDisplay { Ok(()) } + + fn refresh_text_transforms(&mut self, ctx: &mut Context) { + let screen_coords: Rect = graphics::screen_coordinates(ctx); + + let mut offset_y: f32 = screen_coords.h; + + let set_transform = |text: &mut Text, + transform: &mut Transform, + offset_y: &mut f32, + y: &mut f32, + is_string: bool, + is_artist: bool| { + let mut current_x = INIT_FONT_SIZE_X; + let mut current_y = INIT_FONT_SIZE_Y; + let mut width: f32; + let mut height: f32 = 0.0; + let mut iteration_count: u8 = 0; + loop { + iteration_count += 1; + if iteration_count > 8 { + break; + } + + text.set_font( + Font::default(), + PxScale { + x: current_x, + y: current_y, + }, + ); + width = text.width(ctx); + height = text.height(ctx); + + if is_string { + if screen_coords.w < width + || height + >= (if is_artist { + ARTIST_HEIGHT_LIMIT + } else { + TEXT_HEIGHT_LIMIT + }) + { + current_x = current_x * 4.0f32 / 5.0f32; + current_y = current_y * 4.0f32 / 5.0f32; + continue; + } else if screen_coords.w * 2.0 / 3.0 > width { + current_x = current_x * 5.0f32 / 4.0f32; + current_y = current_y * 5.0f32 / 4.0f32; + continue; + } else { + break; + } + } else { + break; + } + } + + *y = *offset_y - height; + *transform = Transform::Values { + dest: [TEXT_X_OFFSET, *offset_y - height].into(), + rotation: 0.0, + scale: [1.0, 1.0].into(), + offset: [0.0, 0.0].into(), + }; + + *offset_y -= height + TEXT_OFFSET_Y_SPACING; + }; + + if !self.filename_text.contents().is_empty() { + set_transform( + &mut self.filename_text, + &mut self.filename_transform, + &mut offset_y, + &mut self.filename_y, + true, + false, + ); + } else { + log("filename text is empty"); + } + + if !self.artist_text.contents().is_empty() { + set_transform( + &mut self.artist_text, + &mut self.artist_transform, + &mut offset_y, + &mut self.artist_y, + true, + true, + ); + } else { + log("artist text is empty"); + } + + if !self.title_text.contents().is_empty() { + set_transform( + &mut self.title_text, + &mut self.title_transform, + &mut offset_y, + &mut self.title_y, + true, + false, + ); + } else { + log("title text is empty"); + } + + set_transform( + &mut self.timer_text, + &mut self.timer_transform, + &mut offset_y, + &mut self.timer_y, + false, + false, + ); + } } impl EventHandler for MPDDisplay { @@ -158,17 +327,44 @@ impl EventHandler for MPDDisplay { .dirty_flag .as_ref() .unwrap() - .swap(false, std::sync::atomic::Ordering::Relaxed) + .swap(false, Ordering::Relaxed) { + log("dirty_flag cleared, acquiring shared data..."); 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())); - self.title_text = Text::new(TextFragment::new(shared.title.clone())); - self.artist_text = Text::new(TextFragment::new(shared.artist.clone())); - self.filename_text = Text::new(TextFragment::new(shared.filename.clone())); } + if !shared.title.is_empty() { + self.title_text = Text::new(shared.title.clone()); + } else { + self.dirty_flag + .as_ref() + .unwrap() + .store(true, Ordering::Relaxed); + } + if !shared.artist.is_empty() { + self.artist_text = Text::new(shared.artist.clone()); + } else { + self.dirty_flag + .as_ref() + .unwrap() + .store(true, Ordering::Relaxed); + } + if !shared.filename.is_empty() { + self.filename_text = Text::new(shared.filename.clone()); + } else { + self.dirty_flag + .as_ref() + .unwrap() + .store(true, Ordering::Relaxed); + } + self.timer = shared.pos; + self.length = shared.length; + self.refresh_text_transforms(ctx); + } else { + log("Failed to acquire read lock for getting shared data"); } let album_art_data_result = MPDHandler::get_art_data(self.mpd_handler.clone().unwrap()); @@ -188,6 +384,18 @@ impl EventHandler for MPDDisplay { } } + let delta = timer::delta(ctx); + self.timer += delta.as_secs_f64(); + let timer_diff = seconds_to_time(self.length - self.timer); + self.timer_text = Text::new(timer_diff); + self.timer_text.set_font( + Font::default(), + PxScale { + x: INIT_FONT_SIZE_X, + y: INIT_FONT_SIZE_Y, + }, + ); + Ok(()) } @@ -207,6 +415,88 @@ impl EventHandler for MPDDisplay { self.notice_text.draw(ctx, DrawParam::default())?; + if self.is_valid && self.is_initialized { + let filename_dimensions = self.filename_text.dimensions(ctx); + let artist_dimensions = self.artist_text.dimensions(ctx); + let title_dimensions = self.title_text.dimensions(ctx); + let timer_dimensions = self.timer_text.dimensions(ctx); + let mesh: Mesh = MeshBuilder::new() + .rectangle( + DrawMode::fill(), + Rect { + x: TEXT_X_OFFSET, + y: self.filename_y, + w: filename_dimensions.w, + h: filename_dimensions.h, + }, + Color::from_rgba(0, 0, 0, 160), + )? + .rectangle( + DrawMode::fill(), + Rect { + x: TEXT_X_OFFSET, + y: self.artist_y, + w: artist_dimensions.w, + h: artist_dimensions.h, + }, + Color::from_rgba(0, 0, 0, 160), + )? + .rectangle( + DrawMode::fill(), + Rect { + x: TEXT_X_OFFSET, + y: self.title_y, + w: title_dimensions.w, + h: title_dimensions.h, + }, + Color::from_rgba(0, 0, 0, 160), + )? + .rectangle( + DrawMode::fill(), + Rect { + x: TEXT_X_OFFSET, + y: self.timer_y, + w: timer_dimensions.w, + h: timer_dimensions.h, + }, + Color::from_rgba(0, 0, 0, 160), + )? + .build(ctx)?; + mesh.draw(ctx, DrawParam::default())?; + + self.filename_text.draw( + ctx, + DrawParam { + trans: self.filename_transform, + ..Default::default() + }, + )?; + + self.artist_text.draw( + ctx, + DrawParam { + trans: self.artist_transform, + ..Default::default() + }, + )?; + + self.title_text.draw( + ctx, + DrawParam { + trans: self.title_transform, + ..Default::default() + }, + )?; + + self.timer_text.draw( + ctx, + DrawParam { + trans: self.timer_transform, + ..Default::default() + }, + )?; + } + graphics::present(ctx) } @@ -250,5 +540,6 @@ impl EventHandler for MPDDisplay { fn resize_event(&mut self, ctx: &mut Context, _width: f32, _height: f32) { self.get_album_art_transform(ctx, false); + self.refresh_text_transforms(ctx); } } diff --git a/src/mpd_handler.rs b/src/mpd_handler.rs index 9c09aa2..f97f10e 100644 --- a/src/mpd_handler.rs +++ b/src/mpd_handler.rs @@ -217,29 +217,6 @@ fn read_line( Err((String::from("Newline not reached"), result)) } -fn seconds_to_time(seconds: f64) -> String { - let seconds_int: u64 = seconds.floor() as u64; - let minutes = seconds_int / 60; - let new_seconds: f64 = seconds - (minutes * 60) as f64; - let mut result: String; - if minutes > 0 { - result = minutes.to_string(); - result.push(':'); - if new_seconds < 10.0 { - result.push('0'); - } - } else { - result = String::new(); - } - result.push_str(&new_seconds.to_string()); - let idx_result = result.find('.'); - if let Some(idx) = idx_result { - result.truncate(idx + 2); - } - - result -} - impl MPDHandler { pub fn new(host: Ipv4Addr, port: u16, password: String) -> Result>, String> { let stream = TcpStream::connect_timeout(