+42
-4
src/app.rs
+42
-4
src/app.rs
···
1
-
use crossterm::event::{self, Event, KeyCode, KeyModifiers};
1
+
use crossterm::event::{self, Event, KeyCode, KeyModifiers, MediaKeyCode};
2
2
use ratatui::{
3
3
prelude::*,
4
4
widgets::{block::*, *},
···
11
11
thread,
12
12
time::{Duration, Instant},
13
13
};
14
-
use tokio::sync::mpsc::UnboundedReceiver;
14
+
use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
15
15
16
16
use crate::{
17
17
extract::get_currently_playing,
18
18
input::stream_to_matrix,
19
+
play::SinkCommand,
19
20
tui,
20
21
visualization::{
21
22
oscilloscope::Oscilloscope, spectroscope::Spectroscope, vectorscope::Vectorscope,
···
196
197
&mut self,
197
198
terminal: &mut tui::Tui,
198
199
mut cmd_rx: UnboundedReceiver<State>,
200
+
mut sink_cmd_tx: UnboundedSender<SinkCommand>,
199
201
id: &str,
200
202
) {
201
203
let new_state = cmd_rx.recv().await.unwrap();
···
274
276
// process all enqueued events
275
277
let event = event::read().unwrap();
276
278
277
-
if self.process_events(event.clone()).unwrap() {
279
+
if self
280
+
.process_events(event.clone(), &mut sink_cmd_tx)
281
+
.unwrap()
282
+
{
278
283
return;
279
284
}
280
285
self.current_display_mut().handle(event);
···
298
303
}
299
304
}
300
305
301
-
fn process_events(&mut self, event: Event) -> Result<bool, io::Error> {
306
+
fn process_events(
307
+
&mut self,
308
+
event: Event,
309
+
sink_cmd_tx: &mut UnboundedSender<SinkCommand>,
310
+
) -> Result<bool, io::Error> {
302
311
let mut quit = false;
303
312
if let Event::Key(key) = event {
304
313
if let KeyModifiers::CONTROL = key.modifiers {
···
357
366
}
358
367
}
359
368
}
369
+
KeyCode::Media(media_key_code) => match media_key_code {
370
+
MediaKeyCode::Play => {
371
+
sink_cmd_tx
372
+
.send(SinkCommand::Play)
373
+
.expect("receiver never dropped");
374
+
}
375
+
MediaKeyCode::Pause => {
376
+
sink_cmd_tx
377
+
.send(SinkCommand::Pause)
378
+
.expect("receiver never dropped");
379
+
}
380
+
MediaKeyCode::PlayPause => {
381
+
sink_cmd_tx
382
+
.send(SinkCommand::PlayPause)
383
+
.expect("receiver never dropped");
384
+
}
385
+
MediaKeyCode::Stop => {
386
+
quit = true;
387
+
}
388
+
MediaKeyCode::LowerVolume
389
+
| MediaKeyCode::RaiseVolume
390
+
| MediaKeyCode::MuteVolume
391
+
| MediaKeyCode::TrackNext
392
+
| MediaKeyCode::TrackPrevious
393
+
| MediaKeyCode::Reverse
394
+
| MediaKeyCode::FastForward
395
+
| MediaKeyCode::Rewind
396
+
| MediaKeyCode::Record => todo!(),
397
+
},
360
398
_ => {}
361
399
}
362
400
};
+30
-1
src/play.rs
+30
-1
src/play.rs
···
34
34
let now_playing = station.playing.clone().unwrap_or_default();
35
35
36
36
let (cmd_tx, cmd_rx) = tokio::sync::mpsc::unbounded_channel::<State>();
37
+
let (sink_cmd_tx, mut sink_cmd_rx) = tokio::sync::mpsc::unbounded_channel::<SinkCommand>();
37
38
let (frame_tx, frame_rx) = std::sync::mpsc::channel::<minimp3::Frame>();
38
39
39
40
let ui = UiOptions {
···
112
113
sink.append(decoder);
113
114
114
115
loop {
116
+
while let Ok(sink_cmd) = sink_cmd_rx.try_recv() {
117
+
match sink_cmd {
118
+
SinkCommand::Play => {
119
+
sink.play();
120
+
}
121
+
SinkCommand::Pause => {
122
+
sink.pause();
123
+
}
124
+
SinkCommand::PlayPause => {
125
+
if sink.is_paused() {
126
+
sink.play();
127
+
} else {
128
+
sink.pause();
129
+
}
130
+
}
131
+
}
132
+
}
115
133
std::thread::sleep(Duration::from_millis(10));
116
134
}
117
135
});
118
136
119
137
let mut terminal = tui::init()?;
120
-
app.run(&mut terminal, cmd_rx, &id).await;
138
+
app.run(&mut terminal, cmd_rx, sink_cmd_tx, &id).await;
121
139
tui::restore()?;
122
140
Ok(())
123
141
}
142
+
143
+
/// Command for a sink.
144
+
#[derive(Debug, Clone, PartialEq)]
145
+
pub enum SinkCommand {
146
+
/// Play.
147
+
Play,
148
+
/// Pause.
149
+
Pause,
150
+
/// Toggle between play and pause.
151
+
PlayPause,
152
+
}