@akiojin/gwt 9.7.0 → 9.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +37 -6
- package/README.md +38 -8
- package/assets/icons/icon.icns +0 -0
- package/assets/icons/icon.ico +0 -0
- package/assets/icons/icon.png +0 -0
- package/assets/release-assets.json +18 -0
- package/docker-compose.yml +2 -2
- package/package.json +6 -3
- package/scripts/check-release-flow.sh +17 -0
- package/scripts/release-assets.cjs +11 -0
- package/scripts/test-all.sh +15 -1
- package/scripts/test_release_assets.cjs +67 -0
- package/vendor/vt100/LICENSE +21 -0
- package/vendor/vt100/src/attrs.rs +144 -0
- package/vendor/vt100/src/callbacks.rs +69 -0
- package/vendor/vt100/src/cell.rs +179 -0
- package/vendor/vt100/src/grid.rs +742 -0
- package/vendor/vt100/src/lib.rs +64 -0
- package/vendor/vt100/src/parser.rs +96 -0
- package/vendor/vt100/src/perform.rs +277 -0
- package/vendor/vt100/src/row.rs +488 -0
- package/vendor/vt100/src/screen.rs +1354 -0
- package/vendor/vt100/src/term.rs +551 -0
- package/wix/main.wxs +40 -5
|
@@ -0,0 +1,1354 @@
|
|
|
1
|
+
use crate::term::BufWrite as _;
|
|
2
|
+
use unicode_width::UnicodeWidthChar as _;
|
|
3
|
+
|
|
4
|
+
const MODE_APPLICATION_KEYPAD: u8 = 0b0000_0001;
|
|
5
|
+
const MODE_APPLICATION_CURSOR: u8 = 0b0000_0010;
|
|
6
|
+
const MODE_HIDE_CURSOR: u8 = 0b0000_0100;
|
|
7
|
+
const MODE_ALTERNATE_SCREEN: u8 = 0b0000_1000;
|
|
8
|
+
const MODE_BRACKETED_PASTE: u8 = 0b0001_0000;
|
|
9
|
+
|
|
10
|
+
/// The xterm mouse handling mode currently in use.
|
|
11
|
+
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
|
|
12
|
+
pub enum MouseProtocolMode {
|
|
13
|
+
/// Mouse handling is disabled.
|
|
14
|
+
#[default]
|
|
15
|
+
None,
|
|
16
|
+
|
|
17
|
+
/// Mouse button events should be reported on button press. Also known as
|
|
18
|
+
/// X10 mouse mode.
|
|
19
|
+
Press,
|
|
20
|
+
|
|
21
|
+
/// Mouse button events should be reported on button press and release.
|
|
22
|
+
/// Also known as VT200 mouse mode.
|
|
23
|
+
PressRelease,
|
|
24
|
+
|
|
25
|
+
// Highlight,
|
|
26
|
+
/// Mouse button events should be reported on button press and release, as
|
|
27
|
+
/// well as when the mouse moves between cells while a button is held
|
|
28
|
+
/// down.
|
|
29
|
+
ButtonMotion,
|
|
30
|
+
|
|
31
|
+
/// Mouse button events should be reported on button press and release,
|
|
32
|
+
/// and mouse motion events should be reported when the mouse moves
|
|
33
|
+
/// between cells regardless of whether a button is held down or not.
|
|
34
|
+
AnyMotion,
|
|
35
|
+
// DecLocator,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/// The encoding to use for the enabled [`MouseProtocolMode`].
|
|
39
|
+
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
|
|
40
|
+
pub enum MouseProtocolEncoding {
|
|
41
|
+
/// Default single-printable-byte encoding.
|
|
42
|
+
#[default]
|
|
43
|
+
Default,
|
|
44
|
+
|
|
45
|
+
/// UTF-8-based encoding.
|
|
46
|
+
Utf8,
|
|
47
|
+
|
|
48
|
+
/// SGR-like encoding.
|
|
49
|
+
Sgr,
|
|
50
|
+
// Urxvt,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/// Represents the overall terminal state.
|
|
54
|
+
#[derive(Clone, Debug)]
|
|
55
|
+
pub struct Screen {
|
|
56
|
+
grid: crate::grid::Grid,
|
|
57
|
+
alternate_grid: crate::grid::Grid,
|
|
58
|
+
|
|
59
|
+
attrs: crate::attrs::Attrs,
|
|
60
|
+
saved_attrs: crate::attrs::Attrs,
|
|
61
|
+
|
|
62
|
+
modes: u8,
|
|
63
|
+
mouse_protocol_mode: MouseProtocolMode,
|
|
64
|
+
mouse_protocol_encoding: MouseProtocolEncoding,
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
impl Screen {
|
|
68
|
+
pub(crate) fn new(
|
|
69
|
+
size: crate::grid::Size,
|
|
70
|
+
scrollback_len: usize,
|
|
71
|
+
) -> Self {
|
|
72
|
+
let mut grid = crate::grid::Grid::new(size, scrollback_len);
|
|
73
|
+
grid.allocate_rows();
|
|
74
|
+
Self {
|
|
75
|
+
grid,
|
|
76
|
+
alternate_grid: crate::grid::Grid::new(size, 0),
|
|
77
|
+
|
|
78
|
+
attrs: crate::attrs::Attrs::default(),
|
|
79
|
+
saved_attrs: crate::attrs::Attrs::default(),
|
|
80
|
+
|
|
81
|
+
modes: 0,
|
|
82
|
+
mouse_protocol_mode: MouseProtocolMode::default(),
|
|
83
|
+
mouse_protocol_encoding: MouseProtocolEncoding::default(),
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/// Resizes the terminal.
|
|
88
|
+
pub fn set_size(&mut self, rows: u16, cols: u16) {
|
|
89
|
+
self.grid.set_size(crate::grid::Size { rows, cols });
|
|
90
|
+
self.alternate_grid
|
|
91
|
+
.set_size(crate::grid::Size { rows, cols });
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/// Returns the current size of the terminal.
|
|
95
|
+
///
|
|
96
|
+
/// The return value will be (rows, cols).
|
|
97
|
+
#[must_use]
|
|
98
|
+
pub fn size(&self) -> (u16, u16) {
|
|
99
|
+
let size = self.grid().size();
|
|
100
|
+
(size.rows, size.cols)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/// Scrolls to the given position in the scrollback.
|
|
104
|
+
///
|
|
105
|
+
/// This position indicates the offset from the top of the screen, and
|
|
106
|
+
/// should be `0` to put the normal screen in view.
|
|
107
|
+
///
|
|
108
|
+
/// This affects the return values of methods called on the screen: for
|
|
109
|
+
/// instance, `screen.cell(0, 0)` will return the top left corner of the
|
|
110
|
+
/// screen after taking the scrollback offset into account.
|
|
111
|
+
///
|
|
112
|
+
/// The value given will be clamped to the actual size of the scrollback.
|
|
113
|
+
pub fn set_scrollback(&mut self, rows: usize) {
|
|
114
|
+
self.grid_mut().set_scrollback(rows);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/// Returns the current position in the scrollback.
|
|
118
|
+
///
|
|
119
|
+
/// This position indicates the offset from the top of the screen, and is
|
|
120
|
+
/// `0` when the normal screen is in view.
|
|
121
|
+
#[must_use]
|
|
122
|
+
pub fn scrollback(&self) -> usize {
|
|
123
|
+
self.grid().scrollback()
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/// Returns the text contents of the terminal.
|
|
127
|
+
///
|
|
128
|
+
/// This will not include any formatting information, and will be in plain
|
|
129
|
+
/// text format.
|
|
130
|
+
#[must_use]
|
|
131
|
+
pub fn contents(&self) -> String {
|
|
132
|
+
let mut contents = String::new();
|
|
133
|
+
self.write_contents(&mut contents);
|
|
134
|
+
contents
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
fn write_contents(&self, contents: &mut String) {
|
|
138
|
+
self.grid().write_contents(contents);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/// Returns the text contents of the terminal by row, restricted to the
|
|
142
|
+
/// given subset of columns.
|
|
143
|
+
///
|
|
144
|
+
/// This will not include any formatting information, and will be in plain
|
|
145
|
+
/// text format.
|
|
146
|
+
///
|
|
147
|
+
/// Newlines will not be included.
|
|
148
|
+
pub fn rows(
|
|
149
|
+
&self,
|
|
150
|
+
start: u16,
|
|
151
|
+
width: u16,
|
|
152
|
+
) -> impl Iterator<Item = String> + '_ {
|
|
153
|
+
self.grid().visible_rows().map(move |row| {
|
|
154
|
+
let mut contents = String::new();
|
|
155
|
+
row.write_contents(&mut contents, start, width, false);
|
|
156
|
+
contents
|
|
157
|
+
})
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/// Returns the text contents of the terminal logically between two cells.
|
|
161
|
+
/// This will include the remainder of the starting row after `start_col`,
|
|
162
|
+
/// followed by the entire contents of the rows between `start_row` and
|
|
163
|
+
/// `end_row`, followed by the beginning of the `end_row` up until
|
|
164
|
+
/// `end_col`. This is useful for things like determining the contents of
|
|
165
|
+
/// a clipboard selection.
|
|
166
|
+
#[must_use]
|
|
167
|
+
pub fn contents_between(
|
|
168
|
+
&self,
|
|
169
|
+
start_row: u16,
|
|
170
|
+
start_col: u16,
|
|
171
|
+
end_row: u16,
|
|
172
|
+
end_col: u16,
|
|
173
|
+
) -> String {
|
|
174
|
+
match start_row.cmp(&end_row) {
|
|
175
|
+
std::cmp::Ordering::Less => {
|
|
176
|
+
let (_, cols) = self.size();
|
|
177
|
+
let mut contents = String::new();
|
|
178
|
+
for (i, row) in self
|
|
179
|
+
.grid()
|
|
180
|
+
.visible_rows()
|
|
181
|
+
.enumerate()
|
|
182
|
+
.skip(usize::from(start_row))
|
|
183
|
+
.take(usize::from(end_row) - usize::from(start_row) + 1)
|
|
184
|
+
{
|
|
185
|
+
if i == usize::from(start_row) {
|
|
186
|
+
row.write_contents(
|
|
187
|
+
&mut contents,
|
|
188
|
+
start_col,
|
|
189
|
+
cols - start_col,
|
|
190
|
+
false,
|
|
191
|
+
);
|
|
192
|
+
if !row.wrapped() {
|
|
193
|
+
contents.push('\n');
|
|
194
|
+
}
|
|
195
|
+
} else if i == usize::from(end_row) {
|
|
196
|
+
row.write_contents(&mut contents, 0, end_col, false);
|
|
197
|
+
} else {
|
|
198
|
+
row.write_contents(&mut contents, 0, cols, false);
|
|
199
|
+
if !row.wrapped() {
|
|
200
|
+
contents.push('\n');
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
contents
|
|
205
|
+
}
|
|
206
|
+
std::cmp::Ordering::Equal => {
|
|
207
|
+
if start_col < end_col {
|
|
208
|
+
self.rows(start_col, end_col - start_col)
|
|
209
|
+
.nth(usize::from(start_row))
|
|
210
|
+
.unwrap_or_default()
|
|
211
|
+
} else {
|
|
212
|
+
String::new()
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
std::cmp::Ordering::Greater => String::new(),
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/// Return escape codes sufficient to reproduce the entire contents of the
|
|
220
|
+
/// current terminal state. This is a convenience wrapper around
|
|
221
|
+
/// [`contents_formatted`](Self::contents_formatted) and
|
|
222
|
+
/// [`input_mode_formatted`](Self::input_mode_formatted).
|
|
223
|
+
#[must_use]
|
|
224
|
+
pub fn state_formatted(&self) -> Vec<u8> {
|
|
225
|
+
let mut contents = vec![];
|
|
226
|
+
self.write_contents_formatted(&mut contents);
|
|
227
|
+
self.write_input_mode_formatted(&mut contents);
|
|
228
|
+
contents
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/// Return escape codes sufficient to turn the terminal state of the
|
|
232
|
+
/// screen `prev` into the current terminal state. This is a convenience
|
|
233
|
+
/// wrapper around [`contents_diff`](Self::contents_diff) and
|
|
234
|
+
/// [`input_mode_diff`](Self::input_mode_diff).
|
|
235
|
+
#[must_use]
|
|
236
|
+
pub fn state_diff(&self, prev: &Self) -> Vec<u8> {
|
|
237
|
+
let mut contents = vec![];
|
|
238
|
+
self.write_contents_diff(&mut contents, prev);
|
|
239
|
+
self.write_input_mode_diff(&mut contents, prev);
|
|
240
|
+
contents
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/// Returns the formatted visible contents of the terminal.
|
|
244
|
+
///
|
|
245
|
+
/// Formatting information will be included inline as terminal escape
|
|
246
|
+
/// codes. The result will be suitable for feeding directly to a raw
|
|
247
|
+
/// terminal parser, and will result in the same visual output.
|
|
248
|
+
#[must_use]
|
|
249
|
+
pub fn contents_formatted(&self) -> Vec<u8> {
|
|
250
|
+
let mut contents = vec![];
|
|
251
|
+
self.write_contents_formatted(&mut contents);
|
|
252
|
+
contents
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
fn write_contents_formatted(&self, contents: &mut Vec<u8>) {
|
|
256
|
+
crate::term::HideCursor::new(self.hide_cursor()).write_buf(contents);
|
|
257
|
+
let prev_attrs = self.grid().write_contents_formatted(contents);
|
|
258
|
+
self.attrs.write_escape_code_diff(contents, &prev_attrs);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/// Returns the formatted visible contents of the terminal by row,
|
|
262
|
+
/// restricted to the given subset of columns.
|
|
263
|
+
///
|
|
264
|
+
/// Formatting information will be included inline as terminal escape
|
|
265
|
+
/// codes. The result will be suitable for feeding directly to a raw
|
|
266
|
+
/// terminal parser, and will result in the same visual output.
|
|
267
|
+
///
|
|
268
|
+
/// You are responsible for positioning the cursor before printing each
|
|
269
|
+
/// row, and the final cursor position after displaying each row is
|
|
270
|
+
/// unspecified.
|
|
271
|
+
// the unwraps in this method shouldn't be reachable
|
|
272
|
+
#[allow(clippy::missing_panics_doc)]
|
|
273
|
+
pub fn rows_formatted(
|
|
274
|
+
&self,
|
|
275
|
+
start: u16,
|
|
276
|
+
width: u16,
|
|
277
|
+
) -> impl Iterator<Item = Vec<u8>> + '_ {
|
|
278
|
+
let mut wrapping = false;
|
|
279
|
+
self.grid().visible_rows().enumerate().map(move |(i, row)| {
|
|
280
|
+
// number of rows in a grid is stored in a u16 (see Size), so
|
|
281
|
+
// visible_rows can never return enough rows to overflow here
|
|
282
|
+
let i = i.try_into().unwrap();
|
|
283
|
+
let mut contents = vec![];
|
|
284
|
+
row.write_contents_formatted(
|
|
285
|
+
&mut contents,
|
|
286
|
+
start,
|
|
287
|
+
width,
|
|
288
|
+
i,
|
|
289
|
+
wrapping,
|
|
290
|
+
None,
|
|
291
|
+
None,
|
|
292
|
+
);
|
|
293
|
+
if start == 0 && width == self.grid.size().cols {
|
|
294
|
+
wrapping = row.wrapped();
|
|
295
|
+
}
|
|
296
|
+
contents
|
|
297
|
+
})
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/// Returns a terminal byte stream sufficient to turn the visible contents
|
|
301
|
+
/// of the screen described by `prev` into the visible contents of the
|
|
302
|
+
/// screen described by `self`.
|
|
303
|
+
///
|
|
304
|
+
/// The result of rendering `prev.contents_formatted()` followed by
|
|
305
|
+
/// `self.contents_diff(prev)` should be equivalent to the result of
|
|
306
|
+
/// rendering `self.contents_formatted()`. This is primarily useful when
|
|
307
|
+
/// you already have a terminal parser whose state is described by `prev`,
|
|
308
|
+
/// since the diff will likely require less memory and cause less
|
|
309
|
+
/// flickering than redrawing the entire screen contents.
|
|
310
|
+
#[must_use]
|
|
311
|
+
pub fn contents_diff(&self, prev: &Self) -> Vec<u8> {
|
|
312
|
+
let mut contents = vec![];
|
|
313
|
+
self.write_contents_diff(&mut contents, prev);
|
|
314
|
+
contents
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
fn write_contents_diff(&self, contents: &mut Vec<u8>, prev: &Self) {
|
|
318
|
+
if self.hide_cursor() != prev.hide_cursor() {
|
|
319
|
+
crate::term::HideCursor::new(self.hide_cursor())
|
|
320
|
+
.write_buf(contents);
|
|
321
|
+
}
|
|
322
|
+
let prev_attrs = self.grid().write_contents_diff(
|
|
323
|
+
contents,
|
|
324
|
+
prev.grid(),
|
|
325
|
+
prev.attrs,
|
|
326
|
+
);
|
|
327
|
+
self.attrs.write_escape_code_diff(contents, &prev_attrs);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/// Returns a sequence of terminal byte streams sufficient to turn the
|
|
331
|
+
/// visible contents of the subset of each row from `prev` (as described
|
|
332
|
+
/// by `start` and `width`) into the visible contents of the corresponding
|
|
333
|
+
/// row subset in `self`.
|
|
334
|
+
///
|
|
335
|
+
/// You are responsible for positioning the cursor before printing each
|
|
336
|
+
/// row, and the final cursor position after displaying each row is
|
|
337
|
+
/// unspecified.
|
|
338
|
+
// the unwraps in this method shouldn't be reachable
|
|
339
|
+
#[allow(clippy::missing_panics_doc)]
|
|
340
|
+
pub fn rows_diff<'a>(
|
|
341
|
+
&'a self,
|
|
342
|
+
prev: &'a Self,
|
|
343
|
+
start: u16,
|
|
344
|
+
width: u16,
|
|
345
|
+
) -> impl Iterator<Item = Vec<u8>> + 'a {
|
|
346
|
+
self.grid()
|
|
347
|
+
.visible_rows()
|
|
348
|
+
.zip(prev.grid().visible_rows())
|
|
349
|
+
.enumerate()
|
|
350
|
+
.map(move |(i, (row, prev_row))| {
|
|
351
|
+
// number of rows in a grid is stored in a u16 (see Size), so
|
|
352
|
+
// visible_rows can never return enough rows to overflow here
|
|
353
|
+
let i = i.try_into().unwrap();
|
|
354
|
+
let mut contents = vec![];
|
|
355
|
+
row.write_contents_diff(
|
|
356
|
+
&mut contents,
|
|
357
|
+
prev_row,
|
|
358
|
+
start,
|
|
359
|
+
width,
|
|
360
|
+
i,
|
|
361
|
+
false,
|
|
362
|
+
false,
|
|
363
|
+
crate::grid::Pos { row: i, col: start },
|
|
364
|
+
crate::attrs::Attrs::default(),
|
|
365
|
+
);
|
|
366
|
+
contents
|
|
367
|
+
})
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/// Returns terminal escape sequences sufficient to set the current
|
|
371
|
+
/// terminal's input modes.
|
|
372
|
+
///
|
|
373
|
+
/// Supported modes are:
|
|
374
|
+
/// * application keypad
|
|
375
|
+
/// * application cursor
|
|
376
|
+
/// * bracketed paste
|
|
377
|
+
/// * xterm mouse support
|
|
378
|
+
#[must_use]
|
|
379
|
+
pub fn input_mode_formatted(&self) -> Vec<u8> {
|
|
380
|
+
let mut contents = vec![];
|
|
381
|
+
self.write_input_mode_formatted(&mut contents);
|
|
382
|
+
contents
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
fn write_input_mode_formatted(&self, contents: &mut Vec<u8>) {
|
|
386
|
+
crate::term::ApplicationKeypad::new(
|
|
387
|
+
self.mode(MODE_APPLICATION_KEYPAD),
|
|
388
|
+
)
|
|
389
|
+
.write_buf(contents);
|
|
390
|
+
crate::term::ApplicationCursor::new(
|
|
391
|
+
self.mode(MODE_APPLICATION_CURSOR),
|
|
392
|
+
)
|
|
393
|
+
.write_buf(contents);
|
|
394
|
+
crate::term::BracketedPaste::new(self.mode(MODE_BRACKETED_PASTE))
|
|
395
|
+
.write_buf(contents);
|
|
396
|
+
crate::term::MouseProtocolMode::new(
|
|
397
|
+
self.mouse_protocol_mode,
|
|
398
|
+
MouseProtocolMode::None,
|
|
399
|
+
)
|
|
400
|
+
.write_buf(contents);
|
|
401
|
+
crate::term::MouseProtocolEncoding::new(
|
|
402
|
+
self.mouse_protocol_encoding,
|
|
403
|
+
MouseProtocolEncoding::Default,
|
|
404
|
+
)
|
|
405
|
+
.write_buf(contents);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/// Returns terminal escape sequences sufficient to change the previous
|
|
409
|
+
/// terminal's input modes to the input modes enabled in the current
|
|
410
|
+
/// terminal.
|
|
411
|
+
#[must_use]
|
|
412
|
+
pub fn input_mode_diff(&self, prev: &Self) -> Vec<u8> {
|
|
413
|
+
let mut contents = vec![];
|
|
414
|
+
self.write_input_mode_diff(&mut contents, prev);
|
|
415
|
+
contents
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
fn write_input_mode_diff(&self, contents: &mut Vec<u8>, prev: &Self) {
|
|
419
|
+
if self.mode(MODE_APPLICATION_KEYPAD)
|
|
420
|
+
!= prev.mode(MODE_APPLICATION_KEYPAD)
|
|
421
|
+
{
|
|
422
|
+
crate::term::ApplicationKeypad::new(
|
|
423
|
+
self.mode(MODE_APPLICATION_KEYPAD),
|
|
424
|
+
)
|
|
425
|
+
.write_buf(contents);
|
|
426
|
+
}
|
|
427
|
+
if self.mode(MODE_APPLICATION_CURSOR)
|
|
428
|
+
!= prev.mode(MODE_APPLICATION_CURSOR)
|
|
429
|
+
{
|
|
430
|
+
crate::term::ApplicationCursor::new(
|
|
431
|
+
self.mode(MODE_APPLICATION_CURSOR),
|
|
432
|
+
)
|
|
433
|
+
.write_buf(contents);
|
|
434
|
+
}
|
|
435
|
+
if self.mode(MODE_BRACKETED_PASTE) != prev.mode(MODE_BRACKETED_PASTE)
|
|
436
|
+
{
|
|
437
|
+
crate::term::BracketedPaste::new(self.mode(MODE_BRACKETED_PASTE))
|
|
438
|
+
.write_buf(contents);
|
|
439
|
+
}
|
|
440
|
+
crate::term::MouseProtocolMode::new(
|
|
441
|
+
self.mouse_protocol_mode,
|
|
442
|
+
prev.mouse_protocol_mode,
|
|
443
|
+
)
|
|
444
|
+
.write_buf(contents);
|
|
445
|
+
crate::term::MouseProtocolEncoding::new(
|
|
446
|
+
self.mouse_protocol_encoding,
|
|
447
|
+
prev.mouse_protocol_encoding,
|
|
448
|
+
)
|
|
449
|
+
.write_buf(contents);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/// Returns terminal escape sequences sufficient to set the current
|
|
453
|
+
/// terminal's drawing attributes.
|
|
454
|
+
///
|
|
455
|
+
/// Supported drawing attributes are:
|
|
456
|
+
/// * fgcolor
|
|
457
|
+
/// * bgcolor
|
|
458
|
+
/// * bold
|
|
459
|
+
/// * dim
|
|
460
|
+
/// * italic
|
|
461
|
+
/// * underline
|
|
462
|
+
/// * inverse
|
|
463
|
+
///
|
|
464
|
+
/// This is not typically necessary, since
|
|
465
|
+
/// [`contents_formatted`](Self::contents_formatted) will leave
|
|
466
|
+
/// the current active drawing attributes in the correct state, but this
|
|
467
|
+
/// can be useful in the case of drawing additional things on top of a
|
|
468
|
+
/// terminal output, since you will need to restore the terminal state
|
|
469
|
+
/// without the terminal contents necessarily being the same.
|
|
470
|
+
#[must_use]
|
|
471
|
+
pub fn attributes_formatted(&self) -> Vec<u8> {
|
|
472
|
+
let mut contents = vec![];
|
|
473
|
+
self.write_attributes_formatted(&mut contents);
|
|
474
|
+
contents
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
fn write_attributes_formatted(&self, contents: &mut Vec<u8>) {
|
|
478
|
+
crate::term::ClearAttrs.write_buf(contents);
|
|
479
|
+
self.attrs.write_escape_code_diff(
|
|
480
|
+
contents,
|
|
481
|
+
&crate::attrs::Attrs::default(),
|
|
482
|
+
);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/// Returns the current cursor position of the terminal.
|
|
486
|
+
///
|
|
487
|
+
/// The return value will be (row, col).
|
|
488
|
+
#[must_use]
|
|
489
|
+
pub fn cursor_position(&self) -> (u16, u16) {
|
|
490
|
+
let pos = self.grid().pos();
|
|
491
|
+
(pos.row, pos.col)
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/// Returns terminal escape sequences sufficient to set the current
|
|
495
|
+
/// cursor state of the terminal.
|
|
496
|
+
///
|
|
497
|
+
/// This is not typically necessary, since
|
|
498
|
+
/// [`contents_formatted`](Self::contents_formatted) will leave
|
|
499
|
+
/// the cursor in the correct state, but this can be useful in the case of
|
|
500
|
+
/// drawing additional things on top of a terminal output, since you will
|
|
501
|
+
/// need to restore the terminal state without the terminal contents
|
|
502
|
+
/// necessarily being the same.
|
|
503
|
+
///
|
|
504
|
+
/// Note that the bytes returned by this function may alter the active
|
|
505
|
+
/// drawing attributes, because it may require redrawing existing cells in
|
|
506
|
+
/// order to position the cursor correctly (for instance, in the case
|
|
507
|
+
/// where the cursor is past the end of a row). Therefore, you should
|
|
508
|
+
/// ensure to reset the active drawing attributes if necessary after
|
|
509
|
+
/// processing this data, for instance by using
|
|
510
|
+
/// [`attributes_formatted`](Self::attributes_formatted).
|
|
511
|
+
#[must_use]
|
|
512
|
+
pub fn cursor_state_formatted(&self) -> Vec<u8> {
|
|
513
|
+
let mut contents = vec![];
|
|
514
|
+
self.write_cursor_state_formatted(&mut contents);
|
|
515
|
+
contents
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
fn write_cursor_state_formatted(&self, contents: &mut Vec<u8>) {
|
|
519
|
+
crate::term::HideCursor::new(self.hide_cursor()).write_buf(contents);
|
|
520
|
+
self.grid()
|
|
521
|
+
.write_cursor_position_formatted(contents, None, None);
|
|
522
|
+
|
|
523
|
+
// we don't just call write_attributes_formatted here, because that
|
|
524
|
+
// would still be confusing - consider the case where the user sets
|
|
525
|
+
// their own unrelated drawing attributes (on a different parser
|
|
526
|
+
// instance) and then calls cursor_state_formatted. just documenting
|
|
527
|
+
// it and letting the user handle it on their own is more
|
|
528
|
+
// straightforward.
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/// Returns the [`Cell`](crate::Cell) object at the given location in the
|
|
532
|
+
/// terminal, if it exists.
|
|
533
|
+
#[must_use]
|
|
534
|
+
pub fn cell(&self, row: u16, col: u16) -> Option<&crate::Cell> {
|
|
535
|
+
self.grid().visible_cell(crate::grid::Pos { row, col })
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/// Returns whether the text in row `row` should wrap to the next line.
|
|
539
|
+
#[must_use]
|
|
540
|
+
pub fn row_wrapped(&self, row: u16) -> bool {
|
|
541
|
+
self.grid()
|
|
542
|
+
.visible_row(row)
|
|
543
|
+
.is_some_and(crate::row::Row::wrapped)
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/// Returns whether the alternate screen is currently in use.
|
|
547
|
+
#[must_use]
|
|
548
|
+
pub fn alternate_screen(&self) -> bool {
|
|
549
|
+
self.mode(MODE_ALTERNATE_SCREEN)
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/// Returns whether the terminal should be in application keypad mode.
|
|
553
|
+
#[must_use]
|
|
554
|
+
pub fn application_keypad(&self) -> bool {
|
|
555
|
+
self.mode(MODE_APPLICATION_KEYPAD)
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/// Returns whether the terminal should be in application cursor mode.
|
|
559
|
+
#[must_use]
|
|
560
|
+
pub fn application_cursor(&self) -> bool {
|
|
561
|
+
self.mode(MODE_APPLICATION_CURSOR)
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/// Returns whether the terminal should be in hide cursor mode.
|
|
565
|
+
#[must_use]
|
|
566
|
+
pub fn hide_cursor(&self) -> bool {
|
|
567
|
+
self.mode(MODE_HIDE_CURSOR)
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
/// Returns whether the terminal should be in bracketed paste mode.
|
|
571
|
+
#[must_use]
|
|
572
|
+
pub fn bracketed_paste(&self) -> bool {
|
|
573
|
+
self.mode(MODE_BRACKETED_PASTE)
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/// Returns the currently active [`MouseProtocolMode`].
|
|
577
|
+
#[must_use]
|
|
578
|
+
pub fn mouse_protocol_mode(&self) -> MouseProtocolMode {
|
|
579
|
+
self.mouse_protocol_mode
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/// Returns the currently active [`MouseProtocolEncoding`].
|
|
583
|
+
#[must_use]
|
|
584
|
+
pub fn mouse_protocol_encoding(&self) -> MouseProtocolEncoding {
|
|
585
|
+
self.mouse_protocol_encoding
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/// Returns the currently active foreground color.
|
|
589
|
+
#[must_use]
|
|
590
|
+
pub fn fgcolor(&self) -> crate::Color {
|
|
591
|
+
self.attrs.fgcolor
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/// Returns the currently active background color.
|
|
595
|
+
#[must_use]
|
|
596
|
+
pub fn bgcolor(&self) -> crate::Color {
|
|
597
|
+
self.attrs.bgcolor
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/// Returns whether newly drawn text should be rendered with the bold text
|
|
601
|
+
/// attribute.
|
|
602
|
+
#[must_use]
|
|
603
|
+
pub fn bold(&self) -> bool {
|
|
604
|
+
self.attrs.bold()
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/// Returns whether newly drawn text should be rendered with the dim text
|
|
608
|
+
/// attribute.
|
|
609
|
+
#[must_use]
|
|
610
|
+
pub fn dim(&self) -> bool {
|
|
611
|
+
self.attrs.dim()
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/// Returns whether newly drawn text should be rendered with the italic
|
|
615
|
+
/// text attribute.
|
|
616
|
+
#[must_use]
|
|
617
|
+
pub fn italic(&self) -> bool {
|
|
618
|
+
self.attrs.italic()
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/// Returns whether newly drawn text should be rendered with the
|
|
622
|
+
/// underlined text attribute.
|
|
623
|
+
#[must_use]
|
|
624
|
+
pub fn underline(&self) -> bool {
|
|
625
|
+
self.attrs.underline()
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/// Returns whether newly drawn text should be rendered with the inverse
|
|
629
|
+
/// text attribute.
|
|
630
|
+
#[must_use]
|
|
631
|
+
pub fn inverse(&self) -> bool {
|
|
632
|
+
self.attrs.inverse()
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
pub(crate) fn grid(&self) -> &crate::grid::Grid {
|
|
636
|
+
if self.mode(MODE_ALTERNATE_SCREEN) {
|
|
637
|
+
&self.alternate_grid
|
|
638
|
+
} else {
|
|
639
|
+
&self.grid
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
fn grid_mut(&mut self) -> &mut crate::grid::Grid {
|
|
644
|
+
if self.mode(MODE_ALTERNATE_SCREEN) {
|
|
645
|
+
&mut self.alternate_grid
|
|
646
|
+
} else {
|
|
647
|
+
&mut self.grid
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
fn enter_alternate_grid(&mut self) {
|
|
652
|
+
self.grid_mut().set_scrollback(0);
|
|
653
|
+
self.set_mode(MODE_ALTERNATE_SCREEN);
|
|
654
|
+
self.alternate_grid.allocate_rows();
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
fn exit_alternate_grid(&mut self) {
|
|
658
|
+
self.clear_mode(MODE_ALTERNATE_SCREEN);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
fn save_cursor(&mut self) {
|
|
662
|
+
self.grid_mut().save_cursor();
|
|
663
|
+
self.saved_attrs = self.attrs;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
fn restore_cursor(&mut self) {
|
|
667
|
+
self.grid_mut().restore_cursor();
|
|
668
|
+
self.attrs = self.saved_attrs;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
fn set_mode(&mut self, mode: u8) {
|
|
672
|
+
self.modes |= mode;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
fn clear_mode(&mut self, mode: u8) {
|
|
676
|
+
self.modes &= !mode;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
fn mode(&self, mode: u8) -> bool {
|
|
680
|
+
self.modes & mode != 0
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
fn set_mouse_mode(&mut self, mode: MouseProtocolMode) {
|
|
684
|
+
self.mouse_protocol_mode = mode;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
fn clear_mouse_mode(&mut self, mode: MouseProtocolMode) {
|
|
688
|
+
if self.mouse_protocol_mode == mode {
|
|
689
|
+
self.mouse_protocol_mode = MouseProtocolMode::default();
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
fn set_mouse_encoding(&mut self, encoding: MouseProtocolEncoding) {
|
|
694
|
+
self.mouse_protocol_encoding = encoding;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
fn clear_mouse_encoding(&mut self, encoding: MouseProtocolEncoding) {
|
|
698
|
+
if self.mouse_protocol_encoding == encoding {
|
|
699
|
+
self.mouse_protocol_encoding = MouseProtocolEncoding::default();
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
impl Screen {
|
|
705
|
+
pub(crate) fn text(&mut self, c: char) {
|
|
706
|
+
let pos = self.grid().pos();
|
|
707
|
+
let size = self.grid().size();
|
|
708
|
+
let attrs = self.attrs;
|
|
709
|
+
|
|
710
|
+
let width = c.width();
|
|
711
|
+
if width.is_none() && (u32::from(c)) < 256 {
|
|
712
|
+
// don't even try to draw control characters
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
let width = width
|
|
716
|
+
.unwrap_or(1)
|
|
717
|
+
.try_into()
|
|
718
|
+
// width() can only return 0, 1, or 2
|
|
719
|
+
.unwrap();
|
|
720
|
+
|
|
721
|
+
// it doesn't make any sense to wrap if the last column in a row
|
|
722
|
+
// didn't already have contents. don't try to handle the case where a
|
|
723
|
+
// character wraps because there was only one column left in the
|
|
724
|
+
// previous row - literally everything handles this case differently,
|
|
725
|
+
// and this is tmux behavior (and also the simplest). i'm open to
|
|
726
|
+
// reconsidering this behavior, but only with a really good reason
|
|
727
|
+
// (xterm handles this by introducing the concept of triple width
|
|
728
|
+
// cells, which i really don't want to do).
|
|
729
|
+
let mut wrap = false;
|
|
730
|
+
if pos.col > size.cols - width {
|
|
731
|
+
let last_cell = self
|
|
732
|
+
.grid()
|
|
733
|
+
.drawing_cell(crate::grid::Pos {
|
|
734
|
+
row: pos.row,
|
|
735
|
+
col: size.cols - 1,
|
|
736
|
+
})
|
|
737
|
+
// pos.row is valid, since it comes directly from
|
|
738
|
+
// self.grid().pos() which we assume to always have a valid
|
|
739
|
+
// row value. size.cols - 1 is also always a valid column.
|
|
740
|
+
.unwrap();
|
|
741
|
+
if last_cell.has_contents() || last_cell.is_wide_continuation() {
|
|
742
|
+
wrap = true;
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
self.grid_mut().col_wrap(width, wrap);
|
|
746
|
+
let pos = self.grid().pos();
|
|
747
|
+
|
|
748
|
+
if width == 0 {
|
|
749
|
+
if pos.col > 0 {
|
|
750
|
+
let mut prev_cell = self
|
|
751
|
+
.grid_mut()
|
|
752
|
+
.drawing_cell_mut(crate::grid::Pos {
|
|
753
|
+
row: pos.row,
|
|
754
|
+
col: pos.col - 1,
|
|
755
|
+
})
|
|
756
|
+
// pos.row is valid, since it comes directly from
|
|
757
|
+
// self.grid().pos() which we assume to always have a
|
|
758
|
+
// valid row value. pos.col - 1 is valid because we just
|
|
759
|
+
// checked for pos.col > 0.
|
|
760
|
+
.unwrap();
|
|
761
|
+
if prev_cell.is_wide_continuation() {
|
|
762
|
+
prev_cell = self
|
|
763
|
+
.grid_mut()
|
|
764
|
+
.drawing_cell_mut(crate::grid::Pos {
|
|
765
|
+
row: pos.row,
|
|
766
|
+
col: pos.col - 2,
|
|
767
|
+
})
|
|
768
|
+
// pos.row is valid, since it comes directly from
|
|
769
|
+
// self.grid().pos() which we assume to always have a
|
|
770
|
+
// valid row value. we know pos.col - 2 is valid
|
|
771
|
+
// because the cell at pos.col - 1 is a wide
|
|
772
|
+
// continuation character, which means there must be
|
|
773
|
+
// the first half of the wide character before it.
|
|
774
|
+
.unwrap();
|
|
775
|
+
}
|
|
776
|
+
prev_cell.append(c);
|
|
777
|
+
} else if pos.row > 0 {
|
|
778
|
+
let prev_row = self
|
|
779
|
+
.grid()
|
|
780
|
+
.drawing_row(pos.row - 1)
|
|
781
|
+
// pos.row is valid, since it comes directly from
|
|
782
|
+
// self.grid().pos() which we assume to always have a
|
|
783
|
+
// valid row value. pos.row - 1 is valid because we just
|
|
784
|
+
// checked for pos.row > 0.
|
|
785
|
+
.unwrap();
|
|
786
|
+
if prev_row.wrapped() {
|
|
787
|
+
let mut prev_cell = self
|
|
788
|
+
.grid_mut()
|
|
789
|
+
.drawing_cell_mut(crate::grid::Pos {
|
|
790
|
+
row: pos.row - 1,
|
|
791
|
+
col: size.cols - 1,
|
|
792
|
+
})
|
|
793
|
+
// pos.row is valid, since it comes directly from
|
|
794
|
+
// self.grid().pos() which we assume to always have a
|
|
795
|
+
// valid row value. pos.row - 1 is valid because we
|
|
796
|
+
// just checked for pos.row > 0. col of size.cols - 1
|
|
797
|
+
// is always valid.
|
|
798
|
+
.unwrap();
|
|
799
|
+
if prev_cell.is_wide_continuation() {
|
|
800
|
+
prev_cell = self
|
|
801
|
+
.grid_mut()
|
|
802
|
+
.drawing_cell_mut(crate::grid::Pos {
|
|
803
|
+
row: pos.row - 1,
|
|
804
|
+
col: size.cols - 2,
|
|
805
|
+
})
|
|
806
|
+
// pos.row is valid, since it comes directly from
|
|
807
|
+
// self.grid().pos() which we assume to always
|
|
808
|
+
// have a valid row value. pos.row - 1 is valid
|
|
809
|
+
// because we just checked for pos.row > 0. col of
|
|
810
|
+
// size.cols - 2 is valid because the cell at
|
|
811
|
+
// size.cols - 1 is a wide continuation character,
|
|
812
|
+
// so it must have the first half of the wide
|
|
813
|
+
// character before it.
|
|
814
|
+
.unwrap();
|
|
815
|
+
}
|
|
816
|
+
prev_cell.append(c);
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
} else {
|
|
820
|
+
if self
|
|
821
|
+
.grid()
|
|
822
|
+
.drawing_cell(pos)
|
|
823
|
+
// pos.row is valid because we assume self.grid().pos() to
|
|
824
|
+
// always have a valid row value. pos.col is valid because we
|
|
825
|
+
// called col_wrap() immediately before this, which ensures
|
|
826
|
+
// that self.grid().pos().col has a valid value.
|
|
827
|
+
.unwrap()
|
|
828
|
+
.is_wide_continuation()
|
|
829
|
+
{
|
|
830
|
+
let prev_cell = self
|
|
831
|
+
.grid_mut()
|
|
832
|
+
.drawing_cell_mut(crate::grid::Pos {
|
|
833
|
+
row: pos.row,
|
|
834
|
+
col: pos.col - 1,
|
|
835
|
+
})
|
|
836
|
+
// pos.row is valid because we assume self.grid().pos() to
|
|
837
|
+
// always have a valid row value. pos.col is valid because
|
|
838
|
+
// we called col_wrap() immediately before this, which
|
|
839
|
+
// ensures that self.grid().pos().col has a valid value.
|
|
840
|
+
// pos.col - 1 is valid because the cell at pos.col is a
|
|
841
|
+
// wide continuation character, so it must have the first
|
|
842
|
+
// half of the wide character before it.
|
|
843
|
+
.unwrap();
|
|
844
|
+
prev_cell.clear(attrs);
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
if self
|
|
848
|
+
.grid()
|
|
849
|
+
.drawing_cell(pos)
|
|
850
|
+
// pos.row is valid because we assume self.grid().pos() to
|
|
851
|
+
// always have a valid row value. pos.col is valid because we
|
|
852
|
+
// called col_wrap() immediately before this, which ensures
|
|
853
|
+
// that self.grid().pos().col has a valid value.
|
|
854
|
+
.unwrap()
|
|
855
|
+
.is_wide()
|
|
856
|
+
{
|
|
857
|
+
let next_cell = self
|
|
858
|
+
.grid_mut()
|
|
859
|
+
.drawing_cell_mut(crate::grid::Pos {
|
|
860
|
+
row: pos.row,
|
|
861
|
+
col: pos.col + 1,
|
|
862
|
+
})
|
|
863
|
+
// pos.row is valid because we assume self.grid().pos() to
|
|
864
|
+
// always have a valid row value. pos.col is valid because
|
|
865
|
+
// we called col_wrap() immediately before this, which
|
|
866
|
+
// ensures that self.grid().pos().col has a valid value.
|
|
867
|
+
// pos.col + 1 is valid because the cell at pos.col is a
|
|
868
|
+
// wide character, so it must have the second half of the
|
|
869
|
+
// wide character after it.
|
|
870
|
+
.unwrap();
|
|
871
|
+
next_cell.set(' ', attrs);
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
let cell = self
|
|
875
|
+
.grid_mut()
|
|
876
|
+
.drawing_cell_mut(pos)
|
|
877
|
+
// pos.row is valid because we assume self.grid().pos() to
|
|
878
|
+
// always have a valid row value. pos.col is valid because we
|
|
879
|
+
// called col_wrap() immediately before this, which ensures
|
|
880
|
+
// that self.grid().pos().col has a valid value.
|
|
881
|
+
.unwrap();
|
|
882
|
+
cell.set(c, attrs);
|
|
883
|
+
self.grid_mut().col_inc(1);
|
|
884
|
+
if width > 1 {
|
|
885
|
+
let pos = self.grid().pos();
|
|
886
|
+
if self
|
|
887
|
+
.grid()
|
|
888
|
+
.drawing_cell(pos)
|
|
889
|
+
// pos.row is valid because we assume self.grid().pos() to
|
|
890
|
+
// always have a valid row value. pos.col is valid because
|
|
891
|
+
// we called col_wrap() earlier, which ensures that
|
|
892
|
+
// self.grid().pos().col has a valid value. this is true
|
|
893
|
+
// even though we just called col_inc, because this branch
|
|
894
|
+
// only happens if width > 1, and col_wrap takes width
|
|
895
|
+
// into account.
|
|
896
|
+
.unwrap()
|
|
897
|
+
.is_wide()
|
|
898
|
+
{
|
|
899
|
+
let next_next_pos = crate::grid::Pos {
|
|
900
|
+
row: pos.row,
|
|
901
|
+
col: pos.col + 1,
|
|
902
|
+
};
|
|
903
|
+
let next_next_cell = self
|
|
904
|
+
.grid_mut()
|
|
905
|
+
.drawing_cell_mut(next_next_pos)
|
|
906
|
+
// pos.row is valid because we assume
|
|
907
|
+
// self.grid().pos() to always have a valid row value.
|
|
908
|
+
// pos.col is valid because we called col_wrap()
|
|
909
|
+
// earlier, which ensures that self.grid().pos().col
|
|
910
|
+
// has a valid value. this is true even though we just
|
|
911
|
+
// called col_inc, because this branch only happens if
|
|
912
|
+
// width > 1, and col_wrap takes width into account.
|
|
913
|
+
// pos.col + 1 is valid because the cell at pos.col is
|
|
914
|
+
// wide, and so it must have the second half of the
|
|
915
|
+
// wide character after it.
|
|
916
|
+
.unwrap();
|
|
917
|
+
next_next_cell.clear(attrs);
|
|
918
|
+
if next_next_pos.col == size.cols - 1 {
|
|
919
|
+
self.grid_mut()
|
|
920
|
+
.drawing_row_mut(pos.row)
|
|
921
|
+
// we assume self.grid().pos().row is always valid
|
|
922
|
+
.unwrap()
|
|
923
|
+
.wrap(false);
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
let next_cell = self
|
|
927
|
+
.grid_mut()
|
|
928
|
+
.drawing_cell_mut(pos)
|
|
929
|
+
// pos.row is valid because we assume self.grid().pos() to
|
|
930
|
+
// always have a valid row value. pos.col is valid because
|
|
931
|
+
// we called col_wrap() earlier, which ensures that
|
|
932
|
+
// self.grid().pos().col has a valid value. this is true
|
|
933
|
+
// even though we just called col_inc, because this branch
|
|
934
|
+
// only happens if width > 1, and col_wrap takes width
|
|
935
|
+
// into account.
|
|
936
|
+
.unwrap();
|
|
937
|
+
next_cell.clear(crate::attrs::Attrs::default());
|
|
938
|
+
next_cell.set_wide_continuation(true);
|
|
939
|
+
self.grid_mut().col_inc(1);
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
// control codes
|
|
945
|
+
|
|
946
|
+
pub(crate) fn bs(&mut self) {
|
|
947
|
+
self.grid_mut().col_dec(1);
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
pub(crate) fn tab(&mut self) {
|
|
951
|
+
self.grid_mut().col_tab();
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
pub(crate) fn lf(&mut self) {
|
|
955
|
+
self.grid_mut().row_inc_scroll(1);
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
pub(crate) fn vt(&mut self) {
|
|
959
|
+
self.lf();
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
pub(crate) fn ff(&mut self) {
|
|
963
|
+
self.lf();
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
pub(crate) fn cr(&mut self) {
|
|
967
|
+
self.grid_mut().col_set(0);
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
// escape codes
|
|
971
|
+
|
|
972
|
+
// ESC 7
|
|
973
|
+
pub(crate) fn decsc(&mut self) {
|
|
974
|
+
self.save_cursor();
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
// ESC 8
|
|
978
|
+
pub(crate) fn decrc(&mut self) {
|
|
979
|
+
self.restore_cursor();
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
// ESC =
|
|
983
|
+
pub(crate) fn deckpam(&mut self) {
|
|
984
|
+
self.set_mode(MODE_APPLICATION_KEYPAD);
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
// ESC >
|
|
988
|
+
pub(crate) fn deckpnm(&mut self) {
|
|
989
|
+
self.clear_mode(MODE_APPLICATION_KEYPAD);
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
// ESC M
|
|
993
|
+
pub(crate) fn ri(&mut self) {
|
|
994
|
+
self.grid_mut().row_dec_scroll(1);
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
// ESC c
|
|
998
|
+
pub(crate) fn ris(&mut self) {
|
|
999
|
+
*self = Self::new(self.grid.size(), self.grid.scrollback_len());
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
// csi codes
|
|
1003
|
+
|
|
1004
|
+
// CSI @
|
|
1005
|
+
pub(crate) fn ich(&mut self, count: u16) {
|
|
1006
|
+
self.grid_mut().insert_cells(count);
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
// CSI A
|
|
1010
|
+
pub(crate) fn cuu(&mut self, offset: u16) {
|
|
1011
|
+
self.grid_mut().row_dec_clamp(offset);
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
// CSI B
|
|
1015
|
+
pub(crate) fn cud(&mut self, offset: u16) {
|
|
1016
|
+
self.grid_mut().row_inc_clamp(offset);
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
// CSI C
|
|
1020
|
+
pub(crate) fn cuf(&mut self, offset: u16) {
|
|
1021
|
+
self.grid_mut().col_inc_clamp(offset);
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
// CSI D
|
|
1025
|
+
pub(crate) fn cub(&mut self, offset: u16) {
|
|
1026
|
+
self.grid_mut().col_dec(offset);
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
// CSI E
|
|
1030
|
+
pub(crate) fn cnl(&mut self, offset: u16) {
|
|
1031
|
+
self.grid_mut().col_set(0);
|
|
1032
|
+
self.grid_mut().row_inc_clamp(offset);
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
// CSI F
|
|
1036
|
+
pub(crate) fn cpl(&mut self, offset: u16) {
|
|
1037
|
+
self.grid_mut().col_set(0);
|
|
1038
|
+
self.grid_mut().row_dec_clamp(offset);
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
// CSI G
|
|
1042
|
+
pub(crate) fn cha(&mut self, col: u16) {
|
|
1043
|
+
self.grid_mut().col_set(col - 1);
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
// CSI H
|
|
1047
|
+
pub(crate) fn cup(&mut self, (row, col): (u16, u16)) {
|
|
1048
|
+
self.grid_mut().set_pos(crate::grid::Pos {
|
|
1049
|
+
row: row - 1,
|
|
1050
|
+
col: col - 1,
|
|
1051
|
+
});
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
// CSI J
|
|
1055
|
+
pub(crate) fn ed(
|
|
1056
|
+
&mut self,
|
|
1057
|
+
mode: u16,
|
|
1058
|
+
mut unhandled: impl FnMut(&mut Self),
|
|
1059
|
+
) {
|
|
1060
|
+
let attrs = self.attrs;
|
|
1061
|
+
match mode {
|
|
1062
|
+
0 => self.grid_mut().erase_all_forward(attrs),
|
|
1063
|
+
1 => self.grid_mut().erase_all_backward(attrs),
|
|
1064
|
+
2 => self.grid_mut().erase_all(attrs),
|
|
1065
|
+
_ => unhandled(self),
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
// CSI ? J
|
|
1070
|
+
pub(crate) fn decsed(
|
|
1071
|
+
&mut self,
|
|
1072
|
+
mode: u16,
|
|
1073
|
+
unhandled: impl FnMut(&mut Self),
|
|
1074
|
+
) {
|
|
1075
|
+
self.ed(mode, unhandled);
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
// CSI K
|
|
1079
|
+
pub(crate) fn el(
|
|
1080
|
+
&mut self,
|
|
1081
|
+
mode: u16,
|
|
1082
|
+
mut unhandled: impl FnMut(&mut Self),
|
|
1083
|
+
) {
|
|
1084
|
+
let attrs = self.attrs;
|
|
1085
|
+
match mode {
|
|
1086
|
+
0 => self.grid_mut().erase_row_forward(attrs),
|
|
1087
|
+
1 => self.grid_mut().erase_row_backward(attrs),
|
|
1088
|
+
2 => self.grid_mut().erase_row(attrs),
|
|
1089
|
+
_ => unhandled(self),
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
// CSI ? K
|
|
1094
|
+
pub(crate) fn decsel(
|
|
1095
|
+
&mut self,
|
|
1096
|
+
mode: u16,
|
|
1097
|
+
unhandled: impl FnMut(&mut Self),
|
|
1098
|
+
) {
|
|
1099
|
+
self.el(mode, unhandled);
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
// CSI L
|
|
1103
|
+
pub(crate) fn il(&mut self, count: u16) {
|
|
1104
|
+
self.grid_mut().insert_lines(count);
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
// CSI M
|
|
1108
|
+
pub(crate) fn dl(&mut self, count: u16) {
|
|
1109
|
+
self.grid_mut().delete_lines(count);
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
// CSI P
|
|
1113
|
+
pub(crate) fn dch(&mut self, count: u16) {
|
|
1114
|
+
self.grid_mut().delete_cells(count);
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
// CSI S
|
|
1118
|
+
pub(crate) fn su(&mut self, count: u16) {
|
|
1119
|
+
self.grid_mut().scroll_up(count);
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
// CSI T
|
|
1123
|
+
pub(crate) fn sd(&mut self, count: u16) {
|
|
1124
|
+
self.grid_mut().scroll_down(count);
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
// CSI X
|
|
1128
|
+
pub(crate) fn ech(&mut self, count: u16) {
|
|
1129
|
+
let attrs = self.attrs;
|
|
1130
|
+
self.grid_mut().erase_cells(count, attrs);
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
// CSI d
|
|
1134
|
+
pub(crate) fn vpa(&mut self, row: u16) {
|
|
1135
|
+
self.grid_mut().row_set(row - 1);
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
// CSI ? h
|
|
1139
|
+
pub(crate) fn decset(
|
|
1140
|
+
&mut self,
|
|
1141
|
+
params: &vte::Params,
|
|
1142
|
+
mut unhandled: impl FnMut(&mut Self),
|
|
1143
|
+
) {
|
|
1144
|
+
for param in params {
|
|
1145
|
+
match param {
|
|
1146
|
+
[1] => self.set_mode(MODE_APPLICATION_CURSOR),
|
|
1147
|
+
[6] => self.grid_mut().set_origin_mode(true),
|
|
1148
|
+
[9] => self.set_mouse_mode(MouseProtocolMode::Press),
|
|
1149
|
+
[25] => self.clear_mode(MODE_HIDE_CURSOR),
|
|
1150
|
+
[47] => self.enter_alternate_grid(),
|
|
1151
|
+
[1000] => {
|
|
1152
|
+
self.set_mouse_mode(MouseProtocolMode::PressRelease);
|
|
1153
|
+
}
|
|
1154
|
+
[1002] => {
|
|
1155
|
+
self.set_mouse_mode(MouseProtocolMode::ButtonMotion);
|
|
1156
|
+
}
|
|
1157
|
+
[1003] => self.set_mouse_mode(MouseProtocolMode::AnyMotion),
|
|
1158
|
+
[1005] => {
|
|
1159
|
+
self.set_mouse_encoding(MouseProtocolEncoding::Utf8);
|
|
1160
|
+
}
|
|
1161
|
+
[1006] => {
|
|
1162
|
+
self.set_mouse_encoding(MouseProtocolEncoding::Sgr);
|
|
1163
|
+
}
|
|
1164
|
+
[1049] => {
|
|
1165
|
+
self.decsc();
|
|
1166
|
+
self.alternate_grid.clear();
|
|
1167
|
+
self.enter_alternate_grid();
|
|
1168
|
+
}
|
|
1169
|
+
[2004] => self.set_mode(MODE_BRACKETED_PASTE),
|
|
1170
|
+
_ => unhandled(self),
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
// CSI ? l
|
|
1176
|
+
pub(crate) fn decrst(
|
|
1177
|
+
&mut self,
|
|
1178
|
+
params: &vte::Params,
|
|
1179
|
+
mut unhandled: impl FnMut(&mut Self),
|
|
1180
|
+
) {
|
|
1181
|
+
for param in params {
|
|
1182
|
+
match param {
|
|
1183
|
+
[1] => self.clear_mode(MODE_APPLICATION_CURSOR),
|
|
1184
|
+
[6] => self.grid_mut().set_origin_mode(false),
|
|
1185
|
+
[9] => self.clear_mouse_mode(MouseProtocolMode::Press),
|
|
1186
|
+
[25] => self.set_mode(MODE_HIDE_CURSOR),
|
|
1187
|
+
[47] => {
|
|
1188
|
+
self.exit_alternate_grid();
|
|
1189
|
+
}
|
|
1190
|
+
[1000] => {
|
|
1191
|
+
self.clear_mouse_mode(MouseProtocolMode::PressRelease);
|
|
1192
|
+
}
|
|
1193
|
+
[1002] => {
|
|
1194
|
+
self.clear_mouse_mode(MouseProtocolMode::ButtonMotion);
|
|
1195
|
+
}
|
|
1196
|
+
[1003] => {
|
|
1197
|
+
self.clear_mouse_mode(MouseProtocolMode::AnyMotion);
|
|
1198
|
+
}
|
|
1199
|
+
[1005] => {
|
|
1200
|
+
self.clear_mouse_encoding(MouseProtocolEncoding::Utf8);
|
|
1201
|
+
}
|
|
1202
|
+
[1006] => {
|
|
1203
|
+
self.clear_mouse_encoding(MouseProtocolEncoding::Sgr);
|
|
1204
|
+
}
|
|
1205
|
+
[1049] => {
|
|
1206
|
+
self.exit_alternate_grid();
|
|
1207
|
+
self.decrc();
|
|
1208
|
+
}
|
|
1209
|
+
[2004] => self.clear_mode(MODE_BRACKETED_PASTE),
|
|
1210
|
+
_ => unhandled(self),
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
// CSI m
|
|
1216
|
+
pub(crate) fn sgr(
|
|
1217
|
+
&mut self,
|
|
1218
|
+
params: &vte::Params,
|
|
1219
|
+
mut unhandled: impl FnMut(&mut Self),
|
|
1220
|
+
) {
|
|
1221
|
+
// XXX really i want to just be able to pass in a default Params
|
|
1222
|
+
// instance with a 0 in it, but vte doesn't allow creating new Params
|
|
1223
|
+
// instances
|
|
1224
|
+
if params.is_empty() {
|
|
1225
|
+
self.attrs = crate::attrs::Attrs::default();
|
|
1226
|
+
return;
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
let mut iter = params.iter();
|
|
1230
|
+
|
|
1231
|
+
macro_rules! next_param {
|
|
1232
|
+
() => {
|
|
1233
|
+
match iter.next() {
|
|
1234
|
+
Some(n) => n,
|
|
1235
|
+
_ => return,
|
|
1236
|
+
}
|
|
1237
|
+
};
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
macro_rules! to_u8 {
|
|
1241
|
+
($n:expr) => {
|
|
1242
|
+
if let Some(n) = u16_to_u8($n) {
|
|
1243
|
+
n
|
|
1244
|
+
} else {
|
|
1245
|
+
return;
|
|
1246
|
+
}
|
|
1247
|
+
};
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
macro_rules! next_param_u8 {
|
|
1251
|
+
() => {
|
|
1252
|
+
if let &[n] = next_param!() {
|
|
1253
|
+
to_u8!(n)
|
|
1254
|
+
} else {
|
|
1255
|
+
return;
|
|
1256
|
+
}
|
|
1257
|
+
};
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
loop {
|
|
1261
|
+
match next_param!() {
|
|
1262
|
+
[0] => self.attrs = crate::attrs::Attrs::default(),
|
|
1263
|
+
[1] => self.attrs.set_bold(),
|
|
1264
|
+
[2] => self.attrs.set_dim(),
|
|
1265
|
+
[3] => self.attrs.set_italic(true),
|
|
1266
|
+
[4] => self.attrs.set_underline(true),
|
|
1267
|
+
[7] => self.attrs.set_inverse(true),
|
|
1268
|
+
[22] => self.attrs.set_normal_intensity(),
|
|
1269
|
+
[23] => self.attrs.set_italic(false),
|
|
1270
|
+
[24] => self.attrs.set_underline(false),
|
|
1271
|
+
[27] => self.attrs.set_inverse(false),
|
|
1272
|
+
[n] if (30..=37).contains(n) => {
|
|
1273
|
+
self.attrs.fgcolor = crate::Color::Idx(to_u8!(*n) - 30);
|
|
1274
|
+
}
|
|
1275
|
+
[38, 2, r, g, b] => {
|
|
1276
|
+
self.attrs.fgcolor =
|
|
1277
|
+
crate::Color::Rgb(to_u8!(*r), to_u8!(*g), to_u8!(*b));
|
|
1278
|
+
}
|
|
1279
|
+
[38, 5, i] => {
|
|
1280
|
+
self.attrs.fgcolor = crate::Color::Idx(to_u8!(*i));
|
|
1281
|
+
}
|
|
1282
|
+
[38] => match next_param!() {
|
|
1283
|
+
[2] => {
|
|
1284
|
+
let r = next_param_u8!();
|
|
1285
|
+
let g = next_param_u8!();
|
|
1286
|
+
let b = next_param_u8!();
|
|
1287
|
+
self.attrs.fgcolor = crate::Color::Rgb(r, g, b);
|
|
1288
|
+
}
|
|
1289
|
+
[5] => {
|
|
1290
|
+
self.attrs.fgcolor =
|
|
1291
|
+
crate::Color::Idx(next_param_u8!());
|
|
1292
|
+
}
|
|
1293
|
+
_ => {
|
|
1294
|
+
unhandled(self);
|
|
1295
|
+
return;
|
|
1296
|
+
}
|
|
1297
|
+
},
|
|
1298
|
+
[39] => {
|
|
1299
|
+
self.attrs.fgcolor = crate::Color::Default;
|
|
1300
|
+
}
|
|
1301
|
+
[n] if (40..=47).contains(n) => {
|
|
1302
|
+
self.attrs.bgcolor = crate::Color::Idx(to_u8!(*n) - 40);
|
|
1303
|
+
}
|
|
1304
|
+
[48, 2, r, g, b] => {
|
|
1305
|
+
self.attrs.bgcolor =
|
|
1306
|
+
crate::Color::Rgb(to_u8!(*r), to_u8!(*g), to_u8!(*b));
|
|
1307
|
+
}
|
|
1308
|
+
[48, 5, i] => {
|
|
1309
|
+
self.attrs.bgcolor = crate::Color::Idx(to_u8!(*i));
|
|
1310
|
+
}
|
|
1311
|
+
[48] => match next_param!() {
|
|
1312
|
+
[2] => {
|
|
1313
|
+
let r = next_param_u8!();
|
|
1314
|
+
let g = next_param_u8!();
|
|
1315
|
+
let b = next_param_u8!();
|
|
1316
|
+
self.attrs.bgcolor = crate::Color::Rgb(r, g, b);
|
|
1317
|
+
}
|
|
1318
|
+
[5] => {
|
|
1319
|
+
self.attrs.bgcolor =
|
|
1320
|
+
crate::Color::Idx(next_param_u8!());
|
|
1321
|
+
}
|
|
1322
|
+
_ => {
|
|
1323
|
+
unhandled(self);
|
|
1324
|
+
return;
|
|
1325
|
+
}
|
|
1326
|
+
},
|
|
1327
|
+
[49] => {
|
|
1328
|
+
self.attrs.bgcolor = crate::Color::Default;
|
|
1329
|
+
}
|
|
1330
|
+
[n] if (90..=97).contains(n) => {
|
|
1331
|
+
self.attrs.fgcolor = crate::Color::Idx(to_u8!(*n) - 82);
|
|
1332
|
+
}
|
|
1333
|
+
[n] if (100..=107).contains(n) => {
|
|
1334
|
+
self.attrs.bgcolor = crate::Color::Idx(to_u8!(*n) - 92);
|
|
1335
|
+
}
|
|
1336
|
+
_ => unhandled(self),
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
// CSI r
|
|
1342
|
+
pub(crate) fn decstbm(&mut self, (top, bottom): (u16, u16)) {
|
|
1343
|
+
self.grid_mut().set_scroll_region(top - 1, bottom - 1);
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
fn u16_to_u8(i: u16) -> Option<u8> {
|
|
1348
|
+
if i > u16::from(u8::MAX) {
|
|
1349
|
+
None
|
|
1350
|
+
} else {
|
|
1351
|
+
// safe because we just ensured that the value fits in a u8
|
|
1352
|
+
Some(i.try_into().unwrap())
|
|
1353
|
+
}
|
|
1354
|
+
}
|