From ae82460a8f53385c02f2f2474997b5634e1928b5 Mon Sep 17 00:00:00 2001 From: bendn Date: Wed, 13 Mar 2024 11:18:39 +0700 Subject: [PATCH] detection of color count --- Cargo.toml | 3 +- src/lib.rs | 1 + src/term.rs | 116 ++++++++++++++++++++++++++-------------------- src/term/b64.rs | 4 +- src/term/sixel.rs | 35 ++++++++++++-- 5 files changed, 104 insertions(+), 55 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f973a48..773e435 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ minifb = { version = "0.25.0", default-features = false, features = [ wgpu = { version = "0.19.1", default-features = false, optional = true } atools = "0.1.0" qwant = { version = "1.0.0", optional = true } +libc = "0.2.153" [target.'cfg(windows)'.dependencies] windows = { version = "0.53.0", features = [ @@ -67,7 +68,7 @@ text = ["fontdue"] blur = ["slur"] term = ["qwant", "save", "scale", "windows"] real-show = ["minifb", "text"] -default = ["save", "scale"] +default = ["save", "scale", "term"] wgpu-convert = ["dep:wgpu"] [profile.release] diff --git a/src/lib.rs b/src/lib.rs index 3953c08..eb2baef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -68,6 +68,7 @@ const_option, array_chunks, let_chains, + try_blocks, test )] #![warn( diff --git a/src/term.rs b/src/term.rs index 5f2df4c..c211340 100644 --- a/src/term.rs +++ b/src/term.rs @@ -114,55 +114,8 @@ where #[cfg(unix)] // https://github.com/benjajaja/ratatui-image/blob/master/src/picker.rs#L226 fn guess_harder(&self, to: &mut impl Write) -> Option { - extern crate libc; - use std::{io::Read, mem::MaybeUninit}; - - fn r(result: i32) -> Option<()> { - (result != -1).then_some(()) - } - - let mut termios = MaybeUninit::::uninit(); - // SAFETY: get termios of stdin - r(unsafe { libc::tcgetattr(0, termios.as_mut_ptr()) })?; - // SAFETY: gotten - let termios = unsafe { termios.assume_init() }; - - // SAFETY: turn off echo and canonical (requires enter before stdin reads) modes - unsafe { - libc::tcsetattr( - 0, - libc::TCSADRAIN, - &libc::termios { - c_lflag: termios.c_lflag & !libc::ICANON & !libc::ECHO, - ..termios - }, - ) - }; - let buf = { - // contains a kitty gfx and sixel query, the `\x1b[c` is for sixels - println!(r"_Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA\"); - let mut stdin = std::io::stdin(); - let mut buf = String::new(); - - let mut b = [0; 16]; - 'l: loop { - let n = stdin.read(&mut b).ok()?; - if n == 0 { - continue; - } - for b in b { - buf.push(b as char); - if b == b'c' { - break 'l; - } - } - } - buf - }; - - // SAFETY: reset attrs to what they were before we became nosy - unsafe { libc::tcsetattr(0, libc::TCSADRAIN, &termios) }; - + // contains a kitty gfx and sixel query, the `\x1b[c` is for sixels + let buf = query(r"_Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA\")?; if buf.contains("_Gi=31;OK") { Some(Kitty(self.as_ref()).write(to)) } else if buf.contains(";4;") @@ -176,3 +129,68 @@ where } } } + +#[cfg(unix)] +// https://github.com/benjajaja/ratatui-image/blob/master/src/picker.rs#L226 +fn query(device_query_code: &'static str) -> Option { + extern crate libc; + use std::mem::MaybeUninit; + fn r(result: i32) -> Option<()> { + (result != -1).then_some(()) + } + + let mut termios = MaybeUninit::::uninit(); + // SAFETY: get termios of stdin + r(unsafe { libc::tcgetattr(0, termios.as_mut_ptr()) })?; + // SAFETY: gotten + let termios = unsafe { termios.assume_init() }; + + // SAFETY: turn off echo and canonical (requires enter before stdin reads) modes + unsafe { + libc::tcsetattr( + 0, + libc::TCSADRAIN, + &libc::termios { + c_lflag: termios.c_lflag & !libc::ICANON & !libc::ECHO, + ..termios + }, + ) + }; + + let buf = try { + // SAFETY: linux time out'd reading + unsafe { + println!("{device_query_code}"); + let mut buf = Vec::new(); + let mut tmp = [0; 1 << 5]; + loop { + let mut x = std::mem::zeroed::(); + libc::FD_SET(0, &mut x); + match libc::select( + 1, + &mut x, + 0 as _, + 0 as _, + &mut libc::timeval { + tv_sec: 0, + tv_usec: 5e5 as _, + }, + ) { + 0 => break, + -1 => return None, + _ => {} + } + match libc::read(libc::STDIN_FILENO, tmp.as_mut_ptr().cast(), tmp.len()) { + 0 => continue, + -1 => return None, + n => buf.extend_from_slice(&tmp[..n as _]), + } + } + String::from_utf8(buf).ok()? + } + }; + + // SAFETY: reset attrs to what they were before we became nosy + unsafe { libc::tcsetattr(0, libc::TCSADRAIN, &termios) }; + buf +} diff --git a/src/term/b64.rs b/src/term/b64.rs index 0d46d65..1038d45 100644 --- a/src/term/b64.rs +++ b/src/term/b64.rs @@ -39,7 +39,7 @@ where LaneCount: SupportedLaneCount, { fn cas(self) -> U { - assert!(std::mem::size_of::() == std::mem::size_of::>()); + assert!(std::mem::size_of::() == std::mem::size_of::()); unsafe { transmute_unchecked(self) } } } @@ -49,7 +49,7 @@ where LaneCount: SupportedLaneCount, { fn cas(self) -> U { - assert!(std::mem::size_of::() == std::mem::size_of::>()); + assert!(std::mem::size_of::() == std::mem::size_of::()); unsafe { transmute_unchecked(self) } } } diff --git a/src/term/sixel.rs b/src/term/sixel.rs index 7091968..2304f51 100644 --- a/src/term/sixel.rs +++ b/src/term/sixel.rs @@ -40,6 +40,34 @@ impl, const N: usize> Sixel { where [(); N]: Basic, { + #[cfg(unix)] + let q = { + extern crate libc; + // SAFETY: is stdout a tty + (unsafe { libc::isatty(0) } == 1) + }; + #[cfg(not(unix))] + let q = true; + let colors = q + .then_some(super::query("[?1;1;0S").and_then(|x| { + // [?1;0;65536S + if let [b'?', b'1', b';', b'0', b';', n @ ..] = x.as_bytes() { + Some( + n.iter() + .copied() + .take_while(u8::is_ascii_digit) + .fold(0u16, |acc, x| { + acc.saturating_mul(10).saturating_add((x - b'0') as u16) + }) + .max(64) + .min(0xfff), + ) + } else { + None + } + })) + .flatten() + .unwrap_or(255); to.write_str("Pq")?; write!(to, r#""1;1;{};{}"#, self.width(), self.height())?; let buf; @@ -63,14 +91,15 @@ impl, const N: usize> Sixel { &*buf }; - let q = qwant::NeuQuant::new(15, 255, rgba); + let q = qwant::NeuQuant::new(15, colors as _, rgba); + // TODO: don't colllect - let pixels: Vec = rgba.iter().map(|&pix| q.index_of(pix) as u8).collect(); + let pixels: Vec = rgba.iter().map(|&pix| q.index_of(pix) as _).collect(); for ([r, g, b], i) in q .color_map_rgb() .map(|x| x.map(|x| (x as f32 * (100. / 255.)) as u32)) - .zip(0u8..) + .zip(0u64..) { write!(to, "#{i};2;{r};{g};{b}")?; }