summaryrefslogtreecommitdiffstats
path: root/src/event/app_event.rs
blob: f6d538bce7f0dd5f47e17e22b4d8ddeed2df6a98 (plain)
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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
use std::fmt::Debug;
use std::io;
use std::path;
use std::sync::mpsc;
use std::thread;

use ratatui_image::protocol::Protocol;
use signal_hook::consts::signal;
use signal_hook::iterator::exfiltrator::SignalOnly;
use signal_hook::iterator::SignalsInfo;

use termion::event::Event;
use termion::input::TermRead;

use uuid::Uuid;

use crate::error::AppResult;
use crate::fs::JoshutoDirList;
use crate::io::FileOperationProgress;
use crate::preview::preview_file::FilePreview;

pub enum PreviewData {
    Script(Box<FilePreview>),
    Image(Box<dyn Protocol>),
}

impl Debug for PreviewData {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Script(_) => f.debug_tuple("Script").field(&"_").finish(),
            Self::Image(_) => f.debug_tuple("Image").field(&"_").finish(),
        }
    }
}

#[derive(Debug)]
pub enum AppEvent {
    // User input events
    Termion(Event),

    // background IO worker events
    IoWorkerCreate,
    FileOperationProgress(FileOperationProgress),
    IoWorkerResult(AppResult<FileOperationProgress>),

    // forked process events
    ChildProcessComplete(u32),

    // preview thread events
    PreviewDir {
        id: Uuid,
        path: path::PathBuf,
        res: Box<io::Result<JoshutoDirList>>,
    },
    PreviewFile {
        path: path::PathBuf,
        res: io::Result<PreviewData>,
    },
    // terminal size change events
    Signal(i32),
    // filesystem change events
    Filesystem(notify::Event),
}

#[derive(Default, Debug, Clone, Copy)]
pub struct Config {}

/// A small event handler that wrap termion input and tick events. Each event
/// type is handled in its own thread and returned to a common `Receiver`
pub struct Events {
    pub event_tx: mpsc::Sender<AppEvent>,
    event_rx: mpsc::Receiver<AppEvent>,
    pub input_tx: mpsc::Sender<()>,
}

impl Events {
    pub fn new() -> Self {
        Self::default()
    }

    // We need a next() and a flush() so we don't continuously consume
    // input from the console. Sometimes, other applications need to
    // read terminal inputs while joshuto is in the background
    pub fn next(&self) -> Result<AppEvent, mpsc::RecvError> {
        let event = self.event_rx.recv()?;
        Ok(event)
    }

    pub fn flush(&self) {
        loop {
            if self.input_tx.send(()).is_ok() {
                break;
            }
        }
    }
}

impl std::default::Default for Events {
    fn default() -> Self {
        let (input_tx, input_rx) = mpsc::channel();
        let (event_tx, event_rx) = mpsc::channel();

        // edge case that starts off the input thread
        let _ = input_tx.send(());

        // signal thread
        let event_tx2 = event_tx.clone();
        let _ = thread::spawn(move || {
            let sigs = vec![signal::SIGWINCH];
            let mut signals = SignalsInfo::<SignalOnly>::new(sigs).unwrap();
            for signal in &mut signals {
                if let Err(e) = event_tx2.send(AppEvent::Signal(signal)) {
                    eprintln!("Signal thread send err: {:#?}", e);
                    return;
                }
            }
        });

        // input thread
        let event_tx2 = event_tx.clone();
        let _ = thread::spawn(move || {
            let stdin = io::stdin();
            let mut events = stdin.events();

            loop {
                let _ = input_rx.recv();
                if let Some(Ok(event)) = events.next() {
                    let _ = event_tx2.send(AppEvent::Termion(event));
                }
            }
        });

        Events {
            event_tx,
            event_rx,
            input_tx,
        }
    }
}