1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
use crossterm::event::Event as CrosstermEvent;
use std::sync::{mpsc, Arc, Mutex};
use std::time;
use std::{
sync::mpsc::{channel, Receiver, Sender},
thread,
};
use super::command::Command;
#[derive(Debug, Clone)]
pub enum Event {
SecondTick,
SongAdded { playlist: String, song: String },
ChangedPlaylist,
Command(Command),
Terminal(CrosstermEvent),
}
pub struct Channel {
pub sender: Sender<Event>,
pub receiver: Receiver<Event>,
/// Whoever owns this mutex can receive events from crossterm::event::read().
///
/// Sometimes, like when the editor is open for editing a playlist, we dont' want to call
/// `crosssterm::event::read()` because it will intercept the keypresses we want to send to the
/// editor. In this case, the thread that opened the editor will hold the mutex until the user
/// closes the editor.
pub receiving_crossterm: Arc<Mutex<()>>,
}
impl Default for Channel {
fn default() -> Self {
let (sender, receiver) = channel();
let receiving_crossterm = Arc::new(Mutex::new(()));
Self {
sender,
receiver,
receiving_crossterm,
}
}
}
impl Channel {
pub fn spawn_terminal_event_getter(&self) -> thread::JoinHandle<()> {
let sender = self.sender.clone();
let receiving_crossterm = self.receiving_crossterm.clone();
thread::spawn(move || loop {
{
// WARNING: very short-lived lock.
// Otherwise this mutex keeps relocking and starving the other thread.
// I'm sure this will work in all cases (spoiler: no it doesn't (but maybe it does))
let _lock = receiving_crossterm.lock().unwrap();
};
if let Ok(event) = crossterm::event::read() {
sender.send(Event::Terminal(event)).unwrap();
}
})
}
pub fn spawn_ticks(&self) -> thread::JoinHandle<()> {
let sender = self.sender.clone();
thread::spawn(move || loop {
thread::sleep(time::Duration::from_secs(1));
let sent = sender.send(Event::SecondTick);
// Stop spawning ticks if the receiver has been dropped. This prevents a
// 'called `Result::unwrap()` on an `Err` value: SendError { .. }' panic when Ctrl+C is
// pressed and the receiver is dropped right before the sender tries to send the
// tick
if sent.is_err() {
return;
}
})
}
pub fn send(&mut self, event: Event) -> Result<(), mpsc::SendError<Event>> {
self.sender.send(event)
}
}