From 47846932d4df0fdf54489e12743fe91034e69337 Mon Sep 17 00:00:00 2001 From: Pig Fang Date: Sat, 19 Aug 2023 11:10:26 +0800 Subject: [PATCH] feat: prepare for Windows (#44) --- Cargo.lock | 169 ++++++++++++++++++++------------- adaptor/src/adaptor.rs | 8 +- adaptor/src/image.rs | 13 +-- adaptor/src/iterm2.rs | 22 ++--- adaptor/src/kitty.rs | 7 +- adaptor/src/sixel.rs | 22 ++--- app/Cargo.toml | 20 ++-- app/src/app.rs | 23 ++++- app/src/context.rs | 18 ++-- app/src/signals.rs | 13 ++- app/src/status/right.rs | 8 +- build.sh | 3 + config/Cargo.toml | 2 +- config/src/boot/boot.rs | 19 +++- config/src/lib.rs | 2 + config/src/preset.rs | 11 ++- config/src/preview/adaptor.rs | 2 +- config/src/xdg.rs | 35 +++++++ core/src/external/file.rs | 2 +- core/src/manager/folder.rs | 11 ++- core/src/manager/manager.rs | 14 ++- core/src/manager/preview.rs | 11 ++- core/src/tasks/tasks.rs | 4 +- core/src/tasks/workers/file.rs | 11 ++- shared/src/fs.rs | 1 + shared/src/lib.rs | 2 - shared/src/term/cursor.rs | 49 ++++++++++ shared/src/term/mod.rs | 5 + shared/src/{ => term}/term.rs | 45 ++++++--- shared/src/tty.rs | 16 ---- 30 files changed, 376 insertions(+), 192 deletions(-) create mode 100644 config/src/xdg.rs create mode 100644 shared/src/term/cursor.rs create mode 100644 shared/src/term/mod.rs rename shared/src/{ => term}/term.rs (54%) delete mode 100644 shared/src/tty.rs diff --git a/Cargo.lock b/Cargo.lock index 57642543..abe6ce57 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -118,9 +118,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.73" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f768393e7fabd388fe8409b13faa4d93ab0fef35db1508438dfdb066918bcf38" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "app" @@ -277,9 +277,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.21" +version = "4.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c27cdf28c0f604ba3f512b0c9a409f8de8513e4816705deb0498b627e7c3a3fd" +checksum = "b417ae4361bca3f5de378294fc7472d3c4ed86a5ef9f49e93ae722f432aae8d2" dependencies = [ "clap_builder", "clap_derive", @@ -288,9 +288,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.3.21" +version = "4.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08a9f1ab5e9f01a9b81f202e8562eb9a10de70abf9eaeac1be465c28b75aa4aa" +checksum = "9c90dc0f0e42c64bff177ca9d7be6fcc9ddb0f26a6e062174a61c84dd6c644d4" dependencies = [ "anstream", "anstyle", @@ -307,7 +307,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.29", ] [[package]] @@ -344,6 +344,7 @@ dependencies = [ "anyhow", "clap", "crossterm 0.27.0", + "dirs", "futures", "glob", "indexmap 2.0.0", @@ -355,7 +356,6 @@ dependencies = [ "shell-words", "toml", "validator", - "xdg", ] [[package]] @@ -515,6 +515,27 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "either" version = "1.9.0" @@ -587,7 +608,7 @@ checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.3.5", "windows-sys 0.48.0", ] @@ -694,7 +715,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.29", ] [[package]] @@ -1212,6 +1233,12 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "overload" version = "0.1.1" @@ -1236,9 +1263,9 @@ checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.3.5", "smallvec", - "windows-targets 0.48.2", + "windows-targets 0.48.3", ] [[package]] @@ -1270,7 +1297,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.29", ] [[package]] @@ -1371,9 +1398,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.32" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -1415,6 +1442,15 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_syscall" version = "0.3.5" @@ -1424,6 +1460,17 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall 0.2.16", + "thiserror", +] + [[package]] name = "regex" version = "1.9.3" @@ -1516,14 +1563,14 @@ checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.29", ] [[package]] name = "serde_json" -version = "1.0.104" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" +checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" dependencies = [ "itoa", "ryu", @@ -1667,9 +1714,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.28" +version = "2.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" +checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" dependencies = [ "proc-macro2", "quote", @@ -1699,22 +1746,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.45" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dedd246497092a89beedfe2c9f176d44c1b672ea6090edc20544ade01fbb7ea0" +checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.45" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d7b1fadccbbc7e19ea64708629f9d8dccd007c260d66485f20a6d41bc1cf4b3" +checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.29", ] [[package]] @@ -1783,9 +1830,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.31.0" +version = "1.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40de3a2ba249dcb097e01be5e67a5ff53cf250397715a071a81543e8a832a920" +checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" dependencies = [ "backtrace", "bytes", @@ -1808,7 +1855,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.29", ] [[package]] @@ -1877,7 +1924,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.29", ] [[package]] @@ -2078,7 +2125,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.29", "wasm-bindgen-shared", ] @@ -2100,7 +2147,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.29", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2163,7 +2210,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows-targets 0.48.2", + "windows-targets 0.48.3", ] [[package]] @@ -2181,7 +2228,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.2", + "windows-targets 0.48.3", ] [[package]] @@ -2201,17 +2248,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.48.2" +version = "0.48.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1eeca1c172a285ee6c2c84c341ccea837e7c01b12fbb2d0fe3c9e550ce49ec8" +checksum = "27f51fb4c64f8b770a823c043c7fad036323e1c48f55287b7bbb7987b2fcdf3b" dependencies = [ - "windows_aarch64_gnullvm 0.48.2", - "windows_aarch64_msvc 0.48.2", - "windows_i686_gnu 0.48.2", - "windows_i686_msvc 0.48.2", - "windows_x86_64_gnu 0.48.2", - "windows_x86_64_gnullvm 0.48.2", - "windows_x86_64_msvc 0.48.2", + "windows_aarch64_gnullvm 0.48.3", + "windows_aarch64_msvc 0.48.3", + "windows_i686_gnu 0.48.3", + "windows_i686_msvc 0.48.3", + "windows_x86_64_gnu 0.48.3", + "windows_x86_64_gnullvm 0.48.3", + "windows_x86_64_msvc 0.48.3", ] [[package]] @@ -2222,9 +2269,9 @@ checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.2" +version = "0.48.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b10d0c968ba7f6166195e13d593af609ec2e3d24f916f081690695cf5eaffb2f" +checksum = "fde1bb55ae4ce76a597a8566d82c57432bc69c039449d61572a7a353da28f68c" [[package]] name = "windows_aarch64_msvc" @@ -2234,9 +2281,9 @@ checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_aarch64_msvc" -version = "0.48.2" +version = "0.48.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "571d8d4e62f26d4932099a9efe89660e8bd5087775a2ab5cdd8b747b811f1058" +checksum = "1513e8d48365a78adad7322fd6b5e4c4e99d92a69db8df2d435b25b1f1f286d4" [[package]] name = "windows_i686_gnu" @@ -2246,9 +2293,9 @@ checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" -version = "0.48.2" +version = "0.48.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2229ad223e178db5fbbc8bd8d3835e51e566b8474bfca58d2e6150c48bb723cd" +checksum = "60587c0265d2b842298f5858e1a5d79d146f9ee0c37be5782e92a6eb5e1d7a83" [[package]] name = "windows_i686_msvc" @@ -2258,9 +2305,9 @@ checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" -version = "0.48.2" +version = "0.48.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "600956e2d840c194eedfc5d18f8242bc2e17c7775b6684488af3a9fff6fe3287" +checksum = "224fe0e0ffff5d2ea6a29f82026c8f43870038a0ffc247aa95a52b47df381ac4" [[package]] name = "windows_x86_64_gnu" @@ -2270,9 +2317,9 @@ checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" -version = "0.48.2" +version = "0.48.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea99ff3f8b49fb7a8e0d305e5aec485bd068c2ba691b6e277d29eaeac945868a" +checksum = "62fc52a0f50a088de499712cbc012df7ebd94e2d6eb948435449d76a6287e7ad" [[package]] name = "windows_x86_64_gnullvm" @@ -2282,9 +2329,9 @@ checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.2" +version = "0.48.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1a05a1ece9a7a0d5a7ccf30ba2c33e3a61a30e042ffd247567d1de1d94120d" +checksum = "2093925509d91ea3d69bcd20238f4c2ecdb1a29d3c281d026a09705d0dd35f3d" [[package]] name = "windows_x86_64_msvc" @@ -2294,25 +2341,19 @@ checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" -version = "0.48.2" +version = "0.48.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d419259aba16b663966e29e6d7c6ecfa0bb8425818bb96f6f1f3c3eb71a6e7b9" +checksum = "b6ade45bc8bf02ae2aa34a9d54ba660a1a58204da34ba793c00d83ca3730b5f1" [[package]] name = "winnow" -version = "0.5.10" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5504cc7644f4b593cbc05c4a55bf9bd4e94b867c3c0bd440934174d50482427d" +checksum = "d09770118a7eb1ccaf4a594a221334119a44a814fcb0d31c5b85e83e97227a97" dependencies = [ "memchr", ] -[[package]] -name = "xdg" -version = "2.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546" - [[package]] name = "yaml-rust" version = "0.4.5" diff --git a/adaptor/src/adaptor.rs b/adaptor/src/adaptor.rs index 515dbd54..9b5a95d5 100644 --- a/adaptor/src/adaptor.rs +++ b/adaptor/src/adaptor.rs @@ -19,10 +19,6 @@ pub struct Adaptor; impl Adaptor { pub async fn image_show(mut path: &Path, rect: Rect) -> Result<()> { - if IMAGE_SHOWN.swap(true, Ordering::Relaxed) { - Self::image_hide(rect).ok(); - } - let cache = BOOT.cache(path); if fs::metadata(&cache).await.is_ok() { path = cache.as_path(); @@ -35,7 +31,9 @@ impl Adaptor { _ => Ok(if let Some(tx) = &*UEBERZUG { tx.send(Some((path.to_path_buf(), rect)))?; }), - } + }?; + + Ok(IMAGE_SHOWN.store(true, Ordering::Relaxed)) } pub fn image_hide(rect: Rect) -> Result<()> { diff --git a/adaptor/src/image.rs b/adaptor/src/image.rs index ee410126..871c1248 100644 --- a/adaptor/src/image.rs +++ b/adaptor/src/image.rs @@ -3,18 +3,19 @@ use std::{path::Path, sync::Arc}; use anyhow::Result; use config::PREVIEW; use image::{imageops::FilterType, DynamicImage, ImageFormat}; -use shared::tty_ratio; +use shared::Term; use tokio::fs; pub struct Image; impl Image { pub(super) async fn crop(path: &Path, size: (u16, u16)) -> Result { - let (w, h) = { - let r = tty_ratio(); - let (w, h) = ((size.0 as f64 * r.0) as u32, (size.1 as f64 * r.1) as u32); - (w.min(PREVIEW.max_width), h.min(PREVIEW.max_height)) - }; + let (w, h) = Term::ratio() + .map(|(w, h)| { + let (w, h) = ((size.0 as f64 * w) as u32, (size.1 as f64 * h) as u32); + (w.min(PREVIEW.max_width), h.min(PREVIEW.max_height)) + }) + .unwrap_or((PREVIEW.max_width, PREVIEW.max_height)); let img = fs::read(path).await?; let img = tokio::task::spawn_blocking(move || -> Result { diff --git a/adaptor/src/iterm2.rs b/adaptor/src/iterm2.rs index 5d791571..6944e845 100644 --- a/adaptor/src/iterm2.rs +++ b/adaptor/src/iterm2.rs @@ -15,21 +15,19 @@ impl Iterm2 { let img = Image::crop(path, (rect.width, rect.height)).await?; let b = Self::encode(img).await?; - let mut stdout = stdout().lock(); - Term::move_to(&mut stdout, rect.x, rect.y)?; - Ok(stdout.write_all(&b)?) + Self::image_hide(rect)?; + Term::move_lock(stdout().lock(), (rect.x, rect.y), |stdout| Ok(stdout.write_all(&b)?)) } - #[inline] pub(super) fn image_hide(rect: Rect) -> Result<()> { - let s = " ".repeat(rect.width as usize); - let mut stdout = BufWriter::new(stdout().lock()); - - for y in rect.top()..=rect.bottom() { - Term::move_to(&mut stdout, rect.x, y)?; - stdout.write_all(s.as_bytes())?; - } - Ok(()) + let stdout = BufWriter::new(stdout().lock()); + Term::move_lock(stdout, (0, 0), |stdout| { + for y in rect.top()..rect.bottom() { + Term::move_to(stdout, rect.x, y)?; + Term::kill_to_end(stdout)?; + } + Ok(()) + }) } async fn encode(img: DynamicImage) -> Result> { diff --git a/adaptor/src/kitty.rs b/adaptor/src/kitty.rs index c2f0997d..439c5bba 100644 --- a/adaptor/src/kitty.rs +++ b/adaptor/src/kitty.rs @@ -15,9 +15,8 @@ impl Kitty { let img = Image::crop(path, (rect.width, rect.height)).await?; let b = Self::encode(img).await?; - let mut stdout = stdout().lock(); - Term::move_to(&mut stdout, rect.x, rect.y)?; - Ok(stdout.write_all(&b)?) + Self::image_hide()?; + Term::move_lock(stdout().lock(), (rect.x, rect.y), |stdout| Ok(stdout.write_all(&b)?)) } #[inline] @@ -32,7 +31,7 @@ impl Kitty { if let Some(first) = it.next() { write!( buf, - "\x1b\\\x1b_Ga=d\x1b\\\x1b_Ga=T,f={},s={},v={},m={};{}\x1b\\", + "\x1b_Ga=T,f={},s={},v={},m={};{}\x1b\\", format, size.0, size.1, diff --git a/adaptor/src/sixel.rs b/adaptor/src/sixel.rs index f53656d3..7353460e 100644 --- a/adaptor/src/sixel.rs +++ b/adaptor/src/sixel.rs @@ -15,21 +15,19 @@ impl Sixel { let img = Image::crop(path, (rect.width, rect.height)).await?; let b = Self::encode(img).await?; - let mut stdout = stdout().lock(); - Term::move_to(&mut stdout, rect.x, rect.y)?; - Ok(stdout.write_all(&b)?) + Self::image_hide(rect)?; + Term::move_lock(stdout().lock(), (rect.x, rect.y), |stdout| Ok(stdout.write_all(&b)?)) } - #[inline] pub(super) fn image_hide(rect: Rect) -> Result<()> { - let s = " ".repeat(rect.width as usize); - let mut stdout = BufWriter::new(stdout().lock()); - - for y in rect.top()..=rect.bottom() { - Term::move_to(&mut stdout, rect.x, y)?; - stdout.write_all(s.as_bytes())?; - } - Ok(()) + let stdout = BufWriter::new(stdout().lock()); + Term::move_lock(stdout, (0, 0), |stdout| { + for y in rect.top()..rect.bottom() { + Term::move_to(stdout, rect.x, y)?; + Term::kill_to_end(stdout)?; + } + Ok(()) + }) } async fn encode(img: DynamicImage) -> Result> { diff --git a/app/Cargo.toml b/app/Cargo.toml index 099f2ce1..2d73f53d 100644 --- a/app/Cargo.toml +++ b/app/Cargo.toml @@ -10,21 +10,23 @@ core = { path = "../core" } shared = { path = "../shared" } # External dependencies -ansi-to-tui = "^3" -anyhow = "^1" -crossterm = { version = "^0", features = [ "event-stream" ] } -futures = "^0" -libc = "^0" -ratatui = "^0" -signal-hook-tokio = { version = "^0", features = [ "futures-v0_3" ] } -tokio = { version = "^1", features = [ "parking_lot" ] } -unicode-width = "^0" +ansi-to-tui = "^3" +anyhow = "^1" +crossterm = { version = "^0", features = [ "event-stream" ] } +futures = "^0" +ratatui = "^0" +tokio = { version = "^1", features = [ "parking_lot" ] } +unicode-width = "^0" # Logging tracing = "^0" tracing-appender = "^0" tracing-subscriber = "^0" +[target.'cfg(not(target_os = "windows"))'.dependencies] +libc = "^0" +signal-hook-tokio = { version = "^0", features = [ "futures-v0_3" ] } + [[bin]] name = "yazi" path = "src/main.rs" diff --git a/app/src/app.rs b/app/src/app.rs index ef308dfa..2b36a781 100644 --- a/app/src/app.rs +++ b/app/src/app.rs @@ -1,5 +1,5 @@ use core::{emit, files::FilesOp, input::InputMode, Event}; -use std::{ffi::OsString, os::unix::prelude::OsStrExt}; +use std::ffi::OsString; use anyhow::{Ok, Result}; use config::{keymap::{Control, Key, KeymapLayer}, BOOT}; @@ -44,7 +44,16 @@ impl App { fn dispatch_quit(&mut self) { if let Some(p) = &BOOT.cwd_file { let cwd = self.cx.manager.cwd().as_os_str(); - std::fs::write(p, cwd.as_bytes()).ok(); + + #[cfg(target_os = "windows")] + { + std::fs::write(p, cwd.to_string_lossy().as_bytes()).ok(); + } + #[cfg(not(target_os = "windows"))] + { + use std::os::unix::ffi::OsStrExt; + std::fs::write(p, cwd.as_bytes()).ok(); + } } } @@ -174,7 +183,15 @@ impl App { s }); - std::fs::write(p, paths.as_bytes()).ok(); + #[cfg(target_os = "windows")] + { + std::fs::write(p, paths.to_string_lossy().as_bytes()).ok(); + } + #[cfg(not(target_os = "windows"))] + { + use std::os::unix::ffi::OsStrExt; + std::fs::write(p, paths.as_bytes()).ok(); + } return emit!(Quit); } diff --git a/app/src/context.rs b/app/src/context.rs index 8bbd8694..e5b982fe 100644 --- a/app/src/context.rs +++ b/app/src/context.rs @@ -1,9 +1,9 @@ use core::{input::Input, manager::Manager, select::Select, tasks::Tasks, which::Which, Position}; use config::keymap::KeymapLayer; -use libc::winsize; +use crossterm::terminal::WindowSize; use ratatui::prelude::Rect; -use shared::tty_size; +use shared::Term; pub struct Ctx { pub manager: Manager, @@ -25,14 +25,14 @@ impl Ctx { } pub(super) fn area(&self, pos: &Position) -> Rect { - let winsize { ws_row, ws_col, .. } = tty_size(); + let WindowSize { columns, rows, .. } = Term::size(); let (x, y) = match pos { Position::None => return Rect::default(), Position::Top(Rect { mut x, mut y, width, height }) => { - x = x.min(ws_col.saturating_sub(*width)); - y = y.min(ws_row.saturating_sub(*height)); - ((tty_size().ws_col / 2).saturating_sub(width / 2) + x, y) + x = x.min(columns.saturating_sub(*width)); + y = y.min(rows.saturating_sub(*height)); + ((columns / 2).saturating_sub(width / 2) + x, y) } Position::Hovered(rect @ Rect { mut x, y, width, height }) => { let Some(r) = @@ -41,8 +41,8 @@ impl Ctx { return self.area(&Position::Top(*rect)); }; - x = x.min(ws_col.saturating_sub(*width)); - if y + height + r.y + r.height > ws_row { + x = x.min(columns.saturating_sub(*width)); + if y + height + r.y + r.height > rows { (x + r.x, r.y.saturating_sub(height.saturating_sub(1))) } else { (x + r.x, y + r.y + r.height) @@ -51,7 +51,7 @@ impl Ctx { }; let (w, h) = pos.dimension().unwrap(); - Rect { x, y, width: w.min(ws_col.saturating_sub(x)), height: h.min(ws_row.saturating_sub(y)) } + Rect { x, y, width: w.min(columns.saturating_sub(x)), height: h.min(rows.saturating_sub(y)) } } #[inline] diff --git a/app/src/signals.rs b/app/src/signals.rs index 94e696ea..4dd921df 100644 --- a/app/src/signals.rs +++ b/app/src/signals.rs @@ -1,9 +1,8 @@ use core::Event; use anyhow::Result; -use crossterm::event::{Event as CrosstermEvent, EventStream}; +use crossterm::event::{Event as CrosstermEvent, EventStream, KeyEvent, KeyEventKind}; use futures::StreamExt; -use libc::{SIGHUP, SIGINT, SIGQUIT, SIGTERM}; use tokio::{select, sync::{mpsc::{self, UnboundedReceiver, UnboundedSender}, oneshot}, task::JoinHandle}; pub(super) struct Signals { @@ -46,7 +45,13 @@ impl Signals { } } + #[cfg(target_os = "windows")] + fn spawn_system_task(&self) -> Result<()> { Ok(()) } + + #[cfg(not(target_os = "windows"))] fn spawn_system_task(&self) -> Result> { + use libc::{SIGHUP, SIGINT, SIGQUIT, SIGTERM}; + let tx = self.tx.clone(); let mut signals = signal_hook_tokio::Signals::new([SIGHUP, SIGTERM, SIGINT, SIGQUIT])?; @@ -76,7 +81,9 @@ impl Signals { _ = &mut stop_rx => break, Some(Ok(event)) = reader.next() => { let event = match event { - CrosstermEvent::Key(key) => Event::Key(key), + // We need to check key event kind; + // otherwise event will be dispatched twice. + CrosstermEvent::Key(key @ KeyEvent { kind: KeyEventKind::Press, .. }) => Event::Key(key), CrosstermEvent::Paste(str) => Event::Paste(str), CrosstermEvent::Resize(cols, rows) => Event::Resize(cols, rows), _ => continue, diff --git a/app/src/status/right.rs b/app/src/status/right.rs index ba7906cb..3543ae9a 100644 --- a/app/src/status/right.rs +++ b/app/src/status/right.rs @@ -1,8 +1,5 @@ -use std::os::unix::prelude::PermissionsExt; - use config::THEME; use ratatui::{buffer::Buffer, layout::{Alignment, Rect}, text::{Line, Span}, widgets::{Paragraph, Widget}}; -use shared::file_mode; use super::Progress; use crate::Ctx; @@ -14,6 +11,7 @@ pub(super) struct Right<'a> { impl<'a> Right<'a> { pub(super) fn new(cx: &'a Ctx) -> Self { Self { cx } } + #[cfg(not(target_os = "windows"))] fn permissions(&self, s: &str) -> Vec { // Colors let mode = self.cx.manager.active().mode(); @@ -70,8 +68,10 @@ impl Widget for Right<'_> { let mut spans = vec![]; // Permissions + #[cfg(not(target_os = "windows"))] if let Some(h) = &manager.hovered { - spans.extend(self.permissions(&file_mode(h.meta.permissions().mode()))) + use std::os::unix::prelude::PermissionsExt; + spans.extend(self.permissions(&shared::file_mode(h.meta.permissions().mode()))) } // Position diff --git a/build.sh b/build.sh index a4c3c3fe..649dfc81 100755 --- a/build.sh +++ b/build.sh @@ -1,11 +1,14 @@ cargo +stable build --release --target aarch64-apple-darwin cargo +stable build --release --target x86_64-apple-darwin cargo +stable build --release --target x86_64-unknown-linux-gnu +cargo +stable build --release --target x86_64-pc-windows-gnu mv target/aarch64-apple-darwin/release/yazi yazi-aarch64-apple-darwin mv target/x86_64-apple-darwin/release/yazi yazi-x86_64-apple-darwin mv target/x86_64-unknown-linux-gnu/release/yazi yazi-x86_64-unknown-linux-gnu +mv target/x86_64-pc-windows-gnu/release/yazi yazi-x86_64-pc-windows-gnu zip -j yazi-aarch64-apple-darwin.zip yazi-aarch64-apple-darwin zip -j yazi-x86_64-apple-darwin.zip yazi-x86_64-apple-darwin zip -j yazi-x86_64-unknown-linux-gnu.zip yazi-x86_64-unknown-linux-gnu +zip -j yazi-x86_64-pc-windows-gnu.zip yazi-x86_64-pc-windows-gnu diff --git a/config/Cargo.toml b/config/Cargo.toml index 166b95dc..798e566a 100644 --- a/config/Cargo.toml +++ b/config/Cargo.toml @@ -10,6 +10,7 @@ shared = { path = "../shared" } anyhow = "^1" clap = { version = "^4", features = [ "derive" ] } crossterm = "^0" +dirs = "^5" futures = "^0" glob = "^0" indexmap = "^2" @@ -20,4 +21,3 @@ serde = { version = "^1", features = [ "derive" ] } shell-words = "^1" toml = { version = "^0", features = [ "preserve_order" ] } validator = { version = "^0", features = [ "derive" ] } -xdg = "^2" diff --git a/config/src/boot/boot.rs b/config/src/boot/boot.rs index e15d8870..c5f48c4c 100644 --- a/config/src/boot/boot.rs +++ b/config/src/boot/boot.rs @@ -1,9 +1,11 @@ -use std::{env, fs, os::unix::prelude::OsStrExt, path::{Path, PathBuf}, time::{self, SystemTime}}; +use std::{env, fs, path::{Path, PathBuf}, time::{self, SystemTime}}; use clap::{command, Parser}; use md5::{Digest, Md5}; use shared::absolute_path; +use crate::Xdg; + #[derive(Debug)] pub struct Boot { pub cwd: PathBuf, @@ -43,7 +45,7 @@ impl Default for Boot { let boot = Self { cwd: cwd.unwrap_or("/".into()), cache_dir: env::temp_dir().join("yazi"), - state_dir: xdg::BaseDirectories::with_prefix("yazi").unwrap().get_state_home(), + state_dir: Xdg::state_dir().unwrap(), cwd_file: args.cwd_file, chooser_file: args.chooser_file, @@ -63,9 +65,16 @@ impl Default for Boot { impl Boot { #[inline] pub fn cache(&self, path: &Path) -> PathBuf { - self - .cache_dir - .join(format!("{:x}", Md5::new_with_prefix(path.as_os_str().as_bytes()).finalize())) + #[cfg(target_os = "windows")] + let h = Md5::new_with_prefix(path.to_string_lossy().as_bytes()); + + #[cfg(not(target_os = "windows"))] + let h = { + use std::os::unix::ffi::OsStrExt; + Md5::new_with_prefix(path.as_os_str().as_bytes()) + }; + + self.cache_dir.join(format!("{:x}", h.finalize())) } #[inline] diff --git a/config/src/lib.rs b/config/src/lib.rs index ca467e9e..12cc0ce9 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -13,9 +13,11 @@ pub mod preview; pub mod tasks; pub mod theme; mod validation; +mod xdg; pub(crate) use pattern::*; pub(crate) use preset::*; +pub(crate) use xdg::*; static MERGED_KEYMAP: RoCell = RoCell::new(); static MERGED_THEME: RoCell = RoCell::new(); diff --git a/config/src/preset.rs b/config/src/preset.rs index 88536a22..37e3bd64 100644 --- a/config/src/preset.rs +++ b/config/src/preset.rs @@ -1,7 +1,8 @@ use std::fs; use toml::Table; -use xdg::BaseDirectories; + +use crate::xdg::Xdg; pub(crate) struct Preset; @@ -28,7 +29,7 @@ impl Preset { } fn merge_str(user: &str, base: &str) -> String { - let path = BaseDirectories::new().unwrap().get_config_file(user); + let path = Xdg::config_dir().unwrap().join(user); let mut user = fs::read_to_string(path).unwrap_or_default().parse::().unwrap(); let base = base.parse::
().unwrap(); @@ -38,16 +39,16 @@ impl Preset { #[inline] pub(crate) fn keymap() -> String { - Self::merge_str("yazi/keymap.toml", include_str!("../preset/keymap.toml")) + Self::merge_str("keymap.toml", include_str!("../preset/keymap.toml")) } #[inline] pub(crate) fn theme() -> String { - Self::merge_str("yazi/theme.toml", include_str!("../preset/theme.toml")) + Self::merge_str("theme.toml", include_str!("../preset/theme.toml")) } #[inline] pub(crate) fn yazi() -> String { - Self::merge_str("yazi/yazi.toml", include_str!("../preset/yazi.toml")) + Self::merge_str("yazi.toml", include_str!("../preset/yazi.toml")) } } diff --git a/config/src/preview/adaptor.rs b/config/src/preview/adaptor.rs index 24bc1916..5cc62b3b 100644 --- a/config/src/preview/adaptor.rs +++ b/config/src/preview/adaptor.rs @@ -27,7 +27,7 @@ impl Default for PreviewAdaptor { } match env::var("TERM_PROGRAM").unwrap_or_default().as_str() { "iTerm.app" => return Self::Iterm2, - "WezTerm" => return Self::Kitty, + "WezTerm" => return if cfg!(windows) { Self::Iterm2 } else { Self::Kitty }, "vscode" => return Self::Sixel, "Hyper" => return Self::Sixel, _ => {} diff --git a/config/src/xdg.rs b/config/src/xdg.rs new file mode 100644 index 00000000..a97be928 --- /dev/null +++ b/config/src/xdg.rs @@ -0,0 +1,35 @@ +use std::path::PathBuf; + +pub(super) struct Xdg; + +impl Xdg { + pub(super) fn config_dir() -> Option { + #[cfg(target_os = "windows")] + { + dirs::config_dir().map(|p| p.join("yazi").join("config")) + } + #[cfg(not(target_os = "windows"))] + { + std::env::var_os("XDG_CONFIG_HOME") + .map(PathBuf::from) + .and_then(|p| p.is_absolute().then_some(p)) + .or_else(|| dirs::home_dir().map(|h| h.join(".config"))) + .map(|p| p.join("yazi")) + } + } + + pub(super) fn state_dir() -> Option { + #[cfg(target_os = "windows")] + { + dirs::data_dir().map(|p| p.join("yazi").join("state")) + } + #[cfg(not(target_os = "windows"))] + { + std::env::var_os("XDG_STATE_HOME") + .map(PathBuf::from) + .and_then(|p| p.is_absolute().then_some(p)) + .or_else(|| dirs::home_dir().map(|h| h.join(".local/state"))) + .map(|p| p.join("yazi")) + } + } +} diff --git a/core/src/external/file.rs b/core/src/external/file.rs index e52771ee..2a46276f 100644 --- a/core/src/external/file.rs +++ b/core/src/external/file.rs @@ -10,7 +10,7 @@ pub async fn file(files: &[impl AsRef]) -> Result usize { tty_size().ws_row.saturating_sub(DIR_PADDING) as usize } + pub fn limit() -> usize { Term::size().rows.saturating_sub(DIR_PADDING) as usize } pub fn update(&mut self, op: FilesOp) -> bool { let b = match op { @@ -183,12 +184,12 @@ impl Folder { pub fn rect_current(&self, path: &Path) -> Option { let pos = self.position(path)? - self.offset; - let s = tty_size(); + let WindowSize { columns, .. } = Term::size(); Some(Rect { - x: (s.ws_col as u32 * PARENT_RATIO / ALL_RATIO) as u16, + x: (columns as u32 * PARENT_RATIO / ALL_RATIO) as u16, y: pos as u16, - width: (s.ws_col as u32 * CURRENT_RATIO / ALL_RATIO) as u16, + width: (columns as u32 * CURRENT_RATIO / ALL_RATIO) as u16, height: 1, }) } diff --git a/core/src/manager/manager.rs b/core/src/manager/manager.rs index e5e0d1e9..616c228f 100644 --- a/core/src/manager/manager.rs +++ b/core/src/manager/manager.rs @@ -1,4 +1,4 @@ -use std::{collections::{BTreeMap, BTreeSet, HashMap, HashSet}, env, ffi::OsStr, io::{stdout, BufWriter, Write}, mem, os::unix::prelude::OsStrExt, path::{Path, PathBuf}}; +use std::{collections::{BTreeMap, BTreeSet, HashMap, HashSet}, env, ffi::OsStr, io::{stdout, BufWriter, Write}, mem, path::{Path, PathBuf}}; use anyhow::{anyhow, bail, Error, Result}; use config::{open::Opener, BOOT, OPEN}; @@ -231,9 +231,17 @@ impl Manager { }; { - let b = old.iter().map(|o| o.as_os_str()).collect::>().join(OsStr::new("\n")); + let s = old.iter().map(|o| o.as_os_str()).collect::>().join(OsStr::new("\n")); let mut f = OpenOptions::new().write(true).create_new(true).open(&tmp).await?; - f.write_all(b.as_bytes()).await?; + #[cfg(target_os = "windows")] + { + f.write_all(s.to_string_lossy().as_bytes()).await?; + } + #[cfg(not(target_os = "windows"))] + { + use std::os::unix::ffi::OsStrExt; + f.write_all(s.as_bytes()).await?; + } } let _guard = BLOCKER.acquire().await.unwrap(); diff --git a/core/src/manager/preview.rs b/core/src/manager/preview.rs index 583a2e22..e55aa843 100644 --- a/core/src/manager/preview.rs +++ b/core/src/manager/preview.rs @@ -3,8 +3,9 @@ use std::{io::BufRead, mem, path::{Path, PathBuf}, sync::{atomic::{AtomicUsize, use adaptor::Adaptor; use anyhow::{anyhow, bail, Result}; use config::{BOOT, PREVIEW}; +use crossterm::terminal::WindowSize; use ratatui::prelude::Rect; -use shared::{tty_size, MimeKind}; +use shared::{MimeKind, Term}; use syntect::{easy::HighlightFile, util::as_24_bit_terminal_escaped}; use tokio::{fs, task::JoinHandle}; @@ -31,16 +32,16 @@ pub enum PreviewData { impl Preview { fn rect() -> Rect { - let s = tty_size(); + let WindowSize { columns, rows, .. } = Term::size(); - let x = (s.ws_col as u32 * (PARENT_RATIO + CURRENT_RATIO) / ALL_RATIO) as u16; - let width = (s.ws_col as u32 * PREVIEW_RATIO / ALL_RATIO) as u16; + let x = (columns as u32 * (PARENT_RATIO + CURRENT_RATIO) / ALL_RATIO) as u16; + let width = (columns as u32 * PREVIEW_RATIO / ALL_RATIO) as u16; Rect { x: x.saturating_add(PREVIEW_BORDER / 2), y: PREVIEW_MARGIN / 2, width: width.saturating_sub(PREVIEW_BORDER), - height: s.ws_row.saturating_sub(PREVIEW_MARGIN), + height: rows.saturating_sub(PREVIEW_MARGIN), } } diff --git a/core/src/tasks/tasks.rs b/core/src/tasks/tasks.rs index 511db0b2..d3134b51 100644 --- a/core/src/tasks/tasks.rs +++ b/core/src/tasks/tasks.rs @@ -2,7 +2,7 @@ use std::{collections::{BTreeMap, HashMap, HashSet}, ffi::OsStr, io::{stdout, Wr use config::{manager::SortBy, open::Opener, OPEN}; use crossterm::terminal::{disable_raw_mode, enable_raw_mode}; -use shared::{tty_size, Defer, MimeKind, Term}; +use shared::{Defer, MimeKind, Term}; use tokio::{io::{stdin, AsyncReadExt}, select, sync::mpsc, time}; use tracing::trace; @@ -29,7 +29,7 @@ impl Tasks { #[inline] pub fn limit() -> usize { - (tty_size().ws_row * TASKS_PERCENT / 100).saturating_sub(TASKS_PADDING) as usize + (Term::size().rows * TASKS_PERCENT / 100).saturating_sub(TASKS_PADDING) as usize } pub fn toggle(&mut self) -> bool { diff --git a/core/src/tasks/workers/file.rs b/core/src/tasks/workers/file.rs index 8f2f98f9..e808fae4 100644 --- a/core/src/tasks/workers/file.rs +++ b/core/src/tasks/workers/file.rs @@ -125,7 +125,16 @@ impl File { match fs::remove_file(&task.to).await { Err(e) if e.kind() != NotFound => Err(e)?, - _ => fs::symlink(src, &task.to).await?, + _ => { + #[cfg(target_os = "windows")] + { + fs::symlink_file(src, &task.to).await? + } + #[cfg(not(target_os = "windows"))] + { + fs::symlink(src, &task.to).await? + } + } } if task.cut { diff --git a/shared/src/fs.rs b/shared/src/fs.rs index 8240e7f0..351e990a 100644 --- a/shared/src/fs.rs +++ b/shared/src/fs.rs @@ -92,6 +92,7 @@ pub fn copy_with_progress(from: &Path, to: &Path) -> mpsc::Receiver String { use libc::{S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFSOCK, S_IRGRP, S_IROTH, S_IRUSR, S_ISGID, S_ISUID, S_ISVTX, S_IWGRP, S_IWOTH, S_IWUSR, S_IXGRP, S_IXOTH, S_IXUSR}; diff --git a/shared/src/lib.rs b/shared/src/lib.rs index d94a58e6..84a13d97 100644 --- a/shared/src/lib.rs +++ b/shared/src/lib.rs @@ -7,7 +7,6 @@ mod mime; mod ro_cell; mod term; mod throttle; -mod tty; pub use buffer::*; pub use chars::*; @@ -18,4 +17,3 @@ pub use mime::*; pub use ro_cell::*; pub use term::*; pub use throttle::*; -pub use tty::*; diff --git a/shared/src/term/cursor.rs b/shared/src/term/cursor.rs new file mode 100644 index 00000000..1d73ef4e --- /dev/null +++ b/shared/src/term/cursor.rs @@ -0,0 +1,49 @@ +use std::io::{stdout, Write}; + +use anyhow::Result; +use crossterm::{cursor::{MoveTo, RestorePosition, SavePosition, SetCursorStyle}, execute, queue, terminal::{Clear, ClearType}}; + +use crate::Term; + +impl Term { + #[inline] + pub fn kill_to_end(stdout: &mut impl Write) -> Result<()> { + Ok(queue!(stdout, Clear(ClearType::UntilNewLine))?) + } + + #[inline] + pub fn move_to(stdout: &mut impl Write, x: u16, y: u16) -> Result<()> { + Ok(queue!(stdout, MoveTo(x, y))?) + } + + #[inline] + pub fn move_lock(mut stdout: W, (x, y): (u16, u16), cb: F) -> Result<()> + where + W: Write, + F: FnOnce(&mut W) -> Result<()>, + { + #[cfg(target_os = "windows")] + { + use crossterm::cursor::{Hide, Show}; + queue!(&mut stdout, SavePosition, MoveTo(x, y), Show)?; + let result = cb(&mut stdout); + queue!(&mut stdout, Hide, RestorePosition)?; + stdout.flush()?; + result + } + #[cfg(not(target_os = "windows"))] + { + queue!(&mut stdout, SavePosition, MoveTo(x, y))?; + let result = cb(&mut stdout); + queue!(&mut stdout, RestorePosition)?; + stdout.flush()?; + result + } + } + + #[inline] + pub fn set_cursor_block() -> Result<()> { Ok(execute!(stdout(), SetCursorStyle::BlinkingBlock)?) } + + #[inline] + pub fn set_cursor_bar() -> Result<()> { Ok(execute!(stdout(), SetCursorStyle::BlinkingBar)?) } +} diff --git a/shared/src/term/mod.rs b/shared/src/term/mod.rs new file mode 100644 index 00000000..f95ce14e --- /dev/null +++ b/shared/src/term/mod.rs @@ -0,0 +1,5 @@ +mod cursor; +mod term; + +pub use cursor::*; +pub use term::*; diff --git a/shared/src/term.rs b/shared/src/term/term.rs similarity index 54% rename from shared/src/term.rs rename to shared/src/term/term.rs index d2e28e74..f84ac7ff 100644 --- a/shared/src/term.rs +++ b/shared/src/term/term.rs @@ -1,7 +1,7 @@ -use std::{io::{stdout, Stdout, Write}, ops::{Deref, DerefMut}}; +use std::{io::{stdout, Stdout, Write}, mem, ops::{Deref, DerefMut}}; use anyhow::Result; -use crossterm::{cursor::{MoveTo, SetCursorStyle}, event::{DisableBracketedPaste, DisableFocusChange, EnableBracketedPaste, EnableFocusChange, KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags}, execute, queue, terminal::{disable_raw_mode, enable_raw_mode, supports_keyboard_enhancement, Clear, ClearType, EnterAlternateScreen, LeaveAlternateScreen}}; +use crossterm::{event::{DisableBracketedPaste, DisableFocusChange, EnableBracketedPaste, EnableFocusChange, KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags}, execute, queue, terminal::{disable_raw_mode, enable_raw_mode, supports_keyboard_enhancement, Clear, ClearType, EnterAlternateScreen, LeaveAlternateScreen, WindowSize}}; use ratatui::{backend::CrosstermBackend, Terminal}; pub struct Term { @@ -32,23 +32,40 @@ impl Term { Ok(term) } + pub fn size() -> WindowSize { + let mut size = WindowSize { rows: 0, columns: 0, width: 0, height: 0 }; + if let Ok(s) = crossterm::terminal::window_size() { + let _ = mem::replace(&mut size, s); + } + + if size.rows == 0 || size.columns == 0 { + if let Ok(s) = crossterm::terminal::size() { + size.columns = s.0; + size.rows = s.1; + } + } + + // TODO: Use `CSI 14 t` to get the actual size of the terminal + // if size.width == 0 || size.height == 0 {} + + size + } + + #[inline] + pub fn ratio() -> Option<(f64, f64)> { + let s = Self::size(); + if s.width == 0 || s.height == 0 { + return None; + } + Some((f64::from(s.width) / f64::from(s.columns), f64::from(s.height) / f64::from(s.rows))) + } + #[inline] pub fn clear(stdout: &mut impl Write) -> Result<()> { - execute!(stdout, Clear(ClearType::All))?; + queue!(stdout, Clear(ClearType::All))?; writeln!(stdout)?; Ok(stdout.flush()?) } - - #[inline] - pub fn move_to(stdout: &mut impl Write, x: u16, y: u16) -> Result<()> { - Ok(execute!(stdout, MoveTo(x, y))?) - } - - #[inline] - pub fn set_cursor_block() -> Result<()> { Ok(execute!(stdout(), SetCursorStyle::BlinkingBlock)?) } - - #[inline] - pub fn set_cursor_bar() -> Result<()> { Ok(execute!(stdout(), SetCursorStyle::BlinkingBar)?) } } impl Drop for Term { diff --git a/shared/src/tty.rs b/shared/src/tty.rs deleted file mode 100644 index 035f09ec..00000000 --- a/shared/src/tty.rs +++ /dev/null @@ -1,16 +0,0 @@ -use libc::{ioctl, winsize, STDOUT_FILENO, TIOCGWINSZ}; - -#[inline] -pub fn tty_size() -> winsize { - unsafe { - let s: winsize = std::mem::zeroed(); - ioctl(STDOUT_FILENO, TIOCGWINSZ, &s); - s - } -} - -#[inline] -pub fn tty_ratio() -> (f64, f64) { - let s = tty_size(); - (f64::from(s.ws_xpixel) / f64::from(s.ws_col), f64::from(s.ws_ypixel) / f64::from(s.ws_row)) -}