From b7773e1713c3949e10d9aa28c0c3c29ba3dc497b Mon Sep 17 00:00:00 2001 From: Mauritius Clemens Date: Tue, 12 Aug 2025 13:52:55 +0200 Subject: [PATCH] Implement complete screenshot functionality with multi-backend support - Create libcosmic GUI - Add KWin ScreenShot2 and Freedesktop Portal screenshot backends - Implement interactive region selection with fullscreen snipper interface - Add D-Bus service interface for non-interactive screenshots - Include CLI with all screenshot types and backend selection - Add persistent settings management with cosmic-config integration - Implement comprehensive error handling with GUI dialogs and notifications - Add library API for integration with other applications - Update/add desktop integration files --- Cargo.lock | 6793 ++++++++++++++--- Cargo.toml | 60 +- README.md | 299 +- build.rs | 10 + debian/control | 58 +- debian/copyright | 25 +- debian/cosmic-screenshot.install | 5 + debian/libcosmic-screenshot-dev.install | 2 + justfile | 13 + .../com.system76.CosmicScreenshot.desktop | 64 +- .../com.system76.CosmicScreenshot.service | 4 + resources/com.system76.CosmicScreenshot.xml | 135 + rustfmt.toml | 1 + src/app.rs | 484 ++ src/dbus.rs | 311 + src/error_handling.rs | 193 + src/lib.rs | 30 + src/main.rs | 482 +- src/notifications.rs | 183 + src/screenshot.rs | 287 + src/screenshot/freedesktop_portal.rs | 163 + src/screenshot/kwin_screenshot2.rs | 370 + src/screenshot/windows_native.rs | 51 + src/screenshot/xorg_native.rs | 58 + src/settings.rs | 221 + src/snipper.rs | 1006 +++ src/ui.rs | 981 +++ 27 files changed, 11376 insertions(+), 913 deletions(-) create mode 100644 build.rs create mode 100644 debian/cosmic-screenshot.install create mode 100644 debian/libcosmic-screenshot-dev.install create mode 100644 resources/com.system76.CosmicScreenshot.service create mode 100644 resources/com.system76.CosmicScreenshot.xml create mode 100644 rustfmt.toml create mode 100644 src/app.rs create mode 100644 src/dbus.rs create mode 100644 src/error_handling.rs create mode 100644 src/lib.rs create mode 100644 src/notifications.rs create mode 100644 src/screenshot.rs create mode 100644 src/screenshot/freedesktop_portal.rs create mode 100644 src/screenshot/kwin_screenshot2.rs create mode 100644 src/screenshot/windows_native.rs create mode 100644 src/screenshot/xorg_native.rs create mode 100644 src/settings.rs create mode 100644 src/snipper.rs create mode 100644 src/ui.rs diff --git a/Cargo.lock b/Cargo.lock index 2eff10a..f8c9e58 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,31 +1,188 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 + +[[package]] +name = "ab_glyph" +version = "0.2.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e074464580a518d16a7126262fffaaa47af89d4099d4cb403f8ed938ba12ee7d" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser", +] + +[[package]] +name = "ab_glyph_rasterizer" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2187590a23ab1e3df8681afdf0987c48504d80291f002fcdb651f0ef5e25169" + +[[package]] +name = "accesskit" +version = "0.16.0" +source = "git+https://github.com/wash2/accesskit?tag=iced-xdg-surface-0.13#956955342dadab7e588e21be726817fca39510f3" + +[[package]] +name = "accesskit_atspi_common" +version = "0.9.0" +source = "git+https://github.com/wash2/accesskit?tag=iced-xdg-surface-0.13#956955342dadab7e588e21be726817fca39510f3" +dependencies = [ + "accesskit", + "accesskit_consumer", + "atspi-common", + "serde", + "thiserror 1.0.69", + "zvariant 3.15.2", +] + +[[package]] +name = "accesskit_consumer" +version = "0.24.0" +source = "git+https://github.com/wash2/accesskit?tag=iced-xdg-surface-0.13#956955342dadab7e588e21be726817fca39510f3" +dependencies = [ + "accesskit", + "immutable-chunkmap", +] + +[[package]] +name = "accesskit_macos" +version = "0.17.0" +source = "git+https://github.com/wash2/accesskit?tag=iced-xdg-surface-0.13#956955342dadab7e588e21be726817fca39510f3" +dependencies = [ + "accesskit", + "accesskit_consumer", + "objc2 0.5.2", + "objc2-app-kit 0.2.2", + "objc2-foundation 0.2.2", + "once_cell", +] + +[[package]] +name = "accesskit_unix" +version = "0.12.0" +source = "git+https://github.com/wash2/accesskit?tag=iced-xdg-surface-0.13#956955342dadab7e588e21be726817fca39510f3" +dependencies = [ + "accesskit", + "accesskit_atspi_common", + "atspi", + "futures-lite 1.13.0", + "serde", + "tokio", + "tokio-stream", + "zbus 3.15.2", +] + +[[package]] +name = "accesskit_windows" +version = "0.22.0" +source = "git+https://github.com/wash2/accesskit?tag=iced-xdg-surface-0.13#956955342dadab7e588e21be726817fca39510f3" +dependencies = [ + "accesskit", + "accesskit_consumer", + "paste", + "static_assertions", + "windows 0.54.0", +] + +[[package]] +name = "accesskit_winit" +version = "0.22.0" +source = "git+https://github.com/wash2/accesskit?tag=iced-xdg-surface-0.13#956955342dadab7e588e21be726817fca39510f3" +dependencies = [ + "accesskit", + "accesskit_macos", + "accesskit_unix", + "accesskit_windows", + "raw-window-handle", + "winit", +] [[package]] name = "addr2line" -version = "0.21.0" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] [[package]] -name = "adler" -version = "1.0.2" +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 = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "getrandom 0.3.3", + "once_cell", + "version_check", + "zerocopy", +] [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] +[[package]] +name = "aliasable" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" + +[[package]] +name = "aligned-vec" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" +dependencies = [ + "equator", +] + +[[package]] +name = "almost" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3aa2999eb46af81abb65c2d30d446778d7e613b60bbf4e174a027e80f90a3c14" + +[[package]] +name = "android-activity" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" +dependencies = [ + "android-properties", + "bitflags 2.9.1", + "cc", + "cesu8", + "jni", + "jni-sys", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys 0.6.0+11769913", + "num_enum", + "thiserror 1.0.69", +] + +[[package]] +name = "android-properties" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" + [[package]] name = "android-tzdata" version = "0.1.1" @@ -43,68 +200,138 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.7" +version = "0.6.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd2405b3ac1faab2990b74d728624cd9fd115651fcecc7c2d8daf01376275ba" +checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.4" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] name = "anstyle-parse" -version = "0.2.3" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.2" +version = "3.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "once_cell_polyfill", + "windows-sys 0.59.0", +] + +[[package]] +name = "anyhow" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + +[[package]] +name = "apply" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f47b57fc4521e3cae26a4d45b5227f8fadee4c345be0fefd8d5d1711afb8aeb9" + +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + +[[package]] +name = "arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" + +[[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.104", +] + +[[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-raw-xcb-connection" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" + +[[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 = "ashpd" -version = "0.6.8" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac22eda5891cc086690cb6fa10121c0390de0e3b04eb269f2d766b00d3f2d81" +checksum = "6cbdf310d77fd3aaee6ea2093db7011dc2d35d2eb3481e5607f1f8d942ed99df" dependencies = [ "enumflags2", "futures-channel", "futures-util", - "once_cell", - "rand", + "rand 0.9.2", + "raw-window-handle", "serde", "serde_repr", "tokio", "url", - "zbus", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "zbus 5.9.0", ] [[package]] @@ -117,19 +344,44 @@ dependencies = [ "futures-core", ] +[[package]] +name = "async-broadcast" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +dependencies = [ + "event-listener 5.4.1", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + [[package]] name = "async-channel" -version = "2.1.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ca33f4bc4ed1babef42cad36cc1f51fa88be00420404e5b1e80ab1b18f7678c" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" dependencies = [ "concurrent-queue", - "event-listener 4.0.3", "event-listener-strategy", "futures-core", "pin-project-lite", ] +[[package]] +name = "async-executor" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb812ffb58524bdd10860d7d974e2f01cc0950c2438a74ee5ec2e2280c6c4ffa" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand 2.3.0", + "futures-lite 2.6.1", + "pin-project-lite", + "slab", +] + [[package]] name = "async-io" version = "1.13.0" @@ -144,7 +396,7 @@ dependencies = [ "log", "parking", "polling 2.8.0", - "rustix 0.37.27", + "rustix 0.37.28", "slab", "socket2 0.4.10", "waker-fn", @@ -152,21 +404,20 @@ dependencies = [ [[package]] name = "async-io" -version = "2.3.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb41eb19024a91746eba0773aa5e16036045bbf45733766661099e182ea6a744" +checksum = "19634d6336019ef220f09fd31168ce5c184b295cbf80345437cc36094ef223ca" dependencies = [ - "async-lock 3.3.0", + "async-lock 3.4.1", "cfg-if", "concurrent-queue", "futures-io", - "futures-lite 2.2.0", + "futures-lite 2.6.1", "parking", - "polling 3.3.2", - "rustix 0.38.30", + "polling 3.10.0", + "rustix 1.0.8", "slab", - "tracing", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] @@ -180,11 +431,11 @@ dependencies = [ [[package]] name = "async-lock" -version = "3.3.0" +version = "3.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" +checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" dependencies = [ - "event-listener 4.0.3", + "event-listener 5.4.1", "event-listener-strategy", "pin-project-lite", ] @@ -202,54 +453,72 @@ dependencies = [ "cfg-if", "event-listener 3.1.0", "futures-lite 1.13.0", - "rustix 0.38.30", + "rustix 0.38.44", "windows-sys 0.48.0", ] +[[package]] +name = "async-process" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65daa13722ad51e6ab1a1b9c01299142bc75135b337923cfa10e79bbbd669f00" +dependencies = [ + "async-channel", + "async-io 2.5.0", + "async-lock 3.4.1", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener 5.4.1", + "futures-lite 2.6.1", + "rustix 1.0.8", +] + [[package]] name = "async-recursion" -version = "1.0.5" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.104", ] [[package]] name = "async-signal" -version = "0.2.5" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" +checksum = "f567af260ef69e1d52c2b560ce0ea230763e6fbb9214a85d768760a920e3e3c1" dependencies = [ - "async-io 2.3.0", - "async-lock 2.8.0", + "async-io 2.5.0", + "async-lock 3.4.1", "atomic-waker", "cfg-if", "futures-core", "futures-io", - "rustix 0.38.30", + "rustix 1.0.8", "signal-hook-registry", "slab", - "windows-sys 0.48.0", + "windows-sys 0.60.2", ] [[package]] name = "async-task" -version = "4.7.0" +version = "4.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.77" +version = "0.1.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.104", ] [[package]] @@ -259,1365 +528,5697 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +name = "atomicwrites" +version = "0.4.2" +source = "git+https://github.com/jackpot51/rust-atomicwrites#043ab4859d53ffd3d55334685303d8df39c9f768" +dependencies = [ + "rustix 0.38.44", + "tempfile", + "windows-sys 0.48.0", +] [[package]] -name = "backtrace" -version = "0.3.69" +name = "atspi" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "6059f350ab6f593ea00727b334265c4dfc7fd442ee32d264794bd9bdc68e87ca" dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", + "atspi-common", + "atspi-connection", + "atspi-proxies", ] [[package]] -name = "bitflags" -version = "1.3.2" +name = "atspi-common" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "92af95f966d2431f962bc632c2e68eda7777330158bf640c4af4249349b2cdf5" +dependencies = [ + "enumflags2", + "serde", + "static_assertions", + "zbus 3.15.2", + "zbus_names 2.6.1", + "zvariant 3.15.2", +] [[package]] -name = "bitflags" -version = "2.4.1" +name = "atspi-connection" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "a0c65e7d70f86d4c0e3b2d585d9bf3f979f0b19d635a336725a88d279f76b939" +dependencies = [ + "atspi-common", + "atspi-proxies", + "futures-lite 1.13.0", + "zbus 3.15.2", +] [[package]] -name = "block-buffer" -version = "0.10.4" +name = "atspi-proxies" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +checksum = "6495661273703e7a229356dcbe8c8f38223d697aacfaf0e13590a9ac9977bb52" dependencies = [ - "generic-array", + "atspi-common", + "serde", + "zbus 3.15.2", ] [[package]] -name = "blocking" -version = "1.5.1" +name = "auto_enums" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" +checksum = "9c170965892137a3a9aeb000b4524aa3cc022a310e709d848b6e1cdce4ab4781" dependencies = [ - "async-channel", - "async-lock 3.3.0", - "async-task", - "fastrand 2.0.1", - "futures-io", - "futures-lite 2.2.0", - "piper", - "tracing", + "derive_utils", + "proc-macro2", + "quote", + "syn 2.0.104", ] [[package]] -name = "bumpalo" -version = "3.14.0" +name = "autocfg" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] -name = "byteorder" -version = "1.5.0" +name = "av1-grain" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +checksum = "4f3efb2ca85bc610acfa917b5aaa36f3fcbebed5b3182d7f877b02531c4b80c8" +dependencies = [ + "anyhow", + "arrayvec", + "log", + "nom", + "num-rational", + "v_frame", +] [[package]] -name = "bytes" -version = "1.5.0" +name = "avif-serialize" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "2ea8ef51aced2b9191c08197f55450d830876d9933f8f48a429b354f1d496b42" +dependencies = [ + "arrayvec", +] [[package]] -name = "cc" -version = "1.0.83" +name = "backtrace" +version = "0.3.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ + "addr2line", + "cfg-if", "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", ] [[package]] -name = "cfg-if" -version = "1.0.0" +name = "base64" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] -name = "chrono" -version = "0.4.31" +name = "bit-set" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "f0481a0e032742109b1133a095184ee93d88f3dc9e0d28a5d033dc77a073f44f" dependencies = [ - "android-tzdata", - "iana-time-zone", - "num-traits", - "windows-targets 0.48.5", + "bit-vec", ] [[package]] -name = "clap" -version = "4.4.17" +name = "bit-vec" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80932e03c33999b9235edb8655bc9df3204adc9887c2f95b50cb1deb9fd54253" -dependencies = [ - "clap_builder", - "clap_derive", -] +checksum = "d2c54ff287cfc0a34f38a6b832ea1bd8e448a330b3e40a50859e6488bee07f22" [[package]] -name = "clap_builder" -version = "4.4.17" +name = "bit_field" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6c0db58c659eef1c73e444d298c27322a1b52f6927d2ad470c0c0f96fa7b8fa" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" [[package]] -name = "clap_derive" -version = "4.4.7" +name = "bitflags" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.48", -] +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] -name = "clap_lex" -version = "0.6.0" +name = "bitflags" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +dependencies = [ + "serde", +] [[package]] -name = "colorchoice" -version = "1.0.0" +name = "bitstream-io" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2" [[package]] -name = "concurrent-queue" -version = "2.4.0" +name = "block" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" -dependencies = [ - "crossbeam-utils", -] +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" [[package]] -name = "core-foundation-sys" -version = "0.8.6" +name = "block-buffer" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" - -[[package]] -name = "cosmic-screenshot" -version = "0.1.0" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "ashpd", - "chrono", - "clap", - "dirs", - "tokio", - "zbus", + "generic-array", ] [[package]] -name = "cpufeatures" -version = "0.2.12" +name = "block2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" dependencies = [ - "libc", + "objc2 0.5.2", ] [[package]] -name = "crossbeam-utils" -version = "0.8.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" - -[[package]] -name = "crypto-common" -version = "0.1.6" +name = "block2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "340d2f0bdb2a43c1d3cd40513185b2bd7def0aa1052f956455114bc98f82dcf2" dependencies = [ - "generic-array", - "typenum", + "objc2 0.6.1", ] [[package]] -name = "derivative" -version = "2.2.0" +name = "blocking" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "async-channel", + "async-task", + "futures-io", + "futures-lite 2.6.1", + "piper", ] [[package]] -name = "digest" -version = "0.10.7" +name = "built" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] +checksum = "56ed6191a7e78c36abdb16ab65341eefd73d64d303fffccdbb00d51e4205967b" [[package]] -name = "dirs" -version = "5.0.1" +name = "bumpalo" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys", -] +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] -name = "dirs-sys" -version = "0.4.1" +name = "by_address" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.48.0", -] +checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06" [[package]] -name = "enumflags2" -version = "0.7.8" +name = "bytemuck" +version = "1.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5998b4f30320c9d93aed72f63af821bfdac50465b75428fce77b48ec482c3939" +checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" dependencies = [ - "enumflags2_derive", - "serde", + "bytemuck_derive", ] [[package]] -name = "enumflags2_derive" -version = "0.7.8" +name = "bytemuck_derive" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f95e2801cd355d4a1a3e3953ce6ee5ae9603a5c833455343a8bfe3f44d418246" +checksum = "441473f2b4b0459a68628c744bc61d23e730fb00128b841d30fa4bb3972257e4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.104", ] [[package]] -name = "equivalent" -version = "1.0.1" +name = "byteorder" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] -name = "errno" -version = "0.3.8" +name = "byteorder-lite" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] -name = "event-listener" -version = "2.5.3" +name = "bytes" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] -name = "event-listener" -version = "3.1.0" +name = "calloop" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2" +checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", + "bitflags 2.9.1", + "log", + "polling 3.10.0", + "rustix 0.38.44", + "slab", + "thiserror 1.0.69", ] [[package]] -name = "event-listener" -version = "4.0.3" +name = "calloop-wayland-source" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" +checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20" dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", + "calloop", + "rustix 0.38.44", + "wayland-backend", + "wayland-client", ] [[package]] -name = "event-listener-strategy" -version = "0.4.0" +name = "cc" +version = "1.2.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" +checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2" dependencies = [ - "event-listener 4.0.3", - "pin-project-lite", + "jobserver", + "libc", + "shlex", ] [[package]] -name = "fastrand" -version = "1.9.0" +name = "cesu8" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" [[package]] -name = "fastrand" -version = "2.0.1" +name = "cfg-expr" +version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] [[package]] -name = "form_urlencoded" -version = "1.2.1" +name = "cfg-if" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" -dependencies = [ - "percent-encoding", -] +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] -name = "futures-channel" -version = "0.3.30" +name = "cfg_aliases" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" -dependencies = [ - "futures-core", -] +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" [[package]] -name = "futures-core" -version = "0.3.30" +name = "cfg_aliases" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] -name = "futures-io" -version = "0.3.30" +name = "chrono" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link", +] [[package]] -name = "futures-lite" -version = "1.13.0" +name = "clap" +version = "4.5.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +checksum = "ed87a9d530bb41a67537289bafcac159cb3ee28460e0a4571123d2a778a6a882" dependencies = [ - "fastrand 1.9.0", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", + "clap_builder", + "clap_derive", ] [[package]] -name = "futures-lite" -version = "2.2.0" +name = "clap_builder" +version = "4.5.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445ba825b27408685aaecefd65178908c36c6e96aaf6d8599419d46e624192ba" +checksum = "64f4f3f3c77c94aff3c7e9aac9a2ca1974a5adf392a8bb751e827d6d127ab966" dependencies = [ - "futures-core", - "pin-project-lite", + "anstream", + "anstyle", + "clap_lex", + "strsim", ] [[package]] -name = "futures-macro" -version = "0.3.30" +name = "clap_derive" +version = "4.5.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" dependencies = [ + "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.104", ] [[package]] -name = "futures-sink" -version = "0.3.30" +name = "clap_lex" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] -name = "futures-task" -version = "0.3.30" +name = "clipboard-win" +version = "5.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4" +dependencies = [ + "error-code", +] [[package]] -name = "futures-util" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +name = "clipboard_macos" +version = "0.1.0" +source = "git+https://github.com/pop-os/window_clipboard.git?tag=pop-0.13-2#6b9faab87bea9cebec6ae036906fd67fed254f5f" dependencies = [ - "futures-core", - "futures-macro", - "futures-sink", - "futures-task", - "pin-project-lite", - "pin-utils", - "slab", + "objc", + "objc-foundation", + "objc_id", ] [[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +name = "clipboard_wayland" +version = "0.2.2" +source = "git+https://github.com/pop-os/window_clipboard.git?tag=pop-0.13-2#6b9faab87bea9cebec6ae036906fd67fed254f5f" dependencies = [ - "typenum", - "version_check", + "dnd", + "mime", + "smithay-clipboard", ] [[package]] -name = "getrandom" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +name = "clipboard_x11" +version = "0.4.2" +source = "git+https://github.com/pop-os/window_clipboard.git?tag=pop-0.13-2#6b9faab87bea9cebec6ae036906fd67fed254f5f" dependencies = [ - "cfg-if", - "libc", - "wasi", + "thiserror 1.0.69", + "x11rb", ] [[package]] -name = "gimli" -version = "0.28.1" +name = "cocoa" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" +dependencies = [ + "bitflags 1.3.2", + "block", + "cocoa-foundation", + "core-foundation", + "core-graphics", + "foreign-types", + "libc", + "objc", +] [[package]] -name = "hashbrown" -version = "0.14.3" +name = "cocoa-foundation" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" +dependencies = [ + "bitflags 1.3.2", + "block", + "core-foundation", + "core-graphics-types", + "libc", + "objc", +] [[package]] -name = "heck" -version = "0.4.1" +name = "codespan-reporting" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] [[package]] -name = "hermit-abi" -version = "0.3.3" +name = "color_quant" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" [[package]] -name = "hex" -version = "0.4.3" +name = "colorchoice" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] -name = "iana-time-zone" -version = "0.1.59" +name = "com" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" +checksum = "7e17887fd17353b65b1b2ef1c526c83e26cd72e74f598a8dc1bee13a48f3d9f6" dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", + "com_macros", ] [[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" +name = "com_macros" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +checksum = "d375883580a668c7481ea6631fc1a8863e33cc335bf56bfad8d7e6d4b04b13a5" dependencies = [ - "cc", + "com_macros_support", + "proc-macro2", + "syn 1.0.109", ] [[package]] -name = "idna" -version = "0.5.0" +name = "com_macros_support" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "ad899a1087a9296d5644792d7cb72b8e34c1bec8e7d4fbc002230169a6e8710c" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] -name = "indexmap" -version = "2.1.0" +name = "combine" +version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" dependencies = [ - "equivalent", - "hashbrown", + "bytes", + "memchr", ] [[package]] -name = "instant" -version = "0.1.12" +name = "concurrent-queue" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" dependencies = [ - "cfg-if", + "crossbeam-utils", ] [[package]] -name = "io-lifetimes" -version = "1.0.11" +name = "core-foundation" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ - "hermit-abi", + "core-foundation-sys", "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "js-sys" -version = "0.3.67" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" -dependencies = [ - "wasm-bindgen", ] [[package]] -name = "libc" -version = "0.2.152" +name = "core-foundation-sys" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] -name = "libredox" -version = "0.0.1" +name = "core-graphics" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" dependencies = [ - "bitflags 2.4.1", + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types", "libc", - "redox_syscall", ] [[package]] -name = "linux-raw-sys" -version = "0.3.8" +name = "core-graphics-types" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] [[package]] -name = "linux-raw-sys" -version = "0.4.12" +name = "core_maths" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" +checksum = "77745e017f5edba1a9c1d854f6f3a52dac8a12dd5af5d2f54aecf61e43d80d30" +dependencies = [ + "libm", +] [[package]] -name = "log" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +name = "cosmic-client-toolkit" +version = "0.1.0" +source = "git+https://github.com/pop-os/cosmic-protocols?rev=178eb0b#178eb0b14a0e5c192f64f6dee6c40341a8e5ee51" +dependencies = [ + "cosmic-protocols", + "libc", + "smithay-client-toolkit", + "wayland-client", + "wayland-protocols", +] [[package]] -name = "memchr" -version = "2.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +name = "cosmic-config" +version = "0.1.0" +source = "git+https://github.com/pop-os/libcosmic.git#b58d864e85b3b842132e9be1b6445ad21c1f0258" +dependencies = [ + "atomicwrites", + "cosmic-config-derive", + "cosmic-settings-daemon", + "dirs 6.0.0", + "futures-util", + "iced_futures", + "known-folders", + "notify", + "once_cell", + "ron", + "serde", + "tokio", + "tracing", + "xdg", + "zbus 5.9.0", +] [[package]] -name = "memoffset" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +name = "cosmic-config-derive" +version = "0.1.0" +source = "git+https://github.com/pop-os/libcosmic.git#b58d864e85b3b842132e9be1b6445ad21c1f0258" dependencies = [ - "autocfg", + "quote", + "syn 2.0.104", ] [[package]] -name = "memoffset" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +name = "cosmic-freedesktop-icons" +version = "0.3.0" +source = "git+https://github.com/pop-os/freedesktop-icons#8a05c322c482ff3c69cf34bacfee98907ac45307" dependencies = [ - "autocfg", + "dirs 5.0.1", + "ini_core", + "memmap2 0.9.7", + "thiserror 2.0.12", + "tracing", + "xdg", ] [[package]] -name = "miniz_oxide" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +name = "cosmic-protocols" +version = "0.1.0" +source = "git+https://github.com/pop-os/cosmic-protocols?rev=178eb0b#178eb0b14a0e5c192f64f6dee6c40341a8e5ee51" dependencies = [ - "adler", + "bitflags 2.9.1", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-protocols-wlr", + "wayland-scanner", + "wayland-server", ] [[package]] -name = "mio" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +name = "cosmic-screenshot" +version = "0.1.0" dependencies = [ + "ashpd", + "async-trait", + "chrono", + "clap", + "cosmic-config", + "dirs 5.0.1", + "futures-util", + "image", "libc", - "wasi", - "windows-sys 0.48.0", + "libcosmic", + "nix 0.29.0", + "open", + "serde", + "tempfile", + "thiserror 1.0.69", + "tokio", + "vergen", + "zbus 5.9.0", ] [[package]] -name = "nix" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +name = "cosmic-settings-daemon" +version = "0.1.0" +source = "git+https://github.com/pop-os/dbus-settings-bindings#3b86984332be2c930a3536ab714b843c851fa8ca" dependencies = [ - "bitflags 1.3.2", - "cfg-if", - "libc", - "memoffset 0.7.1", + "zbus 5.9.0", ] [[package]] -name = "num-traits" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +name = "cosmic-text" +version = "0.14.2" +source = "git+https://github.com/pop-os/cosmic-text.git#de355a1fd9855e78273d4b7f2b5716eaaa38484f" dependencies = [ - "autocfg", + "bitflags 2.9.1", + "fontdb 0.23.0", + "log", + "rangemap", + "rustc-hash 1.1.0", + "rustybuzz", + "self_cell", + "smol_str", + "swash", + "sys-locale", + "ttf-parser 0.21.1", + "unicode-bidi", + "unicode-linebreak", + "unicode-script", + "unicode-segmentation", ] [[package]] -name = "object" -version = "0.32.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +name = "cosmic-theme" +version = "0.1.0" +source = "git+https://github.com/pop-os/libcosmic.git#b58d864e85b3b842132e9be1b6445ad21c1f0258" dependencies = [ - "memchr", + "almost", + "cosmic-config", + "csscolorparser", + "dirs 6.0.0", + "lazy_static", + "palette", + "ron", + "serde", + "serde_json", + "thiserror 2.0.12", ] [[package]] -name = "once_cell" -version = "1.19.0" +name = "cpufeatures" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] [[package]] -name = "option-ext" -version = "0.2.0" +name = "crc32fast" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] [[package]] -name = "ordered-stream" -version = "0.2.0" +name = "crossbeam-deque" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ - "futures-core", - "pin-project-lite", + "crossbeam-epoch", + "crossbeam-utils", ] [[package]] -name = "parking" -version = "2.2.0" +name = "crossbeam-epoch" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] [[package]] -name = "percent-encoding" -version = "2.3.1" +name = "crossbeam-utils" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] -name = "pin-project-lite" -version = "0.2.13" +name = "crunchy" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] -name = "pin-utils" -version = "0.1.0" +name = "crypto-common" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] [[package]] -name = "piper" -version = "0.2.1" +name = "css-color" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" -dependencies = [ - "atomic-waker", - "fastrand 2.0.1", - "futures-io", -] +checksum = "42aaeae719fd78ce501d77c6cdf01f7e96f26bcd5617a4903a1c2b97e388543a" [[package]] -name = "polling" -version = "2.8.0" +name = "csscolorparser" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" +checksum = "5fda6aace1fbef3aa217b27f4c8d7d071ef2a70a5ca51050b1f17d40299d3f16" dependencies = [ - "autocfg", - "bitflags 1.3.2", - "cfg-if", - "concurrent-queue", - "libc", - "log", - "pin-project-lite", - "windows-sys 0.48.0", + "phf", + "serde", ] [[package]] -name = "polling" -version = "3.3.2" +name = "ctor-lite" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "545c980a3880efd47b2e262f6a4bb6daad6555cf3367aa9c4e52895f69537a41" -dependencies = [ - "cfg-if", - "concurrent-queue", - "pin-project-lite", - "rustix 0.38.30", - "tracing", - "windows-sys 0.52.0", -] +checksum = "1f791803201ab277ace03903de1594460708d2d54df6053f2d9e82f592b19e3b" [[package]] -name = "ppv-lite86" -version = "0.2.17" +name = "cursor-icon" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" [[package]] -name = "proc-macro-crate" -version = "1.3.1" +name = "d3d12" +version = "22.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +checksum = "bdbd1f579714e3c809ebd822c81ef148b1ceaeb3d535352afc73fd0c4c6a0017" dependencies = [ - "once_cell", - "toml_edit", + "bitflags 2.9.1", + "libloading", + "winapi", ] [[package]] -name = "proc-macro2" -version = "1.0.76" +name = "darling" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ - "unicode-ident", + "darling_core", + "darling_macro", ] [[package]] -name = "quote" -version = "1.0.35" +name = "darling_core" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" dependencies = [ + "fnv", + "ident_case", "proc-macro2", + "quote", + "strsim", + "syn 2.0.104", ] [[package]] -name = "rand" -version = "0.8.5" +name = "darling_macro" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ - "libc", - "rand_chacha", - "rand_core", + "darling_core", + "quote", + "syn 2.0.104", ] [[package]] -name = "rand_chacha" +name = "data-url" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] +checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" [[package]] -name = "rand_core" -version = "0.6.4" +name = "deranged" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" dependencies = [ - "getrandom", + "powerfmt", ] [[package]] -name = "redox_syscall" -version = "0.4.1" +name = "derivative" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "bitflags 1.3.2", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] -name = "redox_users" -version = "0.4.4" +name = "derive_setters" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" +checksum = "ae5c625eda104c228c06ecaf988d1c60e542176bd7a490e60eeda3493244c0c9" dependencies = [ - "getrandom", - "libredox", - "thiserror", + "darling", + "proc-macro2", + "quote", + "syn 2.0.104", ] [[package]] -name = "regex" -version = "1.10.2" +name = "derive_utils" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "ccfae181bab5ab6c5478b2ccb69e4c68a02f8c3ec72f6616bfec9dbc599d2ee0" dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", + "proc-macro2", + "quote", + "syn 2.0.104", ] [[package]] -name = "regex-automata" -version = "0.4.3" +name = "digest" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", + "block-buffer", + "crypto-common", ] [[package]] -name = "regex-syntax" -version = "0.8.2" +name = "dirs" +version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys 0.4.1", +] [[package]] -name = "rustc-demangle" -version = "0.1.23" +name = "dirs" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys 0.5.0", +] [[package]] -name = "rustix" -version = "0.37.27" +name = "dirs-sys" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", "libc", - "linux-raw-sys 0.3.8", + "option-ext", + "redox_users 0.4.6", "windows-sys 0.48.0", ] [[package]] -name = "rustix" -version = "0.38.30" +name = "dirs-sys" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ - "bitflags 2.4.1", - "errno", "libc", - "linux-raw-sys 0.4.12", - "windows-sys 0.52.0", + "option-ext", + "redox_users 0.5.2", + "windows-sys 0.60.2", ] [[package]] -name = "serde" -version = "1.0.195" +name = "dispatch" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" -dependencies = [ - "serde_derive", -] +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" [[package]] -name = "serde_derive" -version = "1.0.195" +name = "dispatch2" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" +checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.48", + "bitflags 2.9.1", + "block2 0.6.1", + "libc", + "objc2 0.6.1", ] [[package]] -name = "serde_repr" -version = "0.1.18" +name = "displaydoc" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.104", ] [[package]] -name = "sha1" -version = "0.10.6" +name = "dlib" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" dependencies = [ - "cfg-if", - "cpufeatures", - "digest", + "libloading", ] [[package]] -name = "signal-hook-registry" -version = "1.4.1" +name = "dnd" +version = "0.1.0" +source = "git+https://github.com/pop-os/window_clipboard.git?tag=pop-0.13-2#6b9faab87bea9cebec6ae036906fd67fed254f5f" +dependencies = [ + "bitflags 2.9.1", + "mime", + "raw-window-handle", + "smithay-client-toolkit", + "smithay-clipboard", +] + +[[package]] +name = "document-features" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d" dependencies = [ - "libc", + "litrs", ] [[package]] -name = "slab" -version = "0.4.9" +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.1" +source = "git+https://github.com/pop-os/winit.git?tag=iced-xdg-surface-0.13#1cc02bdab141072eaabad639d74b032fd0fcc62e" + +[[package]] +name = "drm" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +checksum = "a0f8a69e60d75ae7dab4ef26a59ca99f2a89d4c142089b537775ae0c198bdcde" dependencies = [ - "autocfg", + "bitflags 2.9.1", + "bytemuck", + "drm-ffi", + "drm-fourcc", + "rustix 0.38.44", ] [[package]] -name = "socket2" -version = "0.4.10" +name = "drm-ffi" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +checksum = "41334f8405792483e32ad05fbb9c5680ff4e84491883d2947a4757dc54cb2ac6" dependencies = [ - "libc", - "winapi", + "drm-sys", + "rustix 0.38.44", ] [[package]] -name = "socket2" -version = "0.5.5" +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.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +checksum = "2d09ff881f92f118b11105ba5e34ff8f4adf27b30dae8f12e28c193af1c83176" dependencies = [ "libc", - "windows-sys 0.48.0", + "linux-raw-sys 0.6.5", ] [[package]] -name = "static_assertions" -version = "1.1.0" +name = "either" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] -name = "strsim" -version = "0.10.0" +name = "endi" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" [[package]] -name = "syn" -version = "1.0.109" +name = "enumflags2" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", + "enumflags2_derive", + "serde", ] [[package]] -name = "syn" -version = "2.0.48" +name = "enumflags2_derive" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "unicode-ident", + "syn 2.0.104", ] [[package]] -name = "tempfile" -version = "3.9.0" +name = "equator" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" +checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" dependencies = [ - "cfg-if", - "fastrand 2.0.1", - "redox_syscall", - "rustix 0.38.30", - "windows-sys 0.52.0", + "equator-macro", ] [[package]] -name = "thiserror" -version = "1.0.56" +name = "equator-macro" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" dependencies = [ - "thiserror-impl", + "proc-macro2", + "quote", + "syn 2.0.104", ] [[package]] -name = "thiserror-impl" -version = "1.0.56" +name = "equivalent" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.48", -] +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] -name = "tinyvec" -version = "1.6.0" +name = "errno" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ - "tinyvec_macros", + "libc", + "windows-sys 0.60.2", ] [[package]] -name = "tinyvec_macros" -version = "0.1.1" +name = "error-code" +version = "3.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" [[package]] -name = "tokio" -version = "1.35.1" +name = "etagere" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +checksum = "fc89bf99e5dc15954a60f707c1e09d7540e5cd9af85fa75caa0b510bc08c5342" dependencies = [ - "backtrace", - "bytes", - "libc", - "mio", - "pin-project-lite", - "signal-hook-registry", - "socket2 0.5.5", - "tokio-macros", - "tracing", - "windows-sys 0.48.0", + "euclid", + "svg_fmt", ] [[package]] -name = "tokio-macros" -version = "2.2.0" +name = "euclid" +version = "0.22.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "ad9cdb4b747e485a12abb0e6566612956c7a1bafa3bdb8d682c5b6d403589e48" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.48", + "num-traits", ] [[package]] -name = "toml_datetime" -version = "0.6.5" +name = "event-listener" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] -name = "toml_edit" -version = "0.19.15" +name = "event-listener" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2" dependencies = [ - "indexmap", - "toml_datetime", - "winnow", + "concurrent-queue", + "parking", + "pin-project-lite", ] [[package]] -name = "tracing" -version = "0.1.40" +name = "event-listener" +version = "5.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" dependencies = [ + "concurrent-queue", + "parking", "pin-project-lite", - "tracing-attributes", - "tracing-core", ] [[package]] -name = "tracing-attributes" -version = "0.1.27" +name = "event-listener-strategy" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.48", + "event-listener 5.4.1", + "pin-project-lite", ] [[package]] -name = "tracing-core" -version = "0.1.32" +name = "exr" +version = "1.73.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "f83197f59927b46c04a183a619b7c29df34e63e63c7869320862268c0ef687e0" dependencies = [ - "once_cell", + "bit_field", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", ] [[package]] -name = "typenum" -version = "1.17.0" +name = "fast-srgb8" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1" [[package]] -name = "uds_windows" -version = "1.1.0" +name = "fastrand" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" dependencies = [ - "memoffset 0.9.0", - "tempfile", - "winapi", + "instant", ] [[package]] -name = "unicode-bidi" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" - -[[package]] -name = "unicode-ident" -version = "1.0.12" +name = "fastrand" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] -name = "unicode-normalization" -version = "0.1.22" +name = "fdeflate" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" dependencies = [ - "tinyvec", + "simd-adler32", ] [[package]] -name = "url" -version = "2.5.0" +name = "flate2" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", + "crc32fast", + "miniz_oxide", ] [[package]] -name = "utf8parse" -version = "0.2.1" +name = "float-cmp" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" [[package]] -name = "version_check" -version = "0.9.4" +name = "float_next_after" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8" [[package]] -name = "waker-fn" -version = "1.1.1" +name = "fnv" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +name = "foldhash" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] -name = "wasm-bindgen" -version = "0.2.90" +name = "font-types" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" +checksum = "02a596f5713680923a2080d86de50fe472fb290693cf0f701187a1c8b36996b7" dependencies = [ - "cfg-if", - "wasm-bindgen-macro", + "bytemuck", ] [[package]] -name = "wasm-bindgen-backend" -version = "0.2.90" +name = "fontconfig-parser" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" +checksum = "bbc773e24e02d4ddd8395fd30dc147524273a83e54e0f312d986ea30de5f5646" dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.48", - "wasm-bindgen-shared", + "roxmltree", ] [[package]] -name = "wasm-bindgen-macro" -version = "0.2.90" +name = "fontdb" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" +checksum = "e32eac81c1135c1df01d4e6d4233c47ba11f6a6d07f33e0bba09d18797077770" dependencies = [ - "quote", - "wasm-bindgen-macro-support", + "fontconfig-parser", + "log", + "memmap2 0.9.7", + "slotmap", + "tinyvec", + "ttf-parser 0.21.1", ] [[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.90" +name = "fontdb" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" +checksum = "457e789b3d1202543297a350643cf459f836cade38934e7a4cf6a39e7cde2905" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.48", - "wasm-bindgen-backend", - "wasm-bindgen-shared", + "fontconfig-parser", + "log", + "memmap2 0.9.7", + "slotmap", + "tinyvec", + "ttf-parser 0.25.1", ] [[package]] -name = "wasm-bindgen-shared" -version = "0.2.90" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" - -[[package]] -name = "winapi" -version = "0.3.9" +name = "foreign-types" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", + "foreign-types-macros", + "foreign-types-shared", ] [[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" +name = "foreign-types-macros" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] [[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" +name = "foreign-types-shared" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" [[package]] -name = "windows-core" -version = "0.52.0" +name = "form_urlencoded" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ - "windows-targets 0.52.0", + "percent-encoding", ] [[package]] -name = "windows-sys" -version = "0.48.0" +name = "fsevent-sys" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" dependencies = [ - "windows-targets 0.48.5", + "libc", ] [[package]] -name = "windows-sys" -version = "0.52.0" +name = "futures" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ - "windows-targets 0.52.0", + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", ] [[package]] -name = "windows-targets" -version = "0.48.5" +name = "futures-channel" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 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", + "futures-core", + "futures-sink", ] [[package]] -name = "windows-targets" -version = "0.52.0" +name = "futures-core" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" -dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", -] +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" +name = "futures-executor" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", + "num_cpus", +] [[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.0" +name = "futures-io" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" +name = "futures-lite" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand 1.9.0", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand 2.3.0", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "gethostname" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +dependencies = [ + "libc", + "windows-targets 0.48.5", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "gif" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[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 = "glam" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3" + +[[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 = "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.9.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.9.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 0.52.0", +] + +[[package]] +name = "gpu-descriptor" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" +dependencies = [ + "bitflags 2.9.1", + "gpu-descriptor-types", + "hashbrown", +] + +[[package]] +name = "gpu-descriptor-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" +dependencies = [ + "bitflags 2.9.1", +] + +[[package]] +name = "grid" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df00eed8d1f0db937f6be10e46e8072b0671accb504cf0f959c5c52c679f5b9" + +[[package]] +name = "guillotiere" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62d5865c036cb1393e23c50693df631d3f5d7bcca4c04fe4cc0fd592e74a782" +dependencies = [ + "euclid", + "svg_fmt", +] + +[[package]] +name = "half" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hassle-rs" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af2a7e73e1f34c48da31fb668a907f250794837e08faa144fd24f0b8b741e890" +dependencies = [ + "bitflags 2.9.1", + "com", + "libc", + "libloading", + "thiserror 1.0.69", + "widestring", + "winapi", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[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.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core 0.61.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 = "iced" +version = "0.14.0-dev" +source = "git+https://github.com/pop-os/libcosmic.git#b58d864e85b3b842132e9be1b6445ad21c1f0258" +dependencies = [ + "dnd", + "iced_accessibility", + "iced_core", + "iced_futures", + "iced_renderer", + "iced_widget", + "iced_winit", + "image", + "mime", + "thiserror 1.0.69", + "window_clipboard", +] + +[[package]] +name = "iced_accessibility" +version = "0.1.0" +source = "git+https://github.com/pop-os/libcosmic.git#b58d864e85b3b842132e9be1b6445ad21c1f0258" +dependencies = [ + "accesskit", + "accesskit_winit", +] + +[[package]] +name = "iced_core" +version = "0.14.0-dev" +source = "git+https://github.com/pop-os/libcosmic.git#b58d864e85b3b842132e9be1b6445ad21c1f0258" +dependencies = [ + "bitflags 2.9.1", + "bytes", + "cosmic-client-toolkit", + "dnd", + "glam", + "log", + "mime", + "num-traits", + "once_cell", + "palette", + "raw-window-handle", + "rustc-hash 2.1.1", + "serde", + "smol_str", + "thiserror 1.0.69", + "web-time", + "window_clipboard", +] + +[[package]] +name = "iced_futures" +version = "0.14.0-dev" +source = "git+https://github.com/pop-os/libcosmic.git#b58d864e85b3b842132e9be1b6445ad21c1f0258" +dependencies = [ + "futures", + "iced_core", + "log", + "rustc-hash 2.1.1", + "tokio", + "wasm-bindgen-futures", + "wasm-timer", +] + +[[package]] +name = "iced_glyphon" +version = "0.6.0" +source = "git+https://github.com/pop-os/glyphon.git?tag=iced-0.14-dev#6ef9d12a20cfd0f7bdf38136a26ded9f7459ec8b" +dependencies = [ + "cosmic-text", + "etagere", + "lru", + "rustc-hash 2.1.1", + "wgpu", +] + +[[package]] +name = "iced_graphics" +version = "0.14.0-dev" +source = "git+https://github.com/pop-os/libcosmic.git#b58d864e85b3b842132e9be1b6445ad21c1f0258" +dependencies = [ + "bitflags 2.9.1", + "bytemuck", + "cosmic-text", + "half", + "iced_core", + "iced_futures", + "image", + "kamadak-exif", + "log", + "lyon_path", + "once_cell", + "raw-window-handle", + "rustc-hash 2.1.1", + "thiserror 1.0.69", + "unicode-segmentation", +] + +[[package]] +name = "iced_renderer" +version = "0.14.0-dev" +source = "git+https://github.com/pop-os/libcosmic.git#b58d864e85b3b842132e9be1b6445ad21c1f0258" +dependencies = [ + "iced_graphics", + "iced_tiny_skia", + "iced_wgpu", + "log", + "thiserror 1.0.69", +] + +[[package]] +name = "iced_runtime" +version = "0.14.0-dev" +source = "git+https://github.com/pop-os/libcosmic.git#b58d864e85b3b842132e9be1b6445ad21c1f0258" +dependencies = [ + "bytes", + "cosmic-client-toolkit", + "dnd", + "iced_core", + "iced_futures", + "raw-window-handle", + "thiserror 1.0.69", + "window_clipboard", +] + +[[package]] +name = "iced_tiny_skia" +version = "0.14.0-dev" +source = "git+https://github.com/pop-os/libcosmic.git#b58d864e85b3b842132e9be1b6445ad21c1f0258" +dependencies = [ + "bytemuck", + "cosmic-text", + "iced_graphics", + "kurbo 0.10.4", + "log", + "resvg", + "rustc-hash 2.1.1", + "softbuffer", + "tiny-skia", +] + +[[package]] +name = "iced_wgpu" +version = "0.14.0-dev" +source = "git+https://github.com/pop-os/libcosmic.git#b58d864e85b3b842132e9be1b6445ad21c1f0258" +dependencies = [ + "as-raw-xcb-connection", + "bitflags 2.9.1", + "bytemuck", + "cosmic-client-toolkit", + "futures", + "glam", + "guillotiere", + "iced_glyphon", + "iced_graphics", + "log", + "lyon", + "once_cell", + "raw-window-handle", + "resvg", + "rustc-hash 2.1.1", + "rustix 0.38.44", + "thiserror 1.0.69", + "tiny-xlib", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-sys", + "wgpu", + "x11rb", +] + +[[package]] +name = "iced_widget" +version = "0.14.0-dev" +source = "git+https://github.com/pop-os/libcosmic.git#b58d864e85b3b842132e9be1b6445ad21c1f0258" +dependencies = [ + "cosmic-client-toolkit", + "dnd", + "iced_renderer", + "iced_runtime", + "log", + "num-traits", + "once_cell", + "ouroboros", + "rustc-hash 2.1.1", + "thiserror 1.0.69", + "unicode-segmentation", + "window_clipboard", +] + +[[package]] +name = "iced_winit" +version = "0.14.0-dev" +source = "git+https://github.com/pop-os/libcosmic.git#b58d864e85b3b842132e9be1b6445ad21c1f0258" +dependencies = [ + "cosmic-client-toolkit", + "dnd", + "iced_futures", + "iced_graphics", + "iced_runtime", + "log", + "raw-window-handle", + "rustc-hash 2.1.1", + "rustix 0.38.44", + "thiserror 1.0.69", + "tracing", + "wasm-bindgen-futures", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "web-sys", + "winapi", + "window_clipboard", + "winit", + "xkbcommon", + "xkbcommon-dl", + "xkeysym", +] + +[[package]] +name = "icu_collections" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" + +[[package]] +name = "icu_properties" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" + +[[package]] +name = "icu_provider" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "image" +version = "0.25.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a" +dependencies = [ + "bytemuck", + "byteorder-lite", + "color_quant", + "exr", + "gif", + "image-webp", + "num-traits", + "png", + "qoi", + "ravif", + "rayon", + "rgb", + "tiff", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "image-webp" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6970fe7a5300b4b42e62c52efa0187540a5bef546c60edaf554ef595d2e6f0b" +dependencies = [ + "byteorder-lite", + "quick-error", +] + +[[package]] +name = "imagesize" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284" + +[[package]] +name = "imgref" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408" + +[[package]] +name = "immutable-chunkmap" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f97096f508d54f8f8ab8957862eee2ccd628847b6217af1a335e1c44dee578" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "indexmap" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "ini_core" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a467a31a9f439b5262fa99c17084537bff57f24703d5a09a2b5c9657ec73a61" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "inotify" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" +dependencies = [ + "bitflags 2.9.1", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + +[[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.104", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "io-uring" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" +dependencies = [ + "bitflags 2.9.1", + "cfg-if", + "libc", +] + +[[package]] +name = "is-docker" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" +dependencies = [ + "once_cell", +] + +[[package]] +name = "is-wsl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" +dependencies = [ + "is-docker", + "once_cell", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jobserver" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +dependencies = [ + "getrandom 0.3.3", + "libc", +] + +[[package]] +name = "jpeg-decoder" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00810f1d8b74be64b13dbf3db89ac67740615d6c891f0e7b6179326533011a07" + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "kamadak-exif" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef4fc70d0ab7e5b6bafa30216a6b48705ea964cdfc29c050f2412295eba58077" +dependencies = [ + "mutate_once", +] + +[[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 = "known-folders" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c644f4623d1c55eb60a9dac35e0858a59f982fb87db6ce34c872372b0a5b728f" +dependencies = [ + "windows-sys 0.60.2", +] + +[[package]] +name = "kqueue" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + +[[package]] +name = "kurbo" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1618d4ebd923e97d67e7cd363d80aef35fe961005cbbbb3d2dad8bdd1bc63440" +dependencies = [ + "arrayvec", + "smallvec", +] + +[[package]] +name = "kurbo" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62026ae44756f8a599ba21140f350303d4f08dcdcc71b5ad9c9bb8128c13c62" +dependencies = [ + "arrayvec", + "euclid", + "smallvec", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + +[[package]] +name = "libc" +version = "0.2.174" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" + +[[package]] +name = "libcosmic" +version = "0.1.0" +source = "git+https://github.com/pop-os/libcosmic.git#b58d864e85b3b842132e9be1b6445ad21c1f0258" +dependencies = [ + "apply", + "ashpd", + "auto_enums", + "chrono", + "cosmic-client-toolkit", + "cosmic-config", + "cosmic-freedesktop-icons", + "cosmic-settings-daemon", + "cosmic-theme", + "css-color", + "derive_setters", + "futures", + "iced", + "iced_core", + "iced_futures", + "iced_renderer", + "iced_runtime", + "iced_tiny_skia", + "iced_wgpu", + "iced_widget", + "iced_winit", + "image", + "lazy_static", + "palette", + "raw-window-handle", + "rfd", + "ron", + "serde", + "slotmap", + "taffy", + "thiserror 2.0.12", + "tokio", + "tracing", + "unicode-segmentation", + "url", + "zbus 5.9.0", +] + +[[package]] +name = "libfuzzer-sys" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404" +dependencies = [ + "arbitrary", + "cc", +] + +[[package]] +name = "libloading" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" +dependencies = [ + "cfg-if", + "windows-targets 0.53.3", +] + +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + +[[package]] +name = "libredox" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" +dependencies = [ + "bitflags 2.9.1", + "libc", + "redox_syscall 0.5.17", +] + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[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.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a385b1be4e5c3e362ad2ffa73c392e53f031eaa5b7d648e64cd87f27f6063d7" + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + +[[package]] +name = "litemap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" + +[[package]] +name = "litrs" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed" + +[[package]] +name = "lock_api" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "loop9" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] + +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" + +[[package]] +name = "lyon" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e7f9cda98b5430809e63ca5197b06c7d191bf7e26dfc467d5a3f0290e2a74f" +dependencies = [ + "lyon_algorithms", + "lyon_tessellation", +] + +[[package]] +name = "lyon_algorithms" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f13c9be19d257c7d37e70608ed858e8eab4b2afcea2e3c9a622e892acbf43c08" +dependencies = [ + "lyon_path", + "num-traits", +] + +[[package]] +name = "lyon_geom" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8af69edc087272df438b3ee436c4bb6d7c04aa8af665cfd398feae627dbd8570" +dependencies = [ + "arrayvec", + "euclid", + "num-traits", +] + +[[package]] +name = "lyon_path" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0047f508cd7a85ad6bad9518f68cce7b1bf6b943fb71f6da0ee3bc1e8cb75f25" +dependencies = [ + "lyon_geom", + "num-traits", +] + +[[package]] +name = "lyon_tessellation" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579d42360a4b09846eff2feef28f538696c7d6c7439bfa65874ff3cbe0951b2c" +dependencies = [ + "float_next_after", + "lyon_path", + "num-traits", +] + +[[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.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "memmap2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed" +dependencies = [ + "libc", +] + +[[package]] +name = "memmap2" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "483758ad303d734cec05e5c12b41d7e93e6a6390c5e9dae6bdeb7c1259012d28" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + +[[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.9.1", + "block", + "core-graphics-types", + "foreign-types", + "log", + "objc", + "paste", +] + +[[package]] +name = "mime" +version = "0.1.0" +source = "git+https://github.com/pop-os/window_clipboard.git?tag=pop-0.13-2#6b9faab87bea9cebec6ae036906fd67fed254f5f" +dependencies = [ + "smithay-clipboard", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[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 = "mio" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +dependencies = [ + "libc", + "log", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", +] + +[[package]] +name = "mutate_once" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16cf681a23b4d0a43fc35024c176437f9dcd818db34e0f42ab456a0ee5ad497b" + +[[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.9.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.9.1", + "jni-sys", + "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", +] + +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset 0.7.1", +] + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.9.1", + "cfg-if", + "cfg_aliases 0.2.1", + "libc", +] + +[[package]] +name = "nix" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" +dependencies = [ + "bitflags 2.9.1", + "cfg-if", + "cfg_aliases 0.2.1", + "libc", + "memoffset 0.9.1", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "noop_proc_macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" + +[[package]] +name = "notify" +version = "8.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" +dependencies = [ + "bitflags 2.9.1", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "log", + "mio", + "notify-types", + "walkdir", + "windows-sys 0.60.2", +] + +[[package]] +name = "notify-types" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d" + +[[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-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[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.104", +] + +[[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", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi 0.5.2", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" +dependencies = [ + "proc-macro-crate 3.3.0", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + +[[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-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +dependencies = [ + "block", + "objc", + "objc_id", +] + +[[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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88c6597e14493ab2e44ce58f2fdecf095a51f12ca57bec060a11c57332520551" +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.9.1", + "block2 0.5.1", + "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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc" +dependencies = [ + "bitflags 2.9.1", + "block2 0.6.1", + "objc2 0.6.1", + "objc2-foundation 0.3.1", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" +dependencies = [ + "bitflags 2.9.1", + "block2 0.5.1", + "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 0.5.1", + "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.9.1", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" +dependencies = [ + "bitflags 2.9.1", + "dispatch2", + "objc2 0.6.1", +] + +[[package]] +name = "objc2-core-image" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" +dependencies = [ + "block2 0.5.1", + "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 0.5.1", + "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.9.1", + "block2 0.5.1", + "dispatch", + "libc", + "objc2 0.5.2", +] + +[[package]] +name = "objc2-foundation" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" +dependencies = [ + "bitflags 2.9.1", + "objc2 0.6.1", + "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 0.5.1", + "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.9.1", + "block2 0.5.1", + "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.9.1", + "block2 0.5.1", + "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.9.1", + "block2 0.5.1", + "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 0.5.1", + "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.9.1", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-core-location", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +dependencies = [ + "objc", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" + +[[package]] +name = "open" +version = "5.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2483562e62ea94312f3576a7aca397306df7990b8d89033e18766744377ef95" +dependencies = [ + "is-wsl", + "libc", + "pathdiff", +] + +[[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.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba0b26cec2e24f08ed8bb31519a9333140a6599b867dac464bb150bdb796fd43" +dependencies = [ + "libredox", +] + +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "ouroboros" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0f050db9c44b97a94723127e6be766ac5c340c48f2c4bb3ffa11713744be59" +dependencies = [ + "aliasable", + "ouroboros_macro", + "static_assertions", +] + +[[package]] +name = "ouroboros_macro" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c7028bdd3d43083f6d8d4d5187680d0d3560d54df4cc9d752005268b41e64d0" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "owned_ttf_parser" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36820e9051aca1014ddc75770aab4d68bc1e9e632f0f5627c4086bc216fb583b" +dependencies = [ + "ttf-parser 0.25.1", +] + +[[package]] +name = "palette" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cbf71184cc5ecc2e4e1baccdb21026c20e5fc3dcf63028a086131b3ab00b6e6" +dependencies = [ + "approx", + "fast-srgb8", + "palette_derive", + "phf", + "serde", +] + +[[package]] +name = "palette_derive" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5030daf005bface118c096f510ffb781fc28f9ab6a32ab224d8631be6851d30" +dependencies = [ + "by_address", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core 0.8.6", +] + +[[package]] +name = "parking_lot" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +dependencies = [ + "lock_api", + "parking_lot_core 0.9.11", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall 0.2.16", + "smallvec", + "winapi", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.5.17", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pathdiff" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pico-args" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" + +[[package]] +name = "pin-project" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "piper" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +dependencies = [ + "atomic-waker", + "fastrand 2.3.0", + "futures-io", +] + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[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 = "polling" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if", + "concurrent-queue", + "libc", + "log", + "pin-project-lite", + "windows-sys 0.48.0", +] + +[[package]] +name = "polling" +version = "3.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5bd19146350fe804f7cb2669c851c03d69da628803dab0d98018142aaa5d829" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi 0.5.2", + "pin-project-lite", + "rustix 1.0.8", + "windows-sys 0.60.2", +] + +[[package]] +name = "pollster" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" + +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[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 = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +dependencies = [ + "toml_edit 0.22.27", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", + "version_check", + "yansi", +] + +[[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.104", +] + +[[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.37.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +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 = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[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 0.9.3", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", +] + +[[package]] +name = "range-alloc" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d6831663a5098ea164f89cff59c6284e95f4e3c76ce9848d4529f5ccca9bde" + +[[package]] +name = "rangemap" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93e7e49bb0bf967717f7bd674458b3d6b0c5f48ec7e3038166026a69fc22223" + +[[package]] +name = "rav1e" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9" +dependencies = [ + "arbitrary", + "arg_enum_proc_macro", + "arrayvec", + "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", + "once_cell", + "paste", + "profiling", + "rand 0.8.5", + "rand_chacha 0.3.1", + "simd_helpers", + "system-deps", + "thiserror 1.0.69", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.11.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5825c26fddd16ab9f515930d49028a630efec172e903483c94796cfe31893e6b" +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.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "read-fonts" +version = "0.29.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04ca636dac446b5664bd16c069c00a9621806895b8bb02c2dc68542b23b8f25d" +dependencies = [ + "bytemuck", + "font-types", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.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.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" +dependencies = [ + "bitflags 2.9.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.16", + "libredox", + "thiserror 1.0.69", +] + +[[package]] +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +dependencies = [ + "getrandom 0.2.16", + "libredox", + "thiserror 2.0.12", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "renderdoc-sys" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" + +[[package]] +name = "resvg" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "944d052815156ac8fa77eaac055220e95ba0b01fa8887108ca710c03805d9051" +dependencies = [ + "gif", + "jpeg-decoder", + "log", + "pico-args", + "rgb", + "svgtypes", + "tiny-skia", + "usvg", +] + +[[package]] +name = "rfd" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2bee61e6cffa4635c72d7d81a84294e28f0930db0ddcb0f66d10244674ebed" +dependencies = [ + "ashpd", + "block2 0.6.1", + "dispatch2", + "js-sys", + "log", + "objc2 0.6.1", + "objc2-app-kit 0.3.1", + "objc2-core-foundation", + "objc2-foundation 0.3.1", + "pollster", + "raw-window-handle", + "urlencoding", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "rgb" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "ron" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63f3aa105dea217ef30d89581b65a4d527a19afc95ef5750be3890e8d3c5b837" +dependencies = [ + "base64", + "bitflags 2.9.1", + "serde", + "serde_derive", + "unicode-ident", +] + +[[package]] +name = "roxmltree" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" + +[[package]] +name = "rustc-demangle" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" + +[[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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustix" +version = "0.37.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "519165d378b97752ca44bbe15047d5d3409e875f39327546b42ac81d7e18c1b6" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.3.8", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags 2.9.1", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +dependencies = [ + "bitflags 2.9.1", + "errno", + "libc", + "linux-raw-sys 0.9.4", + "windows-sys 0.60.2", +] + +[[package]] +name = "rustversion" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" + +[[package]] +name = "rustybuzz" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfb9cf8877777222e4a3bc7eb247e398b56baba500c38c1c46842431adc8b55c" +dependencies = [ + "bitflags 2.9.1", + "bytemuck", + "libm", + "smallvec", + "ttf-parser 0.21.1", + "unicode-bidi-mirroring", + "unicode-ccc", + "unicode-properties", + "unicode-script", +] + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[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 = "sctk-adwaita" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6277f0217056f77f1d8f49f2950ac6c278c0d607c45f5ee99328d792ede24ec" +dependencies = [ + "ab_glyph", + "log", + "memmap2 0.9.7", + "smithay-client-toolkit", + "tiny-skia", +] + +[[package]] +name = "self_cell" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "serde_json" +version = "1.0.142" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" +dependencies = [ + "indexmap", + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +dependencies = [ + "libc", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "simd_helpers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" +dependencies = [ + "quote", +] + +[[package]] +name = "simplecss" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9c6883ca9c3c7c90e888de77b7a5c849c779d25d74a1269b0218b14e8b136c" +dependencies = [ + "log", +] + +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + +[[package]] +name = "skrifa" +version = "0.31.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbeb4ca4399663735553a09dd17ce7e49a0a0203f03b706b39628c4d913a8607" +dependencies = [ + "bytemuck", + "read-fonts", +] + +[[package]] +name = "slab" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" + +[[package]] +name = "slotmap" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" +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.9.1", + "bytemuck", + "calloop", + "calloop-wayland-source", + "cursor-icon", + "libc", + "log", + "memmap2 0.9.7", + "pkg-config", + "rustix 0.38.44", + "thiserror 1.0.69", + "wayland-backend", + "wayland-client", + "wayland-csd-frame", + "wayland-cursor", + "wayland-protocols", + "wayland-protocols-wlr", + "wayland-scanner", + "xkbcommon", + "xkeysym", +] + +[[package]] +name = "smithay-clipboard" +version = "0.8.0" +source = "git+https://github.com/pop-os/smithay-clipboard?tag=pop-dnd-5#5a3007def49eb678d1144850c9ee04b80707c56a" +dependencies = [ + "libc", + "raw-window-handle", + "smithay-client-toolkit", + "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 = "socket2" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "socket2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "softbuffer" +version = "0.4.1" +source = "git+https://github.com/pop-os/softbuffer?tag=cosmic-4.0#6e75b1ad7e98397d37cb187886d05969bc480995" +dependencies = [ + "as-raw-xcb-connection", + "bytemuck", + "cfg_aliases 0.2.1", + "cocoa", + "core-graphics", + "drm", + "fastrand 2.3.0", + "foreign-types", + "js-sys", + "log", + "memmap2 0.9.7", + "objc", + "raw-window-handle", + "redox_syscall 0.4.1", + "rustix 0.38.44", + "tiny-xlib", + "wasm-bindgen", + "wayland-backend", + "wayland-client", + "wayland-sys", + "web-sys", + "windows-sys 0.52.0", + "x11rb", +] + +[[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.9.1", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[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" +dependencies = [ + "float-cmp", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "svg_fmt" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0193cc4331cfd2f3d2011ef287590868599a2f33c3e69bc22c1a3d3acf9e02fb" + +[[package]] +name = "svgtypes" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68c7541fff44b35860c1a7a47a7cadf3e4a304c457b58f9870d9706ece028afc" +dependencies = [ + "kurbo 0.11.3", + "siphasher", +] + +[[package]] +name = "swash" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f745de914febc7c9ab4388dfaf94bbc87e69f57bb41133a9b0c84d4be49856f3" +dependencies = [ + "skrifa", + "yazi", + "zeno", +] + +[[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.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +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.104", +] + +[[package]] +name = "sys-locale" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" +dependencies = [ + "libc", +] + +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck 0.5.0", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "taffy" +version = "0.3.11" +source = "git+https://github.com/DioxusLabs/taffy?rev=7781c70#7781c70241f7f572130c13106f2a869a9cf80885" +dependencies = [ + "arrayvec", + "grid", + "num-traits", + "slotmap", +] + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] +name = "tempfile" +version = "3.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +dependencies = [ + "fastrand 2.3.0", + "getrandom 0.3.3", + "once_cell", + "rustix 1.0.8", + "windows-sys 0.59.0", +] + +[[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.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", +] + +[[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.104", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "tiff" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + +[[package]] +name = "time" +version = "0.3.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +dependencies = [ + "deranged", + "itoa", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" + +[[package]] +name = "time-macros" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +dependencies = [ + "num-conv", + "time-core", +] + +[[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", + "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 = "tiny-xlib" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0324504befd01cab6e0c994f34b2ffa257849ee019d3fb3b64fb2c858887d89e" +dependencies = [ + "as-raw-xcb-connection", + "ctor-lite", + "libloading", + "pkg-config", + "tracing", +] + +[[package]] +name = "tinystr" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.47.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +dependencies = [ + "backtrace", + "bytes", + "io-uring", + "libc", + "mio", + "parking_lot 0.12.4", + "pin-project-lite", + "signal-hook-registry", + "slab", + "socket2 0.6.0", + "tokio-macros", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "tokio-macros" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "tokio-stream" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[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", + "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_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow 0.5.40", +] + +[[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", + "winnow 0.7.12", +] + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "tracing-core" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +dependencies = [ + "once_cell", +] + +[[package]] +name = "ttf-parser" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8" + +[[package]] +name = "ttf-parser" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" +dependencies = [ + "core_maths", +] + +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + +[[package]] +name = "uds_windows" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" +dependencies = [ + "memoffset 0.9.1", + "tempfile", + "winapi", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" + +[[package]] +name = "unicode-bidi-mirroring" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cb788ffebc92c5948d0e997106233eeb1d8b9512f93f41651f52b6c5f5af86" + +[[package]] +name = "unicode-ccc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + +[[package]] +name = "unicode-properties" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" + +[[package]] +name = "unicode-script" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb421b350c9aff471779e262955939f565ec18b86c15364e6bdf0d662ca7c1f" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-vo" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" + +[[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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + +[[package]] +name = "usvg" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b84ea542ae85c715f07b082438a4231c3760539d902e11d093847a0b22963032" +dependencies = [ + "base64", + "data-url", + "flate2", + "fontdb 0.18.0", + "imagesize", + "kurbo 0.11.3", + "log", + "pico-args", + "roxmltree", + "rustybuzz", + "simplecss", + "siphasher", + "strict-num", + "svgtypes", + "tiny-skia-path", + "unicode-bidi", + "unicode-script", + "unicode-vo", + "xmlwriter", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[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 = "vergen" +version = "8.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2990d9ea5967266ea0ccf413a4aa5c42a93dbcfda9cb49a97de6931726b12566" +dependencies = [ + "anyhow", + "cfg-if", + "rustversion", + "time", +] + +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "waker-fn" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" + +[[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 = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.104", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-timer" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f" +dependencies = [ + "futures", + "js-sys", + "parking_lot 0.11.2", + "pin-utils", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wayland-backend" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673a33c33048a5ade91a6b139580fa174e19fb0d23f396dca9fa15f2e1e49b35" +dependencies = [ + "cc", + "downcast-rs", + "rustix 1.0.8", + "scoped-tls", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d" +dependencies = [ + "bitflags 2.9.1", + "rustix 1.0.8", + "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.9.1", + "cursor-icon", + "wayland-backend", +] + +[[package]] +name = "wayland-cursor" +version = "0.31.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "447ccc440a881271b19e9989f75726d60faa09b95b0200a9b7eb5cc47c3eeb29" +dependencies = [ + "rustix 1.0.8", + "wayland-client", + "xcursor", +] + +[[package]] +name = "wayland-protocols" +version = "0.32.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efa790ed75fbfd71283bd2521a1cfdc022aabcc28bdcff00851f9e4ae88d9901" +dependencies = [ + "bitflags 2.9.1", + "wayland-backend", + "wayland-client", + "wayland-scanner", + "wayland-server", +] + +[[package]] +name = "wayland-protocols-plasma" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a07a14257c077ab3279987c4f8bb987851bf57081b93710381daea94f2c2c032" +dependencies = [ + "bitflags 2.9.1", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efd94963ed43cf9938a090ca4f7da58eb55325ec8200c3848963e98dc25b78ec" +dependencies = [ + "bitflags 2.9.1", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", + "wayland-server", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54cb1e9dc49da91950bdfd8b848c49330536d9d1fb03d4bfec8cae50caa50ae3" +dependencies = [ + "proc-macro2", + "quick-xml", + "quote", +] + +[[package]] +name = "wayland-server" +version = "0.31.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcbd4f3aba6c9fba70445ad2a484c0ef0356c1a9459b1e8e435bedc1971a6222" +dependencies = [ + "bitflags 2.9.1", + "downcast-rs", + "rustix 1.0.8", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-sys" +version = "0.31.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34949b42822155826b41db8e5d0c1be3a2bd296c747577a43a3e6daefc296142" +dependencies = [ + "dlib", + "log", + "once_cell", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +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 = "weezl" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3" + +[[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 0.12.4", + "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.9.1", + "cfg_aliases 0.1.1", + "document-features", + "indexmap", + "log", + "naga", + "once_cell", + "parking_lot 0.12.4", + "profiling", + "raw-window-handle", + "rustc-hash 1.1.0", + "smallvec", + "thiserror 1.0.69", + "wgpu-hal", + "wgpu-types", +] + [[package]] -name = "windows_aarch64_msvc" +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", + "bit-set", + "bitflags 2.9.1", + "block", + "cfg_aliases 0.1.1", + "core-graphics-types", + "d3d12", + "glow", + "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 0.12.4", + "profiling", + "range-alloc", + "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.9.1", + "js-sys", + "web-sys", +] + +[[package]] +name = "widestring" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d" + +[[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.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[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 = "window_clipboard" +version = "0.4.1" +source = "git+https://github.com/pop-os/window_clipboard.git?tag=pop-0.13-2#6b9faab87bea9cebec6ae036906fd67fed254f5f" +dependencies = [ + "clipboard-win", + "clipboard_macos", + "clipboard_wayland", + "clipboard_x11", + "dnd", + "mime", + "raw-window-handle", + "thiserror 1.0.69", +] + +[[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" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49" +dependencies = [ + "windows-core 0.54.0", + "windows-implement 0.53.0", + "windows-interface 0.53.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.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" +dependencies = [ + "windows-result 0.1.2", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement 0.60.0", + "windows-interface 0.59.1", + "windows-link", + "windows-result 0.3.4", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942ac266be9249c84ca862f0a164a39533dc2f6f33dc98ec89c8da99b82ea0bd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "windows-interface" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da33557140a288fae4e1d5f8873aaf9eb6613a9cf82c3e070223ff177f598b60" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[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 = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +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.3", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" @@ -1627,9 +6228,33 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" @@ -1639,9 +6264,21 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" @@ -1651,9 +6288,21 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_gnullvm" @@ -1663,9 +6312,21 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" @@ -1675,37 +6336,243 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +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.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "winit" +version = "0.30.5" +source = "git+https://github.com/pop-os/winit.git?tag=iced-xdg-surface-0.13#1cc02bdab141072eaabad639d74b032fd0fcc62e" +dependencies = [ + "ahash", + "android-activity", + "atomic-waker", + "bitflags 2.9.1", + "block2 0.5.1", + "bytemuck", + "calloop", + "cfg_aliases 0.2.1", + "concurrent-queue", + "core-foundation", + "core-graphics", + "cursor-icon", + "dpi", + "js-sys", + "libc", + "memmap2 0.9.7", + "ndk", + "objc2 0.5.2", + "objc2-app-kit 0.2.2", + "objc2-foundation 0.2.2", + "objc2-ui-kit", + "orbclient", + "percent-encoding", + "pin-project", + "raw-window-handle", + "redox_syscall 0.4.1", + "rustix 0.38.44", + "sctk-adwaita", + "smithay-client-toolkit", + "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", + "x11-dl", + "x11rb", + "xkbcommon-dl", +] + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] [[package]] name = "winnow" -version = "0.5.34" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7cf47b659b318dccbd69cc4797a39ae128f533dce7902a1096044d1967b9c16" +checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" dependencies = [ "memchr", ] +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.9.1", +] + +[[package]] +name = "writeable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "x11rb" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12" +dependencies = [ + "as-raw-xcb-connection", + "gethostname", + "libc", + "libloading", + "once_cell", + "rustix 0.38.44", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" + +[[package]] +name = "xcursor" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b" + +[[package]] +name = "xdg" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546" + [[package]] name = "xdg-home" -version = "1.0.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2769203cd13a0c6015d515be729c526d041e9cf2c0cc478d57faee85f40c6dcd" +checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6" dependencies = [ - "nix", - "winapi", + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "xkbcommon" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13867d259930edc7091a6c41b4ce6eee464328c6ff9659b7e4c668ca20d4c91e" +dependencies = [ + "libc", + "memmap2 0.8.0", + "xkeysym", +] + +[[package]] +name = "xkbcommon-dl" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" +dependencies = [ + "bitflags 2.9.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" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "xml-rs" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fd8403733700263c6eb89f192880191f1b83e332f7a20371ddcf421c4a337c7" + +[[package]] +name = "xmlwriter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" + +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + +[[package]] +name = "yazi" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01738255b5a16e78bbb83e7fbba0a1e7dd506905cfc53f4622d89015a03fbb5" + +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", + "synstructure", ] [[package]] name = "zbus" -version = "3.14.1" +version = "3.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31de390a2d872e4cd04edd71b425e29853f786dc99317ed72d73d6fcf5ebb948" +checksum = "675d170b632a6ad49804c8cf2105d7c31eddd3312555cffd4b740e08e97c25e6" dependencies = [ - "async-broadcast", - "async-process", + "async-broadcast 0.5.1", + "async-process 1.8.1", "async-recursion", "async-trait", "byteorder", @@ -1716,10 +6583,10 @@ dependencies = [ "futures-sink", "futures-util", "hex", - "nix", + "nix 0.26.4", "once_cell", "ordered-stream", - "rand", + "rand 0.8.5", "serde", "serde_repr", "sha1", @@ -1729,62 +6596,254 @@ dependencies = [ "uds_windows", "winapi", "xdg-home", - "zbus_macros", - "zbus_names", - "zvariant", + "zbus_macros 3.15.2", + "zbus_names 2.6.1", + "zvariant 3.15.2", +] + +[[package]] +name = "zbus" +version = "5.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb4f9a464286d42851d18a605f7193b8febaf5b0919d71c6399b7b26e5b0aad" +dependencies = [ + "async-broadcast 0.7.2", + "async-executor", + "async-io 2.5.0", + "async-lock 3.4.1", + "async-process 2.4.0", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener 5.4.1", + "futures-core", + "futures-lite 2.6.1", + "hex", + "nix 0.30.1", + "ordered-stream", + "serde", + "serde_repr", + "tokio", + "tracing", + "uds_windows", + "windows-sys 0.59.0", + "winnow 0.7.12", + "zbus_macros 5.9.0", + "zbus_names 4.2.0", + "zvariant 5.6.0", ] [[package]] name = "zbus_macros" -version = "3.14.1" +version = "3.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d1794a946878c0e807f55a397187c11fc7a038ba5d868e7db4f3bd7760bc9d" +checksum = "7131497b0f887e8061b430c530240063d33bf9455fa34438f388a245da69e0a5" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", "regex", "syn 1.0.109", - "zvariant_utils", + "zvariant_utils 1.0.1", +] + +[[package]] +name = "zbus_macros" +version = "5.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef9859f68ee0c4ee2e8cde84737c78e3f4c54f946f2a38645d0d4c7a95327659" +dependencies = [ + "proc-macro-crate 3.3.0", + "proc-macro2", + "quote", + "syn 2.0.104", + "zbus_names 4.2.0", + "zvariant 5.6.0", + "zvariant_utils 3.2.0", ] [[package]] name = "zbus_names" -version = "2.6.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb80bb776dbda6e23d705cf0123c3b95df99c4ebeaec6c2599d4a5419902b4a9" +checksum = "437d738d3750bed6ca9b8d423ccc7a8eb284f6b1d6d4e225a0e4e6258d864c8d" dependencies = [ "serde", "static_assertions", - "zvariant", + "zvariant 3.15.2", +] + +[[package]] +name = "zbus_names" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97" +dependencies = [ + "serde", + "static_assertions", + "winnow 0.7.12", + "zvariant 5.6.0", +] + +[[package]] +name = "zeno" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524" + +[[package]] +name = "zerocopy" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdbb9122ea75b11bf96e7492afb723e8a7fbe12c67417aa95e7e3d18144d37cd" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + +[[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.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1f7e205ce79eb2da3cd71c5f55f3589785cb7c79f6a03d1c8d1491bda5d089" +dependencies = [ + "zune-core", ] [[package]] name = "zvariant" -version = "3.15.0" +version = "3.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44b291bee0d960c53170780af148dca5fa260a63cdd24f1962fa82e03e53338c" +checksum = "4eef2be88ba09b358d3b58aca6e41cd853631d44787f319a1383ca83424fb2db" dependencies = [ "byteorder", "enumflags2", "libc", "serde", "static_assertions", + "zvariant_derive 3.15.2", +] + +[[package]] +name = "zvariant" +version = "5.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91b3680bb339216abd84714172b5138a4edac677e641ef17e1d8cb1b3ca6e6f" +dependencies = [ + "endi", + "enumflags2", + "serde", "url", - "zvariant_derive", + "winnow 0.7.12", + "zvariant_derive 5.6.0", + "zvariant_utils 3.2.0", ] [[package]] name = "zvariant_derive" -version = "3.15.0" +version = "3.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "934d7a7dfc310d6ee06c87ffe88ef4eca7d3e37bb251dece2ef93da8f17d8ecd" +checksum = "37c24dc0bed72f5f90d1f8bb5b07228cbf63b3c6e9f82d82559d4bae666e7ed9" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", "syn 1.0.109", - "zvariant_utils", + "zvariant_utils 1.0.1", +] + +[[package]] +name = "zvariant_derive" +version = "5.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8c68501be459a8dbfffbe5d792acdd23b4959940fc87785fb013b32edbc208" +dependencies = [ + "proc-macro-crate 3.3.0", + "proc-macro2", + "quote", + "syn 2.0.104", + "zvariant_utils 3.2.0", ] [[package]] @@ -1797,3 +6856,17 @@ dependencies = [ "quote", "syn 1.0.109", ] + +[[package]] +name = "zvariant_utils" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16edfee43e5d7b553b77872d99bc36afdda75c223ca7ad5e3fbecd82ca5fc34" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "static_assertions", + "syn 2.0.104", + "winnow 0.7.12", +] diff --git a/Cargo.toml b/Cargo.toml index 7e500c5..423ebca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,14 +2,66 @@ name = "cosmic-screenshot" version = "0.1.0" edition = "2021" +license = "GPL-3.0-only" +description = "Screenshot functionality for COSMIC desktop with D-Bus interface" +repository = "https://github.com/pop-os/cosmic-screenshot" + +[features] +default = [] +debug = [] + +[lib] +name = "cosmic_screenshot" +crate-type = ["cdylib", "rlib"] + +[[bin]] +name = "cosmic-screenshot" +path = "src/main.rs" [dependencies] -ashpd = { version = "0.6.8", default-features = false, features = ["tokio"] } +ashpd = { version = "0.11", default-features = false, features = ["tokio"] } +async-trait = "0.1" chrono = { version = "0.4.24", default-features = false, features = ["alloc", "clock"] } -dirs = "5.0.1" -tokio = { version = "1.28.1", default-features = false, features = ["macros"] } clap = { version = "4.4.16", features = ["derive"] } -zbus = { version = "3", default-features = false } +cosmic-config = { git = "https://github.com/pop-os/libcosmic.git" } +dirs = "5.0.1" +futures-util = "0.3.31" +image = "0.25" +libc = "0.2" +nix = { version = "0.29", features = ["process", "user", "fs"] } +open = "5.3.0" +serde = { version = "1.0", features = ["derive"] } +tempfile = "3.0" +thiserror = "1.0" +tokio = { version = "1.41.0", features = ["full"] } +zbus = { version = "5", default-features = false } + +[dependencies.libcosmic] +git = "https://github.com/pop-os/libcosmic.git" +# See https://github.com/pop-os/libcosmic/blob/master/Cargo.toml for available features. +features = [ + # Accessibility support +# "a11y", + # Uses cosmic-settings-daemon to watch for config file changes + "dbus-config", + # Support creating additional application windows. + "multi-window", + # On app startup, focuses an existing instance if the app is already open + "single-instance", + # Uses tokio as the executor for the runtime + "tokio", + # Windowing support for X11, Windows, Mac, & Redox + "winit", + # Add Wayland support to winit + "wayland", + # GPU-accelerated rendering + "wgpu", + # File dialogs + "xdg-portal", +] +default-features = false +[build-dependencies] +vergen = { version = "8", features = ["git", "gitcl"] } [profile.release] codegen-units = 1 diff --git a/README.md b/README.md index 3a56ceb..75652c2 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,300 @@ # cosmic-screenshot -Utility for capturing screenshots via XDG Desktop Portal +A screenshot tool for Linux desktop environments with multi-backend support, GUI, CLI, D-Bus service, and library API. + +## Features + +### Core Functionality +- **Multi-backend screenshot system** with automatic fallback +- **Interactive region selection** with fullscreen overlay and snipper +- **All screenshot types**: Full desktop, current screen, window selection, interactive screen selection, and rectangular regions +- **COSMIC UI** with persistent settings and 360p thumbnails +- **Command-line interface** with comprehensive options +- **D-Bus service** for system integration +- **Library API** for use in other applications + +### Screenshot Backends +- **KWin ScreenShot2**: Preferred backend with full feature support +- **Freedesktop Portal**: Fallback backend with workspace/interactive support +- **Automatic backend selection** with intelligent capability-based routing + +### User Interface +- **COSMIC-native GUI** following official design standards +- **Persistent settings** using cosmic-config +- **Screenshot-on-startup** feature +- **Memory of selection areas** across sessions (optional) +- **Professional interaction model** with resize handles and keyboard shortcuts +- **Native file dialogs** with directory persistence + +### Integration +- **Complete D-Bus API** (`com.system76.CosmicScreenshot`) +- **Desktop file** with multiple screenshot actions +- **System service** support with automatic activation +- **Multi-window architecture** with performance optimization +- **Cross-platform compatibility** (Linux desktop environments) + +## Installation + +### From Source + +#### Prerequisites +- Rust (latest stable) +- `just` command runner +- System libraries: libxkbcommon, wayland, vulkan, mesa, fontconfig, freetype, X11 + +```bash +git clone +cd cosmic-screenshot +just build-release +``` + +#### Debian/Ubuntu +```bash +sudo apt install build-essential just pkg-config \ + libxkbcommon-dev libwayland-dev libvulkan-dev libgl-dev \ + mesa-common-dev libinput-dev libfontconfig-dev libfreetype6-dev \ + libx11-dev libxcursor-dev libxi-dev libxrandr-dev +``` + +#### Arch Linux +```bash +sudo pacman -S just pkg-config libxkbcommon wayland vulkan-loader \ + mesa libinput fontconfig freetype2 libx11 libxcursor libxi libxrandr +``` + +### Nix/NixOS +```bash +# Build with Nix +nix-build +``` + +## Usage + +### GUI Application +```bash +# Launch the GUI +cosmic-screenshot gui + +# Or simply +cosmic-screenshot +``` + +### Command Line Interface + +#### Basic Screenshots +```bash +# Full desktop screenshot +cosmic-screenshot take --kind all + +# Current screen only +cosmic-screenshot take --kind screen + +# Window selection +cosmic-screenshot take --kind window + +# Interactive screen selection +cosmic-screenshot take --kind select + +# Interactive region selection (launches GUI) +cosmic-screenshot take --kind region +``` + +#### Advanced Options +```bash +# Screenshot with 3 second delay +cosmic-screenshot take --kind all --delay 3000 + +# Save to clipboard +cosmic-screenshot take --kind all --clipboard + +# Save to specific directory +cosmic-screenshot take --kind all --output-dir ~/Pictures/Screenshots + +# Use specific backend +cosmic-screenshot take --kind all --backend kwin + +# Combined options +cosmic-screenshot take --kind region --delay 2000 --clipboard --output-dir ~/Desktop +``` + +### Backend Management +```bash +# List available backends +cosmic-screenshot backends + +# Test D-Bus functionality +cosmic-screenshot test-dbus + +# Generate D-Bus XML interface definition +cosmic-screenshot generate-dbus-xml +``` + +### D-Bus Service +```bash +# Start the D-Bus service +cosmic-screenshot service + +# Use from other applications via D-Bus +dbus-send --session --print-reply \ + --dest=com.system76.CosmicScreenshot \ + /com/system76/CosmicScreenshot \ + com.system76.CosmicScreenshot.take_screenshot \ + string:"all" uint32:0 boolean:false string:"" +``` + +## D-Bus API Reference + +### Service Information +- **Service**: `com.system76.CosmicScreenshot` +- **Object Path**: `/com/system76/CosmicScreenshot` +- **Interface**: `com.system76.CosmicScreenshot` + +### Available Methods + +#### `take_screenshot(kind, delay_ms, save_to_clipboard, save_dir) → result` +Take a screenshot with basic options. + +#### `take_screenshot_with_backend(kind, delay_ms, save_to_clipboard, save_dir, backend) → result` +Take a screenshot with specific backend selection. + +#### `get_available_backends() → backends` +List all available screenshot backends. + +#### `supports_kind(kind) → supported` +Check if a screenshot type is supported. + +#### `supports_kind_with_backend(kind, backend) → supported` +Check if a specific backend supports a screenshot type. + +#### `get_backend_capabilities() → capabilities` +Get detailed capability matrix for all backends. + +### Screenshot Types +- `"all"` - All screens +- `"screen"` - Current screen +- `"window"` - Window under cursor +- `"select"` - Interactive screen selection +- `"region"` - Interactive region selection (use CLI, not supported via D-Bus) + +### Backend Types +- `"auto"` - Automatic selection (default) +- `"kwin"` - KWin ScreenShot2 backend +- `"freedesktop"` - Freedesktop Portal backend + +## Library Usage + +Add to your `Cargo.toml`: +```toml +[dependencies] +cosmic-screenshot = { path = "path/to/cosmic-screenshot" } +``` + +Basic usage: +```rust +use cosmic_screenshot::screenshot::{ScreenshotManager, ScreenshotKind, ScreenshotOptions}; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let manager = ScreenshotManager::new(); + + let options = ScreenshotOptions { + kind: ScreenshotKind::AllScreens, + delay_ms: 0, + save_to_clipboard: false, + save_dir: Some(PathBuf::from("~/Pictures")), + }; + + let result = manager.take_screenshot(&options).await?; + println!("Screenshot saved to: {:?}", result.path); + + Ok(()) +} +``` + +## Architecture + +### Multi-Backend System +cosmic-screenshot uses a backend system that automatically detects available screenshot methods and routes requests to the most appropriate backend: + +1. **KWin ScreenShot2**: Primary backend for KDE/Plasma environments +2. **Freedesktop Portal**: Universal fallback for all desktop environments +3. **Automatic Selection**: Intelligent routing based on availability and capabilities + +### Performance Features +- Image handle caching +- Window reuse for GUI performance +- Canvas optimization +- Single tokio runtime architecture + +### Settings +User preferences saved with cosmic-config: +- Screenshot type and backend selection +- Delay and clipboard settings +- Directory and selection area memory +- Screenshot-on-startup option + +## Development + +### Building +```bash +# Debug build +just build-debug + +# Release build with debug features +cargo build --features debug --release + +# Release build +just build-release + +# Run with backtrace +just run + +# Linter +just check +``` + +### Testing +```bash +# Test all backends +cosmic-screenshot test-dbus + +# Test specific functionality +cargo test +``` + +### Contributing +1. Follow the existing code style and architecture +2. Test across different desktop environments +3. Update documentation as needed + +## Troubleshooting + +### KWin Backend Issues +If KWin screenshots fail: +```bash +# Check KWin D-Bus availability +qdbus org.kde.KWin /org/kde/KWin/ScreenShot2 + +# Force portal fallback +cosmic-screenshot take --kind all --backend freedesktop +``` + +### Permissions +Required access: +- D-Bus session bus +- Screen capture (via portal/KWin authorization) +- Output directory write permissions + +### Debug Information +Enable debug logging: +```bash +COSMIC_SCREENSHOT_DEBUG=1 cosmic-screenshot [command] +``` + +## License + +GPL-3.0-only - see [LICENSE](LICENSE) file for details. + +## Dependencies + +Built with [libcosmic](https://github.com/pop-os/libcosmic) UI toolkit. diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..0c94e3a --- /dev/null +++ b/build.rs @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-3.0-only + +use vergen::EmitBuilder; + +fn main() -> Result<(), Box> { + EmitBuilder::builder() + .all_git() + .emit()?; + Ok(()) +} \ No newline at end of file diff --git a/debian/control b/debian/control index 69e4725..7b4ae7b 100644 --- a/debian/control +++ b/debian/control @@ -1,15 +1,67 @@ Source: cosmic-screenshot -Section: admin +Section: graphics Priority: optional Maintainer: Jeremy Soller Build-Depends: debhelper-compat (=13), just (>= 1.13.0), rust-all, + pkg-config, + libxkbcommon-dev, + libwayland-dev, + libvulkan-dev, + libgl-dev, + mesa-common-dev, + libinput-dev, + libfontconfig-dev, + libfreetype6-dev, + libx11-dev, + libxcursor-dev, + libxi-dev, + libxrandr-dev, Standards-Version: 4.6.2 Homepage: https://github.com/pop-os/cosmic-screenshot Package: cosmic-screenshot Architecture: amd64 arm64 -Depends: ${misc:Depends}, ${shlibs:Depends} -Description: Cosmic Screenshot Utility +Depends: + ${misc:Depends}, + ${shlibs:Depends}, + libxkbcommon0, + libwayland-client0, + libvulkan1, + libgl1-mesa-glx, + libinput10, + libfontconfig1, + libfreetype6, + libx11-6, + libxcursor1, + libxi6, + libxrandr2 +Description: Screenshot functionality for COSMIC desktop + cosmic-screenshot is a comprehensive screenshot tool built for the COSMIC + desktop environment. It provides multi-backend screenshot capabilities + (KWin, Freedesktop Portal), interactive region selection, D-Bus service + interface, CLI tool, and professional GUI application. + . + Features include: + * Multiple screenshot backends with automatic fallback + * Interactive region selection with fullscreen overlay + * D-Bus service interface for system integration + * Professional COSMIC UI with persistent settings + * CLI interface with all screenshot types supported + * Library API for integration with other applications + +Package: libcosmic-screenshot-dev +Architecture: amd64 arm64 +Depends: + ${misc:Depends}, + cosmic-screenshot (= ${binary:Version}) +Description: Development files for cosmic-screenshot library + This package contains the development files for the cosmic-screenshot library, + including Rust library files (.rlib) and dynamic library files (.so) for + integrating screenshot functionality into other applications. + . + The library provides a complete screenshot API with multi-backend support, + interactive region selection, and D-Bus integration suitable for use in + other COSMIC desktop applications. diff --git a/debian/copyright b/debian/copyright index e6cd8a8..c26268a 100644 --- a/debian/copyright +++ b/debian/copyright @@ -2,6 +2,27 @@ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: cosmic-screenshot Upstream-Contact: Jeremy Soller Source: https://github.com/pop-os/cosmic-screenshot + Files: * -Copyright: System76 -License: GPL-3.0 +Copyright: 2023-2025 System76 +License: GPL-3.0-only + +Files: debian/* +Copyright: 2023-2025 System76 +License: GPL-3.0-only + +License: GPL-3.0-only + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, version 3 of the License. + . + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + . + You should have received a copy of the GNU General Public License + along with this program. If not, see . + . + On Debian systems, the complete text of the GNU General + Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". diff --git a/debian/cosmic-screenshot.install b/debian/cosmic-screenshot.install new file mode 100644 index 0000000..6f81d2e --- /dev/null +++ b/debian/cosmic-screenshot.install @@ -0,0 +1,5 @@ +usr/bin/cosmic-screenshot +usr/share/applications/com.system76.CosmicScreenshot.desktop +usr/share/dbus-1/services/com.system76.CosmicScreenshot.service +usr/share/dbus-1/interfaces/com.system76.CosmicScreenshot.xml +usr/share/icons/hicolor/*/apps/com.system76.CosmicScreenshot.svg \ No newline at end of file diff --git a/debian/libcosmic-screenshot-dev.install b/debian/libcosmic-screenshot-dev.install new file mode 100644 index 0000000..893cffd --- /dev/null +++ b/debian/libcosmic-screenshot-dev.install @@ -0,0 +1,2 @@ +usr/lib/libcosmic_screenshot.so +usr/lib/libcosmic_screenshot.rlib \ No newline at end of file diff --git a/justfile b/justfile index 387e29a..b277d6c 100644 --- a/justfile +++ b/justfile @@ -19,6 +19,14 @@ desktop-dest := clean(rootdir / prefix) / 'share' / 'applications' / desktop icons-src := 'resources' / 'icons' / 'hicolor' icons-dst := clean(rootdir / prefix) / 'share' / 'icons' / 'hicolor' +dbus-service := APPID + '.service' +dbus-service-src := 'resources' / dbus-service +dbus-service-dst := clean(rootdir / prefix) / 'share' / 'dbus-1' / 'services' / dbus-service + +dbus-interface := APPID + '.xml' +dbus-interface-src := 'resources' / dbus-interface +dbus-interface-dst := clean(rootdir / prefix) / 'share' / 'dbus-1' / 'interfaces' / dbus-interface + # Default recipe which runs `just build-release` default: build-release @@ -55,6 +63,11 @@ run *args: install: install -Dm0755 {{bin-src}} {{bin-dst}} install -Dm0644 {{desktop-src}} {{desktop-dest}} + install -Dm0644 {{dbus-service-src}} {{dbus-service-dst}} + install -Dm0644 {{dbus-interface-src}} {{dbus-interface-dst}} + # Install library files for development package + install -Dm0644 {{cargo-target-dir}}/release/libcosmic_screenshot.rlib {{base-dir}}/lib/libcosmic_screenshot.rlib + install -Dm0755 {{cargo-target-dir}}/release/libcosmic_screenshot.so {{base-dir}}/lib/libcosmic_screenshot.so for size in `ls {{icons-src}}`; do \ install -Dm0644 "{{icons-src}}/$size/apps/{{APPID}}.svg" "{{icons-dst}}/$size/apps/{{APPID}}.svg"; \ done diff --git a/resources/com.system76.CosmicScreenshot.desktop b/resources/com.system76.CosmicScreenshot.desktop index 839cb7e..1ca4de0 100644 --- a/resources/com.system76.CosmicScreenshot.desktop +++ b/resources/com.system76.CosmicScreenshot.desktop @@ -4,11 +4,65 @@ Name[zh_CN]=COSMIC 截图 Name[ar]=مُلتَقِطُ شَاشَةِ كوزمِك Name[pt]=Captura de Tela Type=Application -Exec=cosmic-screenshot +Exec=cosmic-screenshot gui Terminal=false -Categories=COSMIC;Utility; -Keywords=COSMIC; -Keywords[ar]=كوزمِك; -Keywords[pt]=screenshot;print; +Categories=COSMIC;Utility;Graphics;Photography; +Keywords=COSMIC;screenshot;capture;screen;region;print; +Keywords[ar]=كوزمِك;لقطة;شاشة; +Keywords[pt]=screenshot;print;captura;tela; Icon=com.system76.CosmicScreenshot StartupNotify=true + +# Permissions for Freedesktop Portal Screenshot +X-Flatpak-Permission=screenshots +X-Permission=screenshots + +# Permissions for KWin Screenshot access +X-KDE-ServiceTypes=KPluginInfo +X-KDE-Library=kwin-screenshot +X-GNOME-Autostart-Phase=Desktop +X-KDE-DBUS-Restricted-Interfaces=org.kde.kwin.Screenshot,org.kde.KWin.ScreenShot2 + +Actions=FullScreen;CurrentScreen;SelectWindow;SelectRegion;SelectArea;StartService; + +[Desktop Action FullScreen] +Name=Take Full Screenshot +Name[zh_CN]=全屏截图 +Name[ar]=التقاط لقطة شاشة كاملة +Name[pt]=Captura de Tela Completa +Exec=cosmic-screenshot take --kind all + +[Desktop Action CurrentScreen] +Name=Screenshot Current Screen +Name[zh_CN]=截取当前屏幕 +Name[ar]=التقاط الشاشة الحالية +Name[pt]=Capturar Tela Atual +Exec=cosmic-screenshot take --kind screen + +[Desktop Action SelectWindow] +Name=Screenshot Window +Name[zh_CN]=窗口截图 +Name[ar]=التقاط نافذة +Name[pt]=Capturar Janela +Exec=cosmic-screenshot take --kind window + +[Desktop Action SelectRegion] +Name=Screenshot Selected Region +Name[zh_CN]=选区截图 +Name[ar]=التقاط منطقة محددة +Name[pt]=Capturar Região Selecionada +Exec=cosmic-screenshot take --kind region + +[Desktop Action SelectArea] +Name=Interactive Screen Selection +Name[zh_CN]=交互式屏幕选择 +Name[ar]=اختيار تفاعلي للشاشة +Name[pt]=Seleção Interativa da Tela +Exec=cosmic-screenshot take --kind select + +[Desktop Action StartService] +Name=Start D-Bus Screenshot Service +Name[zh_CN]=启动D-Bus截图服务 +Name[ar]=بدء خدمة لقطة الشاشة D-Bus +Name[pt]=Iniciar Serviço de Captura D-Bus +Exec=cosmic-screenshot service diff --git a/resources/com.system76.CosmicScreenshot.service b/resources/com.system76.CosmicScreenshot.service new file mode 100644 index 0000000..289489a --- /dev/null +++ b/resources/com.system76.CosmicScreenshot.service @@ -0,0 +1,4 @@ +[D-BUS Service] +Name=com.system76.CosmicScreenshot +Exec=cosmic-screenshot service +User= \ No newline at end of file diff --git a/resources/com.system76.CosmicScreenshot.xml b/resources/com.system76.CosmicScreenshot.xml new file mode 100644 index 0000000..a2ae5d9 --- /dev/null +++ b/resources/com.system76.CosmicScreenshot.xml @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..3a26366 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1 @@ +edition = "2021" diff --git a/src/app.rs b/src/app.rs new file mode 100644 index 0000000..2629c90 --- /dev/null +++ b/src/app.rs @@ -0,0 +1,484 @@ +// SPDX-License-Identifier: GPL-3.0-only + +use crate::ui::{ScreenshotMessage, ScreenshotWidget}; +use cosmic::app::ApplicationExt; +use cosmic::iced::{event, window}; +use cosmic::{app, Element}; + +// GUI Application Implementation +pub struct CosmicScreenshotApp { + core: app::Core, + screenshot_widget: ScreenshotWidget, + snipper_window: Option, + cli_region_mode: bool, +} + +impl app::Application for CosmicScreenshotApp { + type Executor = cosmic::executor::Default; + type Flags = (); + type Message = ScreenshotMessage; + + const APP_ID: &'static str = "com.system76.CosmicScreenshot"; + + fn core(&self) -> &app::Core { + &self.core + } + + fn core_mut(&mut self) -> &mut app::Core { + &mut self.core + } + + fn init( + core: app::Core, + _flags: Self::Flags, + ) -> (Self, cosmic::Task>) { + let cli_region_mode = std::env::var("CLI_MODE_REGION").is_ok(); + + let app = Self { + core, + screenshot_widget: ScreenshotWidget::new(), + snipper_window: None, + cli_region_mode, + }; + + (app, ScreenshotWidget::init().map(cosmic::Action::App)) + } + + fn header_start(&self) -> Vec> { + vec![] + } + + fn header_center(&self) -> Vec> { + vec![] + } + + fn header_end(&self) -> Vec> { + vec![] + } + + fn view(&self) -> Element<'_, Self::Message> { + if self.cli_region_mode { + // In CLI region mode, show minimal empty view since main window is hidden + cosmic::widget::container(cosmic::widget::text("")) + .width(cosmic::iced::Length::Fill) + .height(cosmic::iced::Length::Fill) + .into() + } else { + self.screenshot_widget.view() + } + } + + fn view_window(&self, window_id: cosmic::iced::window::Id) -> Element<'_, Self::Message> { + if Some(window_id) == self.snipper_window { + // This is the snipper window - show fullscreen snipper interface + if let Some(ref snipper) = self.screenshot_widget.snipper { + return snipper.view(); + } + // Fallback for snipper window if snipper is None + return cosmic::widget::container(cosmic::widget::text("Snipper loading...")) + .width(cosmic::iced::Length::Fill) + .height(cosmic::iced::Length::Fill) + .style(|_theme| cosmic::iced::widget::container::Style { + background: Some(cosmic::iced::Color::from_rgba(0.0, 0.0, 0.0, 0.8).into()), + ..Default::default() + }) + .into(); + } + + if Some(window_id) == self.screenshot_widget.error_dialog_window_id { + // This is the error dialog window + if let Some((ref title, ref message)) = self.screenshot_widget.error_dialog { + let spacing = cosmic::theme::active().cosmic().spacing; + return cosmic::widget::container( + cosmic::widget::column() + .push(cosmic::widget::text::title3(title)) + .push(cosmic::widget::vertical_space()) + .push(cosmic::widget::text(message)) + .push(cosmic::widget::vertical_space()) + .push( + cosmic::widget::row() + .push(cosmic::widget::horizontal_space()) + .push( + cosmic::widget::button::standard("OK") + .on_press(ScreenshotMessage::DismissErrorDialog), + ), + ) + .spacing(spacing.space_s) + .max_width(400), + ) + .padding(spacing.space_m) + .width(cosmic::iced::Length::Fill) + .height(cosmic::iced::Length::Fill) + .into(); + } + } + + // This is the main window - show main interface only + self.view() + } + + #[allow(clippy::too_many_lines)] + fn update(&mut self, message: Self::Message) -> cosmic::Task> { + // Handle window lifecycle messages + match message { + ScreenshotMessage::MainWindowOpened(window_id) => { + // Handle OS-level window open events - used for CLI mode logic only + // Note: This receives ALL window opens (main + snipper windows) + // In CLI region mode, minimize the main window immediately + if self.cli_region_mode { + // Check if this is actually the main window + if Some(window_id) == self.core.main_window_id() { + println!("CLI mode: hiding main window"); + return window::minimize(window_id, true).map(cosmic::Action::App); + } + } + } + ScreenshotMessage::SnipperWindowOpened(window_id) => { + // Handle application-level snipper window creation (not OS window events) + // This is sent immediately when creating a snipper window to set up state + self.snipper_window = Some(window_id); + // Set unique title for KWin to identify this window + return self.set_window_title("cosmic-screenshot-snipper".to_string(), window_id); + } + ScreenshotMessage::SnipperWindowClosed(window_id) => { + if Some(window_id) == self.snipper_window { + self.snipper_window = None; + // Also clear the window ID in the screenshot widget + self.screenshot_widget.snipper_window_id = None; + } + } + ScreenshotMessage::ErrorDialogClosed(window_id) => { + if Some(window_id) == self.screenshot_widget.error_dialog_window_id { + self.screenshot_widget.error_dialog_window_id = None; + self.screenshot_widget.error_dialog = None; + } + } + ScreenshotMessage::WindowCloseRequested(window_id) => { + // Route to specific handlers based on window type + if Some(window_id) == self.screenshot_widget.error_dialog_window_id { + return cosmic::Task::perform( + async move { ScreenshotMessage::ErrorDialogClosed(window_id) }, + cosmic::Action::App, + ); + } else if Some(window_id) == self.snipper_window { + return cosmic::Task::perform( + async move { ScreenshotMessage::SnipperWindowClosed(window_id) }, + cosmic::Action::App, + ); + } + } + ScreenshotMessage::WindowClosed(window_id) => { + // Route to specific handlers based on window type + if Some(window_id) == self.screenshot_widget.error_dialog_window_id { + return cosmic::Task::perform( + async move { ScreenshotMessage::ErrorDialogClosed(window_id) }, + cosmic::Action::App, + ); + } else if Some(window_id) == self.snipper_window { + return cosmic::Task::perform( + async move { ScreenshotMessage::SnipperWindowClosed(window_id) }, + cosmic::Action::App, + ); + } + } + ScreenshotMessage::CloseSnipperWindow => { + // Actually close the snipper window (only when truly closing, not hiding) + if let Some(window_id) = self.snipper_window { + println!("Main app closing snipper window: {window_id:?}"); + self.snipper_window = None; + self.screenshot_widget.snipper_window_id = None; + return window::close(window_id).map(cosmic::Action::App); + } + } + ScreenshotMessage::HideSnipperWindow => { + // Hide the snipper window by minimizing it + if let Some(window_id) = self.snipper_window { + println!("Main app hiding snipper window: {window_id:?}"); + return window::minimize(window_id, true).map(cosmic::Action::App); + } + } + ScreenshotMessage::ShowSnipperWindow => { + // Show the snipper window by unminimizing it and bringing it to front + if let Some(window_id) = self.snipper_window { + println!("Main app showing snipper window: {window_id:?}"); + + // Check if we're on Wayland or X11 + let is_wayland = std::env::var("XDG_SESSION_TYPE") + .map(|session_type| session_type == "wayland") + .unwrap_or(false); + + // Check if we're running under KWin + let is_kwin = std::env::var("DESKTOP_SESSION") + .map(|session| session.contains("plasma") || session.contains("kde")) + .unwrap_or(false) + || std::env::var("XDG_CURRENT_DESKTOP") + .map(|desktop| desktop.contains("KDE")) + .unwrap_or(false); + + if is_wayland && is_kwin { + // KWin on Wayland: Use KWin scripting API for proper window raising + return cosmic::Task::batch([ + window::minimize(window_id, false).map(cosmic::Action::App), + window::maximize(window_id, true).map(cosmic::Action::App), + cosmic::Task::perform(raise_window_kwin(window_id), |result| { + if let Err(e) = result { + println!("Failed to raise window via KWin: {e}"); + } else { + println!("Successfully raised window via KWin"); + } + // Return a dummy message that won't trigger anything + ScreenshotMessage::BackendsLoaded(vec![]) + }) + .map(cosmic::Action::App), + ]); + } else if is_wayland { + // Other Wayland compositors: Use activation token approach + return cosmic::Task::batch([ + window::minimize(window_id, false).map(cosmic::Action::App), + window::maximize(window_id, true).map(cosmic::Action::App), + cosmic::iced_winit::platform_specific::wayland::commands::activation::request_token( + Some("cosmic-screenshot".to_string()), + Some(window_id) + ).then(move |token| { + if let Some(token) = token { + cosmic::iced_winit::platform_specific::wayland::commands::activation::activate(window_id, token) + } else { + cosmic::Task::none() + } + }), + ]); + } + // X11: Use gain_focus approach + return cosmic::Task::batch([ + window::minimize(window_id, false).map(cosmic::Action::App), + window::maximize(window_id, true).map(cosmic::Action::App), + window::gain_focus(window_id).map(cosmic::Action::App), + ]); + } + } + ScreenshotMessage::Exit => { + // Exit the application gracefully by closing main window + if let Some(main_window) = self.core.main_window_id() { + return window::close(main_window).map(cosmic::Action::App); + } + } + ScreenshotMessage::OpenErrorDialog(title, message) => { + // Store the error dialog content first + self.screenshot_widget.error_dialog = Some((title.clone(), message.clone())); + + // Create error dialog window + let window_settings = window::Settings { + size: cosmic::iced::Size::new(400.0, 200.0), + position: window::Position::Centered, + resizable: false, + decorations: true, + ..Default::default() + }; + + return cosmic::Task::batch([window::open(window_settings) + .1 + .map(ScreenshotMessage::ErrorDialogOpened)]) + .map(cosmic::Action::App); + } + ScreenshotMessage::ErrorDialogOpened(window_id) => { + self.screenshot_widget.error_dialog_window_id = Some(window_id); + return self.set_window_title("Error".to_string(), window_id); + } + ScreenshotMessage::DismissErrorDialog => { + // Close the error dialog window + if let Some(window_id) = self.screenshot_widget.error_dialog_window_id { + self.screenshot_widget.error_dialog_window_id = None; + self.screenshot_widget.error_dialog = None; + return window::close(window_id).map(cosmic::Action::App); + } + } + _ => {} + } + + self.screenshot_widget + .update(message) + .map(cosmic::Action::App) + } + + fn subscription(&self) -> cosmic::iced::Subscription { + let mut subscriptions = vec![]; + + // Window event subscription - handles OS-level window events + // Note: This sends MainWindowOpened for ALL windows (main + snipper) + // MainWindowOpened is used for CLI mode logic, not snipper setup + subscriptions.push(event::listen_with(|event, _, window_id| { + if let cosmic::iced::Event::Window(window_event) = event { + match window_event { + cosmic::iced::window::Event::Opened { .. } => { + // Send OS window open event - used for CLI mode window hiding + // SnipperWindowOpened is sent separately when creating windows + Some(ScreenshotMessage::MainWindowOpened(window_id)) + } + cosmic::iced::window::Event::CloseRequested => { + // Send generic close event, handlers will check their own windows + Some(ScreenshotMessage::WindowCloseRequested(window_id)) + } + cosmic::iced::window::Event::Closed => { + // Send generic closed event, handlers will check their own windows + Some(ScreenshotMessage::WindowClosed(window_id)) + } + _ => None, + } + } else { + None + } + })); + + // Snipper subscription when in region selection mode + if self.screenshot_widget.region_selection_mode { + subscriptions.push( + crate::snipper::Snipper::subscription() + .map(|snipper_msg| ScreenshotMessage::SnipperMessage(snipper_msg)), + ); + } + + cosmic::iced::Subscription::batch(subscriptions) + } +} + +/// Helper function to raise a window using `KWin`'s scripting API +async fn raise_window_kwin(_window_id: cosmic::iced::window::Id) -> Result<(), String> { + use std::io::Write; + use zbus::Connection; + + // KWin script to find and raise the window (matching kdotool format) + let script = r#" +function output_debug(message) { + // Empty debug for now +} + +function output_error(message) { + print("cosmic-screenshot ERROR", message); +} + +function output_result(message) { + if (message == null) { + message = "null"; + } + print("cosmic-screenshot RESULT", message); +} + +// KDE 6 functions (assume KDE 6 for now) +workspace_windowList = () => workspace.windowList(); +workspace_activeWindow = () => workspace.activeWindow; +workspace_setActiveWindow = (window) => { workspace.activeWindow = window; }; +workspace_raiseWindow = (window) => { + if (workspace.raiseWindow) { + workspace.raiseWindow(window); + } else { + output_error("`windowraise` unsupported in this KDE version"); + } +}; + +function run() { + output_debug("Looking for cosmic-screenshot-snipper window"); + + // Find window by checking all clients + let targetWindow = null; + let windowList = workspace_windowList(); + + for (let i = 0; i < windowList.length; i++) { + let w = windowList[i]; + // Look specifically for the snipper window by its unique title + if (w.caption && w.caption.includes('cosmic-screenshot-snipper')) { + targetWindow = w; + break; // Found the exact window we want + } + } + + if (targetWindow) { + output_debug("Found cosmic-screenshot-snipper window, raising it"); + // First activate the window + workspace_setActiveWindow(targetWindow); + // Then raise it to front + workspace_raiseWindow(targetWindow); + output_result("Snipper window raised successfully"); + } else { + output_error("cosmic-screenshot-snipper window not found"); + } +} + +run(); + "# + .to_string(); + + // Connect to KWin's scripting D-Bus interface + let connection = Connection::session().await.map_err(|e| e.to_string())?; + + // Create a proxy for KWin's scripting interface + let proxy = zbus::Proxy::new( + &connection, + "org.kde.KWin", + "/Scripting", + "org.kde.kwin.Scripting", + ) + .await + .map_err(|e| e.to_string())?; + + // Create a temporary script file (KWin expects a file path, not inline script) + let mut temp_file = tempfile::NamedTempFile::new().map_err(|e| e.to_string())?; + temp_file + .write_all(script.as_bytes()) + .map_err(|e| e.to_string())?; + let temp_path = temp_file.path().to_str().ok_or("Invalid temp path")?; + + // Make script name unique to avoid conflicts + let script_name = format!( + "cosmic-screenshot-raise-{}", + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() + ); + + // Generate unique script name to avoid conflicts + + // Load script into KWin + println!("Loading KWin script from: {temp_path}"); + let result: Result = proxy + .call("loadScript", &(temp_path, script_name.clone())) + .await; + let script_id = match result { + Ok(id) => { + println!("KWin script loaded with ID: {id}"); + if id < 0 { + return Err(format!("KWin returned negative script ID: {id}")); + } + id + } + Err(e) => { + return Err(format!("Failed to call loadScript: {e}")); + } + }; + + // Create a proxy for the specific script instance + let script_path = format!("/Scripting/Script{script_id}"); + let script_proxy = zbus::Proxy::new( + &connection, + "org.kde.KWin", + script_path.as_str(), + "org.kde.kwin.Script", + ) + .await + .map_err(|e| e.to_string())?; + + // Run the script + script_proxy + .call::<_, _, ()>("run", &()) + .await + .map_err(|e| e.to_string())?; + + // Stop and unload the script + script_proxy + .call::<_, _, ()>("stop", &()) + .await + .map_err(|e| e.to_string())?; + let _: Result<(), _> = proxy.call("unloadScript", &(script_id,)).await; + + Ok(()) +} diff --git a/src/dbus.rs b/src/dbus.rs new file mode 100644 index 0000000..8700e89 --- /dev/null +++ b/src/dbus.rs @@ -0,0 +1,311 @@ +// SPDX-License-Identifier: GPL-3.0-only + +use crate::screenshot::{ScreenshotKind, ScreenshotManager, ScreenshotOptions}; +use crate::settings::APP_ID; +use std::collections::HashMap; +use std::path::PathBuf; +use zbus::{connection, fdo, interface, zvariant::Value, Connection}; + +pub struct ScreenshotService { + manager: ScreenshotManager, +} + +impl ScreenshotService { + #[must_use] + pub fn new() -> Self { + Self { + manager: ScreenshotManager::new(), + } + } +} + +impl Default for ScreenshotService { + fn default() -> Self { + Self::new() + } +} + +#[interface(name = "com.system76.CosmicScreenshot")] +impl ScreenshotService { + /// Take a screenshot with the specified options + /// + /// # Arguments + /// * `kind` - Screenshot type: "all", "screen", "window", "select", "region" + /// * `delay_ms` - Delay in milliseconds before taking screenshot + /// * `save_to_clipboard` - Whether to save to clipboard + /// * `save_dir` - Optional directory to save screenshot + /// + /// # Returns + /// A dictionary containing: + /// * `path` - File path if saved to file (optional) + /// * `saved_to_clipboard` - Boolean indicating if saved to clipboard + /// * `thumbnail_data` - PNG thumbnail data as bytes + /// * `full_image_data` - Full resolution PNG image data as bytes + async fn take_screenshot( + &self, + kind: &str, + delay_ms: u32, + save_to_clipboard: bool, + save_dir: String, + ) -> fdo::Result>> { + let screenshot_kind = match kind { + "screen" => ScreenshotKind::ScreenUnderCursor, + "window" => ScreenshotKind::WindowUnderCursor, + "select" => ScreenshotKind::SelectScreen, + "region" => ScreenshotKind::RectangularRegion, + _ => ScreenshotKind::AllScreens, + }; + + let options = ScreenshotOptions { + kind: screenshot_kind, + delay_ms, + save_to_clipboard, + save_dir: if save_dir.is_empty() { + None + } else { + Some(PathBuf::from(save_dir)) + }, + }; + + // For region selection, we cannot run cosmic::app::run from within an async context + // Instead, return an error asking the user to use the CLI + if screenshot_kind == ScreenshotKind::RectangularRegion { + return Err(fdo::Error::Failed( + "Region selection via D-Bus is not supported. Please use 'cosmic-screenshot take --kind region' from the command line.".to_string() + )); + } + + match self.manager.take_screenshot(&options).await { + Ok(result) => { + let mut response = HashMap::new(); + + if let Some(path) = result.path { + response.insert( + "path".to_string(), + Value::Str(path.to_string_lossy().to_string().into()), + ); + } + + response.insert( + "saved_to_clipboard".to_string(), + Value::Bool(result.saved_to_clipboard), + ); + response.insert( + "thumbnail_data".to_string(), + Value::Array(result.thumbnail_data.into()), + ); + response.insert( + "full_image_data".to_string(), + Value::Array(result.full_image_data.into()), + ); + + Ok(response) + } + Err(err) => Err(fdo::Error::Failed(format!("Screenshot failed: {err}"))), + } + } + + /// Get available screenshot backends + /// + /// # Returns + /// Array of available screenshot backend names + async fn get_available_backends(&self) -> fdo::Result> { + Ok(self.manager.get_available_grabbers().await) + } + + /// Take a screenshot with backend selection + /// + /// # Arguments + /// * `kind` - Screenshot type: "all", "screen", "window", "select", "region" + /// * `delay_ms` - Delay in milliseconds before taking screenshot + /// * `save_to_clipboard` - Whether to save to clipboard + /// * `save_dir` - Optional directory to save screenshot + /// * `backend` - Backend to use: "auto", "kwin", "freedesktop", etc. + /// + /// # Returns + /// A dictionary containing: + /// * `path` - File path if saved to file (optional) + /// * `saved_to_clipboard` - Boolean indicating if saved to clipboard + /// * `thumbnail_data` - PNG thumbnail data as bytes + /// * `full_image_data` - Full resolution PNG image data as bytes + async fn take_screenshot_with_backend( + &self, + kind: &str, + delay_ms: u32, + save_to_clipboard: bool, + save_dir: String, + backend: String, + ) -> fdo::Result>> { + let screenshot_kind = match kind { + "screen" => ScreenshotKind::ScreenUnderCursor, + "window" => ScreenshotKind::WindowUnderCursor, + "select" => ScreenshotKind::SelectScreen, + "region" => ScreenshotKind::RectangularRegion, + _ => ScreenshotKind::AllScreens, + }; + + let options = ScreenshotOptions { + kind: screenshot_kind, + delay_ms, + save_to_clipboard, + save_dir: if save_dir.is_empty() { + None + } else { + Some(PathBuf::from(save_dir)) + }, + }; + + // For region selection, we cannot run cosmic::app::run from within an async context + // Instead, return an error asking the user to use the CLI + if screenshot_kind == ScreenshotKind::RectangularRegion { + return Err(fdo::Error::Failed( + "Region selection via D-Bus is not supported. Please use 'cosmic-screenshot take --kind region' from the command line.".to_string() + )); + } + + let backend_name = if backend == "auto" { None } else { Some(backend.as_str()) }; + match self.manager.take_screenshot_with_backend(&options, backend_name).await { + Ok(result) => { + let mut response = HashMap::new(); + + if let Some(path) = result.path { + response.insert( + "path".to_string(), + Value::Str(path.to_string_lossy().to_string().into()), + ); + } + + response.insert( + "saved_to_clipboard".to_string(), + Value::Bool(result.saved_to_clipboard), + ); + response.insert( + "thumbnail_data".to_string(), + Value::Array(result.thumbnail_data.into()), + ); + response.insert( + "full_image_data".to_string(), + Value::Array(result.full_image_data.into()), + ); + + Ok(response) + } + Err(err) => Err(fdo::Error::Failed(format!("Screenshot failed: {err}"))), + } + } + + /// Check if a specific screenshot kind is supported + /// + /// # Arguments + /// * `kind` - Screenshot type to check + /// + /// # Returns + /// Boolean indicating if the kind is supported + async fn supports_kind(&self, kind: &str) -> fdo::Result { + let screenshot_kind = match kind { + "all" => ScreenshotKind::AllScreens, + "screen" => ScreenshotKind::ScreenUnderCursor, + "window" => ScreenshotKind::WindowUnderCursor, + "select" => ScreenshotKind::SelectScreen, + "region" => ScreenshotKind::RectangularRegion, + _ => return Ok(false), + }; + + if let Some(grabber) = self.manager.get_available_grabber().await { + Ok(grabber.supports_kind(screenshot_kind)) + } else { + Ok(false) + } + } + + /// Check if a specific screenshot kind is supported by a backend + /// + /// # Arguments + /// * `kind` - Screenshot type to check + /// * `backend` - Backend name to check + /// + /// # Returns + /// Boolean indicating if the kind is supported by the backend + async fn supports_kind_with_backend(&self, kind: &str, backend: &str) -> fdo::Result { + let screenshot_kind = match kind { + "all" => ScreenshotKind::AllScreens, + "screen" => ScreenshotKind::ScreenUnderCursor, + "window" => ScreenshotKind::WindowUnderCursor, + "select" => ScreenshotKind::SelectScreen, + "region" => ScreenshotKind::RectangularRegion, + _ => return Ok(false), + }; + + Ok(self.manager.supports_kind_with_backend(screenshot_kind, backend).await) + } + + /// Get detailed backend capabilities + /// + /// # Returns + /// A dictionary where keys are backend names and values are arrays of supported screenshot kinds + async fn get_backend_capabilities(&self) -> fdo::Result>> { + let capabilities = self.manager.get_backend_capabilities().await; + let mut result = HashMap::new(); + + for (backend, kinds) in capabilities { + let kind_strings: Vec = kinds.into_iter().map(|kind| { + match kind { + crate::screenshot::ScreenshotKind::AllScreens => "all".to_string(), + crate::screenshot::ScreenshotKind::ScreenUnderCursor => "screen".to_string(), + crate::screenshot::ScreenshotKind::WindowUnderCursor => "window".to_string(), + crate::screenshot::ScreenshotKind::SelectScreen => "select".to_string(), + crate::screenshot::ScreenshotKind::RectangularRegion => "region".to_string(), + } + }).collect(); + result.insert(backend, kind_strings); + } + + Ok(result) + } +} + +pub struct ScreenshotServiceInterface { + connection: Connection, +} + +impl ScreenshotServiceInterface { + /// Create a new D-Bus service interface + #[allow(clippy::missing_errors_doc)] + pub async fn new() -> zbus::Result { + let service = ScreenshotService::new(); + let object_path = format!("/{}", APP_ID.replace('.', "/")); + let connection = connection::Builder::session()? + .name(APP_ID)? + .serve_at(object_path.as_str(), service)? + .build() + .await?; + + Ok(Self { connection }) + } + + /// Run the D-Bus service + #[allow(clippy::missing_errors_doc)] + pub async fn run(&self) -> zbus::Result<()> { + // Wait for termination signals for graceful shutdown + let mut sigterm = tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())?; + let mut sigint = tokio::signal::unix::signal(tokio::signal::unix::SignalKind::interrupt())?; + + println!("D-Bus service running. Press Ctrl+C to stop."); + + tokio::select! { + _ = sigterm.recv() => { + println!("Received SIGTERM, shutting down gracefully..."); + }, + _ = sigint.recv() => { + println!("Received SIGINT (Ctrl+C), shutting down gracefully..."); + }, + } + + Ok(()) + } + + /// Get the D-Bus connection + pub fn connection(&self) -> &Connection { + &self.connection + } +} diff --git a/src/error_handling.rs b/src/error_handling.rs new file mode 100644 index 0000000..d689450 --- /dev/null +++ b/src/error_handling.rs @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: GPL-3.0-only + +//! Universal error handling for cosmic-screenshot +//! +//! This module provides centralized error reporting that adapts to the application mode: +//! - GUI mode: Shows error dialogs and warning notifications +//! - CLI/D-Bus mode: Uses eprintln! for console output +//! - Service mode: Uses structured logging + +use std::sync::atomic::{AtomicBool, Ordering}; +use crate::notifications::{show_system_notification, notifications_available, NotificationType}; + +/// Global flag to track if we're running in GUI mode +static GUI_MODE: AtomicBool = AtomicBool::new(false); + +/// Error severity levels +#[derive(Debug, Clone, PartialEq)] +pub enum ErrorSeverity { + /// Critical errors that prevent the application from functioning + Error, + /// Warnings about fallbacks or degraded functionality + Warning, + /// Informational messages about state changes + Info, +} + +/// Set whether the application is running in GUI mode +pub fn set_gui_mode(enabled: bool) { + GUI_MODE.store(enabled, Ordering::Relaxed); +} + +/// Check if the application is running in GUI mode +pub fn is_gui_mode() -> bool { + GUI_MODE.load(Ordering::Relaxed) +} + +/// Channel for sending GUI error messages +static GUI_ERROR_SENDER: std::sync::OnceLock> = std::sync::OnceLock::new(); + +/// Set up GUI error message channel +pub fn setup_gui_channel() -> std::sync::mpsc::Receiver<(ErrorSeverity, String, String)> { + let (sender, receiver) = std::sync::mpsc::channel(); + let _ = GUI_ERROR_SENDER.set(sender); + receiver +} + +/// Universal error reporting function +/// +/// This function handles error reporting across different application modes: +/// - In GUI mode, it sends messages to show error dialogs or notifications +/// - In CLI/service mode, it uses eprintln! for console output +pub fn report_error(severity: ErrorSeverity, title: &str, message: &str) { + if is_gui_mode() { + match severity { + ErrorSeverity::Error => { + // Errors should show dialogs in GUI mode + if let Some(sender) = GUI_ERROR_SENDER.get() { + let _ = sender.send((severity.clone(), title.to_string(), message.to_string())); + } else { + eprintln!("ERROR: {title}: {message}"); + } + } + ErrorSeverity::Warning | ErrorSeverity::Info => { + // Warnings and info should use system notifications + let notification_type = match severity { + ErrorSeverity::Warning => NotificationType::Warning, + ErrorSeverity::Info => NotificationType::Info, + ErrorSeverity::Error => unreachable!(), + }; + + // Try to show system notification + let title_clone = title.to_string(); + let message_clone = message.to_string(); + tokio::spawn(async move { + // First check if notifications are available + if notifications_available().await { + match show_system_notification(notification_type, &title_clone, &message_clone).await { + Ok(_) => { + // Notification shown successfully + return; + } + Err(e) => { + // Log the notification error but continue to fallback + eprintln!("Notification failed: {e}"); + } + } + } + + // Fall back to console if notifications aren't available or fail + match severity { + ErrorSeverity::Warning => { + eprintln!("WARNING: {title_clone}: {message_clone}"); + } + ErrorSeverity::Info => { + eprintln!("INFO: {title_clone}: {message_clone}"); + } + ErrorSeverity::Error => unreachable!(), + } + }); + } + } + } else { + // In CLI/service mode, use standard error output + match severity { + ErrorSeverity::Error => { + eprintln!("ERROR: {title}: {message}"); + } + ErrorSeverity::Warning => { + eprintln!("WARNING: {title}: {message}"); + } + ErrorSeverity::Info => { + eprintln!("INFO: {title}: {message}"); + } + } + } +} + +/// Convenience macros for common error reporting patterns +#[macro_export] +macro_rules! report_error { + ($title:expr, $msg:expr) => { + $crate::error_handling::report_error( + $crate::error_handling::ErrorSeverity::Error, + $title, + $msg, + ) + }; +} + +#[macro_export] +macro_rules! report_warning { + ($title:expr, $msg:expr) => { + $crate::error_handling::report_error( + $crate::error_handling::ErrorSeverity::Warning, + $title, + $msg, + ) + }; +} + +#[macro_export] +macro_rules! report_info { + ($title:expr, $msg:expr) => { + $crate::error_handling::report_error( + $crate::error_handling::ErrorSeverity::Info, + $title, + $msg, + ) + }; +} + +/// Format error for display (used in GUI dialogs) +#[must_use] +pub fn format_error_message(title: &str, message: &str) -> String { + format!("{title}\n\n{message}") +} + +/// Check if an error should trigger a dialog in GUI mode +#[must_use] +pub fn should_show_dialog(severity: &ErrorSeverity) -> bool { + match severity { + ErrorSeverity::Error => true, + ErrorSeverity::Warning | ErrorSeverity::Info => false, // Warnings should use notifications + } +} + +/// Show a success notification (convenience function) +pub fn report_success(title: &str, message: &str) { + if is_gui_mode() { + let notification_type = NotificationType::Success; + let title_clone = title.to_string(); + let message_clone = message.to_string(); + + tokio::spawn(async move { + match show_system_notification(notification_type, &title_clone, &message_clone).await { + Ok(_) => { + // Success notification shown + } + Err(_) => { + // Fall back to console for success messages + println!("SUCCESS: {title_clone}: {message_clone}"); + } + } + }); + } else { + println!("SUCCESS: {title}: {message}"); + } +} + +/// Check if system notifications are available and working +pub async fn check_notification_support() -> bool { + notifications_available().await +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..3672e9d --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-3.0-only + +//! Screenshot functionality for COSMIC desktop +//! +//! This crate provides screenshot capabilities for COSMIC desktop applications +//! with support for multiple backends and a D-Bus interface for external programs. + +pub mod screenshot; + +// Re-export main types for easier usage +pub use screenshot::{ + ScreenshotKind, ScreenshotOptions, ScreenshotResult, ScreenshotError, + Screengrabber, ScreenshotManager +}; + +// Re-export snipper types for library integration +pub use snipper::{ + Snipper, SnipperMessage, SnipperResult, SnipperState +}; + +pub mod ui; +pub mod dbus; +pub mod snipper; +pub mod app; +pub mod settings; +pub mod error_handling; +pub mod notifications; + +/// The current version of the cosmic-screenshot library +pub const VERSION: &str = env!("CARGO_PKG_VERSION"); \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 3373d73..a86dcc6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,23 @@ use ashpd::desktop::screenshot::Screenshot; -use clap::{command, ArgAction, Parser}; +use clap::{command, ArgAction, Parser, Subcommand}; +use cosmic_screenshot::screenshot::{ScreenshotKind, ScreenshotManager, ScreenshotOptions}; +use cosmic_screenshot::dbus::{ScreenshotService, ScreenshotServiceInterface}; +use cosmic_screenshot::app::CosmicScreenshotApp; +use cosmic_screenshot::settings::APP_ID; +use cosmic_screenshot::error_handling::{self, report_error, report_success, ErrorSeverity}; +use cosmic_screenshot::notifications; +use cosmic::app::Settings; use std::{collections::HashMap, fs, os::unix::fs::MetadataExt, path::PathBuf}; -use zbus::{dbus_proxy, zvariant::Value, Connection}; +use zbus::{proxy, zvariant::Value, Connection}; -#[derive(Parser, Default, Debug, Clone, PartialEq, Eq)] + +#[derive(Parser, Debug)] #[command(version, about, long_about = None)] struct Args { - /// Enable interactive mode in the portal + #[command(subcommand)] + command: Option, + + /// Enable interactive mode in the portal (legacy mode) #[clap(long, default_missing_value("true"), default_value("true"), @@ -14,7 +25,7 @@ struct Args { require_equals(true), action = ArgAction::Set)] interactive: bool, - /// Enable modal mode in the portal + /// Enable modal mode in the portal (legacy mode) #[clap(long, default_missing_value("true"), default_value("true"), @@ -22,7 +33,7 @@ struct Args { require_equals(true), action = ArgAction::Set,)] modal: bool, - /// Send a notification with the path to the saved screenshot + /// Send a notification with the path to the saved screenshot (legacy mode) #[clap(long, default_missing_value("true"), default_value("true"), @@ -30,12 +41,48 @@ struct Args { require_equals(true), action = ArgAction::Set)] notify: bool, - /// The directory to save the screenshot to, if not performing an interactive screenshot + /// The directory to save the screenshot to, if not performing an interactive screenshot (legacy mode) #[clap(short, long)] save_dir: Option, } -#[dbus_proxy(assume_defaults = true)] +#[derive(Subcommand, Debug)] +enum Commands { + /// Run the D-Bus service + Service, + /// Take a screenshot using the new interface + Take { + /// Screenshot type: all, screen, window, select, region + #[clap(short, long, default_value = "all")] + kind: String, + /// Delay in milliseconds + #[clap(short, long, default_value = "0")] + delay: u32, + /// Save to clipboard + #[clap(short = 'c', long)] + clipboard: bool, + /// Directory to save screenshot + #[clap(short = 'o', long)] + output_dir: Option, + /// Force specific backend: auto, kwin, portal + #[clap(short = 'b', long, default_value = "auto")] + backend: String, + }, + /// Launch GUI application + Gui, + /// List available screenshot backends + Backends, + /// Test D-Bus service methods + TestDbus, + /// Generate D-Bus interface XML from implementation + GenerateDbusXml, +} + +#[proxy( + interface = "org.freedesktop.Notifications", + default_service = "org.freedesktop.Notifications", + default_path = "/org/freedesktop/Notifications" +)] trait Notifications { /// Call the org.freedesktop.Notifications.Notify D-Bus method #[allow(clippy::too_many_arguments)] @@ -52,10 +99,345 @@ trait Notifications { ) -> zbus::Result; } -//TODO: better error handling -#[tokio::main(flavor = "current_thread")] -async fn main() { +#[proxy( + interface = "com.system76.CosmicScreenshot", + default_service = "com.system76.CosmicScreenshot", + default_path = "/com/system76/CosmicScreenshot" +)] +trait CosmicScreenshotProxy { + /// Take a screenshot with the specified options + fn take_screenshot( + &self, + kind: &str, + delay_ms: u32, + save_to_clipboard: bool, + save_dir: &str, + ) -> zbus::Result>; + + /// Take a screenshot with backend selection + fn take_screenshot_with_backend( + &self, + kind: &str, + delay_ms: u32, + save_to_clipboard: bool, + save_dir: &str, + backend: &str, + ) -> zbus::Result>; + + /// Get available screenshot backends + fn get_available_backends(&self) -> zbus::Result>; +} + +fn main() -> Result<(), Box> { let args = Args::parse(); + + // Create a single tokio runtime for all async operations + let rt = tokio::runtime::Runtime::new()?; + + match args.command { + Some(Commands::Service) => { + run_dbus_service(&rt) + } + Some(Commands::Gui) => { + println!("Starting GUI mode..."); + + // Enable GUI mode for error handling + error_handling::set_gui_mode(true); + + // Initialize system notifications + rt.block_on(notifications::init_notification_manager()); + + // Force fastest GPU backend for performance + std::env::set_var("WGPU_BACKEND", "primary"); + std::env::set_var("WGPU_POWER_PREF", "high-performance"); + + let settings = Settings::default() + .antialiasing(true); + cosmic::app::run::(settings, ())?; + Ok(()) + } + Some(Commands::Take { kind, delay, clipboard, output_dir, backend }) => { + run_screenshot_command(&rt, &kind, delay, clipboard, output_dir, &backend) + } + Some(Commands::Backends) => { + run_backends_command(&rt) + } + Some(Commands::TestDbus) => { + run_test_dbus_command(&rt) + } + Some(Commands::GenerateDbusXml) => { + generate_dbus_xml(&rt) + } + None => { + run_legacy_screenshot(&rt, args) + } + } +} + +/// Check if the D-Bus service is available +async fn is_dbus_service_available() -> bool { + match Connection::session().await { + Ok(conn) => { + // Check if our service name is available on the bus + match conn.call_method( + Some("org.freedesktop.DBus"), + "/org/freedesktop/DBus", + Some("org.freedesktop.DBus"), + "NameHasOwner", + &(APP_ID,), + ).await { + Ok(response) => { + response.body().deserialize::().unwrap_or_default() + } + Err(_) => false, + } + } + Err(_) => false, + } +} + +/// Delegate screenshot command to existing D-Bus service +async fn delegate_to_dbus_service( + kind: &str, + delay: u32, + clipboard: bool, + output_dir: Option, + backend: &str, +) -> Result<(), Box> { + // Check if this is region selection - D-Bus service doesn't support it + if kind == "region" { + if std::env::var("COSMIC_SCREENSHOT_DEBUG").is_ok() { + eprintln!("Region selection not supported via D-Bus, falling back to direct execution"); + } + return Err("Region selection not supported via D-Bus".into()); + } + + let connection = Connection::session().await?; + let proxy = CosmicScreenshotProxyProxy::new(&connection).await?; + + let save_dir = output_dir + .map(|p| p.to_string_lossy().to_string()) + .unwrap_or_default(); + + let result = if backend == "auto" { + proxy.take_screenshot(kind, delay, clipboard, &save_dir).await? + } else { + proxy.take_screenshot_with_backend(kind, delay, clipboard, &save_dir, backend).await? + }; + + // Parse the response + if let Some(path_value) = result.get("path") { + if let Ok(path) = <&str>::try_from(path_value) { + println!("Screenshot saved to: {path}"); + report_success("Screenshot Saved", &format!("Screenshot saved to {path}")); + } + } + if let Some(clipboard_value) = result.get("saved_to_clipboard") { + if let Ok(saved_to_clipboard) = bool::try_from(clipboard_value) { + if saved_to_clipboard { + println!("Screenshot saved to clipboard"); + report_success("Clipboard", "Screenshot copied to clipboard"); + } + } + } + + Ok(()) +} + +fn run_dbus_service(rt: &tokio::runtime::Runtime) -> Result<(), Box> { + println!("Starting D-Bus service..."); + + rt.block_on(async { + let service = ScreenshotServiceInterface::new().await?; + println!("D-Bus service started on {APP_ID}"); + service.run().await?; + Ok::<(), Box>(()) + })?; + + Ok(()) +} + +fn run_screenshot_command( + rt: &tokio::runtime::Runtime, + kind: &str, + delay: u32, + clipboard: bool, + output_dir: Option, + backend: &str, +) -> Result<(), Box> { + + // Check if D-Bus service is available and delegate to it + let should_delegate = rt.block_on(async { + is_dbus_service_available().await + }); + + if should_delegate { + if std::env::var("COSMIC_SCREENSHOT_DEBUG").is_ok() { + eprintln!("Delegating to existing D-Bus service"); + } + // Try to delegate, but fall back to direct execution if it fails (e.g., for region selection) + match rt.block_on(async { + delegate_to_dbus_service(kind, delay, clipboard, output_dir.clone(), backend).await + }) { + Ok(()) => return Ok(()), + Err(_) => { + if std::env::var("COSMIC_SCREENSHOT_DEBUG").is_ok() { + eprintln!("D-Bus delegation failed, falling back to direct execution"); + } + // Continue with direct execution below + } + } + } + + if std::env::var("COSMIC_SCREENSHOT_DEBUG").is_ok() { + eprintln!("No D-Bus service available, running directly"); + } + + let screenshot_kind = match kind { + "all" => ScreenshotKind::AllScreens, + "screen" => ScreenshotKind::ScreenUnderCursor, + "window" => ScreenshotKind::WindowUnderCursor, + "select" => ScreenshotKind::SelectScreen, + "region" => ScreenshotKind::RectangularRegion, + _ => { + report_error(ErrorSeverity::Error, "Invalid Input", &format!("Invalid screenshot kind: {kind}")); + std::process::exit(1); + } + }; + + // For region selection, we need to launch GUI mode + if screenshot_kind == ScreenshotKind::RectangularRegion { + println!("Region selection requires GUI mode, launching snipper..."); + + // Enable GUI mode for error handling + error_handling::set_gui_mode(true); + + // Initialize system notifications + rt.block_on(notifications::init_notification_manager()); + + // Set CLI mode options as environment variables for the GUI to pick up + if delay > 0 { + std::env::set_var("CLI_DELAY", delay.to_string()); + } + if clipboard { + std::env::set_var("CLI_CLIPBOARD", "true"); + } + if let Some(ref output_dir) = output_dir { + std::env::set_var("CLI_OUTPUT_DIR", output_dir.to_string_lossy().to_string()); + } + std::env::set_var("CLI_BACKEND", backend); + std::env::set_var("CLI_MODE_REGION", "true"); + + // Force fastest GPU backend for performance + std::env::set_var("WGPU_BACKEND", "primary"); + std::env::set_var("WGPU_POWER_PREF", "high-performance"); + + let settings = Settings::default() + .antialiasing(true); + return Ok(cosmic::app::run::(settings, ())?); + } + + rt.block_on(async { + let manager = ScreenshotManager::new(); + let options = ScreenshotOptions { + kind: screenshot_kind, + delay_ms: delay, + save_to_clipboard: clipboard, + save_dir: output_dir, + }; + + let backend_name = if backend == "auto" { None } else { Some(backend) }; + match manager.take_screenshot_with_backend(&options, backend_name).await { + Ok(result) => { + if let Some(path) = result.path { + println!("Screenshot saved to: {}", path.display()); + report_success("Screenshot Saved", &format!("Screenshot saved to {}", path.display())); + } + if result.saved_to_clipboard { + println!("Screenshot saved to clipboard"); + report_success("Clipboard", "Screenshot copied to clipboard"); + } + Ok(()) + } + Err(err) => { + report_error(ErrorSeverity::Error, "Screenshot Failed", &format!("Failed to take screenshot: {err}")); + std::process::exit(1); + } + } + }) +} + +fn run_backends_command(rt: &tokio::runtime::Runtime) -> Result<(), Box> { + rt.block_on(async { + // Check if D-Bus service is available and delegate to it + if is_dbus_service_available().await { + if std::env::var("COSMIC_SCREENSHOT_DEBUG").is_ok() { + eprintln!("Delegating backends query to existing D-Bus service"); + } + + let connection = Connection::session().await?; + let proxy = CosmicScreenshotProxyProxy::new(&connection).await?; + + let backends = proxy.get_available_backends().await?; + println!("Available screenshot backends:"); + for backend in backends { + println!(" - {backend}"); + } + return Ok(()); + } + + if std::env::var("COSMIC_SCREENSHOT_DEBUG").is_ok() { + eprintln!("No D-Bus service available, querying backends directly"); + } + + let manager = ScreenshotManager::new(); + let backends = manager.get_available_grabbers().await; + println!("Available screenshot backends:"); + for backend in backends { + println!(" - {backend}"); + } + Ok::<(), Box>(()) + }) +} + +fn run_test_dbus_command(rt: &tokio::runtime::Runtime) -> Result<(), Box> { + rt.block_on(async { + println!("Testing D-Bus API functionality..."); + let manager = ScreenshotManager::new(); + + println!("\n1. Available backends:"); + let backends = manager.get_available_grabbers().await; + for backend in &backends { + println!(" - {backend}"); + } + + println!("\n2. Backend capabilities:"); + let capabilities = manager.get_backend_capabilities().await; + for (backend, kinds) in capabilities { + println!(" {backend}: {kinds:?}"); + } + + println!("\n3. Testing individual backend capabilities:"); + for backend in &backends { + for kind in ["all", "screen", "window", "select", "region"] { + let screenshot_kind = match kind { + "all" => ScreenshotKind::AllScreens, + "screen" => ScreenshotKind::ScreenUnderCursor, + "window" => ScreenshotKind::WindowUnderCursor, + "select" => ScreenshotKind::SelectScreen, + "region" => ScreenshotKind::RectangularRegion, + _ => continue, + }; + let supports = manager.supports_kind_with_backend(screenshot_kind, backend).await; + println!(" {backend} supports {kind}: {supports}"); + } + } + Ok::<(), Box>(()) + }) +} + +fn run_legacy_screenshot(rt: &tokio::runtime::Runtime, args: Args) -> Result<(), Box> { + rt.block_on(async { let picture_dir = (!args.interactive).then(|| { args.save_dir .filter(|dir| dir.is_dir()) @@ -66,10 +448,8 @@ async fn main() { .interactive(args.interactive) .modal(args.modal) .send() - .await - .expect("failed to send screenshot request") - .response() - .expect("failed to receive screenshot response"); + .await? + .response()?; let uri = response.uri(); let path = match uri.scheme() { @@ -79,45 +459,32 @@ async fn main() { let filename = format!("Screenshot_{}.png", date.format("%Y-%m-%d_%H-%M-%S")); let path = picture_dir.join(filename); let tmp_path = uri.path(); - if fs::metadata(&picture_dir) - .expect("Failed to get medatata on filesystem for screenshot destination") - .dev() - != fs::metadata(tmp_path) - .expect("Failed to get metadata on filesystem for temporary path") - .dev() - { - // copy file instead - fs::copy(tmp_path, &path).expect("failed to move screenshot"); - fs::remove_file(tmp_path).expect("failed to remove temporary screenshot"); + if fs::metadata(&picture_dir)?.dev() == fs::metadata(tmp_path)?.dev() { + fs::rename(tmp_path, &path)?; } else { - fs::rename(tmp_path, &path).expect("failed to move screenshot"); + fs::copy(tmp_path, &path)?; + fs::remove_file(tmp_path)?; } - path.to_string_lossy().to_string() } else { uri.path().to_string() } } "clipboard" => String::new(), - scheme => panic!("unsupported scheme '{}'", scheme), + scheme => return Err(format!("unsupported scheme '{scheme}'").into()), }; println!("{path}"); if args.notify { - let connection = Connection::session() - .await - .expect("failed to connect to session bus"); - + let connection = Connection::session().await?; let message = if path.is_empty() { "Screenshot saved to clipboard" } else { "Screenshot saved to:" }; - let proxy = NotificationsProxy::new(&connection) - .await - .expect("failed to create proxy"); - _ = proxy + let proxy = NotificationsProxy::new(&connection).await?; + proxy .notify( "COSMIC Screenshot", 0, @@ -128,7 +495,46 @@ async fn main() { HashMap::from([("transient", &Value::Bool(true))]), 5000, ) - .await - .expect("failed to send notification"); + .await?; } + + Ok::<(), Box>(()) + }) } + +fn generate_dbus_xml(rt: &tokio::runtime::Runtime) -> Result<(), Box> { + rt.block_on(async { + // Create connection with a unique service name + let connection = Connection::session().await?; + + // Create and register our service at the object server + let service = ScreenshotService::new(); + let _interface_ref = connection + .object_server() + .at("/com/system76/CosmicScreenshot", service) + .await?; + + // Request a service name to ensure the service is properly registered + connection.request_name("com.system76.CosmicScreenshotTemp").await?; + + // Give a moment for the service to be fully registered + tokio::time::sleep(std::time::Duration::from_millis(100)).await; + + // Get introspection XML by calling the D-Bus introspection method + let introspection_xml = connection + .call_method( + Some("com.system76.CosmicScreenshotTemp"), + "/com/system76/CosmicScreenshot", + Some("org.freedesktop.DBus.Introspectable"), + "Introspect", + &(), + ) + .await?; + + // Extract the XML string from the response + let xml: String = introspection_xml.body().deserialize()?; + println!("{xml}"); + + Ok::<(), Box>(()) + }) +} \ No newline at end of file diff --git a/src/notifications.rs b/src/notifications.rs new file mode 100644 index 0000000..de9c5bb --- /dev/null +++ b/src/notifications.rs @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: GPL-3.0-only + +//! System notification support using freedesktop notification standard + +use zbus::{proxy, Connection}; +use std::collections::HashMap; + +/// Notification urgency levels +#[derive(Debug, Clone, Copy)] +pub enum NotificationUrgency { + Low = 0, + Normal = 1, + Critical = 2, +} + +/// Notification types with appropriate urgency and icons +#[derive(Debug, Clone, Copy)] +pub enum NotificationType { + Info, + Warning, + Error, + Success, +} + +impl NotificationType { + #[must_use] + pub fn urgency(&self) -> NotificationUrgency { + match self { + NotificationType::Info | NotificationType::Success => NotificationUrgency::Low, + NotificationType::Warning => NotificationUrgency::Normal, + NotificationType::Error => NotificationUrgency::Critical, + } + } + + #[must_use] + pub fn icon(&self) -> &'static str { + match self { + NotificationType::Info => "dialog-information", + NotificationType::Success => "emblem-default", + NotificationType::Warning => "dialog-warning", + NotificationType::Error => "dialog-error", + } + } +} + +/// Freedesktop Notifications D-Bus proxy +#[allow(clippy::too_many_arguments)] +#[proxy( + interface = "org.freedesktop.Notifications", + default_service = "org.freedesktop.Notifications", + default_path = "/org/freedesktop/Notifications" +)] +trait Notifications { + /// Show a notification + fn notify( + &self, + app_name: &str, + replaces_id: u32, + app_icon: &str, + summary: &str, + body: &str, + actions: Vec<&str>, + hints: HashMap<&str, zbus::zvariant::Value<'_>>, + expire_timeout: i32, + ) -> zbus::Result; + + /// Close a notification + fn close_notification(&self, id: u32) -> zbus::Result<()>; + + /// Get server capabilities + fn get_capabilities(&self) -> zbus::Result>; + + /// Get server information + fn get_server_information(&self) -> zbus::Result<(String, String, String, String)>; +} + +/// System notification manager +pub struct NotificationManager { + connection: Option, +} + +impl NotificationManager { + /// Create a new notification manager + pub async fn new() -> Self { + let connection = Connection::session().await.ok(); + Self { connection } + } + + /// Check if system notifications are available + #[must_use] + pub fn is_available(&self) -> bool { + self.connection.is_some() + } + + /// Show a system notification + #[allow(clippy::missing_errors_doc)] + pub async fn show_notification( + &self, + notification_type: NotificationType, + title: &str, + message: &str, + ) -> Result> { + if let Some(ref connection) = self.connection { + let proxy = NotificationsProxy::new(connection).await?; + + let mut hints = HashMap::new(); + hints.insert("urgency", zbus::zvariant::Value::U8(notification_type.urgency() as u8)); + + let notification_id = proxy.notify( + "COSMIC Screenshot", // app_name + 0, // replaces_id (0 for new notification) + notification_type.icon(), // app_icon + title, // summary + message, // body + vec![], // actions (empty for simple notifications) + hints, // hints + 5000, // expire_timeout (5 seconds) + ).await?; + + Ok(notification_id) + } else { + Err("No D-Bus connection available for notifications".into()) + } + } + + /// Close a notification by ID + #[allow(clippy::missing_errors_doc)] + pub async fn close_notification(&self, id: u32) -> Result<(), Box> { + if let Some(ref connection) = self.connection { + let proxy = NotificationsProxy::new(connection).await?; + proxy.close_notification(id).await?; + Ok(()) + } else { + Err("No D-Bus connection available for notifications".into()) + } + } + + /// Get notification server capabilities + #[allow(clippy::missing_errors_doc)] + pub async fn get_capabilities(&self) -> Result, Box> { + if let Some(ref connection) = self.connection { + let proxy = NotificationsProxy::new(connection).await?; + let capabilities = proxy.get_capabilities().await?; + Ok(capabilities) + } else { + Err("No D-Bus connection available for notifications".into()) + } + } +} + +/// Global notification manager instance +static NOTIFICATION_MANAGER: std::sync::OnceLock> = std::sync::OnceLock::new(); + +/// Initialize the global notification manager +pub async fn init_notification_manager() { + let manager = NotificationManager::new().await; + let _ = NOTIFICATION_MANAGER.set(tokio::sync::Mutex::new(manager)); +} + +/// Show a system notification (convenience function) +#[allow(clippy::missing_errors_doc)] +pub async fn show_system_notification( + notification_type: NotificationType, + title: &str, + message: &str, +) -> Result> { + if let Some(manager_mutex) = NOTIFICATION_MANAGER.get() { + let manager = manager_mutex.lock().await; + manager.show_notification(notification_type, title, message).await + } else { + Err("Notification manager not initialized".into()) + } +} + +/// Check if system notifications are available (convenience function) +pub async fn notifications_available() -> bool { + if let Some(manager_mutex) = NOTIFICATION_MANAGER.get() { + let manager = manager_mutex.lock().await; + manager.is_available() + } else { + false + } +} \ No newline at end of file diff --git a/src/screenshot.rs b/src/screenshot.rs new file mode 100644 index 0000000..6d3fb63 --- /dev/null +++ b/src/screenshot.rs @@ -0,0 +1,287 @@ +// SPDX-License-Identifier: GPL-3.0-only + +use std::path::PathBuf; +use async_trait::async_trait; +use serde::{Deserialize, Serialize}; +use crate::error_handling::{report_error, ErrorSeverity}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +pub enum ScreenshotKind { + AllScreens, + ScreenUnderCursor, + WindowUnderCursor, + SelectScreen, + RectangularRegion, +} + +impl Default for ScreenshotKind { + fn default() -> Self { + Self::AllScreens + } +} + +impl std::fmt::Display for ScreenshotKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::AllScreens => write!(f, "All screens"), + Self::ScreenUnderCursor => write!(f, "Screen under cursor"), + Self::WindowUnderCursor => write!(f, "Window under cursor"), + Self::SelectScreen => write!(f, "Select screen"), + Self::RectangularRegion => write!(f, "Rectangular region"), + } + } +} + +#[derive(Debug, Clone)] +#[derive(Default)] +pub struct ScreenshotOptions { + pub kind: ScreenshotKind, + pub delay_ms: u32, + pub save_to_clipboard: bool, + pub save_dir: Option, +} + + +#[derive(Debug, Clone)] +pub struct ScreenshotResult { + pub path: Option, + pub saved_to_clipboard: bool, + pub thumbnail_data: Vec, + pub full_image_data: Vec, // Full resolution image data for region selection +} + +#[derive(thiserror::Error, Debug)] +pub enum ScreenshotError { + #[error("Portal error: {0}")] + Portal(String), + #[error("KWin error: {0}")] + KWin(String), + #[error("DBus error: {0}")] + DBus(#[from] zbus::Error), + #[error("IO error: {0}")] + Io(#[from] std::io::Error), + #[error("Image processing error: {0}")] + Image(#[from] image::ImageError), + #[error("Screengrabber not available")] + NotAvailable, + #[error("Operation cancelled")] + Cancelled, +} + +#[async_trait] +pub trait Screengrabber: Send + Sync { + async fn is_available(&self) -> bool; + + async fn take_screenshot(&self, options: &ScreenshotOptions) -> Result; + + fn name(&self) -> &'static str; + + fn supports_kind(&self, kind: ScreenshotKind) -> bool; +} + +pub mod freedesktop_portal; +pub mod kwin_screenshot2; + +#[cfg(target_os = "windows")] +pub mod windows_native; + +#[cfg(all(unix, not(target_os = "macos")))] +pub mod xorg_native; + +#[derive(Clone)] +pub struct ScreenshotManager { + grabbers: std::sync::Arc>>, +} + +impl Default for ScreenshotManager { + fn default() -> Self { + Self::new() + } +} + +impl ScreenshotManager { + #[must_use] + pub fn new() -> Self { + // Add platform-specific screengrabbers in order of preference + // Prefer KWin for better screen-specific capture support + let grabbers: Vec> = vec![ + Box::new(kwin_screenshot2::KWinScreengrabber::new()), + Box::new(freedesktop_portal::PortalScreengrabber::new()), + #[cfg(target_os = "windows")] + Box::new(windows_native::WindowsScreengrabber::new()), + #[cfg(all(unix, not(target_os = "macos")))] + Box::new(xorg_native::XorgScreengrabber::new()), + ]; + + Self { grabbers: std::sync::Arc::new(grabbers) } + } + + /// Takes a screenshot using a specific backend + /// + /// # Errors + /// Returns `ScreenshotError` if no compatible backend is found, backend fails, or region selection is attempted + pub async fn take_screenshot_with_backend(&self, options: &ScreenshotOptions, backend_name: Option<&str>) -> Result { + // Note: RectangularRegion should not reach this method + // CLI launches GUI, D-Bus rejects it, GUI/library use snipper directly + if options.kind == ScreenshotKind::RectangularRegion { + return Err(ScreenshotError::Portal("Rectangular region selection should use get_screenshot_for_region_selection() and snipper instead".to_string())); + } + + // If specific backend is requested, try to find it + if let Some(backend_name) = backend_name { + for grabber in self.grabbers.iter() { + if grabber.name().to_lowercase().contains(&backend_name.to_lowercase()) && + grabber.is_available().await && + grabber.supports_kind(options.kind) { + return grabber.take_screenshot(options).await; + } + } + return Err(ScreenshotError::Portal(format!("Backend '{backend_name}' not found or not available"))); + } + + // Auto mode - try backends with fallback + let mut last_error = None; + for grabber in self.grabbers.iter() { + if grabber.is_available().await && grabber.supports_kind(options.kind) { + match grabber.take_screenshot(options).await { + Ok(result) => return Ok(result), + Err(err) => { + report_error(ErrorSeverity::Warning, "Backend Fallback", &format!("Backend {} failed: {}, trying next backend...", grabber.name(), err)); + last_error = Some(err); + // Continue to next backend + } + } + } + } + + // If we get here, all backends failed or none were available + Err(last_error.unwrap_or(ScreenshotError::NotAvailable)) + } + + pub async fn get_available_grabbers(&self) -> Vec { + let mut available = Vec::new(); + for grabber in self.grabbers.iter() { + if grabber.is_available().await { + available.push(grabber.name().to_string()); + } + } + available + } + + pub async fn get_available_grabber(&self) -> Option<&Box> { + for grabber in self.grabbers.iter() { + if grabber.is_available().await { + return Some(grabber); + } + } + None + } + + pub async fn supports_kind_with_backend(&self, kind: ScreenshotKind, backend_name: &str) -> bool { + for grabber in self.grabbers.iter() { + if grabber.name().to_lowercase().contains(&backend_name.to_lowercase()) && + grabber.is_available().await { + return grabber.supports_kind(kind); + } + } + false + } + + pub async fn get_grabber_by_name(&self, backend_name: &str) -> Option<&Box> { + for grabber in self.grabbers.iter() { + if grabber.name().to_lowercase().contains(&backend_name.to_lowercase()) && + grabber.is_available().await { + return Some(grabber); + } + } + None + } + + pub async fn get_backend_capabilities(&self) -> std::collections::HashMap> { + let mut capabilities = std::collections::HashMap::new(); + + for grabber in self.grabbers.iter() { + if grabber.is_available().await { + let mut supported_kinds = Vec::new(); + + for kind in [ + ScreenshotKind::AllScreens, + ScreenshotKind::ScreenUnderCursor, + ScreenshotKind::WindowUnderCursor, + ScreenshotKind::SelectScreen, + ScreenshotKind::RectangularRegion, + ] { + if grabber.supports_kind(kind) { + supported_kinds.push(kind); + } + } + + capabilities.insert(grabber.name().to_string(), supported_kinds); + } + } + + capabilities + } + + /// Takes a screenshot using automatic backend selection + /// + /// # Errors + /// Returns `ScreenshotError` if no backends are available, all backends fail, or region selection is attempted + pub async fn take_screenshot(&self, options: &ScreenshotOptions) -> Result { + // Note: RectangularRegion should not reach this method + // CLI launches GUI, D-Bus rejects it, GUI/library use snipper directly + if options.kind == ScreenshotKind::RectangularRegion { + return Err(ScreenshotError::Portal("Rectangular region selection should use get_screenshot_for_region_selection() and snipper instead".to_string())); + } + + // For all other screenshot types, try backends with fallback + let mut last_error = None; + for grabber in self.grabbers.iter() { + if grabber.is_available().await && grabber.supports_kind(options.kind) { + match grabber.take_screenshot(options).await { + Ok(result) => return Ok(result), + Err(err) => { + report_error(ErrorSeverity::Warning, "Backend Fallback", &format!("Backend {} failed: {}, trying next backend...", grabber.name(), err)); + last_error = Some(err); + // Continue to next backend + } + } + } + } + + // If we get here, all backends failed or none were available + Err(last_error.unwrap_or(ScreenshotError::NotAvailable)) + } + + /// Get screenshot data for interactive region selection + /// Returns the full screenshot data and metadata needed to create a Snipper + /// + /// # Errors + /// Returns `ScreenshotError` if screenshot capture fails or image processing fails + pub async fn get_screenshot_for_region_selection(&self) -> Result<(std::collections::HashMap>, cosmic::iced::Rectangle), ScreenshotError> { + // Take current screen screenshot for region selection + let options = ScreenshotOptions { + kind: ScreenshotKind::ScreenUnderCursor, + delay_ms: 0, + save_to_clipboard: false, + save_dir: None, + }; + + let result = self.take_screenshot(&options).await?; + + // Create screen images map (using "primary" as key for compatibility) + let mut screen_images = std::collections::HashMap::new(); + screen_images.insert("primary".to_string(), result.full_image_data); + + // Get screen bounds (for now, assume full screen - this could be improved) + // TODO: Get actual screen dimensions from the backend + let screen_bounds = cosmic::iced::Rectangle { + x: 0.0, + y: 0.0, + width: 1920.0, // Default fallback - should get actual screen size + height: 1080.0, + }; + + Ok((screen_images, screen_bounds)) + } +} \ No newline at end of file diff --git a/src/screenshot/freedesktop_portal.rs b/src/screenshot/freedesktop_portal.rs new file mode 100644 index 0000000..8bcdb6b --- /dev/null +++ b/src/screenshot/freedesktop_portal.rs @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: GPL-3.0-only + +use super::{Screengrabber, ScreenshotOptions, ScreenshotResult, ScreenshotError, ScreenshotKind}; +use ashpd::desktop::screenshot::Screenshot; +use async_trait::async_trait; +use std::{fs, path::PathBuf, os::unix::fs::MetadataExt}; +use chrono::Local; + +pub struct PortalScreengrabber { + _private: (), +} + +impl Default for PortalScreengrabber { + fn default() -> Self { + Self::new() + } +} + +impl PortalScreengrabber { + #[must_use] + pub fn new() -> Self { + Self { _private: () } + } + + fn generate_thumbnail(image_path: &PathBuf) -> Result, ScreenshotError> { + let img = image::open(image_path)?; + + // Calculate thumbnail size maintaining aspect ratio, targeting 360p + let (orig_width, orig_height) = (img.width(), img.height()); + #[allow(clippy::cast_precision_loss)] + let aspect_ratio = orig_width as f32 / orig_height as f32; + + let (thumb_width, thumb_height) = if orig_height <= 360 { + // Already smaller than 360p, use original size + (orig_width, orig_height) + } else { + // Scale down to 360p height, preserve aspect ratio + let height = 360u32; + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] + let width = (360.0 * aspect_ratio) as u32; + (width, height) + }; + + let thumbnail = img.thumbnail(thumb_width, thumb_height); + + let mut thumbnail_data = Vec::new(); + let mut cursor = std::io::Cursor::new(&mut thumbnail_data); + thumbnail.write_to(&mut cursor, image::ImageFormat::Png)?; + + Ok(thumbnail_data) + } +} + +#[async_trait] +impl Screengrabber for PortalScreengrabber { + async fn is_available(&self) -> bool { + // Try to connect to the portal + (Screenshot::request().send().await).is_ok() + } + + async fn take_screenshot(&self, options: &ScreenshotOptions) -> Result { + // Add delay if specified + if options.delay_ms > 0 { + tokio::time::sleep(tokio::time::Duration::from_millis(u64::from(options.delay_ms))).await; + } + + let mut request = Screenshot::request(); + + // Configure the screenshot based on kind + // NOTE: Freedesktop Portal capabilities: + // - Non-interactive: Full workspace screenshot only + // - Interactive: User selection dialog for screen/region/window + match options.kind { + ScreenshotKind::AllScreens => { + // Non-interactive mode captures the entire workspace + request = request.interactive(false); + } + ScreenshotKind::ScreenUnderCursor | + ScreenshotKind::SelectScreen | + ScreenshotKind::RectangularRegion | + ScreenshotKind::WindowUnderCursor => { + // Interactive mode lets user choose screen, region, or window + // The portal will show a selection dialog where user can pick any type + request = request.interactive(true).modal(true); + } + } + + let response = request + .send() + .await + .map_err(|e| ScreenshotError::Portal(e.to_string()))? + .response() + .map_err(|e| ScreenshotError::Portal(e.to_string()))?; + + let uri = response.uri(); + + match uri.scheme() { + "file" => { + let temp_path = PathBuf::from(uri.path()); + let final_path = if let Some(save_dir) = &options.save_dir { + let date = Local::now(); + let filename = format!("Screenshot_{}.png", date.format("%Y-%m-%d_%H-%M-%S")); + let path = save_dir.join(filename); + + // Move or copy the file + if fs::metadata(save_dir)?.dev() == fs::metadata(&temp_path)?.dev() { + fs::rename(&temp_path, &path)?; + } else { + fs::copy(&temp_path, &path)?; + fs::remove_file(&temp_path)?; + } + + Some(path) + } else { + Some(temp_path) + }; + + let thumbnail_data = if let Some(ref path) = final_path { + Self::generate_thumbnail(path)? + } else { + Vec::new() + }; + + let full_image_data = if let Some(ref path) = final_path { + fs::read(path)? + } else { + Vec::new() + }; + + Ok(ScreenshotResult { + path: final_path, + saved_to_clipboard: false, + thumbnail_data, + full_image_data, + }) + } + "clipboard" => { + Ok(ScreenshotResult { + path: None, + saved_to_clipboard: true, + thumbnail_data: Vec::new(), // Can't generate thumbnail from clipboard + full_image_data: Vec::new(), // Can't get full image from clipboard + }) + } + scheme => Err(ScreenshotError::Portal(format!("Unsupported scheme: {scheme}"))), + } + } + + fn name(&self) -> &'static str { + "Freedesktop Portal" + } + + fn supports_kind(&self, kind: ScreenshotKind) -> bool { + // Portal supports all screenshot kinds via interactive mode + match kind { + ScreenshotKind::AllScreens | + ScreenshotKind::ScreenUnderCursor | + ScreenshotKind::SelectScreen | + ScreenshotKind::RectangularRegion | + ScreenshotKind::WindowUnderCursor => true, // All screenshot types supported via portal + } + } +} \ No newline at end of file diff --git a/src/screenshot/kwin_screenshot2.rs b/src/screenshot/kwin_screenshot2.rs new file mode 100644 index 0000000..21c5597 --- /dev/null +++ b/src/screenshot/kwin_screenshot2.rs @@ -0,0 +1,370 @@ +// SPDX-License-Identifier: GPL-3.0-only + +use super::{Screengrabber, ScreenshotOptions, ScreenshotResult, ScreenshotError, ScreenshotKind}; +use crate::error_handling::{report_error, ErrorSeverity}; +use async_trait::async_trait; +use std::{collections::HashMap, os::unix::io::FromRawFd}; +use zbus::{proxy, zvariant::{Value, OwnedValue}, Connection}; +use zbus::zvariant::Fd; +use chrono::Local; +use tokio::io::AsyncReadExt; + +#[proxy( + interface = "org.kde.KWin.ScreenShot2", + default_service = "org.kde.KWin.ScreenShot2", + default_path = "/org/kde/KWin/ScreenShot2" +)] +trait KWinScreenShot2 { + /// Capture a specific window + fn capture_window( + &self, + handle: &str, + options: HashMap<&str, &Value<'_>>, + pipe: Fd<'_> + ) -> zbus::Result>; + + /// Capture the active window + fn capture_active_window( + &self, + options: HashMap<&str, &Value<'_>>, + pipe: Fd<'_> + ) -> zbus::Result>; + + /// Capture a specific area + fn capture_area( + &self, + x: i32, + y: i32, + width: u32, + height: u32, + options: HashMap<&str, &Value<'_>>, + pipe: Fd<'_> + ) -> zbus::Result>; + + /// Capture a specific screen by name + fn capture_screen( + &self, + name: &str, + options: HashMap<&str, &Value<'_>>, + pipe: Fd<'_> + ) -> zbus::Result>; + + /// Capture the active screen + fn capture_active_screen( + &self, + options: HashMap<&str, &Value<'_>>, + pipe: Fd<'_> + ) -> zbus::Result>; + + /// Interactive capture with user selection + fn capture_interactive( + &self, + kind: u32, // 0 = window, 1 = screen + options: HashMap<&str, &Value<'_>>, + pipe: Fd<'_> + ) -> zbus::Result>; + + /// Capture the entire workspace + fn capture_workspace( + &self, + options: HashMap<&str, &Value<'_>>, + pipe: Fd<'_> + ) -> zbus::Result>; + + /// Get interface version + #[zbus(property)] + fn version(&self) -> zbus::Result; +} + +pub struct KWinScreengrabber { + _private: (), +} + +impl Default for KWinScreengrabber { + fn default() -> Self { + Self::new() + } +} + +impl KWinScreengrabber { + #[must_use] + pub fn new() -> Self { + Self { _private: () } + } + + + async fn read_image_from_pipe(pipe_fd: i32) -> Result, ScreenshotError> { + let file = unsafe { std::fs::File::from_raw_fd(pipe_fd) }; + let mut async_file = tokio::fs::File::from_std(file); + let mut buffer = Vec::new(); + async_file.read_to_end(&mut buffer).await?; + Ok(buffer) + } + + async fn try_screenshot(&self, proxy: &KWinScreenShot2Proxy<'_>, options: &ScreenshotOptions) -> Result { + // Create pipe using libc directly to avoid ownership issues + let mut pipe_fds: [std::os::raw::c_int; 2] = [0; 2]; + let result = unsafe { libc::pipe(pipe_fds.as_mut_ptr()) }; + if result != 0 { + return Err(ScreenshotError::Io(std::io::Error::last_os_error())); + } + + let read_fd = pipe_fds[0]; + let write_fd = pipe_fds[1]; + + // Create OwnedFd from write_fd to pass to KWin + let owned_write_fd = unsafe { std::os::fd::OwnedFd::from_raw_fd(write_fd) }; + let fd = Fd::from(owned_write_fd); + + let mut kwin_options = HashMap::new(); + kwin_options.insert("include-cursor", &Value::Bool(false)); + kwin_options.insert("native-resolution", &Value::Bool(true)); + + let result = match options.kind { + ScreenshotKind::AllScreens => { + proxy.capture_workspace(kwin_options, fd).await? + } + ScreenshotKind::ScreenUnderCursor => { + proxy.capture_active_screen(kwin_options, fd).await? + } + ScreenshotKind::WindowUnderCursor => { + proxy.capture_active_window(kwin_options, fd).await? + } + ScreenshotKind::SelectScreen => { + proxy.capture_interactive(1, kwin_options, fd).await? // 1 = screen + } + ScreenshotKind::RectangularRegion => { + proxy.capture_interactive(0, kwin_options, fd).await? // 0 = window (closest to area selection) + } + }; + + // Write end is automatically closed when fd is dropped after the call + // Now read from read end - KWin has finished writing + let image_data = Self::read_image_from_pipe(read_fd).await?; + + // Close read end manually + unsafe { libc::close(read_fd) }; + + // KWin returns raw pixel data with metadata, not standard image formats + self.save_screenshot_data(image_data, result, options).await + } + + async fn fallback_to_interactive(&self, proxy: &KWinScreenShot2Proxy<'_>, options: &ScreenshotOptions) -> Result { + // Create pipe using libc directly to avoid ownership issues + let mut pipe_fds: [std::os::raw::c_int; 2] = [0; 2]; + let result = unsafe { libc::pipe(pipe_fds.as_mut_ptr()) }; + if result != 0 { + return Err(ScreenshotError::Io(std::io::Error::last_os_error())); + } + + let read_fd = pipe_fds[0]; + let write_fd = pipe_fds[1]; + + // Create OwnedFd from write_fd to pass to KWin + let owned_write_fd = unsafe { std::os::fd::OwnedFd::from_raw_fd(write_fd) }; + let fd = Fd::from(owned_write_fd); + + let mut kwin_options = HashMap::new(); + kwin_options.insert("include-cursor", &Value::Bool(false)); + kwin_options.insert("include-decoration", &Value::Bool(true)); + kwin_options.insert("include-shadow", &Value::Bool(true)); + kwin_options.insert("native-resolution", &Value::Bool(true)); + + // Use interactive capture - this should work even without explicit authorization + // NOTE: Interactive mode cannot capture AllScreens - it requires user selection + let interactive_kind = match options.kind { + ScreenshotKind::AllScreens => { + // Interactive mode can't do AllScreens - return error + return Err(ScreenshotError::KWin("Interactive mode does not support AllScreens capture".to_string())); + } + ScreenshotKind::WindowUnderCursor => 0, // window selection + _ => 1, // screen selection for screen/region types + }; + + let result = proxy.capture_interactive(interactive_kind, kwin_options, fd).await?; + + // Extract image metadata from the result + println!("KWin result metadata: {result:?}"); + + // Write end is automatically closed when fd is dropped after the call + // Now read from read end - KWin has finished writing + let image_data = Self::read_image_from_pipe(read_fd).await?; + + // Debug: Check image data length and format + println!("KWin fallback_to_interactive: Received {} bytes", image_data.len()); + if image_data.len() >= 8 { + println!("First 8 bytes: {:?}", &image_data[0..8]); + } + + // Close read end manually + unsafe { libc::close(read_fd) }; + + // Process the raw image data with metadata from KWin + self.save_screenshot_data(image_data, result, options).await + } + + #[allow(clippy::unused_async)] + async fn save_screenshot_data(&self, image_data: Vec, metadata: std::collections::HashMap, options: &ScreenshotOptions) -> Result { + // Extract image metadata from KWin response + let width = metadata.get("width") + .and_then(|v| v.downcast_ref::().ok()) + .ok_or_else(|| ScreenshotError::Portal("Missing width in KWin response".to_string()))?; + + let height = metadata.get("height") + .and_then(|v| v.downcast_ref::().ok()) + .ok_or_else(|| ScreenshotError::Portal("Missing height in KWin response".to_string()))?; + + // Convert raw image data based on QImage format + let format = metadata.get("format") + .and_then(|v| v.downcast_ref::().ok()) + .ok_or_else(|| ScreenshotError::Portal("Missing format in KWin response".to_string()))?; + + println!("KWin image format: {format}"); + + let img = match format { + 5 => { + // QImage::Format_ARGB32 (0xAARRGGBB) - need to convert ARGB to RGBA + let mut rgba_data = Vec::with_capacity(image_data.len()); + for chunk in image_data.chunks_exact(4) { + let argb = u32::from_ne_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]); + let a = ((argb >> 24) & 0xff) as u8; + let r = ((argb >> 16) & 0xff) as u8; + let g = ((argb >> 8) & 0xff) as u8; + let b = (argb & 0xff) as u8; + rgba_data.extend_from_slice(&[r, g, b, a]); + } + + match image::RgbaImage::from_raw(width, height, rgba_data) { + Some(rgba_img) => image::DynamicImage::ImageRgba8(rgba_img), + None => return Err(ScreenshotError::Portal("Failed to create RGBA image from ARGB data".to_string())), + } + } + 6 => { + // QImage::Format_ARGB32_Premultiplied - similar to Format_ARGB32 but pre-multiplied alpha + let mut rgba_data = Vec::with_capacity(image_data.len()); + for chunk in image_data.chunks_exact(4) { + let argb = u32::from_ne_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]); + let a = ((argb >> 24) & 0xff) as u8; + let r = ((argb >> 16) & 0xff) as u8; + let g = ((argb >> 8) & 0xff) as u8; + let b = (argb & 0xff) as u8; + rgba_data.extend_from_slice(&[r, g, b, a]); + } + + match image::RgbaImage::from_raw(width, height, rgba_data) { + Some(rgba_img) => image::DynamicImage::ImageRgba8(rgba_img), + None => return Err(ScreenshotError::Portal("Failed to create RGBA image from premultiplied ARGB data".to_string())), + } + } + 13 => { + // QImage::Format_RGB888 - RGB format, need to convert to RGBA + let mut rgba_data = Vec::with_capacity(image_data.len() * 4 / 3); + for chunk in image_data.chunks_exact(3) { + rgba_data.extend_from_slice(&[chunk[0], chunk[1], chunk[2], 255]); // Add alpha=255 + } + + match image::RgbaImage::from_raw(width, height, rgba_data) { + Some(rgba_img) => image::DynamicImage::ImageRgba8(rgba_img), + None => return Err(ScreenshotError::Portal("Failed to create RGBA image from RGB data".to_string())), + } + } + _ => { + // Unknown format - try as RGBA as fallback + println!("Unknown QImage format {format}, trying as RGBA"); + match image::RgbaImage::from_raw(width, height, image_data) { + Some(rgba_img) => image::DynamicImage::ImageRgba8(rgba_img), + None => return Err(ScreenshotError::Portal(format!("Failed to create image from unknown format {format}"))), + } + } + }; + + let final_path = if let Some(save_dir) = &options.save_dir { + let date = Local::now(); + let filename = format!("Screenshot_{}.png", date.format("%Y-%m-%d_%H-%M-%S")); + let path = save_dir.join(filename); + img.save(&path)?; + Some(path) + } else { + let temp_dir = std::env::temp_dir(); + let date = Local::now(); + let filename = format!("Screenshot_{}.png", date.format("%Y-%m-%d_%H-%M-%S")); + let path = temp_dir.join(filename); + img.save(&path)?; + Some(path) + }; + + // Generate thumbnail from the converted image + let thumbnail = img.thumbnail(320, 240); + let mut thumbnail_data = Vec::new(); + let mut cursor = std::io::Cursor::new(&mut thumbnail_data); + thumbnail.write_to(&mut cursor, image::ImageFormat::Png)?; + + // Store full resolution image data for region selection + let mut full_image_data = Vec::new(); + let mut cursor_full = std::io::Cursor::new(&mut full_image_data); + img.write_to(&mut cursor_full, image::ImageFormat::Png)?; + + Ok(ScreenshotResult { + path: final_path, + saved_to_clipboard: options.save_to_clipboard, + thumbnail_data, + full_image_data, + }) + } + +} + +#[async_trait] +impl Screengrabber for KWinScreengrabber { + async fn is_available(&self) -> bool { + match Connection::session().await { + Ok(connection) => { + (KWinScreenShot2Proxy::new(&connection).await).is_ok() + } + Err(_) => false, + } + } + + async fn take_screenshot(&self, options: &ScreenshotOptions) -> Result { + let connection = Connection::session().await?; + let proxy = KWinScreenShot2Proxy::new(&connection).await?; + + // Add delay if specified + if options.delay_ms > 0 { + tokio::time::sleep(tokio::time::Duration::from_millis(u64::from(options.delay_ms))).await; + } + + // Try the normal screenshot method first + match self.try_screenshot(&proxy, options).await { + Ok(result) => Ok(result), + Err(ScreenshotError::DBus(zbus_error)) => { + let error_msg = zbus_error.to_string(); + if error_msg.contains("NoAuthorized") || error_msg.contains("org.kde.KWin.ScreenShot2.Error.NoAuthorized") { + // Show authorization error message + report_error( + ErrorSeverity::Warning, + "KWin Authorization", + "Screenshot permission not granted. The application needs the org.kde.KWin.ScreenShot2 interface listed in X-KDE-DBUS-Restricted-Interfaces. Falling back to interactive mode..." + ); + + // Fall back to interactive mode + self.fallback_to_interactive(&proxy, options).await + } else { + Err(ScreenshotError::DBus(zbus_error)) + } + } + Err(err) => Err(err), + } + } + + fn name(&self) -> &'static str { + "KWin ScreenShot2" + } + + fn supports_kind(&self, _kind: ScreenshotKind) -> bool { + // KWin supports all screenshot kinds when properly authorized + // NOTE: If falling back to interactive mode, AllScreens is not supported + // but we can't detect authorization state here + true + } +} \ No newline at end of file diff --git a/src/screenshot/windows_native.rs b/src/screenshot/windows_native.rs new file mode 100644 index 0000000..d532e6d --- /dev/null +++ b/src/screenshot/windows_native.rs @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-3.0-only + +#[cfg(target_os = "windows")] +use super::{Screengrabber, ScreenshotOptions, ScreenshotResult, ScreenshotError, ScreenshotKind}; +#[cfg(target_os = "windows")] +use async_trait::async_trait; + +#[cfg(target_os = "windows")] +pub struct WindowsScreengrabber { + _private: (), +} + +#[cfg(target_os = "windows")] +impl WindowsScreengrabber { + pub fn new() -> Self { + Self { _private: () } + } +} + +#[cfg(target_os = "windows")] +#[async_trait] +impl Screengrabber for WindowsScreengrabber { + async fn is_available(&self) -> bool { + // On Windows, we can always use the native API + true + } + + async fn take_screenshot(&self, _options: &ScreenshotOptions) -> Result { + // TODO: Implement using Windows API (user32.dll, gdi32.dll) + Err(ScreenshotError::NotAvailable) + } + + fn name(&self) -> &'static str { + "Windows Native" + } + + fn supports_kind(&self, _kind: ScreenshotKind) -> bool { + // Windows native API supports most kinds + true + } +} + +#[cfg(not(target_os = "windows"))] +pub struct WindowsScreengrabber; + +#[cfg(not(target_os = "windows"))] +impl WindowsScreengrabber { + pub fn new() -> Self { + Self + } +} \ No newline at end of file diff --git a/src/screenshot/xorg_native.rs b/src/screenshot/xorg_native.rs new file mode 100644 index 0000000..2490c92 --- /dev/null +++ b/src/screenshot/xorg_native.rs @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-3.0-only + +#[cfg(all(unix, not(target_os = "macos")))] +use super::{Screengrabber, ScreenshotOptions, ScreenshotResult, ScreenshotError, ScreenshotKind}; +#[cfg(all(unix, not(target_os = "macos")))] +use async_trait::async_trait; + +#[cfg(all(unix, not(target_os = "macos")))] +pub struct XorgScreengrabber { + _private: (), +} + +#[cfg(all(unix, not(target_os = "macos")))] +impl Default for XorgScreengrabber { + fn default() -> Self { + Self::new() + } +} + +impl XorgScreengrabber { + #[must_use] + pub fn new() -> Self { + Self { _private: () } + } +} + +#[cfg(all(unix, not(target_os = "macos")))] +#[async_trait] +impl Screengrabber for XorgScreengrabber { + async fn is_available(&self) -> bool { + // Check if we're running under X11 + std::env::var("DISPLAY").is_ok() && std::env::var("WAYLAND_DISPLAY").is_err() + } + + async fn take_screenshot(&self, _options: &ScreenshotOptions) -> Result { + // TODO: Implement using X11 API (libX11, libXext) + Err(ScreenshotError::NotAvailable) + } + + fn name(&self) -> &'static str { + "X11 Native" + } + + fn supports_kind(&self, _kind: ScreenshotKind) -> bool { + // X11 supports most screenshot kinds + true + } +} + +#[cfg(not(all(unix, not(target_os = "macos"))))] +pub struct XorgScreengrabber; + +#[cfg(not(all(unix, not(target_os = "macos"))))] +impl XorgScreengrabber { + pub fn new() -> Self { + Self + } +} \ No newline at end of file diff --git a/src/settings.rs b/src/settings.rs new file mode 100644 index 0000000..ada43b7 --- /dev/null +++ b/src/settings.rs @@ -0,0 +1,221 @@ +// SPDX-License-Identifier: GPL-3.0-only + +use cosmic::iced::Rectangle; +use cosmic_config::{Config, CosmicConfigEntry, ConfigGet, ConfigSet}; +use serde::{Deserialize, Serialize}; +use std::path::PathBuf; + +pub const APP_ID: &str = "com.system76.CosmicScreenshot"; + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct ScreenshotSettings { + /// Whether to take screenshot immediately on startup using last settings + pub screenshot_on_startup: bool, + /// Last used screenshot kind (All screens, Current screen, etc.) + pub last_screenshot_kind: String, + /// Last used screenshot delay in seconds + pub last_screenshot_delay: u32, + /// Last used backend index + pub last_selected_backend: usize, + /// Whether to remember the save directory + pub remember_save_directory: bool, + /// Last used save directory + pub last_save_directory: Option, + /// Whether to remember selection area across sessions + pub remember_selection_area: bool, + /// Last selection rectangle (for region screenshots) + pub last_selection_area: Option, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct SelectionArea { + pub x: f32, + pub y: f32, + pub width: f32, + pub height: f32, +} + +impl From for SelectionArea { + fn from(rect: Rectangle) -> Self { + Self { + x: rect.x, + y: rect.y, + width: rect.width, + height: rect.height, + } + } +} + +impl From for Rectangle { + fn from(area: SelectionArea) -> Self { + Rectangle { + x: area.x, + y: area.y, + width: area.width, + height: area.height, + } + } +} + +impl Default for ScreenshotSettings { + fn default() -> Self { + Self { + screenshot_on_startup: false, + last_screenshot_kind: "All screens".to_string(), + last_screenshot_delay: 0, + last_selected_backend: 0, + remember_save_directory: true, + last_save_directory: dirs::picture_dir(), + remember_selection_area: false, + last_selection_area: None, + } + } +} + +impl CosmicConfigEntry for ScreenshotSettings { + const VERSION: u64 = 1; + + fn write_entry(&self, config: &Config) -> Result<(), cosmic_config::Error> { + config.set("screenshot_on_startup", self.screenshot_on_startup)?; + config.set("last_screenshot_kind", &self.last_screenshot_kind)?; + config.set("last_screenshot_delay", self.last_screenshot_delay)?; + config.set("last_selected_backend", self.last_selected_backend)?; + config.set("remember_save_directory", self.remember_save_directory)?; + config.set("last_save_directory", &self.last_save_directory)?; + config.set("remember_selection_area", self.remember_selection_area)?; + config.set("last_selection_area", &self.last_selection_area)?; + Ok(()) + } + + fn get_entry(config: &Config) -> Result, Self)> { + let mut errors = Vec::new(); + let default = Self::default(); + + let screenshot_on_startup = config.get("screenshot_on_startup") + .unwrap_or_else(|e| { errors.push(e); default.screenshot_on_startup }); + + let last_screenshot_kind = config.get("last_screenshot_kind") + .unwrap_or_else(|e| { errors.push(e); default.last_screenshot_kind.clone() }); + + let last_screenshot_delay = config.get("last_screenshot_delay") + .unwrap_or_else(|e| { errors.push(e); default.last_screenshot_delay }); + + let last_selected_backend = config.get("last_selected_backend") + .unwrap_or_else(|e| { errors.push(e); default.last_selected_backend }); + + let remember_save_directory = config.get("remember_save_directory") + .unwrap_or_else(|e| { errors.push(e); default.remember_save_directory }); + + let last_save_directory = config.get("last_save_directory") + .unwrap_or_else(|e| { errors.push(e); default.last_save_directory.clone() }); + + let remember_selection_area = config.get("remember_selection_area") + .unwrap_or_else(|e| { errors.push(e); default.remember_selection_area }); + + let last_selection_area = config.get("last_selection_area") + .unwrap_or_else(|e| { errors.push(e); default.last_selection_area.clone() }); + + let settings = Self { + screenshot_on_startup, + last_screenshot_kind, + last_screenshot_delay, + last_selected_backend, + remember_save_directory, + last_save_directory, + remember_selection_area, + last_selection_area, + }; + + if errors.is_empty() { + Ok(settings) + } else { + Err((errors, settings)) + } + } + + fn update_keys(&mut self, config: &Config, _keys: &[T]) -> (Vec, Vec<&'static str>) + where + T: AsRef + { + // For simple config updates, we just reload all settings + match Self::get_entry(config) { + Ok(new_settings) => { + *self = new_settings; + (vec![], vec![]) + } + Err((errors, new_settings)) => { + *self = new_settings; + (errors, vec![]) + } + } + } +} + +pub struct SettingsManager { + pub config: Config, + pub settings: ScreenshotSettings, +} + +impl SettingsManager { + #[allow(clippy::missing_errors_doc)] + pub fn new() -> Result { + let config = Config::new(APP_ID, ScreenshotSettings::VERSION)?; + let settings = ScreenshotSettings::get_entry(&config).unwrap_or_default(); + + Ok(Self { config, settings }) + } + + #[allow(clippy::missing_errors_doc)] + pub fn save(&self) -> Result<(), cosmic_config::Error> { + self.settings.write_entry(&self.config) + } + + #[allow(clippy::missing_errors_doc)] + pub fn update_screenshot_settings( + &mut self, + kind: &str, + delay: u32, + backend_index: usize, + ) -> Result<(), cosmic_config::Error> { + self.settings.last_screenshot_kind = kind.to_string(); + self.settings.last_screenshot_delay = delay; + self.settings.last_selected_backend = backend_index; + self.save() + } + + #[allow(clippy::missing_errors_doc)] + pub fn update_save_directory( + &mut self, + directory: Option, + ) -> Result<(), cosmic_config::Error> { + self.settings.last_save_directory = directory; + self.save() + } + + #[allow(clippy::missing_errors_doc)] + pub fn update_selection_area( + &mut self, + area: Option, + ) -> Result<(), cosmic_config::Error> { + self.settings.last_selection_area = area.map(SelectionArea::from); + self.save() + } + + #[allow(clippy::missing_errors_doc)] + pub fn set_screenshot_on_startup(&mut self, enabled: bool) -> Result<(), cosmic_config::Error> { + self.settings.screenshot_on_startup = enabled; + self.save() + } + + #[allow(clippy::missing_errors_doc)] + pub fn set_remember_save_directory(&mut self, remember: bool) -> Result<(), cosmic_config::Error> { + self.settings.remember_save_directory = remember; + self.save() + } + + #[allow(clippy::missing_errors_doc)] + pub fn set_remember_selection_area(&mut self, remember: bool) -> Result<(), cosmic_config::Error> { + self.settings.remember_selection_area = remember; + self.save() + } +} \ No newline at end of file diff --git a/src/snipper.rs b/src/snipper.rs new file mode 100644 index 0000000..8e85f14 --- /dev/null +++ b/src/snipper.rs @@ -0,0 +1,1006 @@ +// SPDX-License-Identifier: GPL-3.0-only + +use cosmic::iced::{event, keyboard, mouse, widget::canvas, Color, Point, Rectangle, Size}; +use std::collections::HashMap; +use std::time::{Duration, Instant}; + +type Message = crate::ui::ScreenshotMessage; + + +#[derive(Debug, Clone)] +pub enum SnipperMessage { + StartSelection(Point), + UpdateSelection(Point), + EndSelection, + AcceptSelection, // Double-click or Enter to accept + CancelSelection, + KeyPressed(keyboard::Key), + DoubleClick(Point), +} + +#[derive(Debug, Clone)] +pub enum DragMode { + None, + Creating, // Creating new selection + Moving, // Moving existing selection + ResizingTopLeft, + ResizingTop, + ResizingTopRight, + ResizingRight, + ResizingBottomRight, + ResizingBottom, + ResizingBottomLeft, + ResizingLeft, +} + +#[derive(Debug, Clone)] +pub struct SnipperState { + selection: Option, + drag_mode: DragMode, + drag_start: Point, + initial_selection: Option, + current_mouse: Point, + screen_images: HashMap>, // Screen name -> image data + screen_bounds: Rectangle, + cached_image_handle: Option, + // Double-click detection + last_click_time: Option, + last_click_pos: Option, + // Selection memory - remember last selection position and size + remembered_selection: Option, + // Debugging and profiling (compile-time conditional) + #[cfg(feature = "debug")] + debug_enabled: bool, + #[cfg(feature = "debug")] + event_timestamps: Vec<(String, Instant)>, + #[cfg(feature = "debug")] + last_event_time: Option, + #[cfg(feature = "debug")] + last_mouse_event: Option, + #[cfg(feature = "debug")] + selection_changed_time: Option, + #[cfg(feature = "debug")] + last_cache_clear: Option, + #[cfg(feature = "debug")] + last_significant_selection: Option, +} + +impl Default for SnipperState { + fn default() -> Self { + Self { + selection: None, + drag_mode: DragMode::None, + drag_start: Point::ORIGIN, + initial_selection: None, + current_mouse: Point::ORIGIN, + screen_images: HashMap::new(), + screen_bounds: Rectangle::new(Point::ORIGIN, Size::ZERO), + cached_image_handle: None, + last_click_time: None, + last_click_pos: None, + remembered_selection: None, + #[cfg(feature = "debug")] + debug_enabled: std::env::var("COSMIC_SCREENSHOT_DEBUG").is_ok(), + #[cfg(feature = "debug")] + event_timestamps: Vec::new(), + #[cfg(feature = "debug")] + last_event_time: None, + #[cfg(feature = "debug")] + last_mouse_event: None, + #[cfg(feature = "debug")] + selection_changed_time: None, + #[cfg(feature = "debug")] + last_cache_clear: None, + #[cfg(feature = "debug")] + last_significant_selection: None, + } + } +} + +impl SnipperState { + #[must_use] + pub fn new(screen_images: HashMap>, screen_bounds: Rectangle) -> Self { + #[cfg(feature = "debug")] + let debug_enabled = std::env::var("COSMIC_SCREENSHOT_DEBUG").is_ok(); + #[cfg(feature = "debug")] + let image_processing_start = if debug_enabled { Some(Instant::now()) } else { None }; + + // Pre-cache the image handle during creation for better performance + let cached_image_handle = if let Some(screenshot_data) = screen_images.get("primary") { + #[cfg(feature = "debug")] + if debug_enabled { + eprintln!("[SNIPPER DEBUG] Loading image data of {} bytes", screenshot_data.len()); + } + + #[cfg(feature = "debug")] + let decode_start = if debug_enabled { Some(Instant::now()) } else { None }; + + if let Ok(img) = image::load_from_memory(screenshot_data) { + #[cfg(feature = "debug")] + if let Some(decode_start_time) = decode_start { + let decode_duration = decode_start_time.elapsed(); + if decode_duration.as_millis() > 50 { + eprintln!("[IMAGE PERF WARNING] Image decode took {}ms (>50ms threshold)", decode_duration.as_millis()); + } + } + + #[cfg(feature = "debug")] + let convert_start = if debug_enabled { Some(Instant::now()) } else { None }; + let rgba_img = img.to_rgba8(); + let (width, height) = rgba_img.dimensions(); + + #[cfg(feature = "debug")] + if let Some(convert_start_time) = convert_start { + let convert_duration = convert_start_time.elapsed(); + if convert_duration.as_millis() > 30 { + eprintln!("[IMAGE PERF WARNING] RGBA conversion took {}ms (>30ms threshold)", convert_duration.as_millis()); + } + } + + #[cfg(feature = "debug")] + let handle_start = if debug_enabled { Some(Instant::now()) } else { None }; + let handle = cosmic::iced::widget::image::Handle::from_rgba( + width, + height, + rgba_img.into_raw(), + ); + + #[cfg(feature = "debug")] + if let Some(handle_start_time) = handle_start { + let handle_duration = handle_start_time.elapsed(); + if handle_duration.as_millis() > 20 { + eprintln!("[IMAGE PERF WARNING] Handle creation took {}ms (>20ms threshold)", handle_duration.as_millis()); + } + } + + #[cfg(feature = "debug")] + if debug_enabled { + eprintln!("[SNIPPER DEBUG] Successfully created image handle {width}x{height}"); + } + + Some(handle) + } else { + #[cfg(feature = "debug")] + if debug_enabled { + eprintln!("[SNIPPER ERROR] Failed to decode image data"); + } + None + } + } else { + #[cfg(feature = "debug")] + if debug_enabled { + eprintln!("[SNIPPER ERROR] No 'primary' screen image found"); + } + None + }; + + #[cfg(feature = "debug")] + if let Some(processing_start_time) = image_processing_start { + let total_processing = processing_start_time.elapsed(); + if total_processing.as_millis() > 100 { + eprintln!("[IMAGE PERF WARNING] Total image processing took {}ms (>100ms threshold)", total_processing.as_millis()); + } + } + + Self { + selection: None, + drag_mode: DragMode::None, + drag_start: Point::ORIGIN, + initial_selection: None, + current_mouse: Point::ORIGIN, + screen_images, + screen_bounds, + cached_image_handle, + last_click_time: None, + last_click_pos: None, + remembered_selection: None, + #[cfg(feature = "debug")] + debug_enabled: std::env::var("COSMIC_SCREENSHOT_DEBUG").is_ok(), + #[cfg(feature = "debug")] + event_timestamps: Vec::new(), + #[cfg(feature = "debug")] + last_event_time: None, + #[cfg(feature = "debug")] + last_mouse_event: None, + #[cfg(feature = "debug")] + selection_changed_time: None, + #[cfg(feature = "debug")] + last_cache_clear: None, + #[cfg(feature = "debug")] + last_significant_selection: None, + } + } + + #[must_use] + pub fn new_with_memory(screen_images: HashMap>, screen_bounds: Rectangle, remembered_selection: Option) -> Self { + let mut state = Self::new(screen_images, screen_bounds); + state.remembered_selection = remembered_selection; + // If we have a remembered selection and it fits in the new screen bounds, restore it + if let Some(remembered) = remembered_selection { + if screen_bounds.contains(Point::new(remembered.x, remembered.y)) && + screen_bounds.contains(Point::new(remembered.x + remembered.width, remembered.y + remembered.height)) { + state.selection = Some(remembered); + } + } + state + } + + pub fn selection(&self) -> Option { + self.selection + } + + pub fn save_selection_to_memory(&mut self) { + self.remembered_selection = self.selection; + } + + pub fn get_remembered_selection(&self) -> Option { + self.remembered_selection + } + + // Helper functions for drag mode detection + const HANDLE_SIZE: f32 = 8.0; + + fn get_drag_mode(&self, point: Point) -> DragMode { + if let Some(selection) = self.selection { + let handle_size = Self::HANDLE_SIZE; + + // Check if click is inside selection for moving + if selection.contains(point) { + // Check if near edges for resizing (priority over moving) + if point.x <= selection.x + handle_size && point.y <= selection.y + handle_size { + DragMode::ResizingTopLeft + } else if point.x >= selection.x + selection.width - handle_size && point.y <= selection.y + handle_size { + DragMode::ResizingTopRight + } else if point.x >= selection.x + selection.width - handle_size && point.y >= selection.y + selection.height - handle_size { + DragMode::ResizingBottomRight + } else if point.x <= selection.x + handle_size && point.y >= selection.y + selection.height - handle_size { + DragMode::ResizingBottomLeft + } else if point.y <= selection.y + handle_size { + DragMode::ResizingTop + } else if point.x >= selection.x + selection.width - handle_size { + DragMode::ResizingRight + } else if point.y >= selection.y + selection.height - handle_size { + DragMode::ResizingBottom + } else if point.x <= selection.x + handle_size { + DragMode::ResizingLeft + } else { + // Inside but not near edges - moving + DragMode::Moving + } + } else { + // Outside existing selection - create new one + DragMode::Creating + } + } else { + // No existing selection - create new one + DragMode::Creating + } + } + + #[cfg(feature = "debug")] + fn log_debug_event(&mut self, event_name: &str) { + if self.debug_enabled { + let now = Instant::now(); + let delay = self.last_event_time + .map_or(0, |last| now.duration_since(last).as_millis()); + + // Track when mouse events happen for frame timing + if event_name.contains("Selection") { + self.last_mouse_event = Some(now); + + // Track when selection changes for render timing + if event_name.starts_with("StartSelection") || event_name.starts_with("UpdateSelection") { + self.selection_changed_time = Some(now); + } + } + + // Log event with timing + eprintln!("[SNIPPER DEBUG] {event_name} (+{delay}ms)"); + + self.event_timestamps.push((event_name.to_string(), now)); + self.last_event_time = Some(now); + + // Keep only last 20 events to prevent memory issues + if self.event_timestamps.len() > 20 { + self.event_timestamps.remove(0); + } + } + } + + #[cfg(not(feature = "debug"))] + fn log_debug_event(&mut self, _event_name: &str) { + // Debug logging disabled at compile time + } + + #[cfg(feature = "debug")] + fn log_performance_warning(&self, operation: &str, duration: Duration) { + if self.debug_enabled && duration.as_millis() > 50 { + eprintln!("[SNIPPER PERF WARNING] {} took {}ms (>50ms threshold)", operation, duration.as_millis()); + } + } + + #[cfg(not(feature = "debug"))] + fn log_performance_warning(&self, _operation: &str, _duration: Duration) { + // Performance logging disabled at compile time + } + + fn should_update_cache(_new_selection: Option) -> bool { + // Always update cache when selection changes + true + } + + #[cfg(feature = "debug")] + fn mark_cache_cleared(&mut self, new_selection: Option) { + let now = Instant::now(); + self.last_cache_clear = Some(now); + self.last_significant_selection = new_selection; + + if self.debug_enabled { + eprintln!("[CACHE DEBUG] Cache clear recorded at {now:?}"); + } + } + + #[cfg(not(feature = "debug"))] + fn mark_cache_cleared(&mut self, _new_selection: Option) { + // Cache debug logging disabled at compile time + } + + + + #[cfg(feature = "debug")] + fn reset_timing_after_completion(&mut self) { + // Reset timing to prevent perpetual stale warnings + self.selection_changed_time = None; + self.last_mouse_event = None; + self.last_cache_clear = None; + + if self.debug_enabled { + eprintln!("[TIMING DEBUG] SnipperState timing reset after selection completion"); + } + } + + #[cfg(not(feature = "debug"))] + fn reset_timing_after_completion(&mut self) { + // Timing debug disabled at compile time + } + + #[allow(clippy::too_many_lines)] + pub fn update(&mut self, message: SnipperMessage) -> Option { + let update_start = Instant::now(); + + let result = match message { + SnipperMessage::StartSelection(point) => { + self.log_debug_event(&format!("StartSelection at ({:.1}, {:.1})", point.x, point.y)); + + // Detect double-click first + let now = std::time::Instant::now(); + let is_double_click = if let (Some(last_time), Some(last_pos)) = (self.last_click_time, self.last_click_pos) { + now.duration_since(last_time).as_millis() < 500 && // Within 500ms + (point.x - last_pos.x).abs() < 5.0 && (point.y - last_pos.y).abs() < 5.0 // Within 5px + } else { + false + }; + + self.last_click_time = Some(now); + self.last_click_pos = Some(point); + + if is_double_click && self.selection.is_some() { + // Double-click detected - accept selection if inside + if let Some(selection) = self.selection { + if selection.contains(point) { + self.save_selection_to_memory(); + self.reset_timing_after_completion(); + return Some(SnipperResult::Selected(selection)); + } + } + } + + // Determine drag mode and start dragging + self.drag_mode = self.get_drag_mode(point); + self.drag_start = point; + self.current_mouse = point; + self.initial_selection = self.selection; + + if let DragMode::Creating = self.drag_mode { + self.selection = Some(Rectangle::new(point, Size::ZERO)); + } else { + // Moving or resizing - keep existing selection for now + } + None + } + SnipperMessage::UpdateSelection(point) => { + self.log_debug_event(&format!("UpdateSelection to ({:.1}, {:.1})", point.x, point.y)); + + self.current_mouse = point; + + if !matches!(self.drag_mode, DragMode::None) { + match self.drag_mode { + DragMode::Creating => { + let x = self.drag_start.x.min(point.x); + let y = self.drag_start.y.min(point.y); + let width = (self.drag_start.x - point.x).abs(); + let height = (self.drag_start.y - point.y).abs(); + self.selection = Some(Rectangle::new(Point::new(x, y), Size::new(width, height))); + } + DragMode::Moving => { + if let Some(initial) = self.initial_selection { + let delta_x = point.x - self.drag_start.x; + let delta_y = point.y - self.drag_start.y; + self.selection = Some(Rectangle::new( + Point::new(initial.x + delta_x, initial.y + delta_y), + initial.size() + )); + } + } + DragMode::ResizingTopLeft => { + if let Some(initial) = self.initial_selection { + let new_x = point.x; + let new_y = point.y; + let new_width = (initial.x + initial.width - new_x).max(10.0); + let new_height = (initial.y + initial.height - new_y).max(10.0); + self.selection = Some(Rectangle::new( + Point::new(new_x, new_y), + Size::new(new_width, new_height) + )); + } + } + // Add other resize modes as needed + _ => {} + } + } + None + } + SnipperMessage::EndSelection => { + self.log_debug_event("EndSelection"); + // Stop dragging + self.drag_mode = DragMode::None; + None + } + SnipperMessage::AcceptSelection => { + self.log_debug_event("AcceptSelection"); + // Accept current selection (double-click or Enter) + if let Some(selection) = self.selection { + if selection.width > 10.0 && selection.height > 10.0 { + self.save_selection_to_memory(); + self.reset_timing_after_completion(); + return Some(SnipperResult::Selected(selection)); + } + } + None + } + SnipperMessage::DoubleClick(point) => { + // Accept selection if double-click is inside the selection + if let Some(selection) = self.selection { + if selection.contains(point) { + self.save_selection_to_memory(); + self.reset_timing_after_completion(); + return Some(SnipperResult::Selected(selection)); + } + } + None + } + SnipperMessage::CancelSelection => { + self.log_debug_event("CancelSelection"); + self.reset_timing_after_completion(); + Some(SnipperResult::Cancelled) + } + SnipperMessage::KeyPressed(key) => { + self.log_debug_event(&format!("KeyPressed: {key:?}")); + match key { + keyboard::Key::Named(keyboard::key::Named::Escape) => { + self.reset_timing_after_completion(); + Some(SnipperResult::Cancelled) + } + keyboard::Key::Named(keyboard::key::Named::Enter) => { + // Use AcceptSelection for Enter key + return self.update(SnipperMessage::AcceptSelection); + } + _ => None, + } + } + }; + + // Log performance warning if update took too long + let update_duration = update_start.elapsed(); + self.log_performance_warning("SnipperState::update", update_duration); + + result + } +} + +#[derive(Debug, Clone)] +pub enum SnipperResult { + Selected(Rectangle), + Cancelled, +} + +pub struct Snipper { + state: SnipperState, + canvas_program: SelectionOnlyCanvas, +} + +impl Snipper { + #[must_use] + pub fn new(screen_images: HashMap>, screen_bounds: Rectangle) -> Self { + let creation_start = Instant::now(); + let state = SnipperState::new(screen_images, screen_bounds); + + if std::env::var("COSMIC_SCREENSHOT_DEBUG").is_ok() { + let creation_time = creation_start.elapsed(); + eprintln!("[SNIPPER DEBUG] Snipper::new took {}ms", creation_time.as_millis()); + if creation_time.as_millis() > 100 { + eprintln!("[SNIPPER PERF WARNING] Snipper creation took {}ms (>100ms threshold)", creation_time.as_millis()); + } + } + + Self { + state, + canvas_program: SelectionOnlyCanvas::new(None), + } + } + + #[must_use] + pub fn new_with_memory(screen_images: HashMap>, screen_bounds: Rectangle, remembered_selection: Option) -> Self { + let state = SnipperState::new_with_memory(screen_images, screen_bounds, remembered_selection); + let initial_selection = state.selection(); + Self { + state, + canvas_program: SelectionOnlyCanvas::new(initial_selection), + } + } + + pub fn get_remembered_selection(&self) -> Option { + self.state.get_remembered_selection() + } + + pub fn view(&self) -> cosmic::Element<'_, Message> { + // STATIC background image - should never trigger redraws + let background_image = if let Some(ref image_handle) = self.state.cached_image_handle { + cosmic::widget::container( + cosmic::widget::image(image_handle.clone()) + .width(cosmic::iced::Length::Fill) + .height(cosmic::iced::Length::Fill) + .content_fit(cosmic::iced::ContentFit::Fill), // Ensure full coverage + ) + .width(cosmic::iced::Length::Fill) + .height(cosmic::iced::Length::Fill) + } else { + cosmic::widget::container(cosmic::widget::text("Failed to load screenshot")) + .width(cosmic::iced::Length::Fill) + .height(cosmic::iced::Length::Fill) + .style(|_theme| cosmic::iced::widget::container::Style { + background: Some(cosmic::iced::Color::from_rgba(0.2, 0.2, 0.2, 1.0).into()), + ..Default::default() + }) + }; + + // Use cached canvas program for efficient drawing - cache will only redraw when selection changes + let overlay_element: cosmic::Element = + cosmic::widget::canvas(&self.canvas_program) + .width(cosmic::iced::Length::Fill) + .height(cosmic::iced::Length::Fill) + .into(); + + // Stack: static image + cached dynamic overlay + cosmic::widget::container(cosmic::iced::widget::stack![ + background_image, // Layer 1: Static, never redraws + overlay_element // Layer 2: Cached canvas, redraws only when selection changes + ]) + .width(cosmic::iced::Length::Fill) + .height(cosmic::iced::Length::Fill) + .into() + } + + #[allow(clippy::needless_pass_by_value)] + pub fn update(&mut self, message: SnipperMessage) -> Option { + #[cfg(feature = "debug")] + let update_start = Instant::now(); + let old_selection = self.state.selection; + + let result = self.state.update(message.clone()); + + // Always update canvas program selection, but only clear cache when significant + if self.state.selection != old_selection { + self.canvas_program.selection = self.state.selection; + + // Only clear cache for significant changes + if SnipperState::should_update_cache(self.state.selection) { + #[cfg(feature = "debug")] + let cache_start = Instant::now(); + self.canvas_program.clear_cache(); + + // Record that we cleared the cache + self.state.mark_cache_cleared(self.state.selection); + + #[cfg(feature = "debug")] + if self.state.debug_enabled { + let cache_time = cache_start.elapsed(); + if cache_time.as_millis() > 10 { + eprintln!("[SNIPPER PERF] Cache clear took {}ms", cache_time.as_millis()); + } + eprintln!("[SNIPPER DEBUG] Selection updated, cache cleared - next frame should show visual change"); + } + } + } + + // Reset canvas timing when selection completes to stop perpetual pipeline warnings + if let Some(SnipperResult::Selected(_)) = result { + self.canvas_program.reset_timing(); + } else if let Some(SnipperResult::Cancelled) = result { + self.canvas_program.reset_timing(); + } + + #[cfg(feature = "debug")] + if self.state.debug_enabled { + let total_time = update_start.elapsed(); + if total_time.as_millis() > 20 { + eprintln!("[SNIPPER PERF WARNING] Total Snipper::update took {}ms (>20ms)", total_time.as_millis()); + } + } + + result + } + + pub fn get_selection(&self) -> Option { + self.state.selection() + } + + pub fn update_screenshot( + &mut self, + screen_images: HashMap>, + screen_bounds: Rectangle, + ) { + self.update_screenshot_with_memory(screen_images, screen_bounds, None); + } + + pub fn update_screenshot_with_memory( + &mut self, + screen_images: HashMap>, + screen_bounds: Rectangle, + remembered_selection: Option, + ) { + // Update the cached image handle with new screenshot data + self.state.cached_image_handle = if let Some(screenshot_data) = screen_images.get("primary") + { + if let Ok(img) = image::load_from_memory(screenshot_data) { + let rgba_img = img.to_rgba8(); + let (width, height) = rgba_img.dimensions(); + Some(cosmic::iced::widget::image::Handle::from_rgba( + width, + height, + rgba_img.into_raw(), + )) + } else { + None + } + } else { + None + }; + + // Update screen data + self.state.screen_images = screen_images; + self.state.screen_bounds = screen_bounds; + self.state.remembered_selection = remembered_selection; + + // Reset selection for new screenshot, but restore from memory if available + if let Some(remembered) = remembered_selection { + // Check if remembered selection fits in new screen bounds + if screen_bounds.contains(Point::new(remembered.x, remembered.y)) && + screen_bounds.contains(Point::new(remembered.x + remembered.width, remembered.y + remembered.height)) { + self.state.selection = Some(remembered); + self.canvas_program.selection = Some(remembered); + } else { + self.state.selection = None; + self.canvas_program.selection = None; + } + } else { + self.state.selection = None; + self.canvas_program.selection = None; + } + + // Reset interaction state + self.state.drag_mode = DragMode::None; + self.state.initial_selection = None; + self.state.last_click_time = None; + self.state.last_click_pos = None; + self.canvas_program.clear_cache(); + } + + pub fn subscription() -> cosmic::iced::Subscription { + // Handle keyboard events + event::listen_with(|event, _status, _window_id| { + match event { + event::Event::Keyboard(keyboard::Event::KeyPressed { key, .. }) => { + Some(SnipperMessage::KeyPressed(key)) + } + // Detect double-clicks for selection acceptance + event::Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => { + // TODO: Implement proper double-click detection with timing + // For now, we'll use a simple approach + None + } + _ => None, + } + }) + } +} + +// Removed redundant Snipper canvas implementation - SelectionOnlyCanvas handles all dynamic drawing + +// Canvas that only draws selection overlay - NO image drawing here +#[derive(Debug)] +pub struct SelectionOnlyCanvas { + selection: Option, + // Canvas cache for efficient drawing + cache: canvas::Cache, + // Track when last selection change occurred for render timing + last_selection_time: Option, +} + +impl SelectionOnlyCanvas { + #[must_use] + pub fn new(selection: Option) -> Self { + Self { + selection, + cache: canvas::Cache::default(), + last_selection_time: None, + } + } + + // Clear cache when selection changes + pub fn clear_cache(&mut self) { + self.cache.clear(); + self.last_selection_time = Some(Instant::now()); + + if std::env::var("COSMIC_SCREENSHOT_DEBUG").is_ok() { + eprintln!("[CANVAS DEBUG] Cache cleared - selection changed, next draw will show new selection"); + } + } + + // Reset timing after selection is complete to stop perpetual warnings + pub fn reset_timing(&mut self) { + self.last_selection_time = None; + + if std::env::var("COSMIC_SCREENSHOT_DEBUG").is_ok() { + eprintln!("[CANVAS DEBUG] Canvas timing reset - no more pipeline warnings"); + } + } +} + +impl canvas::Program for SelectionOnlyCanvas { + type State = (); + + fn update( + &self, + _state: &mut Self::State, + event: canvas::Event, + bounds: Rectangle, + cursor: mouse::Cursor, + ) -> (cosmic::iced::event::Status, Option) { + #[cfg(feature = "debug")] + let debug_enabled = std::env::var("COSMIC_SCREENSHOT_DEBUG").is_ok(); + #[cfg(feature = "debug")] + let event_start = if debug_enabled { Some(Instant::now()) } else { None }; + + let result = match event { + canvas::Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => { + #[cfg(feature = "debug")] + if debug_enabled { + eprintln!("[CANVAS DEBUG] ButtonPressed at cursor: {:?}", cursor.position()); + } + if let Some(position) = cursor.position_in(bounds) { + ( + cosmic::iced::event::Status::Captured, + Some(Message::SnipperMessage(SnipperMessage::StartSelection( + position, + ))), + ) + } else { + (cosmic::iced::event::Status::Ignored, None) + } + } + canvas::Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) => { + #[cfg(feature = "debug")] + if debug_enabled { + eprintln!("[CANVAS DEBUG] ButtonReleased"); + } + ( + cosmic::iced::event::Status::Captured, + Some(Message::SnipperMessage(SnipperMessage::EndSelection)), + ) + } + canvas::Event::Mouse(mouse::Event::CursorMoved { position }) => { + #[cfg(feature = "debug")] + if debug_enabled { + // Only log every 10th mouse move to avoid spam + static MOVE_COUNTER: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0); + if MOVE_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed).is_multiple_of(10) { + eprintln!("[CANVAS DEBUG] CursorMoved to ({:.1}, {:.1})", position.x, position.y); + } + } + // Always capture mouse movements for responsive dragging + ( + cosmic::iced::event::Status::Captured, + Some(Message::SnipperMessage(SnipperMessage::UpdateSelection( + position, + ))), + ) + } + // Note: iced doesn't have built-in double-click detection in canvas events + // We'll need to implement this at the subscription level + _ => (cosmic::iced::event::Status::Ignored, None) + }; + + #[cfg(feature = "debug")] + if let Some(event_start_time) = event_start { + let event_duration = event_start_time.elapsed(); + if event_duration.as_micros() > 1000 { // 1ms threshold for event handling + eprintln!("[CANVAS PERF WARNING] Event handling took {}μs (>1000μs threshold)", event_duration.as_micros()); + } + } + + result + } + + #[allow(clippy::too_many_lines)] + fn draw( + &self, + _state: &Self::State, + renderer: &cosmic::Renderer, + _theme: &cosmic::Theme, + bounds: Rectangle, + _cursor: mouse::Cursor, + ) -> Vec { + #[cfg(feature = "debug")] + let debug_enabled = std::env::var("COSMIC_SCREENSHOT_DEBUG").is_ok(); + #[cfg(feature = "debug")] + let now = Instant::now(); + #[cfg(feature = "debug")] + let draw_start = if debug_enabled { Some(now) } else { None }; + + + #[cfg(feature = "debug")] + if debug_enabled { + eprintln!("[CANVAS DEBUG] Drawing frame"); + } + + // Use cache for efficient drawing - only redraws when cache is cleared + let geometry = self.cache.draw(renderer, bounds.size(), |frame| { + #[cfg(feature = "debug")] + let frame_start = if debug_enabled { Some(Instant::now()) } else { None }; + if let Some(selection) = self.selection { + let overlay_color = Color::from_rgba(0.0, 0.0, 0.0, 0.5); + + // Efficient dark overlay rectangles (only draw what's needed) + let rects = [ + // Top + (selection.y > 0.0) + .then_some((Point::ORIGIN, Size::new(bounds.width, selection.y))), + // Bottom + (selection.y + selection.height < bounds.height).then_some(( + Point::new(0.0, selection.y + selection.height), + Size::new(bounds.width, bounds.height - selection.y - selection.height), + )), + // Left + (selection.x > 0.0).then_some(( + Point::new(0.0, selection.y), + Size::new(selection.x, selection.height), + )), + // Right + (selection.x + selection.width < bounds.width).then_some(( + Point::new(selection.x + selection.width, selection.y), + Size::new( + bounds.width - selection.x - selection.width, + selection.height, + ), + )), + ]; + + // Draw overlay rectangles + for rect in rects.iter().flatten() { + frame.fill_rectangle(rect.0, rect.1, overlay_color); + } + + // Selection border (bright red) + let border_color = Color::from_rgb(1.0, 0.0, 0.0); + let border_width = 3.0; // Slightly thinner for performance + + let border_rects = [ + ( + Point::new(selection.x, selection.y), + Size::new(selection.width, border_width), + ), + ( + Point::new(selection.x + selection.width - border_width, selection.y), + Size::new(border_width, selection.height), + ), + ( + Point::new(selection.x, selection.y + selection.height - border_width), + Size::new(selection.width, border_width), + ), + ( + Point::new(selection.x, selection.y), + Size::new(border_width, selection.height), + ), + ]; + + for (pos, size) in border_rects { + frame.fill_rectangle(pos, size, border_color); + } + + // Corner handles (reduced to 4 for performance) + let handle_size = 8.0; // Smaller for performance + let handle_color = Color::from_rgb(1.0, 1.0, 1.0); + let handles = [ + Point::new( + selection.x - handle_size / 2.0, + selection.y - handle_size / 2.0, + ), + Point::new( + selection.x + selection.width - handle_size / 2.0, + selection.y - handle_size / 2.0, + ), + Point::new( + selection.x + selection.width - handle_size / 2.0, + selection.y + selection.height - handle_size / 2.0, + ), + Point::new( + selection.x - handle_size / 2.0, + selection.y + selection.height - handle_size / 2.0, + ), + ]; + + let handle_size_vec = Size::new(handle_size, handle_size); + for handle_pos in handles { + frame.fill_rectangle(handle_pos, handle_size_vec, handle_color); + frame.stroke_rectangle( + handle_pos, + handle_size_vec, + canvas::Stroke::default() + .with_width(1.0) + .with_color(border_color), + ); + } + } else { + // No selection - single full overlay + frame.fill_rectangle( + Point::ORIGIN, + bounds.size(), + Color::from_rgba(0.0, 0.0, 0.0, 0.5), + ); + } + + // Log frame rendering time if debugging enabled + #[cfg(feature = "debug")] + if let Some(frame_start_time) = frame_start { + let frame_duration = frame_start_time.elapsed(); + if frame_duration.as_millis() > 16 { // 60fps = 16ms budget + eprintln!("[CANVAS PERF WARNING] Frame rendering took {}ms (>16ms for 60fps)", frame_duration.as_millis()); + } else if debug_enabled { + eprintln!("[CANVAS DEBUG] Frame content rendered in {}ms", frame_duration.as_millis()); + } + } + }); + + // Log total draw time and event-to-render pipeline timing if debugging enabled + #[cfg(feature = "debug")] + if let Some(draw_start_time) = draw_start { + let total_duration = draw_start_time.elapsed(); + if total_duration.as_millis() > 20 { + eprintln!("[CANVAS PERF WARNING] Total draw() took {}ms (>20ms threshold)", total_duration.as_millis()); + } + + // Show event-to-render pipeline timing + if let Some(selection_time) = self.last_selection_time { + let event_to_render = draw_start_time.duration_since(selection_time).as_millis(); + if event_to_render > 50 { + eprintln!("[PIPELINE PERF WARNING] Selection change to render took {event_to_render}ms (>50ms)"); + } else if debug_enabled { + eprintln!("[PIPELINE DEBUG] Selection change to render: {event_to_render}ms"); + } + } + } + + vec![geometry] + } +} diff --git a/src/ui.rs b/src/ui.rs new file mode 100644 index 0000000..aa22164 --- /dev/null +++ b/src/ui.rs @@ -0,0 +1,981 @@ +// SPDX-License-Identifier: GPL-3.0-only + +use crate::screenshot::{ScreenshotKind, ScreenshotOptions, ScreenshotResult, ScreenshotManager, ScreenshotError}; +use crate::snipper::{Snipper, SnipperMessage, SnipperResult}; +use crate::settings::SettingsManager; +use crate::error_handling::{report_error, report_success, ErrorSeverity}; +use cosmic::widget; +use cosmic::iced::Rectangle; +use cosmic::dialog::file_chooser; +use cosmic_config::CosmicConfigEntry; +use std::collections::HashMap; +use image; + +#[derive(Debug, Clone)] +pub enum ScreenshotMessage { + SetScreenshotKind(ScreenshotKind), + SetScreenshotDelay(String), + SetScreenshotBackend(usize), + TakeScreenshot, + SaveScreenshot, + ScreenshotComplete(Result), + LaunchRegionSelection(ScreenshotResult), + RegionSelected(cosmic::iced::Rectangle), + RegionSelectionCancelled, + SnipperMessage(SnipperMessage), + BackendsLoaded(Vec), + OpenSnipperWindow(ScreenshotResult), + SnipperWindowOpened(cosmic::iced::window::Id), + ShowSnipperWindow, + HideSnipperWindow, + CloseSnipperWindow, + SnipperWindowClosed(cosmic::iced::window::Id), + // Path selection messages + OpenSaveDirectoryDialog, + SaveDirectorySelected(Option), + ToggleRememberSaveDirectory(bool), + // Selection memory messages + ToggleRememberSelectionArea(bool), + // Screenshot on startup settings + ToggleScreenshotOnStartup(bool), + // Main window management + MainWindowOpened(cosmic::iced::window::Id), + // Generic window events + WindowCloseRequested(cosmic::iced::window::Id), + WindowClosed(cosmic::iced::window::Id), + // Application exit + Exit, + // Error handling messages + DismissErrorDialog, + OpenErrorDialog(String, String), // (title, message) - opens new window + ErrorDialogOpened(cosmic::iced::window::Id), + ErrorDialogClosed(cosmic::iced::window::Id), +} + +#[allow(clippy::struct_excessive_bools)] +pub struct ScreenshotWidget { + pub screenshot_manager: ScreenshotManager, + pub screenshot_kind: ScreenshotKind, + pub screenshot_delay_str: String, + pub last_screenshot: Option, + pub screenshot_in_progress: bool, + pub screenshot_options: Vec, + pub available_backends: Vec, + pub selected_backend: usize, + pub snipper: Option, + pub region_selection_mode: bool, + pub cached_thumbnail_handle: Option, + // Window optimization - reuse snipper window + pub snipper_window_id: Option, + // Path selection fields + pub save_directory: Option, + pub remember_save_directory: bool, + pub show_path_selection: bool, + // Selection memory fields + pub remember_selection_area: bool, + pub last_selection_area: Option, + // Settings management + pub settings_manager: SettingsManager, + // Error dialog state + pub error_dialog: Option<(String, String)>, // (title, message) + pub error_dialog_window_id: Option, +} + +impl Default for ScreenshotWidget { + fn default() -> Self { + Self::new() + } +} + +impl ScreenshotWidget { + /// Creates a new `ScreenshotWidget` + /// + /// # Panics + /// Panics if unable to create a `cosmic_config::Config` instance + pub fn new() -> Self { + use crate::settings::{ScreenshotSettings, APP_ID}; + + // Initialize settings manager + let settings_manager = SettingsManager::new().unwrap_or_else(|e| { + report_error(ErrorSeverity::Warning, "Settings Error", &format!("Failed to load settings: {e}")); + // Create a fallback with default settings + let config = cosmic_config::Config::new(APP_ID, ScreenshotSettings::VERSION) + .unwrap_or_else(|_| panic!("Unable to create config")); + SettingsManager { config, settings: ScreenshotSettings::default() } + }); + + // Check if we're in CLI mode and read CLI options + let (screenshot_kind, screenshot_delay_str, save_directory) = if std::env::var("CLI_MODE_REGION").is_ok() { + let delay = std::env::var("CLI_DELAY").ok() + .and_then(|s| s.parse::().ok()) + .unwrap_or(0); + let output_dir = std::env::var("CLI_OUTPUT_DIR").ok() + .and_then(|s| std::path::PathBuf::from(s).canonicalize().ok()) + .or_else(dirs::picture_dir); + + (ScreenshotKind::RectangularRegion, delay.to_string(), output_dir) + } else { + // Use settings for non-CLI mode + let kind = Self::kind_from_string(&settings_manager.settings.last_screenshot_kind); + let delay_str = settings_manager.settings.last_screenshot_delay.to_string(); + let save_dir = if settings_manager.settings.remember_save_directory { + settings_manager.settings.last_save_directory.clone() + } else { + dirs::picture_dir() + }; + (kind, delay_str, save_dir) + }; + + Self { + screenshot_manager: ScreenshotManager::new(), + screenshot_kind, + screenshot_delay_str, + last_screenshot: None, + screenshot_in_progress: false, + screenshot_options: vec![ + "All screens".to_string(), + "Screen under cursor".to_string(), + "Window under cursor".to_string(), + "Select screen".to_string(), + "Rectangular region".to_string(), + ], + available_backends: vec!["Auto".to_string()], + selected_backend: settings_manager.settings.last_selected_backend, + snipper: None, + region_selection_mode: false, + cached_thumbnail_handle: None, + // Initialize path selection + save_directory, + remember_save_directory: settings_manager.settings.remember_save_directory, + show_path_selection: false, + // Initialize selection memory + remember_selection_area: settings_manager.settings.remember_selection_area, + last_selection_area: settings_manager.settings.last_selection_area.clone().map(Into::into), + // Window optimization + snipper_window_id: None, + // Settings management + settings_manager, + // Error dialog state + error_dialog: None, + error_dialog_window_id: None, + } + } + + fn kind_from_string(kind_str: &str) -> ScreenshotKind { + match kind_str { + "All screens" => ScreenshotKind::AllScreens, + "Screen under cursor" => ScreenshotKind::ScreenUnderCursor, + "Window under cursor" => ScreenshotKind::WindowUnderCursor, + "Select screen" => ScreenshotKind::SelectScreen, + "Rectangular region" => ScreenshotKind::RectangularRegion, + _ => ScreenshotKind::default(), + } + } + + fn kind_to_string(kind: ScreenshotKind) -> String { + match kind { + ScreenshotKind::AllScreens => "All screens".to_string(), + ScreenshotKind::ScreenUnderCursor => "Screen under cursor".to_string(), + ScreenshotKind::WindowUnderCursor => "Window under cursor".to_string(), + ScreenshotKind::SelectScreen => "Select screen".to_string(), + ScreenshotKind::RectangularRegion => "Rectangular region".to_string(), + } + } + + fn update_thumbnail_cache(&mut self) { + self.cached_thumbnail_handle = if let Some(ref screenshot) = self.last_screenshot { + if let Ok(img) = image::load_from_memory(&screenshot.thumbnail_data) { + let rgba_img = img.to_rgba8(); + let (width, height) = rgba_img.dimensions(); + Some(cosmic::iced::widget::image::Handle::from_rgba( + width, + height, + rgba_img.into_raw() + )) + } else { + None + } + } else { + None + }; + } + + pub fn init() -> cosmic::Task { + let manager = ScreenshotManager::new(); + + // Check if we're in CLI region mode + if std::env::var("CLI_MODE_REGION").is_ok() { + // Start region selection immediately in CLI mode + cosmic::Task::batch([ + cosmic::Task::perform( + async move { + let mut backends = manager.get_available_grabbers().await; + backends.insert(0, "Auto".to_string()); + ScreenshotMessage::BackendsLoaded(backends) + }, + |msg| msg, + ), + cosmic::Task::done(ScreenshotMessage::TakeScreenshot), + ]) + } else { + // Check if screenshot on startup is enabled + let settings_manager = SettingsManager::new().ok(); + let screenshot_on_startup = settings_manager + .as_ref() + .is_some_and(|sm| sm.settings.screenshot_on_startup); + + if screenshot_on_startup { + // Load backends and take screenshot automatically + cosmic::Task::batch([ + cosmic::Task::perform( + async move { + let mut backends = manager.get_available_grabbers().await; + backends.insert(0, "Auto".to_string()); + ScreenshotMessage::BackendsLoaded(backends) + }, + |msg| msg, + ), + cosmic::Task::done(ScreenshotMessage::TakeScreenshot), + ]) + } else { + // Normal startup - just load backends + cosmic::Task::perform( + async move { + let mut backends = manager.get_available_grabbers().await; + backends.insert(0, "Auto".to_string()); + ScreenshotMessage::BackendsLoaded(backends) + }, + |msg| msg, + ) + } + } + } + + /// Updates the widget state based on the given message + /// + /// # Panics + /// May panic if primary display screen images are not available + #[allow(clippy::too_many_lines)] + pub fn update(&mut self, message: ScreenshotMessage) -> cosmic::Task { + match message { + ScreenshotMessage::MainWindowOpened(_) => { + // OS-level window open events are handled by main app, not the widget + // This is used for CLI mode logic, not snipper setup + cosmic::Task::none() + } + ScreenshotMessage::SetScreenshotKind(kind) => { + self.screenshot_kind = kind; + // Save to settings + let delay = self.screenshot_delay_str.parse().unwrap_or(0); + if let Err(e) = self.settings_manager.update_screenshot_settings( + &Self::kind_to_string(kind), + delay, + self.selected_backend + ) { + report_error(ErrorSeverity::Warning, "Settings Error", &format!("Failed to save screenshot settings: {e}")); + } + cosmic::Task::none() + } + ScreenshotMessage::SetScreenshotDelay(delay_str) => { + self.screenshot_delay_str.clone_from(&delay_str); + // Save to settings + let delay = delay_str.parse().unwrap_or(0); + if let Err(e) = self.settings_manager.update_screenshot_settings( + &Self::kind_to_string(self.screenshot_kind), + delay, + self.selected_backend + ) { + report_error(ErrorSeverity::Warning, "Settings Error", &format!("Failed to save screenshot settings: {e}")); + } + cosmic::Task::none() + } + ScreenshotMessage::SetScreenshotBackend(index) => { + self.selected_backend = index; + // Save to settings + let delay = self.screenshot_delay_str.parse().unwrap_or(0); + if let Err(e) = self.settings_manager.update_screenshot_settings( + &Self::kind_to_string(self.screenshot_kind), + delay, + index + ) { + report_error(ErrorSeverity::Warning, "Settings Error", &format!("Failed to save screenshot settings: {e}")); + } + cosmic::Task::none() + } + ScreenshotMessage::BackendsLoaded(backends) => { + self.available_backends = backends; + cosmic::Task::none() + } + ScreenshotMessage::TakeScreenshot => { + println!("TakeScreenshot triggered with kind: {:?}", self.screenshot_kind); + self.screenshot_in_progress = true; + + // Handle region screenshots differently using the correct API + if self.screenshot_kind == ScreenshotKind::RectangularRegion { + let manager = self.screenshot_manager.clone(); + return cosmic::Task::perform( + async move { + println!("Using get_screenshot_for_region_selection for rectangular region"); + match manager.get_screenshot_for_region_selection().await { + Ok((screen_images, _screen_bounds)) => { + // Create a ScreenshotResult from the region selection data + // Use the primary screen image + let image_data = screen_images.get("primary").unwrap().clone(); + let result = ScreenshotResult { + path: None, // No file saved yet + saved_to_clipboard: false, + full_image_data: image_data.clone(), + thumbnail_data: image_data, // Will be updated after region selection + }; + ScreenshotMessage::ScreenshotComplete(Ok(result)) + }, + Err(err) => ScreenshotMessage::ScreenshotComplete(Err(err.to_string())), + } + }, + |msg| msg, + ); + } + + // For non-region screenshots, use the regular API + let delay_ms = self.screenshot_delay_str.parse::().unwrap_or(0); + let options = ScreenshotOptions { + kind: self.screenshot_kind, + delay_ms, + save_to_clipboard: false, + save_dir: None, + }; + + let manager = self.screenshot_manager.clone(); + let backend_name = if self.selected_backend == 0 { + None + } else { + Some(self.available_backends[self.selected_backend].clone()) + }; + + cosmic::Task::perform( + async move { + println!("About to take screenshot with options: {:?}, backend: {:?}", options.kind, backend_name); + match manager.take_screenshot_with_backend(&options, backend_name.as_deref()).await { + Ok(result) => ScreenshotMessage::ScreenshotComplete(Ok(result)), + Err(err) => ScreenshotMessage::ScreenshotComplete(Err(err.to_string())), + } + }, + |msg| msg, + ) + } + ScreenshotMessage::SaveScreenshot => { + if let Some(ref screenshot) = self.last_screenshot { + let default_dir = std::path::PathBuf::from("."); + let save_dir = self.save_directory.as_ref() + .unwrap_or(&default_dir); + + let filename = format!("Screenshot_{}.png", chrono::Utc::now().format("%Y-%m-%d_%H-%M-%S")); + let full_path = save_dir.join(&filename); + + // For regular screenshots, save full image data + // For region-cropped screenshots (path=None), save the cropped thumbnail_data + let data_to_save = if screenshot.path.is_some() { + // Regular screenshot - use full resolution data + &screenshot.full_image_data + } else { + // Cropped screenshot - thumbnail_data contains the cropped result + &screenshot.thumbnail_data + }; + + match std::fs::write(&full_path, data_to_save) { + Ok(()) => { + println!("Screenshot saved as: {}", full_path.display()); + report_success("Screenshot Saved", &format!("Screenshot saved to {}", full_path.display())); + } + Err(err) => report_error(ErrorSeverity::Error, "Save Failed", &format!("Failed to save screenshot: {err}")), + } + } + cosmic::Task::none() + } + ScreenshotMessage::ScreenshotComplete(result) => { + println!("ScreenshotComplete triggered"); + self.screenshot_in_progress = false; + match result { + Ok(screenshot) => { + println!("Screenshot succeeded, current kind: {:?}", self.screenshot_kind); + // If this was for a rectangular region, launch the snipper + if self.screenshot_kind == ScreenshotKind::RectangularRegion { + println!("Launching region selection!"); + return cosmic::Task::perform( + async move { ScreenshotMessage::LaunchRegionSelection(screenshot) }, + |msg| msg, + ); + } + self.last_screenshot = Some(screenshot); + self.update_thumbnail_cache(); + } + Err(err) => { + report_error(ErrorSeverity::Error, "Screenshot Failed", &err); + } + } + cosmic::Task::none() + } + ScreenshotMessage::LaunchRegionSelection(screenshot) => { + println!("LaunchRegionSelection triggered - opening fullscreen snipper window"); + cosmic::Task::perform( + async move { ScreenshotMessage::OpenSnipperWindow(screenshot) }, + |msg| msg, + ) + } + ScreenshotMessage::OpenSnipperWindow(screenshot) => { + println!("Opening fullscreen snipper window"); + + // Get actual screenshot dimensions from the FULL image data (not thumbnail!) + let screen_bounds = if let Ok(img) = image::load_from_memory(&screenshot.full_image_data) { + println!("[PERF] Full screenshot dimensions: {}x{}", img.width(), img.height()); + Rectangle::new( + cosmic::iced::Point::ORIGIN, + #[allow(clippy::cast_precision_loss)] + cosmic::iced::Size::new(img.width() as f32, img.height() as f32) + ) + } else { + println!("[PERF] Failed to load full screenshot image, using default dimensions"); + Rectangle::new( + cosmic::iced::Point::ORIGIN, + cosmic::iced::Size::new(1920.0, 1080.0) + ) + }; + + // Create snipper with the full screenshot data + let mut screen_images = HashMap::new(); + screen_images.insert("primary".to_string(), screenshot.full_image_data.clone()); + + // Create snipper or update existing one with new screenshot data + if let Some(ref mut snipper) = self.snipper { + println!("[PERF] Updating existing snipper with new screenshot"); + // Pass remembered selection if enabled + let remembered_selection = if self.remember_selection_area { + self.last_selection_area + } else { + None + }; + snipper.update_screenshot_with_memory(screen_images, screen_bounds, remembered_selection); + } else { + println!("[PERF] Creating new snipper - was None"); + // Use remembered selection if enabled + if self.remember_selection_area && self.last_selection_area.is_some() { + self.snipper = Some(Snipper::new_with_memory(screen_images, screen_bounds, self.last_selection_area)); + println!("Created snipper with remembered selection: {:?}", self.last_selection_area); + } else { + self.snipper = Some(Snipper::new(screen_images, screen_bounds)); + } + } + self.last_screenshot = Some(screenshot); + + // Check if we already have a snipper window to reuse + if let Some(window_id) = self.snipper_window_id { + println!("[PERF] Reusing existing snipper window: {window_id:?}"); + // Show the existing window + cosmic::Task::perform( + async move { ScreenshotMessage::ShowSnipperWindow }, + |msg| msg, + ) + } else { + println!("[PERF] Creating new snipper window"); + // Create new window + let (window_id, open_window) = cosmic::iced::window::open(cosmic::iced::window::Settings { + size: cosmic::iced::Size::new(1920.0, 1080.0), // Will be made fullscreen + decorations: false, + transparent: true, + ..Default::default() + }); + + // Send SnipperWindowOpened immediately to set up application state + // This is separate from OS window events handled by MainWindowOpened + open_window.map(move |_| ScreenshotMessage::SnipperWindowOpened(window_id)) + } + } + ScreenshotMessage::SnipperWindowOpened(window_id) => { + // Handle application-level snipper window setup (sent immediately on window creation) + println!("Snipper window opened: {window_id:?}"); + self.snipper_window_id = Some(window_id); + self.region_selection_mode = true; + // Make window fullscreen and maximize + cosmic::iced::window::maximize(window_id, true) + .map(|(): ()| ScreenshotMessage::BackendsLoaded(vec![])) + } + ScreenshotMessage::ShowSnipperWindow => { + if let Some(window_id) = self.snipper_window_id { + println!("[PERF] Showing existing snipper window: {window_id:?}"); + self.region_selection_mode = true; + // Show and maximize the window + cosmic::Task::batch([ + cosmic::iced::window::maximize(window_id, true).map(|(): ()| ScreenshotMessage::BackendsLoaded(vec![])), + // You could also add window::show() here if the window was completely hidden + ]) + } else { + println!("[PERF] No snipper window to show"); + cosmic::Task::none() + } + } + ScreenshotMessage::HideSnipperWindow => { + if let Some(window_id) = self.snipper_window_id { + println!("[PERF] Hiding snipper window: {window_id:?}"); + self.region_selection_mode = false; + // Minimize the window instead of closing it + cosmic::iced::window::maximize(window_id, false) + .map(|(): ()| ScreenshotMessage::BackendsLoaded(vec![])) + } else { + println!("[PERF] No snipper window to hide"); + self.region_selection_mode = false; + cosmic::Task::none() + } + } + ScreenshotMessage::CloseSnipperWindow => { + println!("CloseSnipperWindow received - this should be handled by main app"); + // This message should be handled by the main app, not here + // The main app should close the actual window + cosmic::Task::none() + } + ScreenshotMessage::SnipperWindowClosed(window_id) => { + println!("Snipper window closed: {window_id:?}"); + self.region_selection_mode = false; + // Keep the snipper cached for reuse instead of destroying it + println!("[PERF] NOT destroying snipper - keeping for reuse"); + cosmic::Task::none() + } + ScreenshotMessage::RegionSelected(region) => { + println!("RegionSelected received: {region:?}"); + + // Remember the selection area if enabled + if self.remember_selection_area { + self.last_selection_area = Some(region); + println!("Remembered selection area: {region:?}"); + // Save to settings + if let Err(e) = self.settings_manager.update_selection_area(Some(region)) { + report_error(ErrorSeverity::Warning, "Settings Error", &format!("Failed to save selection area: {e}")); + } + } + + // Crop the screenshot to the selected region + if let Some(ref screenshot) = self.last_screenshot { + let cropped_screenshot = Self::crop_screenshot_to_region(screenshot, region); + match cropped_screenshot { + Ok(cropped) => { + // In CLI mode, save the screenshot and exit + if std::env::var("CLI_MODE_REGION").is_ok() { + // Apply CLI options: clipboard and file saving + let save_to_clipboard = std::env::var("CLI_CLIPBOARD").is_ok(); + let output_dir = std::env::var("CLI_OUTPUT_DIR").ok() + .and_then(|s| std::path::PathBuf::from(s).canonicalize().ok()) + .or_else(|| self.save_directory.clone()) + .or_else(dirs::picture_dir) + .unwrap_or_else(|| std::path::PathBuf::from(".")); + + let filename = format!("screenshot_{}.png", chrono::Utc::now().format("%Y%m%d_%H%M%S")); + let full_path = output_dir.join(&filename); + + // Save to clipboard if requested + if save_to_clipboard { + // TODO: Implement clipboard saving + println!("Clipboard saving not yet implemented"); + } + + // Save to file + match std::fs::write(&full_path, &cropped.thumbnail_data) { + Ok(()) => { + println!("Screenshot saved to: {}", full_path.display()); + report_success("Region Screenshot", &format!("Screenshot saved to {}", full_path.display())); + // Exit the application gracefully + return cosmic::Task::perform(async {}, |()| { + ScreenshotMessage::Exit + }); + } + Err(err) => { + report_error(ErrorSeverity::Error, "Save Failed", &format!("Failed to save screenshot: {err}")); + return cosmic::Task::perform(async {}, |()| { + ScreenshotMessage::Exit + }); + } + } + } + // Regular GUI mode - update UI + self.last_screenshot = Some(cropped); + self.update_thumbnail_cache(); + { + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] + let width = region.width as u32; + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] + let height = region.height as u32; + println!("Region cropped successfully: {width}x{height}"); + } + self.region_selection_mode = false; + + // Keep snipper cached for reuse + println!("[PERF] RegionSelected - keeping snipper cached"); + // Hide the snipper window instead of closing it + return cosmic::Task::perform( + async move { ScreenshotMessage::HideSnipperWindow }, + |msg| msg, + ); + } + Err(err) => { + report_error(ErrorSeverity::Error, "Crop Failed", &format!("Failed to crop screenshot: {err}")); + if std::env::var("CLI_MODE_REGION").is_ok() { + return cosmic::Task::perform(async {}, |()| { + ScreenshotMessage::Exit + }); + } + } + } + } + cosmic::Task::none() + } + ScreenshotMessage::RegionSelectionCancelled => { + println!("Region selection cancelled - hiding snipper window"); + self.region_selection_mode = false; + + // In CLI mode, exit when cancelled + if std::env::var("CLI_MODE_REGION").is_ok() { + println!("CLI mode region selection cancelled, exiting..."); + return cosmic::Task::perform(async {}, |()| { + ScreenshotMessage::Exit + }); + } + + // Keep snipper cached for reuse + println!("[PERF] RegionSelectionCancelled - keeping snipper cached"); + cosmic::Task::perform( + async move { ScreenshotMessage::HideSnipperWindow }, + |msg| msg, + ) + } + ScreenshotMessage::SnipperMessage(snipper_msg) => { + if let Some(ref mut snipper) = self.snipper { + if let Some(result) = snipper.update(snipper_msg) { + match result { + SnipperResult::Selected(region) => { + println!("Region selected - closing snipper window"); + return cosmic::Task::perform( + async move { ScreenshotMessage::RegionSelected(region) }, + |msg| msg, + ); + } + SnipperResult::Cancelled => { + println!("Snipper cancelled - closing snipper window"); + return cosmic::Task::perform( + async move { ScreenshotMessage::RegionSelectionCancelled }, + |msg| msg, + ); + } + } + } + } + cosmic::Task::none() + } + ScreenshotMessage::OpenSaveDirectoryDialog => { + println!("Opening save directory dialog"); + // Use COSMIC's native file chooser for directory selection + cosmic::Task::perform( + async move { + let dialog = file_chooser::open::Dialog::new() + .title("Choose Save Directory"); + + match dialog.open_folder().await { + Ok(response) => { + // Convert URL to PathBuf + if let Ok(path) = response.url().to_file_path() { + ScreenshotMessage::SaveDirectorySelected(Some(path)) + } else { + ScreenshotMessage::SaveDirectorySelected(None) + } + } + Err(file_chooser::Error::Cancelled) => { + println!("Directory selection cancelled"); + ScreenshotMessage::SaveDirectorySelected(None) + } + Err(err) => { + report_error(ErrorSeverity::Warning, "Directory Selection", &format!("Directory selection error: {err}")); + ScreenshotMessage::SaveDirectorySelected(None) + } + } + }, + |msg| msg, + ) + } + ScreenshotMessage::SaveDirectorySelected(path) => { + println!("Save directory selected: {path:?}"); + self.save_directory.clone_from(&path); + self.show_path_selection = false; + // Save to settings + if let Err(e) = self.settings_manager.update_save_directory(path) { + report_error(ErrorSeverity::Warning, "Settings Error", &format!("Failed to save directory setting: {e}")); + } + cosmic::Task::none() + } + ScreenshotMessage::ToggleRememberSaveDirectory(remember) => { + self.remember_save_directory = remember; + if let Err(e) = self.settings_manager.set_remember_save_directory(remember) { + report_error(ErrorSeverity::Warning, "Settings Error", &format!("Failed to save remember save directory setting: {e}")); + } + cosmic::Task::none() + } + ScreenshotMessage::ToggleRememberSelectionArea(remember) => { + self.remember_selection_area = remember; + if !remember { + // Clear remembered selection if disabled + self.last_selection_area = None; + } + if let Err(e) = self.settings_manager.set_remember_selection_area(remember) { + report_error(ErrorSeverity::Warning, "Settings Error", &format!("Failed to save remember selection area setting: {e}")); + } + cosmic::Task::none() + } + ScreenshotMessage::ToggleScreenshotOnStartup(enabled) => { + if let Err(e) = self.settings_manager.set_screenshot_on_startup(enabled) { + report_error(ErrorSeverity::Warning, "Settings Error", &format!("Failed to save screenshot on startup setting: {e}")); + } + cosmic::Task::none() + } + ScreenshotMessage::Exit => { + // This is handled by the main app, just return none here + cosmic::Task::none() + } + ScreenshotMessage::WindowCloseRequested(_) | ScreenshotMessage::WindowClosed(_) => { + // Generic window events are handled by the main app + cosmic::Task::none() + } + ScreenshotMessage::DismissErrorDialog => { + self.error_dialog = None; + cosmic::Task::none() + } + ScreenshotMessage::OpenErrorDialog(title, message) => { + self.error_dialog = Some((title, message)); + // Open new error dialog window + cosmic::Task::perform( + async move { ScreenshotMessage::ErrorDialogOpened(cosmic::iced::window::Id::unique()) }, + |msg| msg, + ) + } + ScreenshotMessage::ErrorDialogOpened(window_id) => { + self.error_dialog_window_id = Some(window_id); + cosmic::Task::none() + } + ScreenshotMessage::ErrorDialogClosed(window_id) => { + if Some(window_id) == self.error_dialog_window_id { + self.error_dialog_window_id = None; + self.error_dialog = None; + } + cosmic::Task::none() + } + } + } + + fn get_screenshot_kind_index(&self) -> usize { + match self.screenshot_kind { + ScreenshotKind::AllScreens => 0, + ScreenshotKind::ScreenUnderCursor => 1, + ScreenshotKind::WindowUnderCursor => 2, + ScreenshotKind::SelectScreen => 3, + ScreenshotKind::RectangularRegion => 4, + } + } + + fn crop_screenshot_to_region(screenshot: &ScreenshotResult, region: Rectangle) -> Result { + // Load the image from full resolution data for accurate cropping + let img = image::load_from_memory(&screenshot.full_image_data) + .map_err(ScreenshotError::Image)?; + + // Ensure region is within image bounds + #[allow(clippy::cast_precision_loss)] + let img_width = img.width() as f32; + #[allow(clippy::cast_precision_loss)] + let img_height = img.height() as f32; + + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] + let crop_x = region.x.max(0.0).min(img_width) as u32; + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] + let crop_y = region.y.max(0.0).min(img_height) as u32; + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss, clippy::cast_precision_loss)] + let crop_width = region.width.min(img_width - crop_x as f32) as u32; + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss, clippy::cast_precision_loss)] + let crop_height = region.height.min(img_height - crop_y as f32) as u32; + + if crop_width == 0 || crop_height == 0 { + return Err(ScreenshotError::Portal("Invalid crop region".to_string())); + } + + // Crop the image + let cropped = img.crop_imm(crop_x, crop_y, crop_width, crop_height); + + // Convert back to bytes + let mut buffer = Vec::new(); + cropped.write_to(&mut std::io::Cursor::new(&mut buffer), image::ImageFormat::Png) + .map_err(ScreenshotError::Image)?; + + // Create new screenshot result with cropped data + Ok(ScreenshotResult { + path: None, // Remove path so saving uses the cropped thumbnail_data + saved_to_clipboard: screenshot.saved_to_clipboard, + thumbnail_data: buffer.clone(), + full_image_data: buffer, // For cropped result, full and thumbnail data are the same + }) + } + + #[allow(clippy::too_many_lines)] + pub fn view(&self) -> cosmic::Element<'_, ScreenshotMessage> { + // Use COSMIC theme spacing for consistency + let spacing = cosmic::theme::active().cosmic().spacing; + + // Header section + let header = widget::container( + cosmic::widget::text::title4("COSMIC Screenshot") + ) + .padding(spacing.space_m); + + // Thumbnail preview section - adaptive sizing for 360p thumbnails + let thumbnail_section = if let Some(ref image_handle) = self.cached_thumbnail_handle { + widget::container( + cosmic::widget::image(image_handle.clone()) + .content_fit(cosmic::iced::ContentFit::ScaleDown) + ) + .width(cosmic::iced::Length::Fixed(640.0)) + .height(cosmic::iced::Length::Fixed(360.0)) + .padding(2) // Add padding to keep image away from border + .style(|theme: &cosmic::Theme| cosmic::widget::container::Style { + border: cosmic::iced::Border { + width: 1.0, + color: theme.cosmic().accent.base.into(), + ..Default::default() + }, + ..Default::default() + }) + } else { + widget::container( + widget::column() + .push(widget::text("No screenshot")) + .push(widget::text("taken yet")) + .align_x(cosmic::iced::Alignment::Center) + .spacing(spacing.space_xxs) + ) + .width(cosmic::iced::Length::Fixed(640.0)) + .height(cosmic::iced::Length::Fixed(360.0)) + .padding(2) // Add padding to keep text away from border + .center_x(cosmic::iced::Length::Fill) + .center_y(cosmic::iced::Length::Fill) + .style(|theme: &cosmic::Theme| cosmic::widget::container::Style { + border: cosmic::iced::Border { + width: 1.0, + color: theme.cosmic().bg_divider().into(), + ..Default::default() + }, + ..Default::default() + }) + }; + + // Controls section - improved layout + let controls_section = widget::column() + .push( + cosmic::widget::text::caption("Screenshot Type:") + ) + .push( + widget::dropdown(&self.screenshot_options, Some(self.get_screenshot_kind_index()), |index| { + match index { + 1 => ScreenshotMessage::SetScreenshotKind(ScreenshotKind::ScreenUnderCursor), + 2 => ScreenshotMessage::SetScreenshotKind(ScreenshotKind::WindowUnderCursor), + 3 => ScreenshotMessage::SetScreenshotKind(ScreenshotKind::SelectScreen), + 4 => ScreenshotMessage::SetScreenshotKind(ScreenshotKind::RectangularRegion), + _ => ScreenshotMessage::SetScreenshotKind(ScreenshotKind::AllScreens), + } + }) + .width(cosmic::iced::Length::Fixed(250.0)) + ) + .push( + cosmic::widget::text::caption("Delay (milliseconds):") + ) + .push( + widget::text_input("0", &self.screenshot_delay_str) + .on_input(ScreenshotMessage::SetScreenshotDelay) + .width(cosmic::iced::Length::Fixed(120.0)) + ) + .push( + cosmic::widget::text::caption("Backend:") + ) + .push( + widget::dropdown(&self.available_backends, Some(self.selected_backend), ScreenshotMessage::SetScreenshotBackend) + .width(cosmic::iced::Length::Fixed(250.0)) + ) + .push( + cosmic::widget::text::caption("Save Directory:") + ) + .push( + widget::row() + .push( + cosmic::widget::text::body( + self.save_directory + .as_ref().map_or_else(|| "No directory selected".to_string(), |p| p.display().to_string()) + ) + .width(cosmic::iced::Length::Fill) + ) + .push( + widget::button::standard("Browse...") + .on_press(ScreenshotMessage::OpenSaveDirectoryDialog) + ) + .spacing(spacing.space_xs) + .width(cosmic::iced::Length::Fixed(250.0)) + ) + .push( + widget::checkbox("Remember save directory", self.remember_save_directory) + .on_toggle(ScreenshotMessage::ToggleRememberSaveDirectory) + ) + .push( + widget::checkbox("Remember selection area", self.remember_selection_area) + .on_toggle(ScreenshotMessage::ToggleRememberSelectionArea) + ) + .push( + widget::checkbox("Take screenshot on startup", self.settings_manager.settings.screenshot_on_startup) + .on_toggle(ScreenshotMessage::ToggleScreenshotOnStartup) + ) + .spacing(spacing.space_xs); + + // Action buttons section + let actions_section = widget::column() + .push( + widget::button::standard(if self.screenshot_in_progress { + "Taking Screenshot..." + } else { + "Take Screenshot" + }) + .on_press_maybe(if self.screenshot_in_progress { + None + } else { + Some(ScreenshotMessage::TakeScreenshot) + }) + ) + .push_maybe(if self.last_screenshot.is_some() { + Some( + widget::button::standard("Save Screenshot") + .on_press(ScreenshotMessage::SaveScreenshot) + ) + } else { + None + }) + .spacing(spacing.space_xs); + + // Main content layout - following szhrmk's row-based layout + let main_content = widget::row() + .push(thumbnail_section) + .push( + widget::column() + .push(controls_section) + .push(actions_section) + .spacing(spacing.space_m) + .padding(spacing.space_s) + ) + .spacing(spacing.space_m) + .align_y(cosmic::iced::Alignment::Start); + + // Complete layout with proper COSMIC styling + widget::column() + .push(header) + .push(main_content) + .spacing(spacing.space_s) + .padding(spacing.space_m) + .into() + } +} \ No newline at end of file