1use std::os::fd::RawFd;
2
3pub struct TerminalState {
5 fd: RawFd,
6 termios: libc::termios,
7}
8
9impl TerminalState {
10 pub fn enter_raw_mode(fd: RawFd) -> Option<Self> {
13 unsafe {
14 let mut saved: libc::termios = std::mem::zeroed();
15 if libc::tcgetattr(fd, &mut saved) != 0 {
16 return None;
17 }
18 let mut raw = saved;
19 libc::cfmakeraw(&mut raw);
20 libc::tcsetattr(fd, libc::TCSANOW, &raw);
21 Some(TerminalState { fd, termios: saved })
22 }
23 }
24
25 pub fn restore(&self) {
26 unsafe {
27 libc::tcsetattr(self.fd, libc::TCSANOW, &self.termios);
28 }
29 }
30}
31
32impl Drop for TerminalState {
33 fn drop(&mut self) {
34 self.restore();
35 }
36}
37
38pub fn terminal_size(fd: RawFd) -> (u16, u16) {
41 unsafe {
42 let mut ws: libc::winsize = std::mem::zeroed();
43 if libc::ioctl(fd, libc::TIOCGWINSZ, &mut ws) == 0 {
44 (ws.ws_row, ws.ws_col)
45 } else {
46 (24, 80)
47 }
48 }
49}
50
51pub fn read_raw(fd: RawFd, buf: &mut [u8]) -> usize {
54 unsafe {
55 let n = libc::read(fd, buf.as_mut_ptr() as *mut libc::c_void, buf.len());
56 if n > 0 {
57 n as usize
58 } else {
59 0
60 }
61 }
62}
63
64pub enum StdinEvent {
68 Ready,
70 Resize,
72 Shutdown,
74}
75
76pub struct ShutdownSignal {
78 pipe_write: RawFd,
79}
80
81unsafe impl Send for ShutdownSignal {}
82
83impl ShutdownSignal {
84 pub fn signal(&self) {
85 unsafe {
86 libc::write(self.pipe_write, [1u8].as_ptr() as *const libc::c_void, 1);
87 }
88 }
89}
90
91impl Drop for ShutdownSignal {
92 fn drop(&mut self) {
93 unsafe {
94 libc::close(self.pipe_write);
95 }
96 }
97}
98
99const EV_STDIN: u64 = 0;
101const EV_PIPE: u64 = 1;
102const EV_SIGNAL: u64 = 2;
103
104pub struct StdinRelay {
107 epoll_fd: RawFd,
108 signal_fd: RawFd,
109 pipe_read: RawFd,
110}
111
112impl StdinRelay {
113 pub fn new(stdin_fd: RawFd) -> Option<(StdinRelay, ShutdownSignal)> {
115 unsafe {
116 let mut fds = [0i32; 2];
118 if libc::pipe(fds.as_mut_ptr()) != 0 {
119 return None;
120 }
121 let pipe_read = fds[0];
122 let pipe_write = fds[1];
123
124 let epoll_fd = libc::epoll_create1(0);
126 if epoll_fd < 0 {
127 libc::close(pipe_read);
128 libc::close(pipe_write);
129 return None;
130 }
131
132 let mut ev = libc::epoll_event {
134 events: libc::EPOLLIN as u32,
135 u64: EV_STDIN,
136 };
137 if libc::epoll_ctl(epoll_fd, libc::EPOLL_CTL_ADD, stdin_fd, &mut ev) < 0 {
138 libc::close(epoll_fd);
139 libc::close(pipe_read);
140 libc::close(pipe_write);
141 return None;
142 }
143
144 let mut ev = libc::epoll_event {
146 events: libc::EPOLLIN as u32,
147 u64: EV_PIPE,
148 };
149 libc::epoll_ctl(epoll_fd, libc::EPOLL_CTL_ADD, pipe_read, &mut ev);
150
151 let mut sigset: libc::sigset_t = std::mem::zeroed();
153 libc::sigemptyset(&mut sigset);
154 libc::sigaddset(&mut sigset, libc::SIGWINCH);
155 libc::sigprocmask(libc::SIG_BLOCK, &sigset, std::ptr::null_mut());
157
158 let signal_fd = libc::signalfd(-1, &sigset, libc::SFD_NONBLOCK);
159 if signal_fd < 0 {
160 libc::close(epoll_fd);
162 libc::close(pipe_read);
163 libc::close(pipe_write);
164 return None;
165 }
166
167 let mut ev = libc::epoll_event {
168 events: libc::EPOLLIN as u32,
169 u64: EV_SIGNAL,
170 };
171 libc::epoll_ctl(epoll_fd, libc::EPOLL_CTL_ADD, signal_fd, &mut ev);
172
173 Some((
174 StdinRelay {
175 epoll_fd,
176 signal_fd,
177 pipe_read,
178 },
179 ShutdownSignal { pipe_write },
180 ))
181 }
182 }
183
184 pub fn wait(&self) -> StdinEvent {
187 unsafe {
188 let mut events = [libc::epoll_event { events: 0, u64: 0 }; 4];
189
190 let n = libc::epoll_wait(self.epoll_fd, events.as_mut_ptr(), 4, -1);
191 if n < 1 {
192 return StdinEvent::Shutdown;
193 }
194
195 for event in events.iter().take(n as usize) {
196 match event.u64 {
197 EV_STDIN => return StdinEvent::Ready,
198 EV_PIPE => return StdinEvent::Shutdown,
199 EV_SIGNAL => {
200 let mut info: libc::signalfd_siginfo = std::mem::zeroed();
202 libc::read(
203 self.signal_fd,
204 &mut info as *mut _ as *mut libc::c_void,
205 std::mem::size_of::<libc::signalfd_siginfo>(),
206 );
207 return StdinEvent::Resize;
208 }
209 _ => {}
210 }
211 }
212
213 StdinEvent::Shutdown
214 }
215 }
216}
217
218impl Drop for StdinRelay {
219 fn drop(&mut self) {
220 unsafe {
221 libc::close(self.epoll_fd);
222 libc::close(self.signal_fd);
223 libc::close(self.pipe_read);
224 let mut sigset: libc::sigset_t = std::mem::zeroed();
226 libc::sigemptyset(&mut sigset);
227 libc::sigaddset(&mut sigset, libc::SIGWINCH);
228 libc::sigprocmask(libc::SIG_UNBLOCK, &sigset, std::ptr::null_mut());
229 }
230 }
231}