remove some signals
This commit is contained in:
parent
4413aaff4b
commit
9ff36afb15
9 changed files with 203 additions and 104 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -217,6 +217,7 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
|||
name = "audrey"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-broadcast",
|
||||
"async-channel",
|
||||
"base16ct",
|
||||
"bindgen",
|
||||
|
|
|
@ -5,6 +5,7 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
adw = { version = "0.7.0", package = "libadwaita", features = ["v1_6"] }
|
||||
async-broadcast = "0.7.1"
|
||||
async-channel = "2.3.1"
|
||||
base16ct = { version = "0.2.0", features = ["std"] }
|
||||
chrono = { version = "0.4.38", features = ["serde"] }
|
||||
|
|
34
src/broadcast.rs
Normal file
34
src/broadcast.rs
Normal file
|
@ -0,0 +1,34 @@
|
|||
// nice wrapper to have a Default impl for a (Sender, InactiveReceiver) pair
|
||||
|
||||
use async_broadcast::{broadcast, InactiveReceiver, Receiver, Sender, TrySendError};
|
||||
|
||||
pub struct Broadcast<const N: usize, T> {
|
||||
pub sender: Sender<T>,
|
||||
inactive_receiver: InactiveReceiver<T>,
|
||||
}
|
||||
|
||||
impl<const N: usize, T> Default for Broadcast<N, T> {
|
||||
fn default() -> Self {
|
||||
let (sender, receiver) = broadcast(N);
|
||||
Self {
|
||||
sender,
|
||||
inactive_receiver: receiver.deactivate(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize, T> Broadcast<N, T> where T: Clone {
|
||||
pub fn receiver(&self) -> Receiver<T> {
|
||||
self.inactive_receiver.activate_cloned()
|
||||
}
|
||||
|
||||
// like sender.try_broadcast, but ignores if there aren't any active listeners
|
||||
pub fn try_broadcast(&self, msg: T) -> Result<(), TrySendError<T>> {
|
||||
match self.sender.try_broadcast(msg) {
|
||||
Err(TrySendError::Inactive(_)) => Ok(()), // ignore
|
||||
Err(err) => Err(err),
|
||||
Ok(Some(_)) => unreachable!("we do not enable overflow mode"),
|
||||
Ok(None) => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
29
src/event.rs
Normal file
29
src/event.rs
Normal file
|
@ -0,0 +1,29 @@
|
|||
#[derive(Clone, Debug)]
|
||||
pub enum Event {
|
||||
PlaybinVolumeChanged,
|
||||
PlaybinMutedChanged,
|
||||
PlaybinPausedChanged,
|
||||
PlaybinCurrentEntryChanged,
|
||||
|
||||
PlaybinEntryInserted(usize),
|
||||
PlaybinStopped,
|
||||
PlaybinEntryRemoved(usize),
|
||||
|
||||
PlaybinFileStarted,
|
||||
}
|
||||
|
||||
use adw::prelude::*;
|
||||
use gtk::glib;
|
||||
|
||||
pub fn spawn_object_listener<O: IsA<glib::Object>>(
|
||||
mut receiver: async_broadcast::Receiver<Event>,
|
||||
obj: &O,
|
||||
mut f: impl FnMut(O, Event) + 'static,
|
||||
) {
|
||||
let weak = obj.downgrade();
|
||||
glib::spawn_future_local(async move {
|
||||
while let Some(obj) = weak.upgrade() {
|
||||
f(obj, receiver.recv_direct().await.unwrap());
|
||||
}
|
||||
});
|
||||
}
|
|
@ -22,6 +22,9 @@ pub type Playbin = playbin::Playbin<PlaybinSong>;
|
|||
mod signal;
|
||||
pub use signal::{Signal, SignalEmitter, SignalHandler};
|
||||
|
||||
pub mod event;
|
||||
pub use event::Event;
|
||||
|
||||
use gettextrs::{bind_textdomain_codeset, bindtextdomain, setlocale, textdomain, LocaleCategory};
|
||||
use gtk::prelude::*;
|
||||
use gtk::{gio, glib};
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::mpv;
|
||||
use crate::signal::{Signal, SignalEmitter};
|
||||
use crate::Event;
|
||||
use event_listener::EventListener;
|
||||
use std::cell::{Ref, RefCell};
|
||||
use tracing::{event, span, Level};
|
||||
|
@ -20,20 +21,19 @@ pub struct Playbin<E> {
|
|||
mpv: mpv::Handle,
|
||||
entries: RefCell<Vec<E>>,
|
||||
|
||||
volume_changed: SignalEmitter<Self, ()>,
|
||||
muted_changed: SignalEmitter<Self, ()>,
|
||||
paused_changed: SignalEmitter<Self, ()>,
|
||||
current_entry_changed: SignalEmitter<Self, ()>,
|
||||
sender: async_broadcast::Sender<Event>,
|
||||
|
||||
entry_inserted: SignalEmitter<Self, usize>,
|
||||
stopped: SignalEmitter<Self, ()>,
|
||||
entry_removed: SignalEmitter<Self, usize>,
|
||||
|
||||
file_started: SignalEmitter<Self, ()>,
|
||||
}
|
||||
|
||||
impl<E> Default for Playbin<E> {
|
||||
fn default() -> Self {
|
||||
impl<E> Playbin<E>
|
||||
where
|
||||
E: PlaybinEntry,
|
||||
{
|
||||
pub fn new(sender: async_broadcast::Sender<Event>) -> Self {
|
||||
let mpv = mpv::Handle::new();
|
||||
mpv.set_property("audio-client-name", "audrey").unwrap();
|
||||
mpv.set_property("user-agent", crate::USER_AGENT).unwrap();
|
||||
|
@ -53,24 +53,15 @@ impl<E> Default for Playbin<E> {
|
|||
mpv,
|
||||
entries: RefCell::new(vec![]),
|
||||
|
||||
volume_changed: Default::default(),
|
||||
muted_changed: Default::default(),
|
||||
paused_changed: Default::default(),
|
||||
current_entry_changed: Default::default(),
|
||||
sender,
|
||||
|
||||
entry_inserted: Default::default(),
|
||||
stopped: Default::default(),
|
||||
entry_removed: Default::default(),
|
||||
|
||||
file_started: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> Playbin<E>
|
||||
where
|
||||
E: PlaybinEntry,
|
||||
{
|
||||
pub fn volume(&self) -> i64 {
|
||||
self.mpv.get_property("volume").unwrap()
|
||||
}
|
||||
|
@ -138,7 +129,9 @@ where
|
|||
entries.push(entry);
|
||||
|
||||
drop(entries);
|
||||
self.entry_inserted.emit(self, index);
|
||||
self.sender
|
||||
.try_broadcast(Event::PlaybinEntryInserted(index))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn insert_entry(&self, index: usize, entry: E) {
|
||||
|
@ -149,7 +142,9 @@ where
|
|||
entries.insert(index, entry);
|
||||
|
||||
drop(entries);
|
||||
self.entry_inserted.emit(self, index);
|
||||
self.sender
|
||||
.try_broadcast(Event::PlaybinEntryInserted(index))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// stop playback and clear playlist
|
||||
|
@ -159,7 +154,7 @@ where
|
|||
entries.clear();
|
||||
|
||||
drop(entries);
|
||||
self.stopped.emit(self, ());
|
||||
self.sender.try_broadcast(Event::PlaybinStopped).unwrap();
|
||||
}
|
||||
|
||||
pub fn remove_entry(&self, index: usize) {
|
||||
|
@ -170,7 +165,9 @@ where
|
|||
entries.remove(index);
|
||||
|
||||
drop(entries);
|
||||
self.entry_removed.emit(self, index);
|
||||
self.sender
|
||||
.try_broadcast(Event::PlaybinEntryRemoved(index))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn move_entry(&self, _from: usize, _to: usize) {
|
||||
|
@ -192,25 +189,33 @@ where
|
|||
mpv::Event::PropertyChange(event) => match event.reply_userdata {
|
||||
0 => {
|
||||
assert_eq!(&event.name, "volume");
|
||||
self.volume_changed.emit(self, ());
|
||||
self.sender
|
||||
.try_broadcast(Event::PlaybinVolumeChanged)
|
||||
.unwrap();
|
||||
event!(Level::DEBUG, "volume change {}", self.volume());
|
||||
}
|
||||
|
||||
1 => {
|
||||
assert_eq!(&event.name, "mute");
|
||||
self.muted_changed.emit(self, ());
|
||||
self.sender
|
||||
.try_broadcast(Event::PlaybinMutedChanged)
|
||||
.unwrap();
|
||||
event!(Level::DEBUG, "mute state change to {}", self.muted());
|
||||
}
|
||||
|
||||
2 => {
|
||||
assert_eq!(&event.name, "pause");
|
||||
self.paused_changed.emit(self, ());
|
||||
self.sender
|
||||
.try_broadcast(Event::PlaybinPausedChanged)
|
||||
.unwrap();
|
||||
event!(Level::DEBUG, "pause state change to {}", self.paused());
|
||||
}
|
||||
|
||||
3 => {
|
||||
assert_eq!(&event.name, "playlist-pos");
|
||||
self.current_entry_changed.emit(self, ());
|
||||
self.sender
|
||||
.try_broadcast(Event::PlaybinCurrentEntryChanged)
|
||||
.unwrap();
|
||||
event!(
|
||||
Level::DEBUG,
|
||||
"playlist-pos change {:?}",
|
||||
|
@ -235,7 +240,9 @@ where
|
|||
mpv::Event::StartFile(_) => {
|
||||
// since we set up the hook before, the current song is guaranteed not to change
|
||||
// under our feet
|
||||
self.file_started.emit(self, ());
|
||||
self.sender
|
||||
.try_broadcast(Event::PlaybinFileStarted)
|
||||
.unwrap();
|
||||
|
||||
// sanity check
|
||||
assert_eq!(
|
||||
|
@ -248,22 +255,6 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub fn volume_changed(&self) -> Signal<'_, Self, ()> {
|
||||
self.volume_changed.signal()
|
||||
}
|
||||
|
||||
pub fn muted_changed(&self) -> Signal<'_, Self, ()> {
|
||||
self.muted_changed.signal()
|
||||
}
|
||||
|
||||
pub fn current_entry_changed(&self) -> Signal<'_, Self, ()> {
|
||||
self.current_entry_changed.signal()
|
||||
}
|
||||
|
||||
pub fn entry_inserted(&self) -> Signal<'_, Self, usize> {
|
||||
self.entry_inserted.signal()
|
||||
}
|
||||
|
||||
pub fn stopped(&self) -> Signal<'_, Self, ()> {
|
||||
self.stopped.signal()
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ mod imp {
|
|||
|
||||
#[template_callback]
|
||||
fn on_song_list_setup(&self, item: >k::ListItem, _factory: >k::SignalListItemFactory) {
|
||||
let child = super::Song::new(self.playbin.borrow().as_ref().unwrap());
|
||||
let child = super::Song::new(Some(&self.obj().window()));
|
||||
|
||||
child.set_draggable(true);
|
||||
child.set_show_position(true);
|
||||
|
@ -75,10 +75,7 @@ mod imp {
|
|||
fn on_song_list_bind(&self, item: >k::ListItem, _factory: >k::SignalListItemFactory) {
|
||||
let child = item.child().and_downcast::<super::Song>().unwrap();
|
||||
|
||||
child.bind(
|
||||
item.position(),
|
||||
item.item().unwrap().downcast_ref::<PlaybinSong>().unwrap(),
|
||||
);
|
||||
child.bind(item.position(), &self.obj().window());
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
|
@ -106,6 +103,7 @@ mod imp {
|
|||
}
|
||||
|
||||
use crate::Playbin;
|
||||
use adw::prelude::*;
|
||||
use adw::subclass::prelude::*;
|
||||
use gtk::glib;
|
||||
use std::rc::Rc;
|
||||
|
@ -124,15 +122,6 @@ impl PlayQueue {
|
|||
.replace(Some(Rc::clone(playbin)))
|
||||
.is_none()); // only set once
|
||||
|
||||
playbin
|
||||
.entry_inserted()
|
||||
.connect_object(self, |playbin, play_queue, index| {
|
||||
play_queue
|
||||
.model()
|
||||
.unwrap()
|
||||
.insert(index as u32, &playbin.entries()[index]);
|
||||
true
|
||||
});
|
||||
playbin
|
||||
.stopped()
|
||||
.connect_object(self, |_playbin, play_queue, ()| {
|
||||
|
@ -146,4 +135,8 @@ impl PlayQueue {
|
|||
true
|
||||
});
|
||||
}
|
||||
|
||||
fn window(&self) -> crate::ui::Window {
|
||||
self.root().unwrap().dynamic_cast().unwrap()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
mod imp {
|
||||
use crate::signal::SignalHandler;
|
||||
use crate::{Playbin, PlaybinSong};
|
||||
use crate::PlaybinSong;
|
||||
use glib::subclass::InitializingObject;
|
||||
use gtk::{gdk, gio, glib, prelude::*, subclass::prelude::*};
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(gtk::CompositeTemplate, glib::Properties, Default)]
|
||||
#[template(resource = "/eu/callcc/audrey/play_queue_song.ui")]
|
||||
|
@ -21,6 +19,8 @@ mod imp {
|
|||
|
||||
#[property(set = Self::set_current, get)]
|
||||
current: Cell<bool>,
|
||||
#[property(set, get)]
|
||||
position: Cell<u32>,
|
||||
|
||||
#[property(set, get)]
|
||||
displayed_position: Cell<u32>,
|
||||
|
@ -29,9 +29,6 @@ mod imp {
|
|||
|
||||
drag_pos: Cell<(i32, i32)>,
|
||||
drag_widget: Cell<Option<gtk::ListBox>>,
|
||||
|
||||
pub(super) playbin: RefCell<Option<Rc<Playbin>>>,
|
||||
pub(super) connection: Cell<SignalHandler>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
|
@ -62,6 +59,7 @@ mod imp {
|
|||
move |_, _, _| {
|
||||
self_
|
||||
.obj()
|
||||
.window()
|
||||
.playbin()
|
||||
.remove_entry(self_.obj().displayed_position() as usize - 1)
|
||||
}
|
||||
|
@ -123,7 +121,7 @@ mod imp {
|
|||
fn on_drag_begin(&self, drag: &gdk::Drag) {
|
||||
let drag_widget = gtk::ListBox::new();
|
||||
|
||||
let drag_row = super::Song::new(&self.obj().playbin());
|
||||
let drag_row = super::Song::new(None);
|
||||
drag_row.set_draggable(false);
|
||||
drag_row.set_show_position(self.obj().show_position());
|
||||
drag_row.set_show_artist(self.obj().show_artist());
|
||||
|
@ -165,11 +163,10 @@ mod imp {
|
|||
}
|
||||
}
|
||||
|
||||
use crate::{Playbin, PlaybinSong};
|
||||
use adw::subclass::prelude::*;
|
||||
use crate::PlaybinSong;
|
||||
use adw::prelude::*;
|
||||
use glib::Object;
|
||||
use gtk::glib;
|
||||
use std::rc::Rc;
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct Song(ObjectSubclass<imp::Song>)
|
||||
|
@ -178,38 +175,41 @@ glib::wrapper! {
|
|||
}
|
||||
|
||||
impl Song {
|
||||
pub fn new(playbin: &Rc<Playbin>) -> Self {
|
||||
pub fn new(window: Option<&crate::ui::Window>) -> Self {
|
||||
let song: Self = Object::new();
|
||||
|
||||
assert!(song
|
||||
.imp()
|
||||
.playbin
|
||||
.replace(Some(Rc::clone(playbin)))
|
||||
.is_none()); // only set once
|
||||
if let Some(window) = window {
|
||||
use crate::Event;
|
||||
|
||||
crate::event::spawn_object_listener(
|
||||
window.receiver(),
|
||||
&song,
|
||||
|song, event| match event {
|
||||
Event::PlaybinCurrentEntryChanged => {
|
||||
song.set_current(
|
||||
song.window().playbin().current_entry()
|
||||
== Some(song.position() as usize),
|
||||
);
|
||||
}
|
||||
|
||||
_ => {}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
song
|
||||
}
|
||||
|
||||
fn playbin(&self) -> Rc<Playbin> {
|
||||
Rc::clone(self.imp().playbin.borrow().as_ref().unwrap())
|
||||
fn window(&self) -> crate::ui::Window {
|
||||
self.root().unwrap().dynamic_cast().unwrap()
|
||||
}
|
||||
|
||||
pub fn bind(&self, position: u32, song: &PlaybinSong) {
|
||||
pub fn bind(&self, position: u32, window: &crate::ui::Window) {
|
||||
self.set_displayed_position(position + 1);
|
||||
self.set_song(song);
|
||||
self.set_current(self.playbin().current_entry() == Some(position as usize));
|
||||
self.imp()
|
||||
.connection
|
||||
.replace(self.playbin().current_entry_changed().connect_object(
|
||||
self,
|
||||
move |playbin, song, ()| {
|
||||
song.set_current(playbin.current_entry() == Some(position as usize));
|
||||
true
|
||||
},
|
||||
));
|
||||
self.set_song(&window.playbin().entries()[position as usize]);
|
||||
self.set_current(window.playbin().current_entry() == Some(position as usize));
|
||||
self.set_position(position);
|
||||
}
|
||||
|
||||
pub fn unbind(&self) {
|
||||
self.imp().connection.take().disconnect();
|
||||
}
|
||||
pub fn unbind(&self) {}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ mod imp {
|
|||
use std::rc::Rc;
|
||||
use tracing::{event, Level};
|
||||
|
||||
#[derive(gtk::CompositeTemplate, glib::Properties, Default)]
|
||||
#[derive(gtk::CompositeTemplate, glib::Properties)]
|
||||
#[template(resource = "/eu/callcc/audrey/window.ui")]
|
||||
#[properties(wrapper_type = super::Window)]
|
||||
pub struct Window {
|
||||
|
@ -31,6 +31,28 @@ mod imp {
|
|||
|
||||
pub(super) playbin: Rc<Playbin>,
|
||||
pub(super) api: RefCell<Option<Rc<crate::subsonic::Client>>>,
|
||||
|
||||
pub(super) sender: async_broadcast::Sender<crate::Event>,
|
||||
pub(super) inactive_receiver: async_broadcast::InactiveReceiver<crate::Event>,
|
||||
}
|
||||
|
||||
impl Default for Window {
|
||||
fn default() -> Self {
|
||||
let (sender, receiver) = async_broadcast::broadcast(100); // TODO: constantize
|
||||
|
||||
Self {
|
||||
playbar: Default::default(),
|
||||
play_queue: Default::default(),
|
||||
can_click_shuffle_all: Cell::new(false),
|
||||
playing_cover_art: Default::default(),
|
||||
song: Default::default(),
|
||||
setup: Default::default(),
|
||||
playbin: Rc::new(Playbin::new(sender.clone())),
|
||||
api: Default::default(),
|
||||
sender,
|
||||
inactive_receiver: receiver.deactivate(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
|
@ -54,6 +76,37 @@ mod imp {
|
|||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
|
||||
crate::event::spawn_object_listener(
|
||||
self.inactive_receiver.activate_cloned(),
|
||||
self.obj().as_ref(),
|
||||
|window, event| {
|
||||
use crate::Event;
|
||||
match dbg!(event) {
|
||||
Event::PlaybinVolumeChanged => {
|
||||
window
|
||||
.imp()
|
||||
.playbar
|
||||
.set_volume(window.playbin().volume() as i32);
|
||||
}
|
||||
|
||||
Event::PlaybinMutedChanged => {
|
||||
window.imp().playbar.set_mute(window.playbin().muted());
|
||||
}
|
||||
|
||||
Event::PlaybinEntryInserted(index) => {
|
||||
window
|
||||
.imp()
|
||||
.play_queue
|
||||
.model()
|
||||
.unwrap()
|
||||
.insert(index as u32, &window.playbin().entries()[index]);
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
let playbin = Rc::downgrade(&self.playbin);
|
||||
glib::spawn_future_local(glib::clone!(async move {
|
||||
loop {
|
||||
|
@ -197,13 +250,6 @@ impl Window {
|
|||
.imp()
|
||||
.playbar
|
||||
.set_volume(window.imp().playbin.volume() as i32);
|
||||
window.imp().playbin.volume_changed().connect_object(
|
||||
&*window.imp().playbar,
|
||||
|playbin, playbar, ()| {
|
||||
playbar.set_volume(playbin.volume() as i32);
|
||||
true
|
||||
},
|
||||
);
|
||||
window.imp().playbar.connect_notify_local(
|
||||
Some("volume"),
|
||||
glib::clone!(
|
||||
|
@ -214,13 +260,6 @@ impl Window {
|
|||
);
|
||||
|
||||
window.imp().playbar.set_mute(window.imp().playbin.muted());
|
||||
window.imp().playbin.muted_changed().connect_object(
|
||||
&*window.imp().playbar,
|
||||
|playbin, playbar, ()| {
|
||||
playbar.set_mute(playbin.muted());
|
||||
true
|
||||
},
|
||||
);
|
||||
window.imp().playbar.connect_notify_local(
|
||||
Some("mute"),
|
||||
glib::clone!(
|
||||
|
@ -282,4 +321,12 @@ impl Window {
|
|||
|
||||
window
|
||||
}
|
||||
|
||||
pub fn playbin(&self) -> &crate::Playbin {
|
||||
&self.imp().playbin
|
||||
}
|
||||
|
||||
pub fn receiver(&self) -> async_broadcast::Receiver<crate::Event> {
|
||||
self.imp().inactive_receiver.activate_cloned()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue