Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 68 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions psst-gui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,20 @@ raw-window-handle = "0.5.2" # Must stay compatible with Druid
souvlaki = { version = "0.8.2", default-features = false, features = ["use_zbus"] }
sanitize_html = "0.9.0"
rustfm-scrobble = "1.1.1"

[target.'cfg(windows)'.build-dependencies]
winres = { version = "0.1.12" }
image = { version = "0.25.6" }

[target.'cfg(windows)'.dependencies]
windows = { version = "0.58.0", features = [
"Win32_UI_Shell",
"Win32_Foundation",
"Win32_System_Com",
"Win32_Graphics_Gdi",
"Win32_UI_WindowsAndMessaging",
] }

[package.metadata.bundle]
name = "Psst"
identifier = "com.jpochyla.psst"
Expand Down
2 changes: 2 additions & 0 deletions psst-gui/src/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,14 @@ pub const PLAY_TRACKS: Selector<PlaybackPayload> = Selector::new("app.play-track
pub const PLAY_PREVIOUS: Selector = Selector::new("app.play-previous");
pub const PLAY_PAUSE: Selector = Selector::new("app.play-pause");
pub const PLAY_RESUME: Selector = Selector::new("app.play-resume");
pub const PLAY_PAUSE_OR_RESUME: Selector = Selector::new("app.play-pause-or-resume");
pub const PLAY_NEXT: Selector = Selector::new("app.play-next");
pub const PLAY_STOP: Selector = Selector::new("app.play-stop");
pub const ADD_TO_QUEUE: Selector<(QueueEntry, PlaybackItem)> = Selector::new("app.add-to-queue");
pub const PLAY_QUEUE_BEHAVIOR: Selector<QueueBehavior> = Selector::new("app.play-queue-behavior");
pub const PLAY_SEEK: Selector<f64> = Selector::new("app.play-seek");
pub const SKIP_TO_POSITION: Selector<u64> = Selector::new("app.skip-to-position");
pub const INITIALIZE_TASKBAR: Selector = Selector::new("app.initialize-taskbar");

// Sorting control
pub const SORT_BY_DATE_ADDED: Selector = Selector::new("app.sort-by-date-added");
Expand Down
1 change: 1 addition & 0 deletions psst-gui/src/controller/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ mod on_update;
mod playback;
mod session;
mod sort;
mod taskbar;

pub use after_delay::AfterDelay;
pub use alert_cleanup::AlertCleanupController;
Expand Down
58 changes: 57 additions & 1 deletion psst-gui/src/controller/playback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crossbeam_channel::Sender;
use druid::{
im::Vector,
widget::{prelude::*, Controller},
Code, ExtEventSink, InternalLifeCycle, KbKey, WindowHandle,
Code, ExtEventSink, InternalLifeCycle, KbKey, Target, WindowHandle,
};
use psst_core::{
audio::{normalize::NormalizationLevel, output::DefaultAudioOutput},
Expand All @@ -25,6 +25,7 @@ use std::time::{SystemTime, UNIX_EPOCH};

use crate::{
cmd,
controller::taskbar::TaskbarManager,
data::Nav,
data::{
AppState, Config, NowPlaying, Playable, Playback, PlaybackOrigin, PlaybackState,
Expand All @@ -38,6 +39,9 @@ pub struct PlaybackController {
thread: Option<JoinHandle<()>>,
output: Option<DefaultAudioOutput>,
media_controls: Option<MediaControls>,
taskbar_manager: Option<TaskbarManager>,
taskbar_buttons_initialized: bool,
last_taskbar_state: Option<PlaybackState>,
has_scrobbled: bool,
scrobbler: Option<Scrobbler>,
startup: bool,
Expand Down Expand Up @@ -75,6 +79,9 @@ impl PlaybackController {
thread: None,
output: None,
media_controls: None,
taskbar_manager: None,
taskbar_buttons_initialized: false,
last_taskbar_state: None,
has_scrobbled: false,
scrobbler: None,
startup: true,
Expand Down Expand Up @@ -104,6 +111,9 @@ impl PlaybackController {
.map_err(|err| log::error!("failed to connect to media control interface: {err:?}"))
.ok();

self.taskbar_manager = TaskbarManager::new(window, event_sink.clone(), widget_id)
.map_err(|err| log::error!("failed to initialize taskbar manager: {:?}", err))
.ok();
self.sender = Some(player.sender());
self.thread = Some(thread::spawn(move || {
Self::service_events(player, event_sink, widget_id);
Expand Down Expand Up @@ -222,6 +232,33 @@ impl PlaybackController {
})
.unwrap_or_default();
}

self.update_taskbar_buttons(playback.state);
}

fn setup_taskbar_buttons_on_first_play(&mut self, playback_state: PlaybackState) {
if !self.taskbar_buttons_initialized {
if let Some(taskbar_manager) = &self.taskbar_manager {
if let Err(e) = taskbar_manager.setup_buttons(playback_state) {
log::error!("Failed to setup taskbar buttons: {:?}", e);
} else {
self.taskbar_buttons_initialized = true;
}
}
}
}

fn update_taskbar_buttons(&mut self, playback_state: PlaybackState) {
if Some(playback_state) != self.last_taskbar_state {
if self.taskbar_buttons_initialized {
if let Some(taskbar_manager) = &self.taskbar_manager {
if let Err(e) = taskbar_manager.update_all_buttons(playback_state) {
log::error!("Failed to update taskbar buttons: {:?}", e);
}
}
}
self.last_taskbar_state = Some(playback_state);
}
}

fn update_media_control_metadata(&mut self, playback: &Playback) {
Expand Down Expand Up @@ -506,6 +543,10 @@ where
self.resume();
ctx.set_handled();
}
Event::Command(cmd) if cmd.is(cmd::PLAY_PAUSE_OR_RESUME) => {
self.pause_or_resume();
ctx.set_handled();
}
Event::Command(cmd) if cmd.is(cmd::PLAY_PREVIOUS) => {
self.previous();
ctx.set_handled();
Expand Down Expand Up @@ -547,6 +588,10 @@ where
self.seek(Duration::from_millis(*location));
ctx.set_handled();
}
Event::Command(cmd) if cmd.is(cmd::INITIALIZE_TASKBAR) => {
self.setup_taskbar_buttons_on_first_play(PlaybackState::Stopped);
ctx.set_handled();
}
// Keyboard shortcuts.
Event::KeyDown(key) if key.code == Code::Space => {
self.pause_or_resume();
Expand Down Expand Up @@ -602,6 +647,17 @@ where
self.set_volume(data.playback.volume);
self.set_queue_behavior(data.playback.queue_behavior);

let event_sink = ctx.get_external_handle();
let widget_id = ctx.widget_id();
std::thread::spawn(move || {
std::thread::sleep(std::time::Duration::from_millis(100));
let _ = event_sink.submit_command(
cmd::INITIALIZE_TASKBAR,
(),
Target::Widget(widget_id),
);
});
Comment on lines +652 to +659
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to wait here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I put that in because we had to wait for something, otherwise things would have gone badly. I don't remember exactly.


// Request focus so we can receive keyboard events.
ctx.submit_command(cmd::SET_FOCUS.to(ctx.widget_id()));
}
Expand Down
Loading
Loading