1
0
mirror of https://github.com/bobwen-dev/hunter synced 2025-04-12 00:55:41 +02:00

initial support for SIXEL/kitty graphics

This commit is contained in:
rabite 2019-07-08 12:53:44 +02:00
parent 9b6bb7fea0
commit 072b39b3cf
8 changed files with 443 additions and 165 deletions

123
Cargo.lock generated
View File

@ -5,6 +5,14 @@ name = "adler32"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "aho-corasick"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "aho-corasick"
version = "0.7.3"
@ -89,6 +97,14 @@ dependencies = [
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "base64"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bitflags"
version = "1.1.0"
@ -448,6 +464,7 @@ version = "1.3.4"
dependencies = [
"alphanumeric-sort 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"async_value 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
"chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
"dirs-2 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -468,8 +485,9 @@ dependencies = [
"rayon 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"signal-notify 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"sixel 0.3.0 (git+https://github.com/rabite0/sixel-rs?tag=v0.3.0)",
"systemstat 0.1.4 (git+https://github.com/myfreeweb/systemstat?tag=v0.1.4)",
"termion 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
"termion 1.5.3 (git+https://github.com/redox-os/termion)",
"tree_magic 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"users 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -586,6 +604,19 @@ name = "lzw"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "make-cmd"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "memchr"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "memchr"
version = "1.0.2"
@ -1086,6 +1117,18 @@ dependencies = [
"redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex"
version = "0.1.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
"utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex"
version = "1.1.7"
@ -1098,6 +1141,11 @@ dependencies = [
"utf8-ranges 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex-syntax"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "regex-syntax"
version = "0.6.7"
@ -1145,6 +1193,15 @@ dependencies = [
"semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "semver-parser"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "semver-parser"
version = "0.7.0"
@ -1164,6 +1221,24 @@ name = "siphasher"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "sixel"
version = "0.3.0"
source = "git+https://github.com/rabite0/sixel-rs?tag=v0.3.0#9018082b3b5d094e7a634292d7c3ae09f4fd2cd1"
dependencies = [
"lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"semver-parser 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
"sixel-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "sixel-sys"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"make-cmd 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "slab"
version = "0.4.2"
@ -1219,6 +1294,17 @@ dependencies = [
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "termion"
version = "1.5.3"
source = "git+https://github.com/redox-os/termion#11fbe7155681c3c87495a2fa8ee9f822b18e2b2a"
dependencies = [
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
"numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "termion"
version = "1.5.3"
@ -1238,6 +1324,23 @@ dependencies = [
"unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "thread-id"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "thread_local"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "thread_local"
version = "0.3.6"
@ -1318,6 +1421,11 @@ dependencies = [
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "utf8-ranges"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "utf8-ranges"
version = "1.0.3"
@ -1391,6 +1499,7 @@ dependencies = [
[metadata]
"checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c"
"checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66"
"checksum aho-corasick 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e6f484ae0c99fec2e858eb6134949117399f222608d84cadb3f58c1f97c2364c"
"checksum alphanumeric-sort 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7cd2580c95c654d681db0194a310af67a293f5e1c8bafa5b35b63269c4665a39"
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
@ -1401,6 +1510,7 @@ dependencies = [
"checksum autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0e49efa51329a5fd37e7c79db4621af617cd4e3e5bc224939808d076077077bf"
"checksum backtrace 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)" = "18b50f5258d1a9ad8396d2d345827875de4261b158124d4c819d9b351454fae5"
"checksum backtrace-sys 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)" = "5b3a000b9c543553af61bc01cbfc403b04b5caa9e421033866f2e98061eb3e61"
"checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e"
"checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd"
"checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400"
"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
@ -1454,6 +1564,8 @@ dependencies = [
"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6"
"checksum lscolors 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e9938fd8c379393454f73ec4c9c5b40f3d8332d80b25a29da05e41ee0ecbb559"
"checksum lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d947cbb889ed21c2a84be6ffbaebf5b4e0f4340638cba0444907e38b56be084"
"checksum make-cmd 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a8ca8afbe8af1785e09636acb5a41e08a765f5f0340568716c18a8700ba3c0d3"
"checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20"
"checksum memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a"
"checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39"
"checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3"
@ -1510,7 +1622,9 @@ dependencies = [
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
"checksum redox_users 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "214a97e49be64fd2c86f568dd0cb2c757d2cc53de95b273b6ad0a1c908482f26"
"checksum redox_users 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe5204c3a17e97dde73f285d49be585df59ed84b50a872baf416e73b62c3828"
"checksum regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4fd4ace6a8cf7860714a2c2280d6c1f7e6a413486c13298bbc86fd3da019402f"
"checksum regex 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "0b2f0808e7d7e4fb1cb07feb6ff2f4bc827938f24f8c2e6a3beb7370af544bdd"
"checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957"
"checksum regex-syntax 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9d76410686f9e3a17f06128962e0ecc5755870bb890c34820c7af7f1db2e1d48"
"checksum rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f4dccf6f4891ebcc0c39f9b6eb1a83b9bf5d747cb439ec6fba4f3b977038af"
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
@ -1518,9 +1632,12 @@ dependencies = [
"checksum scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8"
"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27"
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
"checksum semver-parser 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d8fff3c9c5a54636ab95acd8c1349926e04cb1eb8cd70b5adced8a1d1f703a67"
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
"checksum signal-notify 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "847fbedad7c2e6fbb6077befa1fa61a6336658eaae2d9fe66cb94a0024742f4e"
"checksum siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac"
"checksum sixel 0.3.0 (git+https://github.com/rabite0/sixel-rs?tag=v0.3.0)" = "<none>"
"checksum sixel-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fb46e0cd5569bf910390844174a5a99d52dd40681fff92228d221d9f8bf87dea"
"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
"checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7"
"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8"
@ -1528,8 +1645,11 @@ dependencies = [
"checksum syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d960b829a55e56db167e861ddb43602c003c7be0bee1d345021703fac2fb7c"
"checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f"
"checksum systemstat 0.1.4 (git+https://github.com/myfreeweb/systemstat?tag=v0.1.4)" = "<none>"
"checksum termion 1.5.3 (git+https://github.com/redox-os/termion)" = "<none>"
"checksum termion 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a8fb22f7cde82c8220e5aeacb3258ed7ce996142c77cba193f203515e26c330"
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
"checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03"
"checksum thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8576dbbfcaef9641452d5cf0df9b0e7eeab7694956dd33bb61515fb8f18cfdd5"
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
"checksum tiff 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1e4834f28a0330cb9f3f2c87d2649dca723cb33802e2bdcf18da32759fbec7ce"
"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
@ -1540,6 +1660,7 @@ dependencies = [
"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526"
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
"checksum users 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7fed7d0912567d35f88010c23dbaf865e9da8b5227295e8dc0f2fdd109155ab7"
"checksum utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1ca13c08c41c9c3e04224ed9ff80461d97e121589ff27c753a16cb10830ae0f"
"checksum utf8-ranges 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9d50aa7650df78abf942826607c62468ce18d9019673d4a2ebe1865dbb96ffde"
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"

View File

@ -16,7 +16,8 @@ default-run = "hunter"
[dependencies]
termion = "1.5.1"
# Added terminal_size_pixels recently
termion = { git = "https://github.com/redox-os/termion" }
unicode-width = "0.1.5"
lazy_static = "0.2.11"
alphanumeric-sort = "1.0.6"
@ -39,13 +40,17 @@ osstrtools = "0.1.9"
pathbuftools = "0.1"
clap = "2.33"
mime = "0.3.13"
# Updated to work with recent sixel-sys
sixel = { git = "https://github.com/rabite0/sixel-rs", tag="v0.3.0" }
base64 = { version = "0.10.1" }
image = { version = "0.21.1", optional = true }
gstreamer = { version = "0.11.2", optional = true }
gstreamer-app = { version = "0.11.2", optional = true }
[build-dependencies]
termion = "1.5.1"
# Added terminal_size_pixels recently
termion = { git = "https://github.com/redox-os/termion" }
rustc_version = "0.2.3"
[features]

View File

@ -13,7 +13,8 @@ use crate::fail::{HError, HResult, ErrorLog};
struct ArgvConfig {
animation: Option<bool>,
show_hidden: Option<bool>,
icons: Option<bool>
icons: Option<bool>,
sixel: Option<bool>,
}
impl ArgvConfig {
@ -21,7 +22,8 @@ impl ArgvConfig {
ArgvConfig {
animation: None,
show_hidden: None,
icons: None
icons: None,
sixel: None
}
}
}
@ -35,6 +37,7 @@ pub fn set_argv_config(args: clap::ArgMatches) -> HResult<()> {
let animation = args.is_present("animation-off");
let show_hidden = args.is_present("show-hidden");
let icons = args.is_present("icons");
let sixel = args.is_present("sixel");
let mut config = ArgvConfig::new();
@ -50,6 +53,10 @@ pub fn set_argv_config(args: clap::ArgMatches) -> HResult<()> {
config.icons = Some(true)
}
if sixel == true {
config.sixel = Some(true)
}
*ARGV_CONFIG.write()? = config;
Ok(())
}
@ -64,6 +71,7 @@ fn infuse_argv_config(mut config: Config) -> Config {
argv_config.animation.map(|val| config.animation = val);
argv_config.show_hidden.map(|val| config.show_hidden = val);
argv_config.icons.map(|val| config.icons = val);
argv_config.sixel.map(|val| config.sixel = val);
config
}
@ -78,7 +86,8 @@ pub struct Config {
pub media_autoplay: bool,
pub media_mute: bool,
pub media_previewer: String,
pub ratios: Vec::<usize>
pub ratios: Vec::<usize>,
pub sixel: bool,
}
@ -99,7 +108,8 @@ impl Config {
media_autoplay: false,
media_mute: false,
media_previewer: "hunter-media".to_string(),
ratios: vec![20,30,49]
ratios: vec![20,30,49],
sixel: false
}
}
@ -149,6 +159,7 @@ impl Config {
}
}
}
Ok(("sixel", "off")) => { config.sixel = true; }
_ => { HError::config_error::<Config>(line.to_string()).log(); }
}
config

View File

@ -123,17 +123,23 @@ impl<T> HBox<T> where T: Widget + PartialEq {
let len = coords.len();
let gap = if len == ratios.len() { 0 } else { 1 };
let widget_xsize = *ratio as u16;
let prev_coords = coords.last();
let prev_xsize = prev_coords.map(|c| c.xsize());
let prev_xpos = prev_coords.map(|c| c.xpos());
let widget_xsize = box_xsize * *ratio as u16 / 100;
let widget_xpos = if len == 0 {
box_coords.top().x()
} else {
let prev_coords = coords.last().unwrap();
let prev_xsize = prev_coords.xsize();
let prev_xpos = prev_coords.position().x();
prev_xsize + prev_xpos + gap
prev_xsize.unwrap() + prev_xpos.unwrap() + gap
};
// Ensure that last widget isn't under/over sized due to gap/rounding
let widget_xsize = if len+1 == ratios.len() && len != 0 {
box_xsize - (prev_xpos.unwrap() + prev_xsize.unwrap())
} else { widget_xsize };
coords.push(Coordinates {
size: Size((widget_xsize,
box_ysize)),

View File

@ -1,7 +1,9 @@
// Based on https://github.com/jD91mZM2/termplay
// MIT License
use image::{Pixel, FilterType, DynamicImage, GenericImageView};
use image::{FilterType, DynamicImage, GenericImageView};
use sixel::encoder::Encoder;
use base64;
use termion::color::{Bg, Fg, Rgb};
#[cfg(feature = "video")]
@ -34,48 +36,61 @@ fn main() -> MResult<()> {
.expect("provide ysize")
.parse()
.unwrap();
#[cfg(feature = "video")]
let xpos = args.get(3)
.expect("provide xpos")
let xpix = args.get(3)
.expect("provide xsize in pixels")
.parse::<usize>()
.unwrap();
#[cfg(feature = "video")]
let ypos = args.get(4)
.expect("provide ypos")
let ypix = args.get(4)
.expect("provide ysize in pixels")
.parse::<usize>()
.unwrap();
let preview_type = args.get(5)
.expect("Provide preview type")
.parse::<String>()
.unwrap();
#[cfg(feature = "video")]
// #[cfg(feature = "video")]
let autoplay = args.get(6)
.expect("Autoplay?")
.parse::<bool>()
.unwrap();
#[cfg(feature = "video")]
// #[cfg(feature = "video")]
let mute = args.get(7)
.expect("Muted?")
.parse::<bool>()
.unwrap();
let path = args.get(8).expect("Provide path");
let sixel = args.get(8)
.expect("Use SIXEL?")
.parse::<bool>()
.unwrap();
let path = args.get(9).expect("Provide path");
let target = if sixel {
if std::env::var("TERM") == Ok(String::from("xterm-kitty")) {
RenderTarget::Kitty
} else {
RenderTarget::Sixel
}
} else {
RenderTarget::Unicode
};
let renderer = Renderer::new(target,
xsize,
ysize,
xpix,
ypix);
let result =
match preview_type.as_ref() {
#[cfg(feature = "video")]
"video" => video_preview(path,
xsize,
ysize,
0,
0,
renderer,
autoplay,
mute,
false),
mute),
"image" => image_preview(path,
xsize,
ysize),
renderer),
#[cfg(feature = "video")]
"audio" => audio_preview(path,
@ -83,16 +98,7 @@ fn main() -> MResult<()> {
mute),
#[cfg(feature = "video")]
"video-raw" => video_preview(path,
xsize,
ysize,
xpos,
ypos,
autoplay,
mute,
true),
#[cfg(feature = "video")]
_ => { panic!("Available types: video(-raw)/image/audio") }
_ => { panic!("Available types: video/image/audio") }
#[cfg(not(feature = "video"))]
_ => { panic!("Available type: image") }
@ -107,15 +113,9 @@ fn main() -> MResult<()> {
}
fn image_preview(path: &str,
xsize: usize,
ysize: usize) -> MResult<()> {
renderer: Renderer) -> MResult<()> {
let img = image::open(&path)?;
let renderer = Renderer::new(xsize,
ysize,
0,
0);
renderer.send_image(&img)?;
Ok(())
@ -123,23 +123,19 @@ fn image_preview(path: &str,
#[cfg(feature = "video")]
fn video_preview(path: &String,
xsize: usize,
ysize: usize,
xpos: usize,
ypos: usize,
renderer: Renderer,
autoplay: bool,
mute: bool,
raw: bool)
mute: bool)
-> MResult<()> {
let low_fps = renderer.target == RenderTarget::Sixel;
let (player, appsink) = make_gstreamer()?;
let (player, appsink) = make_gstreamer(low_fps)?;
let uri = format!("file://{}", &path);
player.set_property("uri", &uri)?;
let renderer = Renderer::new(xsize, ysize, xpos, ypos);
let renderer = Arc::new(RwLock::new(renderer));
let crenderer = renderer.clone();
@ -165,30 +161,24 @@ fn video_preview(path: &String,
.map(|d| d.seconds().unwrap_or(0))
.unwrap_or(0);
if let Ok(mut renderer) = crenderer.write() {
match renderer.send_frame(&*sample,
position,
duration,
raw) {
Ok(()) => {
if autoplay == false {
// Just render first frame to get a static image
match p.set_state(gstreamer::State::Paused)
.into_result() {
Ok(_) => gstreamer::FlowReturn::Eos,
Err(_) => gstreamer::FlowReturn::Error
}
} else {
gstreamer::FlowReturn::Ok
}
}
Err(err) => {
println!("{:?}", err);
gstreamer::FlowReturn::Error
}
}
} else { gstreamer::FlowReturn::Error }
let renderer = crenderer.clone();
std::thread::spawn(move || {
renderer.write()
.map(|mut r| r.send_frame(&*sample,
position,
duration)).ok()
});
if autoplay == false {
// Just render first frame to get a static image
match p.set_state(gstreamer::State::Paused)
.into_result() {
Ok(_) => gstreamer::FlowReturn::Eos,
Err(_) => gstreamer::FlowReturn::Error
}
} else {
gstreamer::FlowReturn::Ok
}
}
})
.eos({
@ -276,6 +266,7 @@ fn read_keys(player: gstreamer::Element,
"u" => {
player.set_property("volume", &1.0)?;
}
// TODO add pixel size
"xy" => {
if let Some(ref renderer) = renderer {
let xsize = stdin.read_line()?;
@ -301,7 +292,7 @@ pub fn audio_preview(path: &String,
autoplay: bool,
mute: bool)
-> MResult<()> {
let (player, _) = make_gstreamer()?;
let (player, _) = make_gstreamer(false)?;
let uri = format!("file://{}", &path);
@ -333,7 +324,8 @@ pub fn audio_preview(path: &String,
// MediaView needs empty line as separator
writeln!(stdout, "")?;
// Send position and duration
// Send height, position and duration
writeln!(stdout, "0")?;
writeln!(stdout, "{}", position)?;
writeln!(stdout, "{}", duration)?;
stdout.flush()?;
@ -353,8 +345,8 @@ pub fn audio_preview(path: &String,
}
#[cfg(feature = "video")]
pub fn make_gstreamer() -> MResult<(gstreamer::Element,
gstreamer_app::AppSink)> {
pub fn make_gstreamer(low_fps: bool) -> MResult<(gstreamer::Element,
gstreamer_app::AppSink)> {
gstreamer::init()?;
let player = gstreamer::ElementFactory::make("playbin", None)
@ -374,7 +366,11 @@ pub fn make_gstreamer() -> MResult<(gstreamer::Element,
.unwrap();
videorate.set_property("max-rate", &30)?;
if low_fps {
videorate.set_property("max-rate", &10)?;
} else {
videorate.set_property("max-rate", &30)?;
}
let elems = &[&videorate, &pnmenc, &sink];
@ -395,14 +391,20 @@ pub fn make_gstreamer() -> MResult<(gstreamer::Element,
Ok((player, appsink))
}
#[derive(PartialEq)]
enum RenderTarget {
Unicode,
Sixel,
Kitty
}
struct Renderer {
// encoder: RwLock<Encoder>,
target: RenderTarget,
xsize: usize,
ysize: usize,
#[cfg(feature = "video")]
xpos: usize,
#[cfg(feature = "video")]
ypos: usize,
xsize_pix: usize,
ysize_pix: usize,
#[cfg(feature = "video")]
last_frame: Option<DynamicImage>,
#[cfg(feature = "video")]
@ -412,17 +414,26 @@ struct Renderer {
}
impl Renderer {
fn new(xsize: usize,
fn new(target: RenderTarget,
xsize: usize,
ysize: usize,
xpos: usize,
ypos: usize) -> Renderer {
mut xsize_pix: usize,
mut ysize_pix: usize) -> Renderer {
if std::env::var("TERM") == Ok(String::from("xterm"))
&& target == RenderTarget::Sixel {
// xterm has a hard limit on graphics size
// maybe splitting the image into parts would work?
if xsize_pix > 1000 { xsize_pix = 1000 };
if ysize_pix > 1000 { ysize_pix = 1000 };
}
Renderer {
target,
xsize,
ysize,
#[cfg(feature = "video")]
xpos,
#[cfg(feature = "video")]
ypos,
xsize_pix,
ysize_pix,
#[cfg(feature = "video")]
last_frame: None,
#[cfg(feature = "video")]
@ -432,7 +443,7 @@ impl Renderer {
}
}
// TODO: Add pixel size
#[cfg(feature = "video")]
fn set_size(&mut self, xsize: usize, ysize: usize) -> MResult<()> {
self.xsize = xsize;
@ -456,12 +467,10 @@ impl Renderer {
}
fn send_image(&self, image: &DynamicImage) -> MResult<()> {
let rendered_img = self.render_image(image);
let stdout = std::io::stdout();
let mut stdout = stdout.lock();
for line in rendered_img {
writeln!(stdout, "{}", line)?;
match self.target {
RenderTarget::Sixel => self.print_sixel(image)?,
RenderTarget::Unicode => self.print_unicode(image)?,
RenderTarget::Kitty => self.print_kitty(image)?
}
Ok(())
@ -471,8 +480,7 @@ impl Renderer {
fn send_frame(&mut self,
frame: &gstreamer::sample::SampleRef,
position: u64,
duration: u64,
raw: bool)
duration: u64)
-> MResult<()> {
let buffer = frame.get_buffer()
.ok_or(format_err!("Couldn't get buffer from frame!"))?;
@ -482,50 +490,38 @@ impl Renderer {
let stdout = std::io::stdout();
let mut stdout = stdout.lock();
let img = image::load_from_memory_with_format(&map,
image::ImageFormat::PNM)?;
let (_, height) = self.max_size(&img);
if !raw {
let img = image::load_from_memory_with_format(&map,
image::ImageFormat::PNM)?;
let rendered_img = self.render_image(&img);
self.last_frame = Some(img);
self.position = Some(position as usize);
self.duration = Some(duration as usize);
for line in rendered_img {
writeln!(stdout, "{}", line)?;
}
} else {
stdout.write_all(map.as_slice())?;
// Add newline after frame data
write!(stdout, "\n")?;
match self.target {
RenderTarget::Sixel => self.print_sixel(&img)?,
RenderTarget::Unicode => self.print_unicode(&img)?,
RenderTarget::Kitty => self.print_kitty(&img)?
}
self.last_frame = Some(img);
self.position = Some(position as usize);
self.duration = Some(duration as usize);
// Empty line means end of frame
writeln!(stdout, "")?;
// Send position and duration
// Send size (in rows), position and duration
writeln!(stdout, "{}", height)?;
writeln!(stdout, "{}", position)?;
writeln!(stdout, "{}", duration)?;
#[cfg(feature = "video")]
match raw {
true => {
writeln!(stdout, "{}", self.xpos)?;
writeln!(stdout, "{}", self.ypos)?;
}
_ => {}
}
Ok(())
}
pub fn render_image(&self, image: &DynamicImage) -> Vec<String> {
use image::Pixel;
let (xsize, ysize) = self.max_size(&image);
// double height, because of half-height unicode
let img = image.resize_exact(xsize as u32,
ysize as u32,
ysize as u32 * 2,
FilterType::Nearest).to_rgba();
@ -553,37 +549,134 @@ impl Renderer {
}).collect()
}
fn print_unicode(&self, img: &DynamicImage) -> MResult<()> {
let rendered_img = self.render_image(img);
let stdout = std::io::stdout();
let mut stdout = stdout.lock();
for line in rendered_img {
writeln!(stdout, "{}", line)?;
}
Ok(())
}
fn print_kitty(&self, img: &DynamicImage) -> MResult<()> {
let w = img.width();
let h = img.height();
let (max_x, max_y) = self.max_size(img);
let img = img.to_rgb().into_vec();
let mut file = std::fs::File::create("/tmp/img.raw").unwrap();
file.write_all(&img)?;
// Necessary?
file.flush()?;
std::mem::drop(file);
let path = base64::encode("/tmp/img.raw");
println!("\x1b_Gf=24,s={},v={},c={},r={},a=T,t=t;{}\x1b\\",
w,
h,
max_x,
max_y,
path);
Ok(())
}
fn print_sixel(&self, img: &DynamicImage) -> MResult<()> {
use sixel::optflags::*;
// Currently faster than covnerting/resizing using image...
img.save("/tmp/img.bmp")?;
let encoder = Encoder::new().unwrap();
let (xpix, ypix) = self.max_size_pix(img);
encoder.set_quality(Quality::Low).ok();
encoder.set_encode_policy(EncodePolicy::Fast).ok();
encoder.set_color_option(ColorOption::Builtin("xterm256")).ok();
encoder.set_width(SizeSpecification::Pixel(xpix as u64)).ok();
encoder.set_height(SizeSpecification::Pixel(ypix as u64)).ok();
encoder.encode_file(&std::path::PathBuf::from("/tmp/img.bmp")).ok();
// End line printed by encoder
println!("");
Ok(())
}
pub fn max_size(&self, image: &DynamicImage) -> (usize, usize)
{
// TODO: cell_ratio = xpix / ypix!
let xsize = self.xsize;
let ysize = self.ysize;
let img_xsize = image.width() * 2; // Cells are roughly 2:1
let img_ysize = image.height();
let img_ratio = img_xsize as f32 / img_ysize as f32;
let mut new_x;
let mut new_y;
if img_ratio < 1 as f32 {
new_x = (ysize as f32 * img_ratio) as usize;
new_y = ysize;
} else {
new_x = xsize;
new_y = (xsize as f32 / img_ratio) as usize;
}
// ensure it fits within xsize
if new_x > xsize {
new_x = xsize;
new_y = (xsize as f32 / img_ratio) as usize;
}
// ensure it fits within ysize
if new_y > ysize {
new_y = ysize;
new_x = (ysize as f32 * img_ratio) as usize;
}
(new_x as usize, new_y as usize)
}
pub fn max_size_pix(&self, image: &DynamicImage) -> (usize, usize)
{
let xsize = self.xsize_pix;
let ysize = self.ysize_pix;
let img_xsize = image.width();
let img_ysize = image.height();
let img_ratio = img_xsize as f32 / img_ysize as f32;
let mut new_x = xsize;
let mut new_x;
let mut new_y;
new_y = if img_ratio < 1 as f32 {
(xsize as f32 * img_ratio) as usize
// tall / slim
if img_ratio < 1 as f32 {
new_x = (ysize as f32 * img_ratio) as usize;
new_y = ysize;
// short / wide
} else {
(xsize as f32 / img_ratio) as usize
};
// Multiply by two because of half-block
if new_y > ysize*2 {
new_y = self.ysize * 2;
new_x = if img_ratio < 1 as f32 {
(ysize as f32 / img_ratio) as usize * 2
} else {
(ysize as f32 * img_ratio) as usize * 2
};
new_x = xsize;
new_y = (xsize as f32 / img_ratio) as usize;
}
// To make half-block encoding easier, y should be divisible by 2
if new_y as u32 % 2 == 1 {
new_y += 1;
// ensure it fits within xsize
if new_x > xsize {
new_x = xsize;
new_y = (xsize as f32 / img_ratio) as usize;
}
// ensure it fits within ysize
if new_y > ysize {
new_y = ysize;
new_x = (ysize as f32 * img_ratio) as usize;
}

View File

@ -33,18 +33,25 @@ impl ImgView {
pub fn encode_file(&mut self) -> HResult<()> {
let (xsize, ysize) = self.core.coordinates.size_u();
let (xpos, ypos) = self.core.coordinates.position_u();
let (cols, rows) = termion::terminal_size()?;
let (xpix, ypix) = termion::terminal_size_pixels()?;
let (xpix, ypix) = (xpix/cols, ypix/rows);
let (xpix, ypix) = (xpix * (xsize as u16 + 1),
ypix * (ysize as u16 + 1));
let file = &self.file;
let media_previewer = self.core.config().media_previewer;
let sixel = self.core.config().sixel;
let output = std::process::Command::new(&media_previewer)
.arg(format!("{}", (xsize)))
.arg(format!("{}", (xsize+1)))
.arg(format!("{}", (ysize+1)))
.arg(format!("{}", xpos))
.arg(format!("{}", ypos))
.arg(format!("{}", xpix))
.arg(format!("{}", ypix))
.arg("image")
.arg(format!("true"))
.arg(format!("true"))
.arg(format!("{}", sixel))
.arg(file.to_string_lossy().to_string())
.output()
.map_err(|e| {
@ -111,7 +118,7 @@ impl Widget for ImgView {
.iter()
.enumerate()
.fold(String::new(), |mut draw, (pos, line)| {
draw += &format!("{}", crate::term::goto_xy_u(xpos+1,
draw += &format!("{}", crate::term::goto_xy_u(xpos,
ypos + pos));
draw += line;
draw
@ -122,3 +129,11 @@ impl Widget for ImgView {
Ok(draw)
}
}
impl Drop for ImgView {
fn drop(&mut self) {
if self.core.config().sixel {
print!("\x1b_Ga=d\x1b\\");
}
}
}

View File

@ -151,6 +151,12 @@ fn parse_args() -> HResult<()> {
.long("icons")
.help("Show icons for different file types")
.takes_value(false))
.arg(
Arg::with_name("sixel")
.short("s")
.long("sixel")
.help("Show HQ graphics using sixel")
.takes_value(false))
// For "Add Action" action
.arg(
Arg::with_name("mime")

View File

@ -45,6 +45,7 @@ pub struct MediaView {
controller: Sender<String>,
paused: bool,
media_type: MediaType,
height: Arc<Mutex<usize>>,
position: Arc<Mutex<usize>>,
duration: Arc<Mutex<usize>>,
stale: Stale,
@ -52,6 +53,7 @@ pub struct MediaView {
preview_runner: Option<Box<dyn FnOnce(bool,
bool,
Arc<Mutex<usize>>,
Arc<Mutex<usize>>,
Arc<Mutex<usize>>)
-> HResult<()> + Send + 'static>>
}
@ -90,7 +92,12 @@ impl MediaView {
}
let (xsize, ysize) = core.coordinates.size_u();
let (xpos, ypos) = core.coordinates.position_u();
let (cols, rows) = termion::terminal_size()?;
let (xpix, ypix) = termion::terminal_size_pixels()?;
let (xpix, ypix) = (xpix/cols, ypix/rows);
let (xpix, ypix) = (xpix * (xsize as u16 + 1),
ypix * (ysize as u16 - 1));
let (tx_cmd, rx_cmd) = channel();
let imgview = ImgView {
@ -113,9 +120,11 @@ impl MediaView {
let ctype = media_type.clone();
let ccore = core.clone();
let media_previewer = core.config().media_previewer;
let sixel = core.config().sixel;
let run_preview = Box::new(move | auto,
mute,
height: Arc<Mutex<usize>>,
position: Arc<Mutex<usize>>,
duration: Arc<Mutex<usize>>| -> HResult<()> {
loop {
@ -125,18 +134,20 @@ impl MediaView {
let mut previewer = std::process::Command::new(&media_previewer)
.arg(format!("{}", (xsize)))
.arg(format!("{}", (xsize+1)))
// Leave space for position/seek bar
.arg(format!("{}", (ysize-1)))
.arg(format!("{}", xpos))
.arg(format!("{}", ypos))
.arg(format!("{}", xpix))
.arg(format!("{}", ypix))
.arg(format!("{}", ctype.to_str()))
.arg(format!("{}", auto))
.arg(format!("{}", mute))
.arg(format!("{}", sixel))
.arg(&path)
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.stderr(std::process::Stdio::inherit())
// .stderr(std::process::Stdio::piped())
.spawn()
.map_err(|e| {
let msg = format!("Couldn't run {}{}{}! Error: {:?}",
@ -185,6 +196,12 @@ impl MediaView {
// Newline means frame is complete
if line_buf == newline {
line_buf.clear();
stdout.read_line(&mut line_buf)?;
let h = &line_buf.trim();
*height.lock().unwrap() = h
.parse::<usize>()?;
line_buf.clear();
stdout.read_line(&mut line_buf)?;
let pos = &line_buf.trim();
@ -225,6 +242,7 @@ impl MediaView {
media_type: media_type,
controller: tx_cmd,
paused: false,
height: Arc::new(Mutex::new(0)),
position: Arc::new(Mutex::new(0)),
duration: Arc::new(Mutex::new(0)),
stale: stale,
@ -240,6 +258,7 @@ impl MediaView {
let stale = self.stale.clone();
let autoplay = self.autoplay();
let mute = self.mute();
let height = self.height.clone();
let position = self.position.clone();
let duration = self.duration.clone();
let clear = self.get_core()?.get_clearlist()?;
@ -254,6 +273,7 @@ impl MediaView {
runner.map(|runner| runner(autoplay,
mute,
height,
position,
duration));
}
@ -470,19 +490,20 @@ impl Widget for MediaView {
fn get_drawlist(&self) -> HResult<String> {
let (xpos, ypos) = self.core.coordinates.position_u();
let height = *self.height.lock()?;
let progress_str = self.progress_string()?;
let progress_bar = self.progress_bar()?;
let (frame, lines) = self.imgview
let frame= self.imgview
.lock()
.map(|img| (img.get_drawlist(), img.lines()))?;
.map(|img| img.get_drawlist())?;
let mut frame = frame?;
frame += &crate::term::goto_xy_u(xpos+1, ypos+lines);
frame += &crate::term::goto_xy_u(xpos, ypos+height);
frame += &progress_str;
frame += &self.get_icons(lines)?;
frame += &crate::term::goto_xy_u(xpos+1, ypos+lines+1);
frame += &self.get_icons(height)?;
frame += &crate::term::goto_xy_u(xpos, ypos+height+1);
frame += &progress_bar;
Ok(frame)