From 7fe112293ee0c589b3904046dfb3be261fe78822 Mon Sep 17 00:00:00 2001 From: inorishio Date: Sun, 3 May 2026 23:58:55 +0200 Subject: [PATCH] What That Claude DO?! Screenshot tooling. Also there is a visual bug when creating a screenshot when moving --- Cargo.lock | 4238 +++++++++++++++++++++++++++++++++++++++ Cargo.toml | 55 + README.md | 4 +- assets/Koito-Minase.jpg | Bin 0 -> 70619 bytes src/capture.rs | 37 + src/config.rs | 157 ++ src/effects.rs | 293 +++ src/hyprland.rs | 270 +++ src/main.rs | 180 ++ src/overlay.rs | 358 ++++ src/review.rs | 345 ++++ test_viewport.rs | 3 + 12 files changed, 5939 insertions(+), 1 deletion(-) create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100755 assets/Koito-Minase.jpg create mode 100644 src/capture.rs create mode 100644 src/config.rs create mode 100644 src/effects.rs create mode 100644 src/hyprland.rs create mode 100644 src/main.rs create mode 100644 src/overlay.rs create mode 100644 src/review.rs create mode 100644 test_viewport.rs diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..f26c18f --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,4238 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ab_glyph" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01c0457472c38ea5bd1c3b5ada5e368271cb550be7a4ca4a0b4634e9913f6cc2" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser", +] + +[[package]] +name = "ab_glyph_rasterizer" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "getrandom 0.3.4", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aligned" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee4508988c62edf04abd8d92897fca0c2995d907ce1dfeaf369dac3716a40685" +dependencies = [ + "as-slice", +] + +[[package]] +name = "aligned-vec" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" +dependencies = [ + "equator", +] + +[[package]] +name = "android-activity" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f2a1bb052857d5dd49572219344a7332b31b76405648eabac5bc68978251bcd" +dependencies = [ + "android-properties", + "bitflags 2.11.1", + "cc", + "jni", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys 0.6.0+11769913", + "num_enum", + "thiserror 2.0.18", +] + +[[package]] +name = "android-properties" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" + +[[package]] +name = "arboard" +version = "3.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf" +dependencies = [ + "clipboard-win", + "image", + "log", + "objc2 0.6.4", + "objc2-app-kit 0.3.2", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation 0.3.2", + "parking_lot", + "percent-encoding", + "windows-sys 0.60.2", + "wl-clipboard-rs", + "x11rb", +] + +[[package]] +name = "arg_enum_proc_macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "as-slice" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "ash" +version = "0.38.0+1.3.281" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" +dependencies = [ + "libloading", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "av-scenechange" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f321d77c20e19b92c39e7471cf986812cbb46659d2af674adc4331ef3f18394" +dependencies = [ + "aligned", + "anyhow", + "arg_enum_proc_macro", + "arrayvec", + "log", + "num-rational", + "num-traits", + "pastey", + "rayon", + "thiserror 2.0.18", + "v_frame", + "y4m", +] + +[[package]] +name = "av1-grain" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cfddb07216410377231960af4fcab838eaa12e013417781b78bd95ee22077f8" +dependencies = [ + "anyhow", + "arrayvec", + "log", + "nom", + "num-rational", + "v_frame", +] + +[[package]] +name = "avif-serialize" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "375082f007bd67184fb9c0374614b29f9aaa604ec301635f72338bb65386a53d" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "bit-set" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0481a0e032742109b1133a095184ee93d88f3dc9e0d28a5d033dc77a073f44f" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2c54ff287cfc0a34f38a6b832ea1bd8e448a330b3e40a50859e6488bee07f22" + +[[package]] +name = "bit_field" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" + +[[package]] +name = "bitstream-io" +version = "4.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eff00be299a18769011411c9def0d827e8f2d7bf0c3dbf53633147a8867fd1f" +dependencies = [ + "no_std_io2", +] + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +dependencies = [ + "objc2 0.5.2", +] + +[[package]] +name = "built" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64" + +[[package]] +name = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "calloop" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" +dependencies = [ + "bitflags 2.11.1", + "log", + "polling", + "rustix 0.38.44", + "slab", + "thiserror 1.0.69", +] + +[[package]] +name = "calloop" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dbf9978365bac10f54d1d4b04f7ce4427e51f71d61f2fe15e3fed5166474df7" +dependencies = [ + "bitflags 2.11.1", + "polling", + "rustix 1.1.4", + "slab", + "tracing", +] + +[[package]] +name = "calloop-wayland-source" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20" +dependencies = [ + "calloop 0.13.0", + "rustix 0.38.44", + "wayland-backend", + "wayland-client", +] + +[[package]] +name = "calloop-wayland-source" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138efcf0940a02ebf0cc8d1eff41a1682a46b431630f4c52450d6265876021fa" +dependencies = [ + "calloop 0.14.4", + "rustix 1.1.4", + "wayland-backend", + "wayland-client", +] + +[[package]] +name = "cc" +version = "1.2.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chrono" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "clipboard-win" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4" +dependencies = [ + "error-code", +] + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "com" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e17887fd17353b65b1b2ef1c526c83e26cd72e74f598a8dc1bee13a48f3d9f6" +dependencies = [ + "com_macros", +] + +[[package]] +name = "com_macros" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d375883580a668c7481ea6631fc1a8863e33cc335bf56bfad8d7e6d4b04b13a5" +dependencies = [ + "com_macros_support", + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "com_macros_support" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad899a1087a9296d5644792d7cb72b8e34c1bec8e7d4fbc002230169a6e8710c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "cursor-icon" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" + +[[package]] +name = "directories" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +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 = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags 2.11.1", + "objc2 0.6.4", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "dlib" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab8ecd87370524b461f8557c119c405552c396ed91fc0a8eec68679eab26f94a" +dependencies = [ + "libloading", +] + +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "dpi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" + +[[package]] +name = "drm" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80bc8c5c6c2941f70a55c15f8d9f00f9710ebda3ffda98075f996a0e6c92756f" +dependencies = [ + "bitflags 2.11.1", + "bytemuck", + "drm-ffi", + "drm-fourcc", + "libc", + "rustix 0.38.44", +] + +[[package]] +name = "drm-ffi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51a91c9b32ac4e8105dec255e849e0d66e27d7c34d184364fb93e469db08f690" +dependencies = [ + "drm-sys", + "rustix 1.1.4", +] + +[[package]] +name = "drm-fourcc" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4" + +[[package]] +name = "drm-sys" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8e1361066d91f5ffccff060a3c3be9c3ecde15be2959c1937595f7a82a9f8" +dependencies = [ + "libc", + "linux-raw-sys 0.9.4", +] + +[[package]] +name = "ecolor" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "775cfde491852059e386c4e1deb4aef381c617dc364184c6f6afee99b87c402b" +dependencies = [ + "bytemuck", + "emath", +] + +[[package]] +name = "eframe" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ac2645a9bf4826eb4e91488b1f17b8eaddeef09396706b2f14066461338e24f" +dependencies = [ + "ahash", + "bytemuck", + "document-features", + "egui", + "egui-wgpu", + "egui-winit", + "egui_glow", + "image", + "js-sys", + "log", + "objc2 0.5.2", + "objc2-app-kit 0.2.2", + "objc2-foundation 0.2.2", + "parking_lot", + "percent-encoding", + "pollster", + "raw-window-handle", + "static_assertions", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "web-time", + "wgpu", + "winapi", + "windows-sys 0.52.0", + "winit", +] + +[[package]] +name = "egui" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53eafabcce0cb2325a59a98736efe0bf060585b437763f8c476957fb274bb974" +dependencies = [ + "ahash", + "emath", + "epaint", + "log", + "nohash-hasher", +] + +[[package]] +name = "egui-wgpu" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d00fd5d06d8405397e64a928fa0ef3934b3c30273ea7603e3dc4627b1f7a1a82" +dependencies = [ + "ahash", + "bytemuck", + "document-features", + "egui", + "epaint", + "log", + "thiserror 1.0.69", + "type-map", + "web-time", + "wgpu", + "winit", +] + +[[package]] +name = "egui-winit" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a9c430f4f816340e8e8c1b20eec274186b1be6bc4c7dfc467ed50d57abc36c6" +dependencies = [ + "ahash", + "arboard", + "egui", + "log", + "raw-window-handle", + "smithay-clipboard", + "web-time", + "webbrowser", + "winit", +] + +[[package]] +name = "egui_glow" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e39bccc683cd43adab530d8f21a13eb91e80de10bcc38c3f1c16601b6f62b26" +dependencies = [ + "ahash", + "bytemuck", + "egui", + "glow 0.14.2", + "log", + "memoffset", + "wasm-bindgen", + "web-sys", + "winit", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "emath" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1fe0049ce51d0fb414d029e668dd72eb30bc2b739bf34296ed97bd33df544f3" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "epaint" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a32af8da821bd4f43f2c137e295459ee2e1661d87ca8779dfa0eaf45d870e20f" +dependencies = [ + "ab_glyph", + "ahash", + "bytemuck", + "ecolor", + "emath", + "epaint_default_fonts", + "log", + "nohash-hasher", + "parking_lot", +] + +[[package]] +name = "epaint_default_fonts" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "483440db0b7993cf77a20314f08311dbe95675092405518c0677aa08c151a3ea" + +[[package]] +name = "equator" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" +dependencies = [ + "equator-macro", +] + +[[package]] +name = "equator-macro" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "error-code" +version = "3.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" + +[[package]] +name = "exr" +version = "1.74.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4300e043a56aa2cb633c01af81ca8f699a321879a7854d3896a0ba89056363be" +dependencies = [ + "bit_field", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + +[[package]] +name = "fax" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf1079563223d5d59d83c85886a56e586cfd5c1a26292e971a0fa266531ac5a" + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "gbm" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce852e998d3ca5e4a97014fb31c940dc5ef344ec7d364984525fd11e8a547e6a" +dependencies = [ + "bitflags 2.11.1", + "drm", + "drm-fourcc", + "gbm-sys", + "libc", + "wayland-backend", + "wayland-server", +] + +[[package]] +name = "gbm-sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c13a5f2acc785d8fb6bf6b7ab6bfb0ef5dad4f4d97e8e70bb8e470722312f76f" +dependencies = [ + "libc", +] + +[[package]] +name = "gethostname" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" +dependencies = [ + "rustix 1.1.4", + "windows-link", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "gif" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee8cfcc411d9adbbaba82fb72661cc1bcca13e8bba98b364e62b2dba8f960159" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "gl" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a94edab108827d67608095e269cf862e60d920f144a5026d3dbcfd8b877fb404" +dependencies = [ + "gl_generator", +] + +[[package]] +name = "gl_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +dependencies = [ + "khronos_api", + "log", + "xml-rs", +] + +[[package]] +name = "glow" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd348e04c43b32574f2de31c8bb397d96c9fcfa1371bd4ca6d8bdc464ab121b1" +dependencies = [ + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "glow" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d51fa363f025f5c111e03f13eda21162faeacb6911fe8caa0c0349f9cf0c4483" +dependencies = [ + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "glutin_wgl_sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4ee00b289aba7a9e5306d57c2d05499b2e5dc427f84ac708bd2c090212cf3e" +dependencies = [ + "gl_generator", +] + +[[package]] +name = "gpu-alloc" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" +dependencies = [ + "bitflags 2.11.1", + "gpu-alloc-types", +] + +[[package]] +name = "gpu-alloc-types" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" +dependencies = [ + "bitflags 2.11.1", +] + +[[package]] +name = "gpu-allocator" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd4240fc91d3433d5e5b0fc5b67672d771850dc19bbee03c1381e19322803d7" +dependencies = [ + "log", + "presser", + "thiserror 1.0.69", + "winapi", + "windows", +] + +[[package]] +name = "gpu-descriptor" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" +dependencies = [ + "bitflags 2.11.1", + "gpu-descriptor-types", + "hashbrown 0.15.5", +] + +[[package]] +name = "gpu-descriptor-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" +dependencies = [ + "bitflags 2.11.1", +] + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" + +[[package]] +name = "hassle-rs" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af2a7e73e1f34c48da31fb668a907f250794837e08faa144fd24f0b8b741e890" +dependencies = [ + "bitflags 2.11.1", + "com", + "libc", + "libloading", + "thiserror 1.0.69", + "widestring", + "winapi", +] + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "hexf-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core 0.62.2", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" + +[[package]] +name = "icu_properties" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" + +[[package]] +name = "icu_provider" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "image" +version = "0.25.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" +dependencies = [ + "bytemuck", + "byteorder-lite", + "color_quant", + "exr", + "gif", + "image-webp", + "moxcms", + "num-traits", + "png 0.18.1", + "qoi", + "ravif", + "rayon", + "rgb", + "tiff", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "image-webp" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3" +dependencies = [ + "byteorder-lite", + "quick-error", +] + +[[package]] +name = "imgref" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40fac9d56ed6437b198fddba683305e8e2d651aa42647f00f5ae542e7f5c94a2" + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.0", +] + +[[package]] +name = "interpolate_name" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "jni" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +dependencies = [ + "cfg-if", + "combine", + "jni-macros", + "jni-sys 0.4.1", + "log", + "simd_cesu8", + "thiserror 2.0.18", + "walkdir", + "windows-link", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn 2.0.117", +] + +[[package]] +name = "jni-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" +dependencies = [ + "jni-sys 0.4.1", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.117", +] + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1840c94c045fbcf8ba2812c95db44499f7c64910a912551aaaa541decebcacf" +dependencies = [ + "cfg-if", + "futures-util", + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "khronos-egl" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" +dependencies = [ + "libc", + "libloading", + "pkg-config", +] + +[[package]] +name = "khronos_api" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" + +[[package]] +name = "lebe" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libfuzzer-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f12a681b7dd8ce12bff52488013ba614b869148d54dd79836ab85aafdd53f08d" +dependencies = [ + "arbitrary", + "cc", +] + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "libredox" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" +dependencies = [ + "bitflags 2.11.1", + "libc", + "plain", + "redox_syscall 0.7.4", +] + +[[package]] +name = "libwayshot" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b90b306f57277d75087b3efcc727163ad291552fab45de5ac3def7677ea8937c" +dependencies = [ + "drm", + "gbm", + "gl", + "image", + "memmap2", + "r-egl-wayland", + "rustix 1.1.4", + "thiserror 2.0.18", + "tracing", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-protocols-wlr", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" + +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "loop9" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "maybe-rayon" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" +dependencies = [ + "cfg-if", + "rayon", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "memmap2" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "metal" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21" +dependencies = [ + "bitflags 2.11.1", + "block", + "core-graphics-types", + "foreign-types", + "log", + "objc", + "paste", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "moxcms" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" +dependencies = [ + "num-traits", + "pxfm", +] + +[[package]] +name = "naga" +version = "22.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bd5a652b6faf21496f2cfd88fc49989c8db0825d1f6746b1a71a6ede24a63ad" +dependencies = [ + "arrayvec", + "bit-set", + "bitflags 2.11.1", + "cfg_aliases 0.1.1", + "codespan-reporting", + "hexf-parse", + "indexmap", + "log", + "rustc-hash 1.1.0", + "spirv", + "termcolor", + "thiserror 1.0.69", + "unicode-xid", +] + +[[package]] +name = "ndk" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" +dependencies = [ + "bitflags 2.11.1", + "jni-sys 0.3.1", + "log", + "ndk-sys 0.6.0+11769913", + "num_enum", + "raw-window-handle", + "thiserror 1.0.69", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-sys" +version = "0.5.0+25.2.9519653" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" +dependencies = [ + "jni-sys 0.3.1", +] + +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys 0.3.1", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "no_std_io2" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b51ed7824b6e07d354605f4abb3d9d300350701299da96642ee084f5ce631550" +dependencies = [ + "memchr", +] + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + +[[package]] +name = "noop_proc_macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "objc-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" + +[[package]] +name = "objc2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" +dependencies = [ + "objc-sys", + "objc2-encode", +] + +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-app-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" +dependencies = [ + "bitflags 2.11.1", + "block2", + "libc", + "objc2 0.5.2", + "objc2-core-data", + "objc2-core-image", + "objc2-foundation 0.2.2", + "objc2-quartz-core", +] + +[[package]] +name = "objc2-app-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" +dependencies = [ + "bitflags 2.11.1", + "objc2 0.6.4", + "objc2-core-graphics", + "objc2-foundation 0.3.2", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" +dependencies = [ + "bitflags 2.11.1", + "block2", + "objc2 0.5.2", + "objc2-core-location", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-contacts" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" +dependencies = [ + "block2", + "objc2 0.5.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-core-data" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" +dependencies = [ + "bitflags 2.11.1", + "block2", + "objc2 0.5.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags 2.11.1", + "dispatch2", + "objc2 0.6.4", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags 2.11.1", + "dispatch2", + "objc2 0.6.4", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-core-image" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" +dependencies = [ + "block2", + "objc2 0.5.2", + "objc2-foundation 0.2.2", + "objc2-metal", +] + +[[package]] +name = "objc2-core-location" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" +dependencies = [ + "block2", + "objc2 0.5.2", + "objc2-contacts", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" +dependencies = [ + "bitflags 2.11.1", + "block2", + "dispatch", + "libc", + "objc2 0.5.2", +] + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags 2.11.1", + "objc2 0.6.4", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" +dependencies = [ + "bitflags 2.11.1", + "objc2 0.6.4", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-link-presentation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" +dependencies = [ + "block2", + "objc2 0.5.2", + "objc2-app-kit 0.2.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-metal" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" +dependencies = [ + "bitflags 2.11.1", + "block2", + "objc2 0.5.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" +dependencies = [ + "bitflags 2.11.1", + "block2", + "objc2 0.5.2", + "objc2-foundation 0.2.2", + "objc2-metal", +] + +[[package]] +name = "objc2-symbols" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" +dependencies = [ + "objc2 0.5.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" +dependencies = [ + "bitflags 2.11.1", + "block2", + "objc2 0.5.2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-image", + "objc2-core-location", + "objc2-foundation 0.2.2", + "objc2-link-presentation", + "objc2-quartz-core", + "objc2-symbols", + "objc2-uniform-type-identifiers", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-uniform-type-identifiers" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" +dependencies = [ + "block2", + "objc2 0.5.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" +dependencies = [ + "bitflags 2.11.1", + "block2", + "objc2 0.5.2", + "objc2-core-location", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "orbclient" +version = "0.3.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a570f6bca41d29acb2139229a7c873ec99bc9a313bd10804081d89bfac8ff329" +dependencies = [ + "libc", + "libredox", +] + +[[package]] +name = "os_pipe" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "owned_ttf_parser" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36820e9051aca1014ddc75770aab4d68bc1e9e632f0f5627c4086bc216fb583b" +dependencies = [ + "ttf-parser", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.5.18", + "smallvec", + "windows-link", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pastey" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "petgraph" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" +dependencies = [ + "fixedbitset", + "hashbrown 0.15.5", + "indexmap", +] + +[[package]] +name = "pin-project" +version = "1.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "png" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" +dependencies = [ + "bitflags 2.11.1", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "polling" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix 1.1.4", + "windows-sys 0.61.2", +] + +[[package]] +name = "pollster" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" + +[[package]] +name = "potential_utf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +dependencies = [ + "zerovec", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "presser" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit 0.25.11+spec-1.1.0", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "profiling" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" +dependencies = [ + "profiling-procmacros", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" +dependencies = [ + "quote", + "syn 2.0.117", +] + +[[package]] +name = "pxfm" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0c5ccf5294c6ccd63a74f1565028353830a9c2f5eb0c682c355c471726a6e3f" + +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + +[[package]] +name = "quick-xml" +version = "0.39.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "958f21e8e7ceb5a1aa7fa87fab28e7c75976e0bfe7e23ff069e0a260f894067d" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-egl" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74c68c047fb54b1eb9a0e751d9b238ff41195a4fcdae90ddc6b43b5903e2b09b" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "r-egl-wayland" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9b502a04cf38d8ab73fded506bb3a84999fc6cfea9801d1b459402a79f963d4" +dependencies = [ + "r-egl", + "wayland-backend", + "wayland-client", + "wayland-protocols", +] + +[[package]] +name = "rand" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "rav1e" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b6dd56e85d9483277cde964fd1bdb0428de4fec5ebba7540995639a21cb32b" +dependencies = [ + "aligned-vec", + "arbitrary", + "arg_enum_proc_macro", + "arrayvec", + "av-scenechange", + "av1-grain", + "bitstream-io", + "built", + "cfg-if", + "interpolate_name", + "itertools", + "libc", + "libfuzzer-sys", + "log", + "maybe-rayon", + "new_debug_unreachable", + "noop_proc_macro", + "num-derive", + "num-traits", + "paste", + "profiling", + "rand", + "rand_chacha", + "simd_helpers", + "thiserror 2.0.18", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e52310197d971b0f5be7fe6b57530dcd27beb35c1b013f29d66c1ad73fbbcc45" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error", + "rav1e", + "rayon", + "rgb", +] + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "rayon" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.11.1", +] + +[[package]] +name = "redox_syscall" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f450ad9c3b1da563fb6948a8e0fb0fb9269711c9c73d9ea1de5058c79c8d643a" +dependencies = [ + "bitflags 2.11.1", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror 1.0.69", +] + +[[package]] +name = "renderdoc-sys" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" + +[[package]] +name = "rgb" +version = "0.8.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4" + +[[package]] +name = "rs-pictures" +version = "0.1.0" +dependencies = [ + "anyhow", + "arboard", + "chrono", + "directories", + "eframe", + "egui", + "image", + "libwayshot", + "serde", + "tiny-skia", + "toml", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags 2.11.1", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags 2.11.1", + "errno", + "libc", + "linux-raw-sys 0.12.1", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "simd-adler32" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + +[[package]] +name = "simd_cesu8" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" +dependencies = [ + "rustc_version", + "simdutf8", +] + +[[package]] +name = "simd_helpers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" +dependencies = [ + "quote", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "slotmap" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038" +dependencies = [ + "version_check", +] + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "smithay-client-toolkit" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" +dependencies = [ + "bitflags 2.11.1", + "calloop 0.13.0", + "calloop-wayland-source 0.3.0", + "cursor-icon", + "libc", + "log", + "memmap2", + "rustix 0.38.44", + "thiserror 1.0.69", + "wayland-backend", + "wayland-client", + "wayland-csd-frame", + "wayland-cursor", + "wayland-protocols", + "wayland-protocols-wlr", + "wayland-scanner", + "xkeysym", +] + +[[package]] +name = "smithay-client-toolkit" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0512da38f5e2b31201a93524adb8d3136276fa4fe4aafab4e1f727a82b534cc0" +dependencies = [ + "bitflags 2.11.1", + "calloop 0.14.4", + "calloop-wayland-source 0.4.1", + "cursor-icon", + "libc", + "log", + "memmap2", + "rustix 1.1.4", + "thiserror 2.0.18", + "wayland-backend", + "wayland-client", + "wayland-csd-frame", + "wayland-cursor", + "wayland-protocols", + "wayland-protocols-experimental", + "wayland-protocols-misc", + "wayland-protocols-wlr", + "wayland-scanner", + "xkeysym", +] + +[[package]] +name = "smithay-clipboard" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71704c03f739f7745053bde45fa203a46c58d25bc5c4efba1d9a60e9dba81226" +dependencies = [ + "libc", + "smithay-client-toolkit 0.20.0", + "wayland-backend", +] + +[[package]] +name = "smol_str" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" +dependencies = [ + "serde", +] + +[[package]] +name = "spirv" +version = "0.3.0+sdk-1.3.268.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" +dependencies = [ + "bitflags 2.11.1", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "tiff" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63feaf3343d35b6ca4d50483f94843803b0f51634937cc2ec519fc32232bc52" +dependencies = [ + "fax", + "flate2", + "half", + "quick-error", + "weezl", + "zune-jpeg", +] + +[[package]] +name = "tiny-skia" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "log", + "png 0.17.16", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + +[[package]] +name = "tinystr" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime 0.6.11", + "toml_edit 0.22.27", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime 0.6.11", + "toml_write", + "winnow 0.7.15", +] + +[[package]] +name = "toml_edit" +version = "0.25.11+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" +dependencies = [ + "indexmap", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "winnow 1.0.2", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow 1.0.2", +] + +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tree_magic_mini" +version = "3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8765b90061cba6c22b5831f675da109ae5561588290f9fa2317adab2714d5a6" +dependencies = [ + "memchr", + "nom", + "petgraph", +] + +[[package]] +name = "ttf-parser" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" + +[[package]] +name = "type-map" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb30dbbd9036155e74adad6812e9898d03ec374946234fbcebd5dfc7b9187b90" +dependencies = [ + "rustc-hash 2.1.2", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "v_frame" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2" +dependencies = [ + "aligned-vec", + "num-traits", + "wasm-bindgen", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.3+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af934872acec734c2d80e6617bbb5ff4f12b052dd8e6332b0817bce889516084" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.117", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wayland-backend" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2857dd20b54e916ec7253b3d6b4d5c4d7d4ca2c33c2e11c6c76a99bd8744755d" +dependencies = [ + "cc", + "downcast-rs", + "rustix 1.1.4", + "scoped-tls", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c7c96bb74690c3189b5c9cb4ca1627062bb23693a4fad9d8c3de958260144" +dependencies = [ + "bitflags 2.11.1", + "rustix 1.1.4", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-csd-frame" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" +dependencies = [ + "bitflags 2.11.1", + "cursor-icon", + "wayland-backend", +] + +[[package]] +name = "wayland-cursor" +version = "0.31.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a52d18780be9b1314328a3de5f930b73d2200112e3849ca6cb11822793fb34d" +dependencies = [ + "rustix 1.1.4", + "wayland-client", + "xcursor", +] + +[[package]] +name = "wayland-protocols" +version = "0.32.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "563a85523cade2429938e790815fd7319062103b9f4a2dc806e9b53b95982d8f" +dependencies = [ + "bitflags 2.11.1", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-experimental" +version = "20250721.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40a1f863128dcaaec790d7b4b396cc9b9a7a079e878e18c47e6c2d2c5a8dcbb1" +dependencies = [ + "bitflags 2.11.1", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-misc" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e9567599ef23e09b8dad6e429e5738d4509dfc46b3b21f32841a304d16b29c8" +dependencies = [ + "bitflags 2.11.1", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-plasma" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b6d8cf1eb2c1c31ed1f5643c88a6e53538129d4af80030c8cabd1f9fa884d91" +dependencies = [ + "bitflags 2.11.1", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234" +dependencies = [ + "bitflags 2.11.1", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c324a910fd86ebdc364a3e61ec1f11737d3b1d6c273c0239ee8ff4bc0d24b4a" +dependencies = [ + "proc-macro2", + "quick-xml", + "quote", +] + +[[package]] +name = "wayland-server" +version = "0.31.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc1846eb04c49182e04f4a099e2a830a2b745610bbc1d61246e206f29c7000a0" +dependencies = [ + "bitflags 2.11.1", + "downcast-rs", + "rustix 1.1.4", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-sys" +version = "0.31.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be" +dependencies = [ + "dlib", + "libc", + "log", + "memoffset", + "once_cell", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eadbac71025cd7b0834f20d1fe8472e8495821b4e9801eb0a60bd1f19827602" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webbrowser" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc95580916af1e68ff6a7be07446fc5db73ebf71cf092de939bbf5f7e189f72" +dependencies = [ + "core-foundation 0.10.1", + "jni", + "log", + "ndk-context", + "objc2 0.6.4", + "objc2-foundation 0.3.2", + "url", + "web-sys", +] + +[[package]] +name = "weezl" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" + +[[package]] +name = "wgpu" +version = "22.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d1c4ba43f80542cf63a0a6ed3134629ae73e8ab51e4b765a67f3aa062eb433" +dependencies = [ + "arrayvec", + "cfg_aliases 0.1.1", + "document-features", + "js-sys", + "log", + "naga", + "parking_lot", + "profiling", + "raw-window-handle", + "smallvec", + "static_assertions", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "wgpu-core", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-core" +version = "22.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0348c840d1051b8e86c3bcd31206080c5e71e5933dabd79be1ce732b0b2f089a" +dependencies = [ + "arrayvec", + "bit-vec", + "bitflags 2.11.1", + "cfg_aliases 0.1.1", + "document-features", + "indexmap", + "log", + "naga", + "once_cell", + "parking_lot", + "profiling", + "raw-window-handle", + "rustc-hash 1.1.0", + "smallvec", + "thiserror 1.0.69", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-hal" +version = "22.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6bbf4b4de8b2a83c0401d9e5ae0080a2792055f25859a02bf9be97952bbed4f" +dependencies = [ + "android_system_properties", + "arrayvec", + "ash", + "bitflags 2.11.1", + "block", + "cfg_aliases 0.1.1", + "core-graphics-types", + "glow 0.13.1", + "glutin_wgl_sys", + "gpu-alloc", + "gpu-allocator", + "gpu-descriptor", + "hassle-rs", + "js-sys", + "khronos-egl", + "libc", + "libloading", + "log", + "metal", + "naga", + "ndk-sys 0.5.0+25.2.9519653", + "objc", + "once_cell", + "parking_lot", + "profiling", + "raw-window-handle", + "renderdoc-sys", + "rustc-hash 1.1.0", + "smallvec", + "thiserror 1.0.69", + "wasm-bindgen", + "web-sys", + "wgpu-types", + "winapi", +] + +[[package]] +name = "wgpu-types" +version = "22.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc9d91f0e2c4b51434dfa6db77846f2793149d8e73f800fa2e41f52b8eac3c5d" +dependencies = [ + "bitflags 2.11.1", + "js-sys", + "web-sys", +] + +[[package]] +name = "widestring" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core 0.52.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "winit" +version = "0.30.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6755fa58a9f8350bd1e472d4c3fcc25f824ec358933bba33306d0b63df5978d" +dependencies = [ + "ahash", + "android-activity", + "atomic-waker", + "bitflags 2.11.1", + "block2", + "calloop 0.13.0", + "cfg_aliases 0.2.1", + "concurrent-queue", + "core-foundation 0.9.4", + "core-graphics", + "cursor-icon", + "dpi", + "js-sys", + "libc", + "memmap2", + "ndk", + "objc2 0.5.2", + "objc2-app-kit 0.2.2", + "objc2-foundation 0.2.2", + "objc2-ui-kit", + "orbclient", + "pin-project", + "raw-window-handle", + "redox_syscall 0.4.1", + "rustix 0.38.44", + "smithay-client-toolkit 0.19.2", + "smol_str", + "tracing", + "unicode-segmentation", + "wasm-bindgen", + "wasm-bindgen-futures", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-protocols-plasma", + "web-sys", + "web-time", + "windows-sys 0.52.0", + "xkbcommon-dl", +] + +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "wl-clipboard-rs" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9651471a32e87d96ef3a127715382b2d11cc7c8bb9822ded8a7cc94072eb0a3" +dependencies = [ + "libc", + "log", + "os_pipe", + "rustix 1.1.4", + "thiserror 2.0.18", + "tree_magic_mini", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-protocols-wlr", +] + +[[package]] +name = "writeable" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" + +[[package]] +name = "x11rb" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414" +dependencies = [ + "gethostname", + "rustix 1.1.4", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" + +[[package]] +name = "xcursor" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b" + +[[package]] +name = "xkbcommon-dl" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" +dependencies = [ + "bitflags 2.11.1", + "dlib", + "log", + "once_cell", + "xkeysym", +] + +[[package]] +name = "xkeysym" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" + +[[package]] +name = "xml-rs" +version = "0.8.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" + +[[package]] +name = "y4m" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448" + +[[package]] +name = "yoke" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zerofrom" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zune-core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9" + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "zune-jpeg" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" +dependencies = [ + "zune-core", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..19f3e13 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,55 @@ +[package] +name = "rs-pictures" +version = "0.1.0" +edition = "2024" + +[[bin]] +name = "rs-pictures" +path = "src/main.rs" + +[dependencies] +# Wayland-native screen capture +libwayshot = "0.7" + +# GUI framework (Wayland only) +eframe = { version = "0.29", default-features = false, features = ["wayland", "wgpu"] } +egui = "0.29" + +# Image handling +image = { version = "0.25", features = ["png", "jpeg"] } + +# 2D rendering for effects (rounded corners, drop shadow) +tiny-skia = "0.11" + +# Clipboard (Wayland) +arboard = { version = "3.6", features = ["wayland-data-control"] } + +# Config serialization +serde = { version = "1", features = ["derive"] } +toml = "0.8" + +# Platform config/data directories +directories = "5" + +# Error handling +anyhow = "1" + +# Timestamp-based filenames +chrono = { version = "0.4", features = ["clock"] } + +# ── Build profiles ──────────────────────────────────────────────────────────── + +[profile.release] +opt-level = 3 +lto = "thin" # link-time optimisation across crates +codegen-units = 1 # better inlining at the cost of compile time +strip = true # strip debug symbols → smaller binary + +# Dev builds are slow for pixel-processing code. This gives opt-level 2 +# to our own crate only while keeping dependencies at their default (opt=3 +# they already compiled with), so incremental rebuilds stay fast. +[profile.dev] +opt-level = 0 + +[profile.dev.package."*"] +opt-level = 3 # all deps at full optimisation even in dev mode diff --git a/README.md b/README.md index b3a5d97..bbe4940 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ # What_That_Claude_DO? What That Claude Do? (WTCD) -A repository of random things I ask Claude to do for me. \ No newline at end of file +A repository of random things I ask Claude to do for me. + +In this case it is creating a screenshot tool diff --git a/assets/Koito-Minase.jpg b/assets/Koito-Minase.jpg new file mode 100755 index 0000000000000000000000000000000000000000..13672abaf36e9dfc0315ac25c13d8441e9f586d1 GIT binary patch literal 70619 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
    {5mPPeT>k_5a_}pgg+yZJO~S7F`7m|+CWgu= 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 C3Aa4} literal 0 HcmV?d00001 diff --git a/src/capture.rs b/src/capture.rs new file mode 100644 index 0000000..5c723d4 --- /dev/null +++ b/src/capture.rs @@ -0,0 +1,37 @@ +use anyhow::{Context, Result}; +use image::RgbaImage; +use libwayshot::WayshotConnection; + +/// A rectangular region on screen in physical pixels. +#[derive(Debug, Clone, Copy)] +pub struct Region { + pub x: i32, + pub y: i32, + pub width: u32, + pub height: u32, +} + +/// Captures all connected outputs stitched together into one image. +pub fn capture_all_outputs() -> Result { + let conn = WayshotConnection::new() + .context("Failed to connect to Wayland display")?; + + let all = conn.get_all_outputs(); + eprintln!("[capture] outputs: {:?}", all.iter().map(|o| &o.name).collect::>()); + + let active_name = crate::hyprland::active_monitor_name(); + let outputs: Vec<_> = if let Some(ref name) = active_name { + let filtered: Vec<_> = all.iter().filter(|o| &o.name == name).cloned().collect(); + if filtered.is_empty() { all.to_vec() } else { filtered } + } else { + all.iter().take(1).cloned().collect() + }; + + let rgba = conn + .screenshot_outputs(&outputs, false) + .context("libwayshot failed to capture output")? + .into_rgba8(); + + eprintln!("[capture] capture_all_outputs → {}x{}", rgba.width(), rgba.height()); + Ok(rgba) +} diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..ad090dd --- /dev/null +++ b/src/config.rs @@ -0,0 +1,157 @@ +use anyhow::{Context, Result}; +use directories::ProjectDirs; +use serde::{Deserialize, Serialize}; +use std::path::PathBuf; + +/// Top-level application configuration. +/// Serialized to/from ~/.config/rs-pictures/config.toml +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Config { + /// Directory where screenshots are saved. + pub save_directory: PathBuf, + + /// File format for saved screenshots: "png" or "jpeg". + pub save_format: String, + + /// Filename template. Supports strftime-style tokens via chrono. + /// Example: "screenshot_%Y-%m-%d_%H-%M-%S" + pub filename_template: String, + + /// When true, automatically save to disk after capture without opening + /// the review window. + #[serde(default)] + pub auto_save: bool, + + /// When true, automatically copy to the clipboard after capture without + /// opening the review window. + #[serde(default)] + pub auto_copy: bool, + + /// Milliseconds to wait after launch before capturing the desktop snapshot. + /// Increase this if the overlay background still shows the terminal/launcher + /// that started rs-pictures. Default: 800. + #[serde(default = "default_capture_delay_ms")] + pub capture_delay_ms: u64, + + /// Visual effects applied after capture. + pub effects: EffectsConfig, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct EffectsConfig { + /// Apply rounded corners to the screenshot. + pub rounded_corners: bool, + + /// Radius in pixels for rounded corners. + pub corner_radius: f32, + + /// Apply a drop shadow beneath the screenshot. + pub drop_shadow: bool, + + /// Blur radius for the drop shadow (higher = softer shadow). + pub shadow_blur_radius: f32, + + /// Horizontal offset of the shadow in pixels. + pub shadow_offset_x: f32, + + /// Vertical offset of the shadow in pixels. + pub shadow_offset_y: f32, + + /// Shadow color as [R, G, B, A] in 0..=255. + pub shadow_color: [u8; 4], +} + +impl Default for EffectsConfig { + fn default() -> Self { + Self { + rounded_corners: false, + corner_radius: 12.0, + drop_shadow: false, + shadow_blur_radius: 20.0, + shadow_offset_x: 5.0, + shadow_offset_y: 8.0, + shadow_color: [0, 0, 0, 160], + } + } +} + +impl Default for Config { + fn default() -> Self { + let save_directory = dirs_default_pictures().unwrap_or_else(|| PathBuf::from(".")); + Self { + save_directory, + save_format: "png".into(), + filename_template: "screenshot_%Y-%m-%d_%H-%M-%S".into(), + auto_save: false, + auto_copy: false, + capture_delay_ms: default_capture_delay_ms(), + effects: EffectsConfig::default(), + } + } +} + +impl Config { + /// Returns the path to the config file, creating parent directories if needed. + pub fn config_path() -> Option { + ProjectDirs::from("", "", "rs-pictures") + .map(|pd| pd.config_dir().join("config.toml")) + } + + /// Load config from disk, or return the default config if the file doesn't exist. + pub fn load() -> Result { + let path = match Self::config_path() { + Some(p) => p, + None => return Ok(Self::default()), + }; + + if !path.exists() { + let config = Self::default(); + config.save()?; + return Ok(config); + } + + let raw = std::fs::read_to_string(&path) + .with_context(|| format!("Failed to read config at {}", path.display()))?; + + toml::from_str(&raw) + .with_context(|| format!("Failed to parse config at {}", path.display())) + } + + /// Persist the current config to disk. + pub fn save(&self) -> Result<()> { + let path = Self::config_path() + .context("Could not determine config directory")?; + + if let Some(parent) = path.parent() { + std::fs::create_dir_all(parent) + .with_context(|| format!("Failed to create config dir {}", parent.display()))?; + } + + let serialized = toml::to_string_pretty(self) + .context("Failed to serialize config")?; + + std::fs::write(&path, serialized) + .with_context(|| format!("Failed to write config to {}", path.display()))?; + + Ok(()) + } + + /// Build the full output path for a new screenshot using chrono formatting. + pub fn output_path(&self) -> PathBuf { + let now = chrono::Local::now(); + let filename = now.format(&self.filename_template).to_string(); + let ext = &self.save_format; + self.save_directory.join(format!("{filename}.{ext}")) + } +} + +fn default_capture_delay_ms() -> u64 { 800 } + +fn dirs_default_pictures() -> Option { + // Use XDG_PICTURES_DIR if available, otherwise ~/Pictures + if let Ok(val) = std::env::var("XDG_PICTURES_DIR") { + return Some(PathBuf::from(val)); + } + directories::UserDirs::new() + .and_then(|ud| ud.picture_dir().map(|p| p.to_path_buf())) +} diff --git a/src/effects.rs b/src/effects.rs new file mode 100644 index 0000000..b8b38bc --- /dev/null +++ b/src/effects.rs @@ -0,0 +1,293 @@ +//! Post-capture image effects: rounded corners and drop shadow. +//! +//! Pipeline: +//! RgbaImage (captured) +//! → apply_rounded_corners() – clips corners to transparent via tiny-skia mask +//! → apply_drop_shadow() – composites a blurred shadow beneath the image +//! → final RgbaImage (may be larger when shadow is added) +//! +//! Performance notes: +//! - Box blur uses a sliding-window algorithm: O(W*H) regardless of radius. +//! Three passes of the box filter approximate a Gaussian. +//! - Pixel format conversions between RgbaImage and tiny-skia Pixmap are done +//! with a single pass each way. +//! - This module is called from a background thread in review.rs so the UI +//! never blocks. + +use crate::config::EffectsConfig; +use image::RgbaImage; +use tiny_skia::{ + BlendMode, Color, FillRule, Paint, Path, PathBuilder, Pixmap, PixmapPaint, Transform, +}; + +/// Apply all configured effects in order. Returns a new image. +pub fn apply_effects(img: RgbaImage, cfg: &EffectsConfig) -> RgbaImage { + let img = if cfg.rounded_corners { + apply_rounded_corners(img, cfg.corner_radius) + } else { + img + }; + if cfg.drop_shadow { + apply_drop_shadow( + img, + cfg.shadow_blur_radius, + cfg.shadow_offset_x, + cfg.shadow_offset_y, + cfg.shadow_color, + ) + } else { + img + } +} + +// ─── Rounded corners ───────────────────────────────────────────────────────── + +pub fn apply_rounded_corners(img: RgbaImage, radius: f32) -> RgbaImage { + let (w, h) = img.dimensions(); + let mut mask = Pixmap::new(w, h).expect("mask pixmap"); + let path = rounded_rect_path(0.0, 0.0, w as f32, h as f32, radius); + let mut paint = Paint::default(); + paint.set_color(Color::WHITE); + paint.anti_alias = true; + mask.fill_path(&path, &paint, FillRule::Winding, Transform::identity(), None); + + let mut pixmap = rgba_image_to_pixmap(&img); + let mut dst_paint = PixmapPaint::default(); + dst_paint.blend_mode = BlendMode::DestinationIn; + pixmap.draw_pixmap(0, 0, mask.as_ref(), &dst_paint, Transform::identity(), None); + pixmap_to_rgba_image(pixmap) +} + +// ─── Drop shadow ───────────────────────────────────────────────────────────── + +pub fn apply_drop_shadow( + img: RgbaImage, + blur_radius: f32, + offset_x: f32, + offset_y: f32, + shadow_color: [u8; 4], +) -> RgbaImage { + let (iw, ih) = img.dimensions(); + let br = blur_radius.ceil() as u32; + + let extra_left = br.saturating_sub((-offset_x).max(0.0) as u32); + let extra_top = br.saturating_sub((-offset_y).max(0.0) as u32); + let extra_right = br + offset_x.max(0.0) as u32; + let extra_bottom = br + offset_y.max(0.0) as u32; + + let canvas_w = iw + extra_left + extra_right; + let canvas_h = ih + extra_top + extra_bottom; + + // 1. Place the image silhouette at the shadow position. + let mut shadow_pixmap = Pixmap::new(canvas_w, canvas_h).expect("shadow pixmap"); + let img_pixmap = rgba_image_to_pixmap(&img); + let shadow_x = (extra_left as f32 + offset_x) as i32; + let shadow_y = (extra_top as f32 + offset_y) as i32; + + let mut sp = PixmapPaint::default(); + sp.blend_mode = BlendMode::Source; + shadow_pixmap.draw_pixmap(shadow_x, shadow_y, img_pixmap.as_ref(), &sp, Transform::identity(), None); + + // 2. Tint the silhouette with the shadow colour. + tint_pixmap_as_shadow(&mut shadow_pixmap, shadow_color); + + // 3. Blur the shadow (sliding-window box blur, 3 passes). + let shadow_img = pixmap_to_rgba_image(shadow_pixmap); + let blurred = box_blur_rgba(&shadow_img, br); + let blurred_pixmap = rgba_image_to_pixmap(&blurred); + + // 4. Composite: shadow first, image on top. + let mut canvas = Pixmap::new(canvas_w, canvas_h).expect("canvas pixmap"); + let mut p = PixmapPaint::default(); + p.blend_mode = BlendMode::Source; + canvas.draw_pixmap(0, 0, blurred_pixmap.as_ref(), &p, Transform::identity(), None); + + let mut p2 = PixmapPaint::default(); + p2.blend_mode = BlendMode::SourceOver; + canvas.draw_pixmap(extra_left as i32, extra_top as i32, img_pixmap.as_ref(), &p2, Transform::identity(), None); + + pixmap_to_rgba_image(canvas) +} + +// ─── Helpers ───────────────────────────────────────────────────────────────── + +fn rounded_rect_path(x: f32, y: f32, w: f32, h: f32, r: f32) -> Path { + let r = r.min(w / 2.0).min(h / 2.0); + let mut pb = PathBuilder::new(); + pb.move_to(x + r, y); + pb.line_to(x + w - r, y); + pb.quad_to(x + w, y, x + w, y + r); + pb.line_to(x + w, y + h - r); + pb.quad_to(x + w, y + h, x + w - r, y + h); + pb.line_to(x + r, y + h); + pb.quad_to(x, y + h, x, y + h - r); + pb.line_to(x, y + r); + pb.quad_to(x, y, x + r, y); + pb.close(); + pb.finish().expect("rounded rect path") +} + +fn rgba_image_to_pixmap(img: &RgbaImage) -> Pixmap { + let (w, h) = img.dimensions(); + let mut pixmap = Pixmap::new(w, h).expect("pixmap alloc"); + let pixels = pixmap.pixels_mut(); + for (i, px) in img.pixels().enumerate() { + let [r, g, b, a] = px.0; + let af = a as f32 / 255.0; + pixels[i] = tiny_skia::PremultipliedColorU8::from_rgba( + (r as f32 * af) as u8, + (g as f32 * af) as u8, + (b as f32 * af) as u8, + a, + ) + .unwrap_or(tiny_skia::PremultipliedColorU8::TRANSPARENT); + } + pixmap +} + +fn pixmap_to_rgba_image(pixmap: Pixmap) -> RgbaImage { + let (w, h) = (pixmap.width(), pixmap.height()); + let mut out = RgbaImage::new(w, h); + for (i, px) in pixmap.pixels().iter().enumerate() { + let x = (i as u32) % w; + let y = (i as u32) / w; + let a = px.alpha(); + let (r, g, b) = if a == 0 { + (0, 0, 0) + } else { + let af = a as f32 / 255.0; + ( + (px.red() as f32 / af).round().min(255.0) as u8, + (px.green() as f32 / af).round().min(255.0) as u8, + (px.blue() as f32 / af).round().min(255.0) as u8, + ) + }; + out.put_pixel(x, y, image::Rgba([r, g, b, a])); + } + out +} + +fn tint_pixmap_as_shadow(pixmap: &mut Pixmap, color: [u8; 4]) { + let [sr, sg, sb, _] = color; + for px in pixmap.pixels_mut() { + let a = px.alpha(); + if a > 0 { + let af = a as f32 / 255.0; + *px = tiny_skia::PremultipliedColorU8::from_rgba( + (sr as f32 * af) as u8, + (sg as f32 * af) as u8, + (sb as f32 * af) as u8, + a, + ) + .unwrap_or(tiny_skia::PremultipliedColorU8::TRANSPARENT); + } + } +} + +// ─── Sliding-window box blur (O(W*H) regardless of radius) ─────────────────── +// +// Classic algorithm: maintain a running sum over a window of (2r+1) pixels. +// When the window slides by one pixel, subtract the pixel leaving the window +// and add the pixel entering it. Three passes (H→V→H or H→V→H) approximate +// a Gaussian kernel. + +fn box_blur_rgba(img: &RgbaImage, radius: u32) -> RgbaImage { + if radius == 0 { + return img.clone(); + } + // Three passes of H+V to approximate a Gaussian. + let mut buf = sliding_horizontal(img, radius); + buf = sliding_vertical(&buf, radius); + buf = sliding_horizontal(&buf, radius); + buf = sliding_vertical(&buf, radius); + buf +} + +/// Horizontal sliding-window box blur, single pass. +fn sliding_horizontal(img: &RgbaImage, radius: u32) -> RgbaImage { + let (w, h) = img.dimensions(); + let r = radius as i32; + let diam = (2 * r + 1) as u32; + let mut out = RgbaImage::new(w, h); + + for y in 0..h { + // Accumulator for the current window. + let mut sr = 0u32; + let mut sg = 0u32; + let mut sb = 0u32; + let mut sa = 0u32; + + // Seed the window around x=0. + for dx in -r..=r { + let sx = dx.clamp(0, w as i32 - 1) as u32; + let p = img.get_pixel(sx, y).0; + sr += p[0] as u32; + sg += p[1] as u32; + sb += p[2] as u32; + sa += p[3] as u32; + } + + for x in 0..w { + out.put_pixel(x, y, image::Rgba([ + (sr / diam) as u8, + (sg / diam) as u8, + (sb / diam) as u8, + (sa / diam) as u8, + ])); + + // Slide: remove left edge, add right edge. + let remove_x = (x as i32 - r).clamp(0, w as i32 - 1) as u32; + let add_x = (x as i32 + r + 1).clamp(0, w as i32 - 1) as u32; + let rp = img.get_pixel(remove_x, y).0; + let ap = img.get_pixel(add_x, y).0; + sr = sr.saturating_sub(rp[0] as u32) + ap[0] as u32; + sg = sg.saturating_sub(rp[1] as u32) + ap[1] as u32; + sb = sb.saturating_sub(rp[2] as u32) + ap[2] as u32; + sa = sa.saturating_sub(rp[3] as u32) + ap[3] as u32; + } + } + out +} + +/// Vertical sliding-window box blur, single pass. +fn sliding_vertical(img: &RgbaImage, radius: u32) -> RgbaImage { + let (w, h) = img.dimensions(); + let r = radius as i32; + let diam = (2 * r + 1) as u32; + let mut out = RgbaImage::new(w, h); + + for x in 0..w { + let mut sr = 0u32; + let mut sg = 0u32; + let mut sb = 0u32; + let mut sa = 0u32; + + for dy in -r..=r { + let sy = dy.clamp(0, h as i32 - 1) as u32; + let p = img.get_pixel(x, sy).0; + sr += p[0] as u32; + sg += p[1] as u32; + sb += p[2] as u32; + sa += p[3] as u32; + } + + for y in 0..h { + out.put_pixel(x, y, image::Rgba([ + (sr / diam) as u8, + (sg / diam) as u8, + (sb / diam) as u8, + (sa / diam) as u8, + ])); + + let remove_y = (y as i32 - r).clamp(0, h as i32 - 1) as u32; + let add_y = (y as i32 + r + 1).clamp(0, h as i32 - 1) as u32; + let rp = img.get_pixel(x, remove_y).0; + let ap = img.get_pixel(x, add_y ).0; + sr = sr.saturating_sub(rp[0] as u32) + ap[0] as u32; + sg = sg.saturating_sub(rp[1] as u32) + ap[1] as u32; + sb = sb.saturating_sub(rp[2] as u32) + ap[2] as u32; + sa = sa.saturating_sub(rp[3] as u32) + ap[3] as u32; + } + } + out +} diff --git a/src/hyprland.rs b/src/hyprland.rs new file mode 100644 index 0000000..349d425 --- /dev/null +++ b/src/hyprland.rs @@ -0,0 +1,270 @@ +//! Hyprland window geometry queries. +//! +//! Uses `hyprctl clients -j` and `hyprctl activeworkspace -j` to enumerate +//! windows on the active workspace. Returns logical pixel coordinates that +//! match the coordinate space used by libwayshot LogicalRegion. +//! +//! If `hyprctl` is not available (non-Hyprland compositor) the functions +//! return an empty list so the overlay degrades gracefully to manual +//! selection only. + +/// A window's position and size in Wayland logical pixels. +#[derive(Debug, Clone)] +pub struct WindowRect { + pub x: i32, + pub y: i32, + pub width: i32, + pub height: i32, + pub title: String, +} + +/// Returns the logical size (width, height) of the primary/active monitor. +/// Falls back to None if hyprctl is unavailable. +pub fn active_monitor_logical_size() -> Option<(u32, u32)> { + let info = active_monitor_info()?; + Some((info.0, info.1)) +} + +/// Returns the scale factor of the active monitor (e.g. 1.33 on HiDPI). +/// Falls back to 1.0 if hyprctl is unavailable. +pub fn active_monitor_scale() -> f32 { + active_monitor_info().map(|i| i.2).unwrap_or(1.0) +} + +/// Returns the name of the active/focused monitor (e.g. "DP-1"). +pub fn active_monitor_name() -> Option { + let output = std::process::Command::new("hyprctl") + .args(["monitors", "-j"]) + .output() + .ok()?; + if !output.status.success() { return None; } + let text = std::str::from_utf8(&output.stdout).ok()?; + for obj in split_objects(text) { + if json_bool(obj, "focused") == Some(true) { + return json_string(obj, "name"); + } + } + None +} + +/// Returns (logical_width, logical_height, scale) for the focused monitor. +fn active_monitor_info() -> Option<(u32, u32, f32)> { + let output = std::process::Command::new("hyprctl") + .args(["monitors", "-j"]) + .output() + .ok()?; + + if !output.status.success() { + return None; + } + + let text = std::str::from_utf8(&output.stdout).ok()?; + + // Find the focused monitor (focused: true) or fall back to the first. + for obj in split_objects(text) { + let focused = json_bool(obj, "focused"); + if focused != Some(true) { + continue; + } + let w = json_i64(obj, "width")? as f32; + let h = json_i64(obj, "height")? as f32; + let scale = json_f32(obj, "scale").unwrap_or(1.0); + return Some(((w / scale).round() as u32, (h / scale).round() as u32, scale)); + } + + // No focused monitor found — take the first one. + let obj = split_objects(text).into_iter().next()?; + let w = json_i64(obj, "width")? as f32; + let h = json_i64(obj, "height")? as f32; + let scale = json_f32(obj, "scale").unwrap_or(1.0); + Some(((w / scale).round() as u32, (h / scale).round() as u32, scale)) +} + +/// Returns an empty Vec if hyprctl is unavailable or returns bad data. +pub fn active_workspace_windows() -> Vec { + let workspace_id = match active_workspace_id() { + Some(id) => id, + None => return vec![], + }; + + let output = match std::process::Command::new("hyprctl") + .args(["clients", "-j"]) + .output() + { + Ok(o) if o.status.success() => o.stdout, + _ => return vec![], + }; + + parse_clients(&output, workspace_id) +} + +// ─── Private helpers ────────────────────────────────────────────────────────── + +fn active_workspace_id() -> Option { + let output = std::process::Command::new("hyprctl") + .args(["activeworkspace", "-j"]) + .output() + .ok()?; + + if !output.status.success() { + return None; + } + + // Extract "id": with a tiny hand-rolled parse — no serde dep. + let text = std::str::from_utf8(&output.stdout).ok()?; + json_i64(text, "id") +} + +/// Parse the `hyprctl clients -j` JSON output without pulling in serde_json. +/// +/// We only need four fields per client: `at`, `size`, `workspace.id`, `title`, +/// `mapped`, `hidden`. A minimal hand-rolled extractor is sufficient. +fn parse_clients(data: &[u8], workspace_id: i64) -> Vec { + let text = match std::str::from_utf8(data) { + Ok(s) => s, + Err(_) => return vec![], + }; + + let mut result = Vec::new(); + + // Split on top-level `{` … `}` objects. + // The JSON is a flat array of objects with no nested arrays of objects, + // so a simple brace-depth scan is safe here. + for obj in split_objects(text) { + // Skip unmapped or hidden windows. + if json_bool(obj, "mapped") != Some(true) { continue; } + if json_bool(obj, "hidden") == Some(true) { continue; } + + // Only windows on the active workspace. + if json_i64_nested(obj, "workspace", "id") != Some(workspace_id) { continue; } + + let at = json_pair_i64(obj, "at"); + let size = json_pair_i64(obj, "size"); + + let (Some((x, y)), Some((w, h))) = (at, size) else { continue }; + if w <= 0 || h <= 0 { continue; } + + let title = json_string(obj, "title").unwrap_or_default(); + + result.push(WindowRect { x, y, width: w, height: h, title }); + } + + result +} + +// ─── Tiny JSON field extractors ─────────────────────────────────────────────── + +/// Split a JSON array text into individual object strings. +fn split_objects(text: &str) -> Vec<&str> { + let mut objects = Vec::new(); + let bytes = text.as_bytes(); + let mut depth = 0i32; + let mut start = None; + let mut in_string = false; + let mut escape = false; + + for (i, &b) in bytes.iter().enumerate() { + if escape { escape = false; continue; } + if b == b'\\' && in_string { escape = true; continue; } + if b == b'"' { in_string = !in_string; continue; } + if in_string { continue; } + + match b { + b'{' => { + if depth == 0 { start = Some(i); } + depth += 1; + } + b'}' => { + depth -= 1; + if depth == 0 { + if let Some(s) = start { + objects.push(&text[s..=i]); + } + start = None; + } + } + _ => {} + } + } + objects +} + +/// Extract `"key": ` from a JSON object string. +fn json_i64(text: &str, key: &str) -> Option { + let needle = format!("\"{}\"", key); + let pos = text.find(&needle)?; + let after = text[pos + needle.len()..].trim_start(); + let after = after.strip_prefix(':')?.trim_start(); + let end = after.find(|c: char| !c.is_ascii_digit() && c != '-').unwrap_or(after.len()); + after[..end].parse().ok() +} + +/// Extract `"key": ` from a JSON object string. +fn json_bool(text: &str, key: &str) -> Option { + let needle = format!("\"{}\"", key); + let pos = text.find(&needle)?; + let after = text[pos + needle.len()..].trim_start(); + let after = after.strip_prefix(':')?.trim_start(); + if after.starts_with("true") { return Some(true); } + if after.starts_with("false") { return Some(false); } + None +} + +/// Extract `"key": "string value"` from a JSON object string. +fn json_string<'a>(text: &'a str, key: &str) -> Option { + let needle = format!("\"{}\"", key); + let pos = text.find(&needle)?; + let after = text[pos + needle.len()..].trim_start(); + let after = after.strip_prefix(':')?.trim_start(); + let after = after.strip_prefix('"')?; + // Collect until unescaped closing quote. + let mut out = String::new(); + let mut chars = after.chars(); + loop { + match chars.next()? { + '\\' => { chars.next(); } // skip escaped char + '"' => break, + c => out.push(c), + } + } + Some(out) +} + +/// Extract `"key": [a, b]` → (a, b) as i64 pair. +fn json_pair_i64(text: &str, key: &str) -> Option<(i32, i32)> { + let needle = format!("\"{}\"", key); + let pos = text.find(&needle)?; + let after = text[pos + needle.len()..].trim_start(); + let after = after.strip_prefix(':')?.trim_start(); + let after = after.strip_prefix('[')?; + let end = after.find(']')?; + let inner = &after[..end]; + let mut parts = inner.split(','); + let a: i32 = parts.next()?.trim().parse().ok()?; + let b: i32 = parts.next()?.trim().parse().ok()?; + Some((a, b)) +} + +/// Extract `"outer": { "inner_key": }` — one level of nesting. +fn json_i64_nested(text: &str, outer: &str, inner_key: &str) -> Option { + let needle = format!("\"{}\"", outer); + let pos = text.find(&needle)?; + let after = text[pos + needle.len()..].trim_start(); + let after = after.strip_prefix(':')?.trim_start(); + let brace_start = after.find('{')?; + let brace_end = after.find('}')?; + let nested = &after[brace_start..=brace_end]; + json_i64(nested, inner_key) +} + +/// Extract `"key": ` from a JSON object string. +fn json_f32(text: &str, key: &str) -> Option { + let needle = format!("\"{}\"", key); + let pos = text.find(&needle)?; + let after = text[pos + needle.len()..].trim_start(); + let after = after.strip_prefix(':')?.trim_start(); + let end = after + .find(|c: char| !c.is_ascii_digit() && c != '.' && c != '-') + .unwrap_or(after.len()); + after[..end].parse().ok() +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..19e1dfd --- /dev/null +++ b/src/main.rs @@ -0,0 +1,180 @@ +//! rs-pictures — Wayland screenshot tool +//! +//! Flow: +//! 1. Load config from ~/.config/rs-pictures/config.toml +//! 2. Sleep briefly so the user can switch away from the terminal that +//! launched us, and the compositor has time to repaint. +//! 3. Capture all outputs → frozen desktop snapshot for overlay background. +//! 4. Open fullscreen selection overlay (drag / two-click rubber-band). +//! 5. Sleep 120 ms so compositor repaints after overlay closes. +//! 6. Capture the selected region with libwayshot. +//! 7a. auto_save/auto_copy set → apply effects, act silently, exit. +//! 7b. Otherwise → open review window (effects applied interactively there). + +mod capture; +mod config; +mod effects; +mod hyprland; +mod overlay; +mod review; + +use anyhow::{Context, Result}; +use arboard::{Clipboard, ImageData}; +use eframe::egui; +use overlay::{SelectionOverlay, SelectionResult}; +use review::ReviewWindow; + +fn main() -> Result<()> { + // ── 1. Load config ──────────────────────────────────────────────────────── + let config = config::Config::load().context("Failed to load config")?; + + // ── 2. Query Hyprland metadata (no window open yet) ─────────────────────── + // Scale is only needed for window-rect conversion in the overlay. + let scale = hyprland::active_monitor_scale(); + + // ── 3. Pre-capture delay ────────────────────────────────────────────────── + // Give the compositor time to unmap the terminal/launcher that started us + // and repaint the desktop before we freeze it. + // Configurable via capture_delay_ms in config.toml (default 800ms). + std::thread::sleep(std::time::Duration::from_millis(config.capture_delay_ms)); + + // ── 4. Capture full desktop BEFORE opening any window ──────────────────── + // This must happen before eframe::run_native is called. eframe maps its + // window immediately on entry — before our first update() frame — causing + // a white rectangle to appear in the snapshot if we capture any later. + let background_snapshot = + capture::capture_all_outputs().context("Failed to capture desktop snapshot")?; + + // ── 5. Run the selection overlay ───────────────────────────────────────── + let selection_result = { + use std::sync::{Arc, Mutex}; + let shared: Arc>> = Arc::new(Mutex::new(None)); + let shared_clone = Arc::clone(&shared); + + let native_options = eframe::NativeOptions { + viewport: egui::ViewportBuilder::default() + .with_fullscreen(true) + .with_decorations(false) + .with_transparent(true) + .with_always_on_top() + .with_active(false) + .with_resizable(false), + ..Default::default() + }; + + let bg_clone = background_snapshot.clone(); + eframe::run_native( + "rs-pictures — Select Region", + native_options, + Box::new(move |cc| { + let app = SelectionOverlay::new(cc, bg_clone, scale); + Ok(Box::new(OverlayWrapper { + inner: app, + result_sink: shared_clone, + }) as Box) + }), + ) + .map_err(|e| anyhow::anyhow!("Overlay window error: {e}"))?; + + Arc::try_unwrap(shared) + .ok() + .and_then(|m| m.into_inner().ok()) + .flatten() + }; + + // ── 6. Act on the selection ─────────────────────────────────────────────── + let region = match selection_result { + Some(SelectionResult::Selected(r)) => r, + Some(SelectionResult::Cancelled) | None => { + eprintln!("Selection cancelled."); + return Ok(()); + } + }; + + // ── 7. Crop the selected region from the original snapshot ──────────────── + let raw_image = { + let px = (region.x.max(0) as u32).min(background_snapshot.width().saturating_sub(1)); + let py = (region.y.max(0) as u32).min(background_snapshot.height().saturating_sub(1)); + let pw = region.width.min(background_snapshot.width() - px); + let ph = region.height.min(background_snapshot.height() - py); + image::imageops::crop_imm(&background_snapshot, px, py, pw, ph).to_image() + }; + + // ── 8a. Auto-mode — no review window ───────────────────────────────────── + if config.auto_save || config.auto_copy { + let final_image = effects::apply_effects(raw_image, &config.effects); + if config.auto_save { + let path = config.output_path(); + if let Some(parent) = path.parent() { + std::fs::create_dir_all(parent) + .with_context(|| format!("Cannot create dir {}", parent.display()))?; + } + final_image.save(&path) + .with_context(|| format!("Failed to save to {}", path.display()))?; + eprintln!("Saved to {}", path.display()); + } + if config.auto_copy { + clipboard_copy(&final_image)?; + eprintln!("Copied to clipboard."); + } + return Ok(()); + } + + // ── 8b. Review window ───────────────────────────────────────────────────── + // The review window applies effects internally on a background thread, so + // we hand it the raw (un-effected) image. + let native_options = eframe::NativeOptions { + viewport: egui::ViewportBuilder::default() + .with_title("rs-pictures — Review") + .with_inner_size([900.0, 700.0]) + .with_min_inner_size([400.0, 300.0]), + ..Default::default() + }; + + eframe::run_native( + "rs-pictures — Review", + native_options, + Box::new(move |cc| { + Ok(Box::new(ReviewWindow::new(cc, raw_image, config)) as Box) + }), + ) + .map_err(|e| anyhow::anyhow!("Review window error: {e}"))?; + + Ok(()) +} + +// ─── Clipboard helper (shared with review.rs logic) ─────────────────────────── + +pub fn clipboard_copy(img: &image::RgbaImage) -> Result<()> { + let (w, h) = img.dimensions(); + let bytes = img.as_raw().clone(); + let mut cb = Clipboard::new().context("Could not open clipboard")?; + cb.set_image(ImageData { + width: w as usize, + height: h as usize, + bytes: bytes.into(), + }) + .context("Failed to write image to clipboard") +} + +// ─── Overlay wrapper ────────────────────────────────────────────────────────── + +use std::sync::{Arc, Mutex}; + +struct OverlayWrapper { + inner: SelectionOverlay, + result_sink: Arc>>, +} + +impl eframe::App for OverlayWrapper { + fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) { + self.inner.update(ctx, frame); + if let Some(result) = self.inner.take_result() { + *self.result_sink.lock().unwrap() = Some(result); + } + } + + fn clear_color(&self, visuals: &egui::Visuals) -> [f32; 4] { + self.inner.clear_color(visuals) + } +} diff --git a/src/overlay.rs b/src/overlay.rs new file mode 100644 index 0000000..5ab3522 --- /dev/null +++ b/src/overlay.rs @@ -0,0 +1,358 @@ +//! Region selection overlay. +//! +//! Input modes (all available simultaneously): +//! - Window pick: hover over a window → it highlights; single click captures it. +//! - Drag: press and drag → rubber-band selection (overrides window pick). +//! - Two-click: click once (anchor), move, click again → selection. +//! +//! Coordinate system: +//! egui screen_rect == physical pixels of the monitor. +//! capture::Region also uses physical pixels. +//! Window rects from hyprctl are logical pixels — multiplied by scale to get physical. +//! No scale division anywhere: Region handed to capture.rs is in physical pixels, +//! and capture.rs crops from the physical full-screen capture directly. + +use eframe::egui::{self, Color32, CursorIcon, Pos2, Rect, Rounding, Stroke, Vec2}; +use image::RgbaImage; + +use crate::capture::Region; +use crate::hyprland::WindowRect; + +/// Result returned when the user completes or cancels the selection. +#[derive(Debug)] +pub enum SelectionResult { + Selected(Region), + Cancelled, +} + +/// Input state machine. +#[derive(Default)] +enum SelectionState { + #[default] + Idle, + Dragging { start: Pos2 }, + AwaitingSecondClick { start: Pos2 }, + Done { start: Pos2, end: Pos2 }, + Cancelled, +} + +const MAX_TEX: u32 = 2048; + +pub struct SelectionOverlay { + background: egui::TextureHandle, + /// Hyprland monitor scale (logical→physical). Used only for window rect conversion. + scale: f32, + state: SelectionState, + result: Option, + windows: Vec, + hovered_window: Option, + diag_printed: bool, +} + +impl SelectionOverlay { + pub fn new(cc: &eframe::CreationContext<'_>, background_snapshot: RgbaImage, scale: f32) -> Self { + let tex_image = fit_to_max_texture(background_snapshot); + let (tw, th) = tex_image.dimensions(); + let color_image = egui::ColorImage::from_rgba_unmultiplied( + [tw as usize, th as usize], + tex_image.as_raw(), + ); + let texture = + cc.egui_ctx + .load_texture("background", color_image, egui::TextureOptions::LINEAR); + + let windows = crate::hyprland::active_workspace_windows(); + + Self { + background: texture, + scale, + state: SelectionState::default(), + result: None, + windows, + hovered_window: None, + diag_printed: false, + } + } + + pub fn take_result(&mut self) -> Option { + self.result.take() + } + + /// Convert a hyprctl WindowRect (logical px) to an egui Rect (logical points). + fn window_to_egui_rect(win: &WindowRect, _scale: f32) -> Rect { + Rect::from_min_size( + Pos2::new(win.x as f32, win.y as f32), + Vec2::new(win.width as f32, win.height as f32), + ) + } +} + +impl eframe::App for SelectionOverlay { + fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { + ctx.set_cursor_icon(CursorIcon::Crosshair); + + if ctx.input(|i| i.key_pressed(egui::Key::Escape)) { + self.state = SelectionState::Cancelled; + } + + let screen_rect = ctx.screen_rect(); + + if !self.diag_printed { + self.diag_printed = true; + eprintln!( + "[overlay diag] screen_rect={screen_rect:?} ppp={} scale={}", + ctx.pixels_per_point(), + self.scale, + ); + } + + let dim = Color32::from_black_alpha(140); + + const DRAG_THRESHOLD: f32 = 4.0; + + let (hover_pos, press_origin, primary_down, primary_released) = + ctx.input(|i| { + ( + i.pointer.hover_pos(), + i.pointer.press_origin(), + i.pointer.primary_down(), + i.pointer.primary_released(), + ) + }); + + let travel: f32 = match (press_origin, hover_pos) { + (Some(o), Some(p)) => o.distance(p), + _ => 0.0, + }; + let is_click = primary_released && travel <= DRAG_THRESHOLD; + let is_drag = primary_down && travel > DRAG_THRESHOLD; + + // ── Window hover detection ──────────────────────────────────────────── + self.hovered_window = None; + if matches!(self.state, SelectionState::Idle) { + if let Some(pos) = hover_pos { + for (i, win) in self.windows.iter().enumerate().rev() { + if Self::window_to_egui_rect(win, self.scale).contains(pos) { + self.hovered_window = Some(i); + break; + } + } + } + } + + // ── State transitions ───────────────────────────────────────────────── + match &self.state { + SelectionState::Idle => { + if is_drag { + self.hovered_window = None; + self.state = SelectionState::Dragging { + start: press_origin.unwrap_or_default(), + }; + } else if is_click { + if let Some(idx) = self.hovered_window { + let win = &self.windows[idx]; + let rect = Self::window_to_egui_rect(win, self.scale).intersect(screen_rect); + self.state = SelectionState::Done { + start: rect.min, + end: rect.max, + }; + } else { + self.state = SelectionState::AwaitingSecondClick { + start: press_origin.unwrap_or_default(), + }; + } + } + } + SelectionState::Dragging { start } => { + let start = *start; + if primary_released { + if let Some(end) = hover_pos { + self.state = SelectionState::Done { start, end }; + } else { + self.state = SelectionState::Idle; + } + } + if !primary_down && !primary_released { + self.state = SelectionState::AwaitingSecondClick { start }; + } + } + SelectionState::AwaitingSecondClick { start } => { + let start = *start; + if is_click { + if let Some(end) = hover_pos { + self.state = SelectionState::Done { start, end }; + } + } + } + SelectionState::Done { .. } | SelectionState::Cancelled => {} + } + + // ── Current live rect ───────────────────────────────────────────────── + let current_rect: Option = match &self.state { + SelectionState::Dragging { start } => hover_pos.map(|p| Rect::from_two_pos(*start, p)), + SelectionState::AwaitingSecondClick { start } => hover_pos.map(|p| Rect::from_two_pos(*start, p)), + SelectionState::Done { start, end } => Some(Rect::from_two_pos(*start, *end)), + _ => None, + }; + + // ── Draw ────────────────────────────────────────────────────────────── + egui::CentralPanel::default() + .frame(egui::Frame::none()) + .show(ctx, |ui| { + let painter = ui.painter(); + + // 1. Background screenshot. + painter.image( + self.background.id(), + screen_rect, + Rect::from_min_max(Pos2::ZERO, Pos2::new(1.0, 1.0)), + Color32::WHITE, + ); + + // 2. Dim overlay. + let active_rect = current_rect.or_else(|| { + self.hovered_window + .map(|i| Self::window_to_egui_rect(&self.windows[i], self.scale).intersect(screen_rect)) + }); + + if let Some(sel) = active_rect { + let s = sel.intersect(screen_rect); + painter.rect_filled( + Rect::from_min_max(screen_rect.min, Pos2::new(screen_rect.max.x, s.min.y)), + Rounding::ZERO, dim, + ); + painter.rect_filled( + Rect::from_min_max(Pos2::new(screen_rect.min.x, s.max.y), screen_rect.max), + Rounding::ZERO, dim, + ); + painter.rect_filled( + Rect::from_min_max( + Pos2::new(screen_rect.min.x, s.min.y), + Pos2::new(s.min.x, s.max.y), + ), + Rounding::ZERO, dim, + ); + painter.rect_filled( + Rect::from_min_max( + Pos2::new(s.max.x, s.min.y), + Pos2::new(screen_rect.max.x, s.max.y), + ), + Rounding::ZERO, dim, + ); + + let border_color = if self.hovered_window.is_some() && current_rect.is_none() { + Color32::from_rgb(255, 190, 50) + } else { + Color32::from_rgb(100, 180, 255) + }; + painter.rect_stroke(s, Rounding::ZERO, Stroke::new(1.5, border_color)); + + // Size label in physical pixels. + let label = format!("{} × {}", s.width().round() as u32, s.height().round() as u32); + let lp = Pos2::new(s.min.x + 4.0, s.min.y - 18.0) + .clamp(Pos2::ZERO, screen_rect.max); + painter.text(lp, egui::Align2::LEFT_TOP, label, + egui::FontId::monospace(13.0), Color32::WHITE); + + if let Some(idx) = self.hovered_window { + if current_rect.is_none() { + let title = &self.windows[idx].title; + if !title.is_empty() { + painter.text( + Pos2::new(s.min.x + 4.0, s.min.y + 4.0), + egui::Align2::LEFT_TOP, + title, + egui::FontId::proportional(12.0), + Color32::from_rgba_unmultiplied(255, 190, 50, 220), + ); + } + } + } + } else { + painter.rect_filled(screen_rect, Rounding::ZERO, dim); + } + + // 3. Crosshair. + if let Some(pos) = hover_pos { + let s = Stroke::new(1.0, Color32::from_white_alpha(180)); + painter.line_segment( + [Pos2::new(screen_rect.min.x, pos.y), Pos2::new(screen_rect.max.x, pos.y)], s); + painter.line_segment( + [Pos2::new(pos.x, screen_rect.min.y), Pos2::new(pos.x, screen_rect.max.y)], s); + } + + // 4. Hint text. + let hint = match &self.state { + SelectionState::Idle if self.hovered_window.is_some() => + "Click to capture window | Drag for custom selection | Esc to cancel", + SelectionState::Idle => + "Click or drag to select | Esc to cancel", + SelectionState::Dragging { .. } => "Release to capture", + SelectionState::AwaitingSecondClick { .. } => + "Click to set the second corner | Esc to cancel", + _ => "", + }; + if !hint.is_empty() { + painter.text( + Pos2::new(screen_rect.center().x, screen_rect.max.y - 28.0), + egui::Align2::CENTER_BOTTOM, + hint, + egui::FontId::proportional(14.0), + Color32::from_white_alpha(200), + ); + } + }); + + // ── Resolve ─────────────────────────────────────────────────────────── + match &self.state { + SelectionState::Done { start, end } => { + let rect = Rect::from_two_pos(*start, *end).intersect(screen_rect); + if rect.width() > 2.0 && rect.height() > 2.0 { + let ppp = ctx.pixels_per_point(); + // egui coords are logical points — scale to physical pixels. + let x = ((rect.min.x - screen_rect.min.x) * ppp).round() as i32; + let y = ((rect.min.y - screen_rect.min.y) * ppp).round() as i32; + let width = (rect.width() * ppp).round() as u32; + let height = (rect.height() * ppp).round() as u32; + + eprintln!("[overlay] physical x={x} y={y} w={width} h={height}"); + + self.result = Some(SelectionResult::Selected(Region { + x: x.max(0), + y: y.max(0), + width, + height, + })); + ctx.send_viewport_cmd(egui::ViewportCommand::Close); + } else { + self.state = SelectionState::Idle; + } + } + SelectionState::Cancelled => { + self.result = Some(SelectionResult::Cancelled); + ctx.send_viewport_cmd(egui::ViewportCommand::Close); + } + _ => {} + } + + ctx.request_repaint_after(std::time::Duration::from_millis(16)); + } + + fn clear_color(&self, _visuals: &egui::Visuals) -> [f32; 4] { + [0.0, 0.0, 0.0, 0.0] + } +} + +fn fit_to_max_texture(img: RgbaImage) -> RgbaImage { + let (w, h) = img.dimensions(); + if w <= MAX_TEX && h <= MAX_TEX { + return img; + } + let scale = (MAX_TEX as f32 / w as f32).min(MAX_TEX as f32 / h as f32); + image::imageops::resize( + &img, + (w as f32 * scale) as u32, + (h as f32 * scale) as u32, + image::imageops::FilterType::Triangle, + ) +} diff --git a/src/review.rs b/src/review.rs new file mode 100644 index 0000000..f8ec881 --- /dev/null +++ b/src/review.rs @@ -0,0 +1,345 @@ +//! After-capture review window. +//! +//! Performance design: +//! - Effects (rounded corners, drop shadow) run on a background thread so the +//! UI never blocks. A `Receiver` is polled each frame; when the result +//! arrives the texture is swapped out. +//! - A debounce timer (`dirty_since`) ensures we only spawn a new worker 150 ms +//! after the last slider change, not on every incremental drag tick. +//! - The raw image is wrapped in Arc so it is shared with worker threads +//! without cloning the pixel data. +//! - Texture uploads are guarded by MAX_TEX so we never panic on large images. + +use std::path::PathBuf; +use std::sync::{Arc, mpsc}; +use std::time::{Duration, Instant}; + +use arboard::{Clipboard, ImageData}; +use eframe::egui::{self, Color32, ColorImage, Rounding, ScrollArea, Stroke, TextureHandle, TextureOptions, Vec2}; +use image::RgbaImage; + +use crate::{config::Config, effects::apply_effects}; + +const MAX_TEX: u32 = 2048; +/// How long to wait after the last setting change before spawning the worker. +const DEBOUNCE: Duration = Duration::from_millis(150); + +#[derive(Debug)] +pub enum ReviewAction { + #[allow(dead_code)] // path stored for future use (e.g. desktop notification) + Saved(PathBuf), + Copied, + Discarded, +} + +/// Channel message from the background effects worker. +struct EffectsResult(RgbaImage); + +pub struct ReviewWindow { + /// Full-resolution raw capture — shared with worker threads via Arc. + raw_image: Arc, + /// Last fully-processed preview (what gets saved/copied). + preview_image: Arc, + /// GPU texture (may be downscaled for display). + preview_texture: TextureHandle, + pub config: Config, + save_as_path: String, + status_message: Option, + pub action: Option, + settings_open: bool, + + /// Set when settings change; cleared when a worker is spawned. + dirty_since: Option, + /// Receives the processed image from the background worker. + worker_rx: Option>, + /// True while a worker is running. + worker_running: bool, +} + +impl ReviewWindow { + pub fn new(cc: &eframe::CreationContext<'_>, raw_image: RgbaImage, config: Config) -> Self { + let raw = Arc::new(raw_image); + let preview = Arc::new(apply_effects((*raw).clone(), &config.effects)); + let texture = upload_texture(&cc.egui_ctx, &preview); + let save_as_path = config.output_path().display().to_string(); + Self { + raw_image: raw, + preview_image: preview, + preview_texture: texture, + config, + save_as_path, + status_message: None, + action: None, + settings_open: false, + dirty_since: None, + worker_rx: None, + worker_running: false, + } + } + + /// Mark settings as changed. A worker will be spawned after the debounce. + fn mark_dirty(&mut self) { + // Only reset the timer if we're not already waiting (avoids pushing + // the debounce out indefinitely on fast slider drag). + if self.dirty_since.is_none() { + self.dirty_since = Some(Instant::now()); + } + } + + /// Poll for a finished worker result and/or spawn a new one if due. + fn tick_effects(&mut self, ctx: &egui::Context) { + // 1. Check if the running worker is done. + if let Some(rx) = &self.worker_rx { + if let Ok(EffectsResult(img)) = rx.try_recv() { + let img = Arc::new(img); + self.preview_texture = upload_texture(ctx, &img); + self.preview_image = img; + self.worker_rx = None; + self.worker_running = false; + } + } + + // 2. Spawn a new worker if debounce has elapsed and none is running. + if let Some(since) = self.dirty_since { + if !self.worker_running && since.elapsed() >= DEBOUNCE { + self.dirty_since = None; + self.worker_running = true; + + let raw = Arc::clone(&self.raw_image); + let effects_cfg = self.config.effects.clone(); + let (tx, rx) = mpsc::channel(); + let ctx_clone = ctx.clone(); + + std::thread::spawn(move || { + let result = apply_effects((*raw).clone(), &effects_cfg); + let _ = tx.send(EffectsResult(result)); + // Wake the egui event loop so the new texture is picked up. + ctx_clone.request_repaint(); + }); + + self.worker_rx = Some(rx); + } + + // Keep repainting while waiting for the debounce to fire. + if self.worker_running || self.dirty_since.is_some() { + ctx.request_repaint_after(Duration::from_millis(50)); + } + } + } + + fn copy_to_clipboard(&mut self) { + match Clipboard::new() { + Ok(mut cb) => { + let (w, h) = self.preview_image.dimensions(); + let bytes = self.preview_image.as_raw().clone(); + match cb.set_image(ImageData { width: w as usize, height: h as usize, bytes: bytes.into() }) { + Ok(_) => { + self.status_message = Some("Copied to clipboard.".into()); + self.action = Some(ReviewAction::Copied); + } + Err(e) => self.status_message = Some(format!("Clipboard error: {e}")), + } + } + Err(e) => self.status_message = Some(format!("Could not open clipboard: {e}")), + } + } + + fn save_to_path(&mut self, path: PathBuf) { + if let Some(parent) = path.parent() { + if let Err(e) = std::fs::create_dir_all(parent) { + self.status_message = Some(format!("Could not create directory: {e}")); + return; + } + } + match self.preview_image.save(&path) { + Ok(_) => { + self.status_message = Some(format!("Saved to {}", path.display())); + self.action = Some(ReviewAction::Saved(path)); + } + Err(e) => self.status_message = Some(format!("Save error: {e}")), + } + } +} + +impl eframe::App for ReviewWindow { + fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { + self.tick_effects(ctx); + + // ── Global keybinds ─────────────────────────────────────────────────── + // Ctrl+C — copy to clipboard immediately. + // Checked before any panel so it works regardless of widget focus. + if ctx.input_mut(|i| i.consume_key(egui::Modifiers::CTRL, egui::Key::C)) { + self.copy_to_clipboard(); + } + + // ── Top bar ─────────────────────────────────────────────────────────── + egui::TopBottomPanel::top("actions").show(ctx, |ui| { + ui.add_space(6.0); + ui.horizontal(|ui| { + if ui.button("📋 Copy").clicked() { + self.copy_to_clipboard(); + } + if ui.button("💾 Save").clicked() { + let path = self.config.output_path(); + self.save_to_path(path); + } + ui.separator(); + ui.label("Save As:"); + ui.add( + egui::TextEdit::singleline(&mut self.save_as_path) + .desired_width(300.0) + .hint_text("/home/user/Pictures/shot.png"), + ); + if ui.button("Save").clicked() { + let path = PathBuf::from(&self.save_as_path); + self.save_to_path(path); + } + ui.separator(); + let label = if self.settings_open { "▲ Effects" } else { "▼ Effects" }; + if ui.button(label).clicked() { + self.settings_open = !self.settings_open; + } + // Spinner while worker is active. + if self.worker_running { + ui.spinner(); + } + ui.separator(); + if ui.button("✖ Discard").clicked() { + self.action = Some(ReviewAction::Discarded); + ctx.send_viewport_cmd(egui::ViewportCommand::Close); + } + }); + ui.add_space(4.0); + }); + + // ── Effects panel ───────────────────────────────────────────────────── + if self.settings_open { + egui::TopBottomPanel::top("settings").show(ctx, |ui| { + ui.add_space(6.0); + ui.heading("Effects"); + ui.separator(); + + let e = &mut self.config.effects; + let mut changed = false; + + ui.horizontal(|ui| { + changed |= ui.checkbox(&mut e.rounded_corners, "Rounded corners").changed(); + if e.rounded_corners { + ui.label("Radius:"); + changed |= ui + .add(egui::Slider::new(&mut e.corner_radius, 1.0..=64.0).suffix(" px")) + .changed(); + } + }); + + ui.horizontal(|ui| { + changed |= ui.checkbox(&mut e.drop_shadow, "Drop shadow").changed(); + if e.drop_shadow { + ui.label("Blur:"); + changed |= ui + .add(egui::Slider::new(&mut e.shadow_blur_radius, 0.0..=60.0).suffix(" px")) + .changed(); + ui.label("X:"); + changed |= ui + .add(egui::Slider::new(&mut e.shadow_offset_x, -40.0..=40.0).suffix(" px")) + .changed(); + ui.label("Y:"); + changed |= ui + .add(egui::Slider::new(&mut e.shadow_offset_y, -40.0..=40.0).suffix(" px")) + .changed(); + } + }); + + if changed { + self.mark_dirty(); + let _ = self.config.save(); + } + ui.add_space(4.0); + }); + } + + // ── Status bar ──────────────────────────────────────────────────────── + if let Some(msg) = self.status_message.clone() { + egui::TopBottomPanel::bottom("status").show(ctx, |ui| { + ui.add_space(4.0); + ui.horizontal(|ui| { + ui.label(egui::RichText::new(&msg).color(Color32::LIGHT_GREEN)); + if ui.small_button("✖").clicked() { + self.status_message = None; + } + }); + ui.add_space(4.0); + }); + } + + // ── Preview ─────────────────────────────────────────────────────────── + egui::CentralPanel::default().show(ctx, |ui| { + ScrollArea::both().show(ui, |ui| { + let tex_size = self.preview_texture.size_vec2(); + let available = ui.available_size(); + let scale = (available.x / tex_size.x) + .min(available.y / tex_size.y) + .min(1.0); + let display_size = tex_size * scale; + let img_rect = ui.allocate_space(display_size).1; + + draw_checkerboard(ui.painter(), img_rect); + ui.painter().image( + self.preview_texture.id(), + img_rect, + egui::Rect::from_min_max(egui::Pos2::ZERO, egui::Pos2::new(1.0, 1.0)), + Color32::WHITE, + ); + ui.painter().rect_stroke( + img_rect, + Rounding::ZERO, + Stroke::new(1.0, Color32::from_gray(80)), + ); + }); + }); + + // Close after save/copy — give one extra frame so the status message + // is visible for a moment. + if let Some(ReviewAction::Saved(_) | ReviewAction::Copied) = &self.action { + ctx.send_viewport_cmd(egui::ViewportCommand::Close); + } + } +} + +// ─── Helpers ───────────────────────────────────────────────────────────────── + +fn upload_texture(ctx: &egui::Context, img: &RgbaImage) -> TextureHandle { + let (w, h) = img.dimensions(); + let scaled; + let src: &RgbaImage = if w > MAX_TEX || h > MAX_TEX { + let s = (MAX_TEX as f32 / w as f32).min(MAX_TEX as f32 / h as f32); + scaled = image::imageops::resize( + img, + (w as f32 * s) as u32, + (h as f32 * s) as u32, + image::imageops::FilterType::Triangle, + ); + &scaled + } else { + img + }; + let (uw, uh) = src.dimensions(); + let ci = ColorImage::from_rgba_unmultiplied([uw as usize, uh as usize], src.as_raw()); + ctx.load_texture("preview", ci, TextureOptions::LINEAR) +} + +fn draw_checkerboard(painter: &egui::Painter, rect: egui::Rect) { + let tile = 8.0_f32; + let c0 = Color32::from_gray(200); + let c1 = Color32::from_gray(160); + let cols = (rect.width() / tile).ceil() as u32; + let rows = (rect.height() / tile).ceil() as u32; + for row in 0..rows { + for col in 0..cols { + let color = if (row + col) % 2 == 0 { c0 } else { c1 }; + let min = rect.min + Vec2::new(col as f32 * tile, row as f32 * tile); + let max = (min + Vec2::splat(tile)).min(rect.max); + painter.rect_filled(egui::Rect::from_min_max(min, max), Rounding::ZERO, color); + } + } +} diff --git a/test_viewport.rs b/test_viewport.rs new file mode 100644 index 0000000..785dce5 --- /dev/null +++ b/test_viewport.rs @@ -0,0 +1,3 @@ +fn main() { + let _ = eframe::egui::ViewportBuilder::default().with_active(false); +}