|
1 | 1 | use crate::get_dimensions; |
2 | | -use anyhow::{Context, Result}; |
| 2 | +use anyhow::{Context as _, Result}; |
3 | 3 | use color_quant::NeuQuant; |
4 | 4 | use image::{ |
5 | 5 | imageops::{colorops, FilterType}, |
6 | 6 | DynamicImage, GenericImageView, ImageBuffer, Pixel, Rgb, |
7 | 7 | }; |
8 | | -use libc::{ |
9 | | - c_void, poll, pollfd, read, tcgetattr, tcsetattr, termios, ECHO, ICANON, POLLIN, STDIN_FILENO, |
10 | | - TCSANOW, |
11 | | -}; |
| 8 | + |
| 9 | +use nix::poll::{poll, PollFd, PollFlags, PollTimeout}; |
| 10 | +use nix::sys::termios::{tcgetattr, tcsetattr, LocalFlags, SetArg}; |
| 11 | +use nix::unistd::read; |
| 12 | + |
12 | 13 | use std::io::{stdout, Write}; |
| 14 | +use std::os::fd::AsFd; |
13 | 15 | use std::time::Instant; |
14 | 16 |
|
15 | 17 | pub struct SixelBackend; |
16 | 18 |
|
17 | 19 | impl SixelBackend { |
18 | | - pub fn supported() -> bool { |
| 20 | + pub fn supported() -> Result<bool> { |
| 21 | + let stdin = std::io::stdin(); |
19 | 22 | // save terminal attributes and disable canonical input processing mode |
20 | | - let old_attributes = unsafe { |
21 | | - let mut old_attributes: termios = std::mem::zeroed(); |
22 | | - tcgetattr(STDIN_FILENO, &mut old_attributes); |
| 23 | + let old_attributes = { |
| 24 | + let old = tcgetattr(&stdin).context("Failed to recieve terminal attibutes")?; |
23 | 25 |
|
24 | | - let mut new_attributes = old_attributes; |
25 | | - new_attributes.c_lflag &= !ICANON; |
26 | | - new_attributes.c_lflag &= !ECHO; |
27 | | - tcsetattr(STDIN_FILENO, TCSANOW, &new_attributes); |
28 | | - old_attributes |
| 26 | + let mut new = old.clone(); |
| 27 | + new.local_flags &= !LocalFlags::ICANON; |
| 28 | + new.local_flags &= !LocalFlags::ECHO; |
| 29 | + tcsetattr(&stdin, SetArg::TCSANOW, &new) |
| 30 | + .context("Failed to update terminal attributes")?; |
| 31 | + old |
29 | 32 | }; |
30 | 33 |
|
31 | 34 | // ask for the primary device attribute string |
32 | 35 | print!("\x1B[c"); |
33 | | - stdout().flush().unwrap(); |
| 36 | + stdout().flush()?; |
34 | 37 |
|
35 | 38 | let start_time = Instant::now(); |
36 | | - let mut stdin_pollfd = pollfd { |
37 | | - fd: STDIN_FILENO, |
38 | | - events: POLLIN, |
39 | | - revents: 0, |
40 | | - }; |
| 39 | + let mut stdin_pollfd = [PollFd::new(stdin.as_fd(), PollFlags::POLLIN)]; |
41 | 40 | let mut buf = Vec::<u8>::new(); |
42 | 41 | loop { |
43 | 42 | // check for timeout while polling to avoid blocking the main thread |
44 | | - while unsafe { poll(&mut stdin_pollfd, 1, 0) < 1 } { |
| 43 | + while poll(&mut stdin_pollfd, PollTimeout::ZERO)? < 1 { |
45 | 44 | if start_time.elapsed().as_millis() > 50 { |
46 | | - unsafe { |
47 | | - tcsetattr(STDIN_FILENO, TCSANOW, &old_attributes); |
48 | | - } |
49 | | - return false; |
| 45 | + tcsetattr(stdin, SetArg::TCSANOW, &old_attributes) |
| 46 | + .context("Failed to update terminal attributes")?; |
| 47 | + return Ok(false); |
50 | 48 | } |
51 | 49 | } |
52 | | - let mut byte = 0; |
53 | | - unsafe { |
54 | | - read(STDIN_FILENO, &mut byte as *mut _ as *mut c_void, 1); |
55 | | - } |
56 | | - buf.push(byte); |
| 50 | + let mut byte = [0]; |
| 51 | + read(&stdin, &mut byte)?; |
| 52 | + buf.push(byte[0]); |
57 | 53 | if buf.starts_with(&[0x1B, b'[', b'?']) && buf.ends_with(b"c") { |
58 | 54 | for attribute in buf[3..(buf.len() - 1)].split(|x| *x == b';') { |
59 | 55 | if attribute == [b'4'] { |
60 | | - unsafe { |
61 | | - tcsetattr(STDIN_FILENO, TCSANOW, &old_attributes); |
62 | | - } |
63 | | - return true; |
| 56 | + tcsetattr(stdin, SetArg::TCSANOW, &old_attributes) |
| 57 | + .context("Failed to update terminal attributes")?; |
| 58 | + return Ok(true); |
64 | 59 | } |
65 | 60 | } |
66 | 61 | } |
|
0 commit comments