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
|
use zellij_utils::errors::{prelude::*, ContextType, PtyWriteContext};
use crate::thread_bus::Bus;
// we separate these instruction to a different thread because some programs get deadlocked if
// you write into their STDIN while reading from their STDOUT (I'm looking at you, vim)
// while the same has not been observed to happen with resizes, it could conceivably happen and we have this
// here anyway, so
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum PtyWriteInstruction {
Write(Vec<u8>, u32),
ResizePty(u32, u16, u16, Option<u16>, Option<u16>), // terminal_id, columns, rows, pixel width, pixel height
StartCachingResizes,
ApplyCachedResizes,
Exit,
}
impl From<&PtyWriteInstruction> for PtyWriteContext {
fn from(tty_write_instruction: &PtyWriteInstruction) -> Self {
match *tty_write_instruction {
PtyWriteInstruction::Write(..) => PtyWriteContext::Write,
PtyWriteInstruction::ResizePty(..) => PtyWriteContext::ResizePty,
PtyWriteInstruction::ApplyCachedResizes => PtyWriteContext::ApplyCachedResizes,
PtyWriteInstruction::StartCachingResizes => PtyWriteContext::StartCachingResizes,
PtyWriteInstruction::Exit => PtyWriteContext::Exit,
}
}
}
pub(crate) fn pty_writer_main(bus: Bus<PtyWriteInstruction>) -> Result<()> {
let err_context = || "failed to write to pty".to_string();
loop {
let (event, mut err_ctx) = bus.recv().with_context(err_context)?;
err_ctx.add_call(ContextType::PtyWrite((&event).into()));
let mut os_input = bus
.os_input
.clone()
.context("no OS input API found")
.with_context(err_context)?;
match event {
PtyWriteInstruction::Write(bytes, terminal_id) => {
os_input
.write_to_tty_stdin(terminal_id, &bytes)
.with_context(err_context)
.non_fatal();
os_input
.tcdrain(terminal_id)
.with_context(err_context)
.non_fatal();
},
PtyWriteInstruction::ResizePty(
terminal_id,
columns,
rows,
width_in_pixels,
height_in_pixels,
) => {
os_input
.set_terminal_size_using_terminal_id(
terminal_id,
columns,
rows,
width_in_pixels,
height_in_pixels,
)
.with_context(err_context)
.non_fatal();
},
PtyWriteInstruction::StartCachingResizes => {
// we do this because there are some logic traps inside the screen/tab/layout code
// the cause multiple resizes to be sent to the pty - while the last one is always
// the correct one, many programs and shells debounce those (I guess due to the
// trauma of dealing with GUI resizes of the controlling terminal window), and this
// then causes glitches and missing redraws
// so we do this to play nice and always only send the last resize instruction to
// each pane
// the logic for this happens in the main Screen event loop
os_input.cache_resizes();
},
PtyWriteInstruction::ApplyCachedResizes => {
os_input.apply_cached_resizes();
},
PtyWriteInstruction::Exit => {
return Ok(());
},
}
}
}
|