-
-
Notifications
You must be signed in to change notification settings - Fork 780
/
terminal.rs
174 lines (156 loc) · 5.01 KB
/
terminal.rs
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
use super::*;
use crate::terminalstate::performer::Performer;
use std::sync::Arc;
use termwiz::escape::parser::Parser;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "use_serde", derive(Serialize, Deserialize))]
pub enum ClipboardSelection {
Clipboard,
PrimarySelection,
}
pub trait Clipboard: Send + Sync {
fn set_contents(
&self,
selection: ClipboardSelection,
data: Option<String>,
) -> anyhow::Result<()>;
}
impl Clipboard for Box<dyn Clipboard> {
fn set_contents(
&self,
selection: ClipboardSelection,
data: Option<String>,
) -> anyhow::Result<()> {
self.as_ref().set_contents(selection, data)
}
}
pub trait DeviceControlHandler: Send + Sync {
fn handle_device_control(&mut self, _control: termwiz::escape::DeviceControlMode);
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "use_serde", derive(Serialize, Deserialize))]
pub enum Alert {
Bell,
ToastNotification {
/// The title text for the notification.
title: Option<String>,
/// The message body
body: String,
/// Whether clicking on the notification should focus the
/// window/tab/pane that generated it
focus: bool,
},
CurrentWorkingDirectoryChanged,
IconTitleChanged(Option<String>),
WindowTitleChanged(String),
TabTitleChanged(Option<String>),
/// When the color palette has been updated
PaletteChanged,
/// A UserVar has changed value
SetUserVar {
name: String,
value: String,
},
/// When something bumps the seqno in the terminal model and
/// the terminal is not focused
OutputSinceFocusLost,
}
pub trait AlertHandler: Send + Sync {
fn alert(&mut self, alert: Alert);
}
pub trait DownloadHandler: Send + Sync {
fn save_to_downloads(&self, name: Option<String>, data: Vec<u8>);
}
/// Represents an instance of a terminal emulator.
pub struct Terminal {
/// The terminal model/state
state: TerminalState,
/// Baseline terminal escape sequence parser
parser: Parser,
}
impl Deref for Terminal {
type Target = TerminalState;
fn deref(&self) -> &TerminalState {
&self.state
}
}
impl DerefMut for Terminal {
fn deref_mut(&mut self) -> &mut TerminalState {
&mut self.state
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, FromDynamic, ToDynamic)]
#[cfg_attr(feature = "use_serde", derive(Serialize, Deserialize))]
pub struct TerminalSize {
pub rows: usize,
pub cols: usize,
pub pixel_width: usize,
pub pixel_height: usize,
pub dpi: u32,
}
impl Default for TerminalSize {
fn default() -> Self {
Self {
rows: 24,
cols: 80,
pixel_width: 0,
pixel_height: 0,
dpi: 0,
}
}
}
impl Terminal {
/// Construct a new Terminal.
/// `physical_rows` and `physical_cols` describe the dimensions
/// of the visible portion of the terminal display in terms of
/// the number of text cells.
///
/// `pixel_width` and `pixel_height` describe the dimensions of
/// that same visible area but in pixels.
///
/// `term_program` and `term_version` are required to identify
/// the host terminal program; they are used to respond to the
/// terminal identification sequence `\033[>q`.
///
/// `writer` is anything that implements `std::io::Write`; it
/// is used to send input to the connected program; both keyboard
/// and mouse input is encoded and written to that stream, as
/// are answerback responses to a number of escape sequences.
pub fn new(
size: TerminalSize,
config: Arc<dyn TerminalConfiguration + Send + Sync>,
term_program: &str,
term_version: &str,
// writing to the writer sends data to input of the pty
writer: Box<dyn std::io::Write + Send>,
) -> Terminal {
Terminal {
state: TerminalState::new(size, config, term_program, term_version, writer),
parser: Parser::new(),
}
}
/// Feed the terminal parser a slice of bytes from the output
/// of the associated program.
/// The slice is not required to be a complete sequence of escape
/// characters; it is valid to feed in chunks of data as they arrive.
/// The output is parsed and applied to the terminal model.
pub fn advance_bytes<B: AsRef<[u8]>>(&mut self, bytes: B) {
self.state.increment_seqno();
{
let bytes = bytes.as_ref();
let mut performer = Performer::new(&mut self.state);
self.parser.parse(bytes, |action| performer.perform(action));
}
self.trigger_unseen_output_notif();
}
pub fn perform_actions(&mut self, actions: Vec<termwiz::escape::Action>) {
self.state.increment_seqno();
{
let mut performer = Performer::new(&mut self.state);
for action in actions {
performer.perform(action);
}
}
self.trigger_unseen_output_notif();
}
}