diff options
author | rabite <rabite@posteo.de> | 2019-07-08 12:53:44 +0200 |
---|---|---|
committer | rabite <rabite@posteo.de> | 2019-07-08 13:00:32 +0200 |
commit | 072b39b3cfeac109f8bb0fff747c6dc50d747942 (patch) | |
tree | b96cbdc6e8cdcfcc214cbfcf92145cd19b00e3fc | |
parent | 9b6bb7fea092b621337e797b8fd3628ff7ad8218 (diff) |
initial support for SIXEL/kitty graphics
-rw-r--r-- | Cargo.lock | 123 | ||||
-rw-r--r-- | Cargo.toml | 9 | ||||
-rw-r--r-- | src/config.rs | 19 | ||||
-rw-r--r-- | src/hbox.rs | 18 | ||||
-rw-r--r-- | src/hunter-media.rs | 365 | ||||
-rw-r--r-- | src/imgview.rs | 25 | ||||
-rw-r--r-- | src/main.rs | 6 | ||||
-rw-r--r-- | src/mediaview.rs | 41 |
8 files changed, 442 insertions, 164 deletions
@@ -7,6 +7,14 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ @@ -90,6 +98,14 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -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)", @@ -587,6 +605,19 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1088,6 +1119,18 @@ dependencies = [ [[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" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ @@ -1100,6 +1143,11 @@ dependencies = [ [[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" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ @@ -1147,6 +1195,15 @@ dependencies = [ [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1165,6 +1222,24 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1222,6 +1297,17 @@ dependencies = [ [[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" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1239,6 +1325,23 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1320,6 +1423,11 @@ dependencies = [ [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -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" @@ -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] diff --git a/src/config.rs b/src/config.rs index 5e24123..21d8404 100644 --- a/src/config.rs +++ b/src/config.rs @@ -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 diff --git a/src/hbox.rs b/src/hbox.rs index 8ad23c6..e370fde 100644 --- a/src/hbox.rs +++ b/src/hbox.rs @@ -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)), diff --git a/src/hunter-media.rs b/src/hunter-media.rs index 193dc06..cc1de41 100644 --- a/src/hunter-media.rs +++ b/src/hunter-media.rs @@ -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 + 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::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 si |