diff --git a/.cargo/config.toml b/.cargo/config.toml index 430f9f5..3945625 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,2 +1,3 @@ [env] -CEF_PATH = "vendor/cef" \ No newline at end of file +CEF_PATH = "vendor/cef" +SERVER_PATH = "data/server.js" \ No newline at end of file diff --git a/.gitattributes b/.gitattributes index 05d922a..385bf93 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1 @@ -data/server.js binary \ No newline at end of file +data/server.js binary linguist-vendored \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..d3afa96 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,27 @@ +name: Bug report +description: Report a bug +body: + - type: dropdown + id: install + attributes: + label: "Installation type" + options: + - Flatpak (.flatpak) + - Source + validations: + required: true + + - type: input + id: platform + attributes: + label: "Platform" + placeholder: Fedora 42 + validations: + required: true + + - type: textarea + id: description + attributes: + label: "Describe the bug" + validations: + required: true \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8319bb7..836ef3d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,11 +15,16 @@ jobs: uses: actions/checkout@v4 - name: Rust setup uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy - name: Install dependencies run: | sudo apt-get -y update - sudo apt-get -y install build-essential libssl-dev libnss3 libmpv-dev libgtk-3-dev libappindicator3-dev + sudo apt-get -y install libmpv-dev + sudo sh -c 'echo "deb http://archive.ubuntu.com/ubuntu/ questing main" >> /etc/apt/sources.list' + sudo apt-get -y update + sudo apt-get -y install libssl-dev libgtk-4-dev libadwaita-1-dev gettext nodejs - name: Lint fmt run: cargo fmt --all -- --check diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index abd18ec..b6da9b0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,14 +27,17 @@ jobs: - name: Install requirements run: | sudo apt-get -y update - sudo apt-get -y install build-essential libssl-dev libnss3 libmpv-dev flatpak-builder libgtk-3-dev libappindicator3-dev + sudo apt-get -y install libmpv-dev + sudo sh -c 'echo "deb http://archive.ubuntu.com/ubuntu/ questing main" >> /etc/apt/sources.list' + sudo apt-get -y update + sudo apt-get -y install libssl-dev libgtk-4-dev libadwaita-1-dev gettext nodejs flatpak-builder python3-aiohttp python3-tomlkit sudo flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo sudo flatpak install -y \ - org.freedesktop.Sdk//24.08 \ - org.freedesktop.Platform//24.08 \ - org.freedesktop.Sdk.Extension.rust-stable//24.08 \ - org.freedesktop.Platform.ffmpeg-full//24.08 - python3 -m pip install toml aiohttp + org.gnome.Sdk//49 \ + org.gnome.Platform//49 \ + org.freedesktop.Sdk.Extension.rust-stable//25.08 \ + org.freedesktop.Platform.ffmpeg-full//25.08 \ + org.freedesktop.Platform.VAAPI.Intel//25.08 - name: Build flatpak package run: ./flatpak/build.sh diff --git a/.gitignore b/.gitignore index 7ab9bd5..0f01cbe 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ -/.flatpak-builder /target -/vendor \ No newline at end of file +/vendor +.flatpak-builder diff --git a/.gitmodules b/.gitmodules index ea903ff..9e44df6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ [submodule "flatpak/flatpak-builder-tools"] path = flatpak/flatpak-builder-tools url = https://github.com/flatpak/flatpak-builder-tools -[submodule "flatpak/shared-modules"] - path = flatpak/shared-modules - url = https://github.com/flathub/shared-modules diff --git a/Cargo.lock b/Cargo.lock index 3ee24bc..b5ba2cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,55 +2,17 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "ab_glyph" -version = "0.2.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3672c180e71eeaaac3a541fbbc5f5ad4def8b747c595ad30d674e43049f7b0" -dependencies = [ - "ab_glyph_rasterizer", - "owned_ttf_parser", -] - -[[package]] -name = "ab_glyph_rasterizer" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" - -[[package]] -name = "addr2line" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" -dependencies = [ - "gimli", -] - [[package]] name = "adler2" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" -[[package]] -name = "ahash" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" -dependencies = [ - "cfg-if", - "getrandom 0.3.3", - "once_cell", - "version_check", - "zerocopy", -] - [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] @@ -65,37 +27,19 @@ dependencies = [ ] [[package]] -name = "android-activity" -version = "0.6.0" +name = "android_system_properties" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ - "android-properties", - "bitflags 2.9.1", - "cc", - "cesu8", - "jni", - "jni-sys", "libc", - "log", - "ndk", - "ndk-context", - "ndk-sys", - "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 = "anstream" -version = "0.6.19" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", @@ -108,9 +52,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" @@ -123,41 +67,35 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.9" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "anyhow" -version = "1.0.98" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "arbitrary" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" - -[[package]] -name = "arc-swap" -version = "1.7.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" [[package]] name = "arg_enum_proc_macro" @@ -167,46 +105,33 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn", ] -[[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 = "ashpd" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cbdf310d77fd3aaee6ea2093db7011dc2d35d2eb3481e5607f1f8d942ed99df" +checksum = "da0986d5b4f0802160191ad75f8d33ada000558757db3defb70299ca95d9fcbd" dependencies = [ - "async-fs", - "async-net", "enumflags2", "futures-channel", "futures-util", - "rand 0.9.1", - "raw-window-handle", + "gdk4-wayland", + "gdk4-x11", + "glib", + "gtk4", + "rand 0.9.2", "serde", "serde_repr", + "tokio", "url", - "wayland-backend", - "wayland-client", - "wayland-protocols", "zbus", ] @@ -222,103 +147,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "async-channel" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" -dependencies = [ - "concurrent-queue", - "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", - "futures-lite", - "pin-project-lite", - "slab", -] - -[[package]] -name = "async-fs" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" -dependencies = [ - "async-lock", - "blocking", - "futures-lite", -] - -[[package]] -name = "async-io" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1237c0ae75a0f3765f58910ff9cdd0a12eeb39ab2f4c7de23262f337f0aacbb3" -dependencies = [ - "async-lock", - "cfg-if", - "concurrent-queue", - "futures-io", - "futures-lite", - "parking", - "polling", - "rustix 1.0.7", - "slab", - "tracing", - "windows-sys 0.59.0", -] - -[[package]] -name = "async-lock" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" -dependencies = [ - "event-listener", - "event-listener-strategy", - "pin-project-lite", -] - -[[package]] -name = "async-net" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" -dependencies = [ - "async-io", - "blocking", - "futures-lite", -] - -[[package]] -name = "async-process" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde3f4e40e6021d7acffc90095cbd6dc54cb593903d1de5832f435eb274b85dc" -dependencies = [ - "async-channel", - "async-io", - "async-lock", - "async-signal", - "async-task", - "blocking", - "cfg-if", - "event-listener", - "futures-lite", - "rustix 1.0.7", - "tracing", -] - [[package]] name = "async-recursion" version = "1.1.1" @@ -327,65 +155,18 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", -] - -[[package]] -name = "async-signal" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7605a4e50d4b06df3898d5a70bf5fde51ed9059b0434b73105193bc27acce0d" -dependencies = [ - "async-io", - "async-lock", - "atomic-waker", - "cfg-if", - "futures-core", - "futures-io", - "rustix 1.0.7", - "signal-hook-registry", - "slab", - "windows-sys 0.59.0", + "syn", ] -[[package]] -name = "async-task" -version = "4.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" - [[package]] name = "async-trait" -version = "0.1.88" +version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", -] - -[[package]] -name = "atk" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241b621213072e993be4f6f3a9e4b45f65b7e6faad43001be957184b7bb1824b" -dependencies = [ - "atk-sys", - "glib", - "libc", -] - -[[package]] -name = "atk-sys" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e48b684b0ca77d2bbadeef17424c2ea3c897d44d566a1617e7e8f30614d086" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", + "syn", ] [[package]] @@ -402,9 +183,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "av1-grain" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f3efb2ca85bc610acfa917b5aaa36f3fcbebed5b3182d7f877b02531c4b80c8" +checksum = "8cfddb07216410377231960af4fcab838eaa12e013417781b78bd95ee22077f8" dependencies = [ "anyhow", "arrayvec", @@ -416,37 +197,13 @@ dependencies = [ [[package]] name = "avif-serialize" -version = "0.8.3" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98922d6a4cfbcb08820c69d8eeccc05bb1f29bfa06b4f5b1dbfe9a868bd7608e" +checksum = "47c8fbc0f831f4519fe8b810b6a7a91410ec83031b8233f730a0480029f6a23f" dependencies = [ "arrayvec", ] -[[package]] -name = "backtrace" -version = "0.3.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-targets 0.52.6", -] - -[[package]] -name = "base62" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10e52a7bcb1d6beebee21fb5053af9e3cbb7a7ed1a4909e534040e676437ab1f" -dependencies = [ - "rustversion", -] - [[package]] name = "base64" version = "0.22.1" @@ -455,9 +212,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bit_field" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" +checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" [[package]] name = "bitflags" @@ -467,12 +224,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.1" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" -dependencies = [ - "serde", -] +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "bitstream-io" @@ -481,41 +235,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2" [[package]] -name = "block2" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" -dependencies = [ - "objc2 0.5.2", -] - -[[package]] -name = "block2" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "340d2f0bdb2a43c1d3cd40513185b2bd7def0aa1052f956455114bc98f82dcf2" -dependencies = [ - "objc2 0.6.1", -] - -[[package]] -name = "blocking" -version = "1.6.1" +name = "block" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" -dependencies = [ - "async-channel", - "async-task", - "futures-io", - "futures-lite", - "piper", -] +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" [[package]] name = "bstr" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" +checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" dependencies = [ "memchr", "serde", @@ -529,15 +258,15 @@ checksum = "56ed6191a7e78c36abdb16ab65341eefd73d64d303fffccdbb00d51e4205967b" [[package]] name = "bumpalo" -version = "3.18.1" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "bytemuck" -version = "1.23.1" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" +checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" [[package]] name = "byteorder" @@ -559,70 +288,43 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "bzip2" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bea8dcd42434048e4f7a304411d9273a411f647446c1234a65ce0554923f4cff" +checksum = "f3a53fac24f34a81bc9954b5d6cfce0c21e18ec6959f44f56e8e90e4bb7c346c" dependencies = [ "libbz2-rs-sys", ] [[package]] name = "cairo-rs" -version = "0.18.5" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" +checksum = "dfe4354df4da648870e363387679081f8f9fc538ec8b55901e3740c6a0ef81b1" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.10.0", "cairo-sys-rs", "glib", "libc", - "once_cell", - "thiserror 1.0.69", ] [[package]] name = "cairo-sys-rs" -version = "0.18.2" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" +checksum = "47d6c3300c7103eb8e4de07591003511aa25664438f8c6fc317a3a9902c103f8" dependencies = [ "glib-sys", "libc", - "system-deps", -] - -[[package]] -name = "calloop" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" -dependencies = [ - "bitflags 2.9.1", - "log", - "polling", - "rustix 0.38.44", - "slab", - "thiserror 1.0.69", -] - -[[package]] -name = "calloop-wayland-source" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20" -dependencies = [ - "calloop", - "rustix 0.38.44", - "wayland-backend", - "wayland-client", + "system-deps 7.0.7", ] [[package]] name = "cc" -version = "1.2.27" +version = "1.2.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" +checksum = "35900b6c8d709fb1d854671ae27aeaa9eec2f8b01b364e1619a40da3e6fe2afe" dependencies = [ + "find-msvc-tools", "jobserver", "libc", "shlex", @@ -630,17 +332,19 @@ dependencies = [ [[package]] name = "cef" -version = "138.2.2+138.0.21" -source = "git+https://github.com/Stremio/cef-rs#8aa6a6aa6e3b621e9a571a34b64e90f1a574277b" +version = "143.1.0+143.0.10" +source = "git+https://github.com/Stremio/cef-rs#7e58f4fc8fb83813f5afa877426b43367e1dc326" dependencies = [ "cef-dll-sys", - "windows-sys 0.60.2", + "libloading", + "objc2", + "windows-sys 0.61.2", ] [[package]] name = "cef-dll-sys" -version = "138.2.2+138.0.21" -source = "git+https://github.com/Stremio/cef-rs#8aa6a6aa6e3b621e9a571a34b64e90f1a574277b" +version = "143.1.0+143.0.10" +source = "git+https://github.com/Stremio/cef-rs#7e58f4fc8fb83813f5afa877426b43367e1dc326" dependencies = [ "anyhow", "cmake", @@ -649,26 +353,30 @@ dependencies = [ ] [[package]] -name = "cesu8" -version = "1.1.0" +name = "cfg-expr" +version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon 0.12.16", +] [[package]] name = "cfg-expr" -version = "0.15.8" +version = "0.20.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +checksum = "9acd0bdbbf4b2612d09f52ba61da432140cb10930354079d0d53fafc12968726" dependencies = [ "smallvec", - "target-lexicon", + "target-lexicon 0.13.3", ] [[package]] name = "cfg-if" -version = "1.0.1" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" @@ -677,19 +385,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] -name = "cgl" -version = "0.3.2" +name = "chrono" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" dependencies = [ - "libc", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link 0.2.1", ] [[package]] name = "clap" -version = "4.5.40" +version = "4.5.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" +checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5" dependencies = [ "clap_builder", "clap_derive", @@ -697,9 +409,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.40" +version = "4.5.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" +checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a" dependencies = [ "anstream", "anstyle", @@ -709,21 +421,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.40" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" dependencies = [ - "heck 0.5.0", + "heck", "proc-macro2", "quote", - "syn 2.0.103", + "syn", ] [[package]] name = "clap_lex" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" +checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" [[package]] name = "cmake" @@ -746,16 +458,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" -[[package]] -name = "combine" -version = "4.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" -dependencies = [ - "bytes", - "memchr", -] - [[package]] name = "concurrent-queue" version = "2.5.0" @@ -767,15 +469,15 @@ dependencies = [ [[package]] name = "console" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e09ced7ebbccb63b4c65413d821f2e00ce54c5ca4514ddc6b3c892fdbcbc69d" +checksum = "b430743a6eb14e9764d4260d4c0d8123087d504eeb9c48f2b2a5e810dd369df4" dependencies = [ "encode_unicode", "libc", "once_cell", "unicode-width", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -791,9 +493,9 @@ dependencies = [ [[package]] name = "cookie_store" -version = "0.21.1" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eac901828f88a5241ee0600950ab981148a18f2f756900ffba1b125ca6a3ef9" +checksum = "3fc4bff745c9b4c7fb1e97b25d13153da2bc7796260141df62378998d070207f" dependencies = [ "cookie", "document-features", @@ -824,84 +526,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] -name = "core-graphics" -version = "0.23.2" +name = "crc32fast" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "core-graphics-types", - "foreign-types 0.5.0", - "libc", + "cfg-if", ] [[package]] -name = "core-graphics-types" -version = "0.1.3" +name = "crossbeam-deque" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "libc", + "crossbeam-epoch", + "crossbeam-utils", ] [[package]] -name = "crc32fast" -version = "1.4.2" +name = "crossbeam-epoch" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ - "cfg-if", + "crossbeam-utils", ] [[package]] -name = "crossbeam" -version = "0.8.4" +name = "crossbeam-queue" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-epoch", - "crossbeam-queue", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-queue" -version = "0.3.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" dependencies = [ "crossbeam-utils", ] @@ -914,36 +570,19 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" - -[[package]] -name = "cursor-icon" -version = "1.2.0" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "deranged" -version = "0.4.0" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" dependencies = [ "powerfmt", ] -[[package]] -name = "dircpy" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a88521b0517f5f9d51d11925d8ab4523497dcf947073fa3231a311b63941131c" -dependencies = [ - "jwalk", - "log", - "walkdir", -] - [[package]] name = "dirs" version = "6.0.0" @@ -962,23 +601,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.60.2", -] - -[[package]] -name = "dispatch" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" - -[[package]] -name = "dispatch2" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" -dependencies = [ - "bitflags 2.9.1", - "objc2 0.6.1", + "windows-sys 0.61.2", ] [[package]] @@ -989,37 +612,22 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", -] - -[[package]] -name = "dlib" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" -dependencies = [ - "libloading 0.8.8", + "syn", ] [[package]] name = "document-features" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" dependencies = [ "litrs", ] -[[package]] -name = "downcast-rs" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" - [[package]] name = "download-cef" -version = "2.0.1" -source = "git+https://github.com/Stremio/cef-rs#8aa6a6aa6e3b621e9a571a34b64e90f1a574277b" +version = "2.2.0" +source = "git+https://github.com/Stremio/cef-rs#7e58f4fc8fb83813f5afa877426b43367e1dc326" dependencies = [ "bzip2", "clap", @@ -1030,21 +638,10 @@ dependencies = [ "serde_json", "sha1_smol", "tar", - "thiserror 2.0.12", + "thiserror 2.0.17", "ureq", ] -[[package]] -name = "dpi" -version = "0.1.1" -source = "git+https://github.com/Stremio/winit?branch=feat%2Fwayland-file-window-events#dbb55e126896887283c269c4460d0d7f80349012" - -[[package]] -name = "dpi" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" - [[package]] name = "either" version = "1.15.0" @@ -1090,7 +687,19 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn", +] + +[[package]] +name = "epoxy" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b96028ce3ff03972312fd8243281858e80fc0f9838b1f035676b6c199214d9e" +dependencies = [ + "gl_generator", + "libc", + "pkg-config", + "shared_library", ] [[package]] @@ -1110,7 +719,7 @@ checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn", ] [[package]] @@ -1121,19 +730,19 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.12" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "event-listener" -version = "5.4.0" +version = "5.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" dependencies = [ "concurrent-queue", "parking", @@ -1171,6 +780,26 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "fax" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab" +dependencies = [ + "fax_derive", +] + +[[package]] +name = "fax_derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "fdeflate" version = "0.3.7" @@ -1192,26 +821,44 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.25" +version = "0.2.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" +checksum = "bc0505cd1b6fa6580283f6bdf70a73fcf4aba1184038c90902b92b3dd0df63ed" dependencies = [ "cfg-if", "libc", "libredox", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] +[[package]] +name = "find-msvc-tools" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" + [[package]] name = "flate2" -version = "1.1.2" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" dependencies = [ "crc32fast", "miniz_oxide", ] +[[package]] +name = "flume" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" +dependencies = [ + "futures-core", + "futures-sink", + "nanorand", + "spin", +] + [[package]] name = "fnv" version = "1.0.7" @@ -1224,28 +871,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ - "foreign-types-shared 0.1.1", -] - -[[package]] -name = "foreign-types" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" -dependencies = [ - "foreign-types-macros", - "foreign-types-shared 0.3.1", -] - -[[package]] -name = "foreign-types-macros" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.103", + "foreign-types-shared", ] [[package]] @@ -1254,36 +880,15 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" -[[package]] -name = "foreign-types-shared" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" - [[package]] name = "form_urlencoded" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ "percent-encoding", ] -[[package]] -name = "futures" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - [[package]] name = "futures-channel" version = "0.3.31" @@ -1291,7 +896,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", - "futures-sink", ] [[package]] @@ -1319,9 +923,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" -version = "2.6.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" dependencies = [ "fastrand", "futures-core", @@ -1338,7 +942,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn", ] [[package]] @@ -1359,64 +963,59 @@ 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 = "gdk" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9f245958c627ac99d8e529166f9823fb3b838d1d41fd2b297af3075093c2691" -dependencies = [ - "cairo-rs", - "gdk-pixbuf", - "gdk-sys", - "gio", - "glib", - "libc", - "pango", -] - [[package]] name = "gdk-pixbuf" -version = "0.18.5" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" +checksum = "2a3c64459f569154f37616fc28923bfac490d4aaa134aaf5eca58a2c0c13050f" dependencies = [ "gdk-pixbuf-sys", "gio", "glib", "libc", - "once_cell", ] [[package]] name = "gdk-pixbuf-sys" -version = "0.18.0" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" +checksum = "3854ef7a6a8b8f3b4013a01d5f9cb0d1794ec4e810c6cb4e2cc6d980f1baf724" dependencies = [ "gio-sys", "glib-sys", "gobject-sys", "libc", - "system-deps", + "system-deps 7.0.7", +] + +[[package]] +name = "gdk4" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e292649dc26e3440c508a00f42ab39156008320dd6e962d63eaf626ba4d7f0" +dependencies = [ + "cairo-rs", + "gdk-pixbuf", + "gdk4-sys", + "gio", + "glib", + "libc", + "pango", ] [[package]] -name = "gdk-sys" -version = "0.18.2" +name = "gdk4-sys" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c2d13f38594ac1e66619e188c6d5a1adb98d11b2fcf7894fc416ad76aa2f3f7" +checksum = "f4f3174fa4f1e0bf2a7e04469b65db8f4d1db89a6f5cdc57727b14e97ce438cf" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", @@ -1426,17 +1025,57 @@ dependencies = [ "libc", "pango-sys", "pkg-config", - "system-deps", + "system-deps 7.0.7", ] [[package]] -name = "gethostname" -version = "0.4.3" +name = "gdk4-wayland" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8aa6365a7cd4fd506cb724db4d023f90d9bc4760ecf8934e2bac825ff2b30029" +dependencies = [ + "gdk4", + "gdk4-wayland-sys", + "gio", + "glib", + "libc", +] + +[[package]] +name = "gdk4-wayland-sys" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8aa299e52b5364b14b877a02778dfc193fa112d22da80a5b6722a8ea088a4e8" +dependencies = [ + "glib-sys", + "libc", + "system-deps 7.0.7", +] + +[[package]] +name = "gdk4-x11" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8c1726186e4737a488ed2e431cce499af613edbf2332ede11cff6a98e3c7131" +dependencies = [ + "gdk4", + "gdk4-x11-sys", + "gio", + "glib", + "libc", + "x11", +] + +[[package]] +name = "gdk4-x11-sys" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +checksum = "18801bb9230acdaa7d0aeaf39bc55a8f21eb5526a0a193a8e6de2c54075ad4f2" dependencies = [ + "gdk4-sys", + "glib-sys", "libc", - "windows-targets 0.48.5", + "system-deps 7.0.7", ] [[package]] @@ -1446,43 +1085,59 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", + "js-sys", "libc", - "wasi 0.11.1+wasi-snapshot-preview1", + "wasi", + "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", "r-efi", - "wasi 0.14.2+wasi-0.2.4", + "wasip2", ] [[package]] -name = "gif" -version = "0.13.1" +name = "gettext-rs" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" +checksum = "5d5857dc1b7f0fee86961de833f434e29494d72af102ce5355738c0664222bdf" dependencies = [ - "color_quant", - "weezl", + "gettext-sys", + "locale_config", +] + +[[package]] +name = "gettext-sys" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea859ab0dd7e70ff823032b3e077d03d39c965d68c6c10775add60e999d8ee9" +dependencies = [ + "cc", + "temp-dir", ] [[package]] -name = "gimli" -version = "0.31.1" +name = "gif" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b" +dependencies = [ + "color_quant", + "weezl", +] [[package]] name = "gio" -version = "0.18.4" +version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" +checksum = "daeff3dd716d1ba91850b976b76a1c2d28f99ef6c1602cd8fdaa8fab8017fd9c" dependencies = [ "futures-channel", "futures-core", @@ -1491,39 +1146,28 @@ dependencies = [ "gio-sys", "glib", "libc", - "once_cell", "pin-project-lite", "smallvec", - "thiserror 1.0.69", ] [[package]] name = "gio-sys" -version = "0.18.1" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" +checksum = "171ed2f6dd927abbe108cfd9eebff2052c335013f5879d55bab0dc1dee19b706" dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps", - "winapi", -] - -[[package]] -name = "gl" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a94edab108827d67608095e269cf862e60d920f144a5026d3dbcfd8b877fb404" -dependencies = [ - "gl_generator", + "system-deps 7.0.7", + "windows-sys 0.61.2", ] [[package]] name = "gl_generator" -version = "0.14.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +checksum = "7a795170cbd85b5a7baa58d6d7525cae6a03e486859860c220f7ebbbdd379d0a" dependencies = [ "khronos_api", "log", @@ -1532,11 +1176,11 @@ dependencies = [ [[package]] name = "glib" -version = "0.18.5" +version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" +checksum = "5b9dbecb1c33e483a98be4acfea2ab369e1c28f517c6eadb674537409c25c4b2" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.10.0", "futures-channel", "futures-core", "futures-executor", @@ -1548,46 +1192,37 @@ dependencies = [ "gobject-sys", "libc", "memchr", - "once_cell", "smallvec", - "thiserror 1.0.69", ] [[package]] name = "glib-macros" -version = "0.18.5" +version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" +checksum = "880e524e0085f3546cfb38532b2c202c0d64741d9977a6e4aa24704bfc9f19fb" dependencies = [ - "heck 0.4.1", - "proc-macro-crate 2.0.0", - "proc-macro-error", + "heck", + "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.103", + "syn", ] [[package]] name = "glib-sys" -version = "0.18.1" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" +checksum = "d09d3d0fddf7239521674e57b0465dfbd844632fec54f059f7f56112e3f927e1" dependencies = [ "libc", - "system-deps", + "system-deps 7.0.7", ] -[[package]] -name = "glob" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" - [[package]] name = "globset" -version = "0.4.16" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5" +checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3" dependencies = [ "aho-corasick", "bstr", @@ -1597,145 +1232,127 @@ dependencies = [ ] [[package]] -name = "globwalk" -version = "0.8.1" +name = "gobject-sys" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" +checksum = "538e41d8776173ec107e7b0f2aceced60abc368d7e1d81c1f0e2ecd35f59080d" dependencies = [ - "bitflags 1.3.2", - "ignore", - "walkdir", + "glib-sys", + "libc", + "system-deps 7.0.7", ] [[package]] -name = "glutin" -version = "0.32.3" -source = "git+https://github.com/Stremio/glutin?branch=feat%2Fwayland-file-window-events#7d45516496d1783ba37fa94ebdd255272605892c" +name = "graphene-rs" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7749aaf5d3b955bf3bfce39e3423705878a666b561384134da0e7786a45ddc3" dependencies = [ - "bitflags 2.9.1", - "cfg_aliases", - "cgl", - "dispatch2", - "glutin_egl_sys", - "glutin_glx_sys", - "glutin_wgl_sys", - "libloading 0.8.8", - "objc2 0.6.1", - "objc2-app-kit 0.3.1", - "objc2-core-foundation", - "objc2-foundation 0.3.1", - "once_cell", - "raw-window-handle", - "wayland-sys", - "windows-sys 0.52.0", - "x11-dl", -] - -[[package]] -name = "glutin-winit" -version = "0.5.0" -source = "git+https://github.com/Stremio/glutin?branch=feat%2Fwayland-file-window-events#7d45516496d1783ba37fa94ebdd255272605892c" -dependencies = [ - "cfg_aliases", - "glutin", - "raw-window-handle", - "winit", -] - -[[package]] -name = "glutin_egl_sys" -version = "0.7.1" -source = "git+https://github.com/Stremio/glutin?branch=feat%2Fwayland-file-window-events#7d45516496d1783ba37fa94ebdd255272605892c" -dependencies = [ - "gl_generator", - "windows-sys 0.52.0", + "glib", + "graphene-sys", + "libc", ] [[package]] -name = "glutin_glx_sys" -version = "0.6.1" -source = "git+https://github.com/Stremio/glutin?branch=feat%2Fwayland-file-window-events#7d45516496d1783ba37fa94ebdd255272605892c" +name = "graphene-sys" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "250abaee850a90a276509890a78029c356173f9573412bded5f155b0e41fa568" dependencies = [ - "gl_generator", - "x11-dl", + "glib-sys", + "libc", + "pkg-config", + "system-deps 7.0.7", ] [[package]] -name = "glutin_wgl_sys" -version = "0.6.1" -source = "git+https://github.com/Stremio/glutin?branch=feat%2Fwayland-file-window-events#7d45516496d1783ba37fa94ebdd255272605892c" +name = "gsk4" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6687e9f92ca89c000c376400cfaf7914d099413d72fdf4f84a25775a0b1fb2d" dependencies = [ - "gl_generator", + "cairo-rs", + "gdk4", + "glib", + "graphene-rs", + "gsk4-sys", + "libc", + "pango", ] [[package]] -name = "gobject-sys" -version = "0.18.0" +name = "gsk4-sys" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" +checksum = "5e76bcf64d9c4846f19651f45b400cc0c9c4c17b651849da520f3d77c6988c52" dependencies = [ + "cairo-sys-rs", + "gdk4-sys", "glib-sys", + "gobject-sys", + "graphene-sys", "libc", - "system-deps", + "pango-sys", + "system-deps 7.0.7", ] [[package]] -name = "gtk" -version = "0.18.2" +name = "gtk4" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd56fb197bfc42bd5d2751f4f017d44ff59fbb58140c6b49f9b3b2bdab08506a" +checksum = "acb21d53cfc6f7bfaf43549731c43b67ca47d87348d81c8cfc4dcdd44828e1a4" dependencies = [ - "atk", "cairo-rs", "field-offset", "futures-channel", - "gdk", "gdk-pixbuf", + "gdk4", "gio", "glib", - "gtk-sys", - "gtk3-macros", + "graphene-rs", + "gsk4", + "gtk4-macros", + "gtk4-sys", "libc", "pango", - "pkg-config", ] [[package]] -name = "gtk-sys" -version = "0.18.2" +name = "gtk4-macros" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "821160b4f17e7e4ed748818c23682d0a46bed04c287dbaac54dd4869d2c5e06a" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "gtk4-sys" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f29a1c21c59553eb7dd40e918be54dccd60c52b049b75119d5d96ce6b624414" +checksum = "d274cbaf7d9aa55b7aff78cb21b43299d64e514e1300671469b66f691cc5a011" dependencies = [ - "atk-sys", "cairo-sys-rs", "gdk-pixbuf-sys", - "gdk-sys", + "gdk4-sys", "gio-sys", "glib-sys", "gobject-sys", + "graphene-sys", + "gsk4-sys", "libc", "pango-sys", - "system-deps", -] - -[[package]] -name = "gtk3-macros" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ff3c5b21f14f0736fed6dcfc0bfb4225ebf5725f3c0209edeec181e4d73e9d" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.103", + "system-deps 7.0.7", ] [[package]] name = "h2" -version = "0.4.10" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" dependencies = [ "atomic-waker", "bytes", @@ -1752,25 +1369,20 @@ dependencies = [ [[package]] name = "half" -version = "2.6.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" dependencies = [ "cfg-if", "crunchy", + "zerocopy", ] [[package]] name = "hashbrown" -version = "0.15.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" - -[[package]] -name = "heck" -version = "0.4.1" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" [[package]] name = "heck" @@ -1778,12 +1390,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[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" @@ -1832,19 +1438,21 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "hyper" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" dependencies = [ + "atomic-waker", "bytes", "futures-channel", - "futures-util", + "futures-core", "h2", "http", "http-body", "httparse", "itoa", "pin-project-lite", + "pin-utils", "smallvec", "tokio", "want", @@ -1884,9 +1492,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.14" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb" +checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" dependencies = [ "base64", "bytes", @@ -1908,11 +1516,35 @@ dependencies = [ "windows-registry", ] +[[package]] +name = "iana-time-zone" +version = "0.1.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "icu_collections" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" dependencies = [ "displaydoc", "potential_utf", @@ -1923,9 +1555,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" dependencies = [ "displaydoc", "litemap", @@ -1936,11 +1568,10 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" dependencies = [ - "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", @@ -1951,42 +1582,38 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" 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" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" +checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" [[package]] name = "icu_provider" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" dependencies = [ "displaydoc", "icu_locale_core", - "stable_deref_trait", - "tinystr", "writeable", "yoke", "zerofrom", @@ -1996,9 +1623,9 @@ dependencies = [ [[package]] name = "idna" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ "idna_adapter", "smallvec", @@ -2015,27 +1642,11 @@ dependencies = [ "icu_properties", ] -[[package]] -name = "ignore" -version = "0.4.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" -dependencies = [ - "crossbeam-deque", - "globset", - "log", - "memchr", - "regex-automata", - "same-file", - "walkdir", - "winapi-util", -] - [[package]] name = "image" -version = "0.25.6" +version = "0.25.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a" +checksum = "529feb3e6769d234375c4cf1ee2ce713682b8e76538cb13f9fc23e1400a591e7" dependencies = [ "bytemuck", "byteorder-lite", @@ -2043,6 +1654,7 @@ dependencies = [ "exr", "gif", "image-webp", + "moxcms", "num-traits", "png", "qoi", @@ -2056,9 +1668,9 @@ dependencies = [ [[package]] name = "image-webp" -version = "0.2.2" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14d75c7014ddab93c232bc6bb9f64790d3dfd1d605199acd4b40b6d69e691e9f" +checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3" dependencies = [ "byteorder-lite", "quick-error", @@ -2066,15 +1678,15 @@ dependencies = [ [[package]] name = "imgref" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408" +checksum = "e7c5cedc30da3a610cac6b4ba17597bdf7152cf974e8aab3afb3d54455e371c8" [[package]] name = "indexmap" -version = "2.9.0" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" dependencies = [ "equivalent", "hashbrown", @@ -2082,9 +1694,9 @@ dependencies = [ [[package]] name = "indicatif" -version = "0.18.0" +version = "0.18.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a646d946d06bedbbc4cac4c218acf4bbf2d87757a784857025f4d447e4e1cd" +checksum = "9375e112e4b463ec1b1c6c011953545c65a30164fbab5b581df32b3abf0dcb88" dependencies = [ "console", "portable-atomic", @@ -2101,7 +1713,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn", ] [[package]] @@ -2112,9 +1724,9 @@ checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "iri-string" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" dependencies = [ "memchr", "serde", @@ -2122,18 +1734,9 @@ dependencies = [ [[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.11.0" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "itertools" @@ -2159,81 +1762,45 @@ 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" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", "libc", ] -[[package]] -name = "jpeg-decoder" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" - [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" dependencies = [ "once_cell", "wasm-bindgen", ] [[package]] -name = "jwalk" -version = "0.8.1" +name = "khronos_api" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2735847566356cd2179a2a38264839308f7079fa96e6bd5a42d740460e003c56" -dependencies = [ - "crossbeam", - "rayon", -] +checksum = "037ab472c33f67b5fbd3e9163a2645319e5356fcd355efa6d4eb7fff4bbcb554" [[package]] -name = "keyboard-types" -version = "0.7.0" +name = "ksni" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" +checksum = "3cc9a5e60d55371fd681051b05e9b58e1d818f5085f6364afe872c9347311f91" dependencies = [ - "bitflags 2.9.1", + "futures-util", + "paste", "serde", - "unicode-segmentation", + "tokio", + "zbus", ] -[[package]] -name = "khronos_api" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" - [[package]] name = "lazy_static" version = "1.5.0" @@ -2242,51 +1809,58 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "lebe" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" +checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8" [[package]] -name = "libappindicator" -version = "0.9.0" +name = "libadwaita" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03589b9607c868cc7ae54c0b2a22c8dc03dd41692d48f2d7df73615c6a95dc0a" +checksum = "fb09e12bf8f73342b3315c839d0a7668cc0ccebd78490c49fec48bab15d5484b" dependencies = [ + "gdk4", + "gio", "glib", - "gtk", - "gtk-sys", - "libappindicator-sys", - "log", + "gtk4", + "libadwaita-sys", + "libc", + "pango", ] [[package]] -name = "libappindicator-sys" -version = "0.9.0" +name = "libadwaita-sys" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf" +checksum = "fdf8950090cc180250cdb1ff859a39748feeda7a53a9f28ead3a17a14cc37ae2" dependencies = [ - "gtk-sys", - "libloading 0.7.4", - "once_cell", + "gdk4-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "gtk4-sys", + "libc", + "pango-sys", + "system-deps 7.0.7", ] [[package]] name = "libbz2-rs-sys" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "775bf80d5878ab7c2b1080b5351a48b2f737d9f6f8b383574eebcc22be0dfccb" +checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7" [[package]] name = "libc" -version = "0.2.174" +version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "libfuzzer-sys" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf78f52d400cf2d84a3a973a78a592b4adc535739e0a5597a0da6f0c357adc75" +checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404" dependencies = [ "arbitrary", "cc", @@ -2294,28 +1868,19 @@ dependencies = [ [[package]] name = "libloading" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" -dependencies = [ - "cfg-if", - "winapi", -] - -[[package]] -name = "libloading" -version = "0.8.8" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" +checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" dependencies = [ "cfg-if", - "windows-targets 0.53.2", + "windows-link 0.2.1", ] [[package]] name = "libmpv2" -version = "4.1.0" -source = "git+https://github.com/Stremio/libmpv2-rs#b2b0ce03fde964c0c5cd4f66e193740062468b41" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ceb0d95727ae70984e83973e4c03a16c962ae3ecfd0febc02125aeba4cb9dff9" dependencies = [ "libmpv2-sys", ] @@ -2323,48 +1888,65 @@ dependencies = [ [[package]] name = "libmpv2-sys" version = "4.0.0" -source = "git+https://github.com/Stremio/libmpv2-rs#b2b0ce03fde964c0c5cd4f66e193740062468b41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42124ba90561beede41d5e6ef64eef63fc1395cf83217e3dd1157294f7dcdb56" [[package]] name = "libredox" -version = "0.1.3" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.10.0", "libc", - "redox_syscall 0.5.13", + "redox_syscall", ] [[package]] name = "linux-raw-sys" -version = "0.4.15" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] -name = "linux-raw-sys" -version = "0.9.4" +name = "litemap" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" [[package]] -name = "litemap" -version = "0.8.0" +name = "litrs" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" [[package]] -name = "litrs" -version = "0.4.1" +name = "locale_config" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" +checksum = "08d2c35b16f4483f6c26f0e4e9550717a2f6575bcd6f12a53ff0c490a94a6934" +dependencies = [ + "lazy_static", + "objc", + "objc-foundation", + "regex", + "winapi", +] + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] [[package]] name = "log" -version = "0.4.27" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "loop9" @@ -2375,6 +1957,15 @@ dependencies = [ "imgref", ] +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + [[package]] name = "maybe-rayon" version = "0.1.1" @@ -2387,18 +1978,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" - -[[package]] -name = "memmap2" -version = "0.9.5" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" -dependencies = [ - "libc", -] +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "memoffset" @@ -2415,12 +1997,6 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" -[[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" @@ -2432,478 +2008,193 @@ dependencies = [ ] [[package]] -name = "mio" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" -dependencies = [ - "libc", - "wasi 0.11.1+wasi-snapshot-preview1", - "windows-sys 0.59.0", -] - -[[package]] -name = "muda" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4de14a9b5d569ca68d7c891d613b390cf5ab4f851c77aaa2f9e435555d3d9492" -dependencies = [ - "crossbeam-channel", - "dpi 0.1.2", - "gtk", - "keyboard-types", - "objc2 0.6.1", - "objc2-app-kit 0.3.1", - "objc2-core-foundation", - "objc2-foundation 0.3.1", - "once_cell", - "png", - "thiserror 2.0.12", - "windows-sys 0.59.0", -] - -[[package]] -name = "native-tls" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" -dependencies = [ - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[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", - "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.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.30.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" -dependencies = [ - "bitflags 2.9.1", - "cfg-if", - "cfg_aliases", - "libc", - "memoffset", -] - -[[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 = "normpath" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8911957c4b1549ac0dc74e30db9c8b0e66ddcd6d7acc33098f4c63a64a6d7ed" -dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - -[[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.103", -] - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" -dependencies = [ - "num-bigint", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_enum" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" -dependencies = [ - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" -dependencies = [ - "proc-macro-crate 3.3.0", - "proc-macro2", - "quote", - "syn 2.0.103", -] - -[[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" +name = "mio" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" +checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" 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", + "wasi", + "windows-sys 0.61.2", ] [[package]] -name = "objc2-app-kit" -version = "0.3.1" +name = "moxcms" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc" +checksum = "0fbdd3d7436f8b5e892b8b7ea114271ff0fa00bc5acae845d53b07d498616ef6" dependencies = [ - "bitflags 2.9.1", - "objc2 0.6.1", - "objc2-core-foundation", - "objc2-foundation 0.3.1", + "num-traits", + "pxfm", ] [[package]] -name = "objc2-cloud-kit" -version = "0.2.2" +name = "nanorand" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" dependencies = [ - "bitflags 2.9.1", - "block2 0.5.1", - "objc2 0.5.2", - "objc2-core-location", - "objc2-foundation 0.2.2", + "getrandom 0.2.16", ] [[package]] -name = "objc2-contacts" -version = "0.2.2" +name = "native-tls" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" dependencies = [ - "block2 0.5.1", - "objc2 0.5.2", - "objc2-foundation 0.2.2", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", ] [[package]] -name = "objc2-core-data" -version = "0.2.2" +name = "new_debug_unreachable" +version = "1.0.6" 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", -] +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" [[package]] -name = "objc2-core-foundation" -version = "0.3.1" +name = "nix" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.9.1", - "dispatch2", - "objc2 0.6.1", + "bitflags 2.10.0", + "cfg-if", + "cfg_aliases", + "libc", + "memoffset", ] [[package]] -name = "objc2-core-graphics" -version = "0.3.1" +name = "nom" +version = "8.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989c6c68c13021b5c2d6b71456ebb0f9dc78d752e86a98da7c716f4f9470f5a4" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" dependencies = [ - "bitflags 2.9.1", - "objc2-core-foundation", + "memchr", ] [[package]] -name = "objc2-core-image" -version = "0.2.2" +name = "noop_proc_macro" +version = "0.3.0" 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", -] +checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" [[package]] -name = "objc2-core-location" -version = "0.2.2" +name = "nu-ansi-term" +version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "block2 0.5.1", - "objc2 0.5.2", - "objc2-contacts", - "objc2-foundation 0.2.2", + "windows-sys 0.61.2", ] [[package]] -name = "objc2-encode" -version = "4.1.0" +name = "num-bigint" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] [[package]] -name = "objc2-foundation" -version = "0.2.2" +name = "num-conv" +version = "0.1.0" 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", -] +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] -name = "objc2-foundation" -version = "0.3.1" +name = "num-derive" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ - "bitflags 2.9.1", - "block2 0.6.1", - "objc2 0.6.1", - "objc2-core-foundation", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "objc2-link-presentation" -version = "0.2.2" +name = "num-integer" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "block2 0.5.1", - "objc2 0.5.2", - "objc2-app-kit 0.2.2", - "objc2-foundation 0.2.2", + "num-traits", ] [[package]] -name = "objc2-metal" -version = "0.2.2" +name = "num-rational" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" dependencies = [ - "bitflags 2.9.1", - "block2 0.5.1", - "objc2 0.5.2", - "objc2-foundation 0.2.2", + "num-bigint", + "num-integer", + "num-traits", ] [[package]] -name = "objc2-quartz-core" -version = "0.2.2" +name = "num-traits" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ - "bitflags 2.9.1", - "block2 0.5.1", - "objc2 0.5.2", - "objc2-foundation 0.2.2", - "objc2-metal", + "autocfg", ] [[package]] -name = "objc2-symbols" -version = "0.2.2" +name = "objc" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" dependencies = [ - "objc2 0.5.2", - "objc2-foundation 0.2.2", + "malloc_buf", ] [[package]] -name = "objc2-ui-kit" -version = "0.2.2" +name = "objc-foundation" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" +checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" 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", + "block", + "objc", + "objc_id", ] [[package]] -name = "objc2-uniform-type-identifiers" -version = "0.2.2" +name = "objc2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" +checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05" dependencies = [ - "block2 0.5.1", - "objc2 0.5.2", - "objc2-foundation 0.2.2", + "objc2-encode", ] [[package]] -name = "objc2-user-notifications" -version = "0.2.2" +name = "objc2-encode" +version = "4.1.0" 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", -] +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" [[package]] -name = "object" -version = "0.36.7" +name = "objc_id" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" dependencies = [ - "memchr", + "objc", ] [[package]] @@ -2914,19 +2205,19 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "once_cell_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "openssl" -version = "0.10.73" +version = "0.10.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" +checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.10.0", "cfg-if", - "foreign-types 0.3.2", + "foreign-types", "libc", "once_cell", "openssl-macros", @@ -2941,7 +2232,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn", ] [[package]] @@ -2952,9 +2243,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.109" +version = "0.9.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" dependencies = [ "cc", "libc", @@ -2968,15 +2259,6 @@ 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" @@ -2987,44 +2269,28 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - -[[package]] -name = "owned_ttf_parser" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec719bbf3b2a81c109a4e20b1f129b5566b7dce654bc3872f6a05abf82b2c4" -dependencies = [ - "ttf-parser", -] - [[package]] name = "pango" -version = "0.18.3" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" +checksum = "e37b7a678e18c2e9f2485f7e39b7b2dac99590d5ddef08a7f56eae38a145402e" dependencies = [ "gio", "glib", "libc", - "once_cell", "pango-sys", ] [[package]] name = "pango-sys" -version = "0.18.0" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" +checksum = "f4f5daf21da43fba9f2a0092da0eebeb77637c23552bccaf58f791c518009c94" dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps", + "system-deps 7.0.7", ] [[package]] @@ -3034,36 +2300,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] -name = "paste" -version = "1.0.15" +name = "parking_lot" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] [[package]] -name = "percent-encoding" -version = "2.3.1" +name = "parking_lot_core" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link 0.2.1", +] [[package]] -name = "pin-project" -version = "1.1.10" +name = "paste" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" -dependencies = [ - "pin-project-internal", -] +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] -name = "pin-project-internal" -version = "1.1.10" +name = "percent-encoding" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.103", -] +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pin-project-lite" @@ -3077,17 +2346,6 @@ 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", - "futures-io", -] - [[package]] name = "pkg-config" version = "0.3.32" @@ -3096,32 +2354,17 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "png" -version = "0.17.16" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.10.0", "crc32fast", "fdeflate", "flate2", "miniz_oxide", ] -[[package]] -name = "polling" -version = "3.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b53a684391ad002dd6a596ceb6c74fd004fdce75f4be2e3f615068abbea5fd50" -dependencies = [ - "cfg-if", - "concurrent-queue", - "hermit-abi", - "pin-project-lite", - "rustix 1.0.7", - "tracing", - "windows-sys 0.59.0", -] - [[package]] name = "portable-atomic" version = "1.11.1" @@ -3130,9 +2373,9 @@ checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] name = "potential_utf" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" dependencies = [ "zerovec", ] @@ -3154,82 +2397,48 @@ dependencies = [ [[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 = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" -dependencies = [ - "toml_edit 0.20.7", -] - -[[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-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" dependencies = [ - "proc-macro2", - "quote", - "version_check", + "toml_edit 0.23.7", ] [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] [[package]] name = "profiling" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d" +checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" dependencies = [ "profiling-procmacros", ] [[package]] name = "profiling-procmacros" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30" +checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" dependencies = [ "quote", - "syn 2.0.103", + "syn", +] + +[[package]] +name = "pxfm" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3cbdf373972bf78df4d3b518d07003938e2c7d1fb5891e55f9cb6df57009d84" +dependencies = [ + "num-traits", ] [[package]] @@ -3247,20 +2456,11 @@ 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" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] @@ -3284,9 +2484,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", @@ -3327,7 +2527,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", ] [[package]] @@ -3359,7 +2559,7 @@ dependencies = [ "rand 0.8.5", "rand_chacha 0.3.1", "simd_helpers", - "system-deps", + "system-deps 6.2.2", "thiserror 1.0.69", "v_frame", "wasm-bindgen", @@ -3367,9 +2567,9 @@ dependencies = [ [[package]] name = "ravif" -version = "0.11.12" +version = "0.11.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6a5f31fcf7500f9401fea858ea4ab5525c99f2322cfcee732c0e6c74208c0c6" +checksum = "5825c26fddd16ab9f515930d49028a630efec172e903483c94796cfe31893e6b" dependencies = [ "avif-serialize", "imgref", @@ -3380,17 +2580,11 @@ dependencies = [ "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" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" dependencies = [ "either", "rayon-core", @@ -3398,9 +2592,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -3408,38 +2602,29 @@ dependencies = [ [[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.13" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.10.0", ] [[package]] name = "redox_users" -version = "0.5.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.16", "libredox", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] name = "regex" -version = "1.11.1" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", @@ -3449,9 +2634,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.9" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ "aho-corasick", "memchr", @@ -3460,22 +2645,20 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.5" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "reqwest" -version = "0.12.20" +version = "0.12.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabf4c97d9130e2bf606614eb937e86edac8292eaa6f422f995d7e8de1eb1813" +checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" dependencies = [ "base64", "bytes", "encoding_rs", - "futures-channel", "futures-core", - "futures-util", "h2", "http", "http-body", @@ -3507,85 +2690,25 @@ dependencies = [ ] [[package]] -name = "rgb" -version = "0.8.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" - -[[package]] -name = "ring" -version = "0.17.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" -dependencies = [ - "cc", - "cfg-if", - "getrandom 0.2.16", - "libc", - "untrusted", - "windows-sys 0.52.0", -] - -[[package]] -name = "rust-i18n" -version = "3.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda2551fdfaf6cc5ee283adc15e157047b92ae6535cf80f6d4962d05717dc332" -dependencies = [ - "globwalk", - "once_cell", - "regex", - "rust-i18n-macro", - "rust-i18n-support", - "smallvec", -] - -[[package]] -name = "rust-i18n-macro" -version = "3.1.5" +name = "rgb" +version = "0.8.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22baf7d7f56656d23ebe24f6bb57a5d40d2bce2a5f1c503e692b5b2fa450f965" -dependencies = [ - "glob", - "once_cell", - "proc-macro2", - "quote", - "rust-i18n-support", - "serde", - "serde_json", - "serde_yaml", - "syn 2.0.103", -] +checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce" [[package]] -name = "rust-i18n-support" -version = "3.1.5" +name = "ring" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "940ed4f52bba4c0152056d771e563b7133ad9607d4384af016a134b58d758f19" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ - "arc-swap", - "base62", - "globwalk", - "itertools 0.11.0", - "lazy_static", - "normpath", - "once_cell", - "proc-macro2", - "regex", - "serde", - "serde_json", - "serde_yaml", - "siphasher", - "toml", - "triomphe", + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted", + "windows-sys 0.52.0", ] -[[package]] -name = "rustc-demangle" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" - [[package]] name = "rustc_version" version = "0.4.1" @@ -3597,35 +2720,22 @@ dependencies = [ [[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.7" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.10.0", "errno", "libc", - "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "linux-raw-sys", + "windows-sys 0.61.2", ] [[package]] name = "rustls" -version = "0.23.28" +version = "0.23.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7160e3e10bf4535308537f3c4e1641468cd0e485175d6163087c0393c7d46643" +checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" dependencies = [ "log", "once_cell", @@ -3636,29 +2746,20 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", -] - [[package]] name = "rustls-pki-types" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" dependencies = [ "zeroize", ] [[package]] name = "rustls-webpki" -version = "0.103.3" +version = "0.103.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" +checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" dependencies = [ "ring", "rustls-pki-types", @@ -3667,9 +2768,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" @@ -3677,42 +2778,20 @@ 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 = "schannel" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - -[[package]] -name = "sctk-adwaita" -version = "0.10.1" +name = "scopeguard" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6277f0217056f77f1d8f49f2950ac6c278c0d607c45f5ee99328d792ede24ec" -dependencies = [ - "ab_glyph", - "log", - "memmap2", - "smithay-client-toolkit", - "tiny-skia", -] +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "security-framework" @@ -3720,7 +2799,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.10.0", "core-foundation", "core-foundation-sys", "libc", @@ -3729,9 +2808,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.14.0" +version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" dependencies = [ "core-foundation-sys", "libc", @@ -3739,40 +2818,51 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn", ] [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ "itoa", "memchr", "ryu", "serde", + "serde_core", ] [[package]] @@ -3783,7 +2873,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn", ] [[package]] @@ -3796,28 +2886,24 @@ dependencies = [ ] [[package]] -name = "serde_urlencoded" -version = "0.7.1" +name = "serde_spanned" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392" dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", + "serde_core", ] [[package]] -name = "serde_yaml" -version = "0.9.34+deprecated" +name = "serde_urlencoded" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ - "indexmap", + "form_urlencoded", "itoa", "ryu", "serde", - "unsafe-libyaml", ] [[package]] @@ -3835,6 +2921,16 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shared_library" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11" +dependencies = [ + "lazy_static", + "libc", +] + [[package]] name = "shlex" version = "1.3.0" @@ -3843,9 +2939,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.5" +version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" dependencies = [ "libc", ] @@ -3865,17 +2961,11 @@ dependencies = [ "quote", ] -[[package]] -name = "siphasher" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" - [[package]] name = "slab" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "smallvec" @@ -3883,48 +2973,14 @@ 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", - "calloop", - "calloop-wayland-source", - "cursor-icon", - "libc", - "log", - "memmap2", - "rustix 0.38.44", - "thiserror 1.0.69", - "wayland-backend", - "wayland-client", - "wayland-csd-frame", - "wayland-cursor", - "wayland-protocols", - "wayland-protocols-wlr", - "wayland-scanner", - "xkeysym", -] - -[[package]] -name = "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.5.10" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] @@ -3938,11 +2994,20 @@ dependencies = [ "winapi", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + [[package]] name = "stable_deref_trait" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "static_assertions" @@ -3956,46 +3021,37 @@ version = "1.0.0-beta.12" dependencies = [ "anyhow", "ashpd", - "bytes", "bzip2", "cef", - "cef-dll-sys", + "chrono", "clap", - "crossbeam-channel", - "dircpy", + "crossbeam-queue", "dirs", - "futures", - "gl", + "epoxy", + "flume", + "gettext-rs", "globset", - "glutin", - "glutin-winit", - "gtk", + "gtk4", "image", "itertools 0.14.0", + "ksni", + "libadwaita", "libc", + "libloading", "libmpv2", - "once_cell", - "paste", + "rand 0.9.2", "reqwest", - "rust-i18n", "serde", "serde_json", "tar", - "toml", + "tokio", + "toml 0.9.8", "tracing", "tracing-subscriber", - "tray-icon", "ureq", "url", - "winit", ] -[[package]] -name = "strict-num" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" - [[package]] name = "strsim" version = "0.11.1" @@ -4010,19 +3066,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "1.0.109" +version = "2.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.103" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8" +checksum = "2f17c7e013e88258aa9543dcbe81aca68a667a9ac37cd69c9fbc07858bfe0e2f" dependencies = [ "proc-macro2", "quote", @@ -4046,7 +3092,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn", ] [[package]] @@ -4055,7 +3101,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.10.0", "core-foundation", "system-configuration-sys", ] @@ -4076,10 +3122,23 @@ version = "6.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" dependencies = [ - "cfg-expr", - "heck 0.5.0", + "cfg-expr 0.15.8", + "heck", + "pkg-config", + "toml 0.8.23", + "version-compare", +] + +[[package]] +name = "system-deps" +version = "7.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c8f33736f986f16d69b6cb8b03f55ddcad5c41acc4ccc39dd88e84aa805e7f" +dependencies = [ + "cfg-expr 0.20.4", + "heck", "pkg-config", - "toml", + "toml 0.9.8", "version-compare", ] @@ -4100,17 +3159,29 @@ version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" +[[package]] +name = "target-lexicon" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df7f62577c25e07834649fc3b39fafdc597c0a3527dc1c60129201ccfcbaa50c" + +[[package]] +name = "temp-dir" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83176759e9416cf81ee66cb6508dbfe9c96f20b8b56265a39917551c23c70964" + [[package]] name = "tempfile" -version = "3.20.0" +version = "3.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" dependencies = [ "fastrand", - "getrandom 0.3.3", + "getrandom 0.3.4", "once_cell", - "rustix 1.0.7", - "windows-sys 0.59.0", + "rustix", + "windows-sys 0.61.2", ] [[package]] @@ -4124,11 +3195,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ - "thiserror-impl 2.0.12", + "thiserror-impl 2.0.17", ] [[package]] @@ -4139,18 +3210,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn", ] [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn", ] [[package]] @@ -4164,20 +3235,23 @@ dependencies = [ [[package]] name = "tiff" -version = "0.9.1" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" +checksum = "af9605de7fee8d9551863fd692cce7637f548dbd9db9180fcc07ccc6d26c336f" dependencies = [ + "fax", "flate2", - "jpeg-decoder", + "half", + "quick-error", "weezl", + "zune-jpeg", ] [[package]] name = "time" -version = "0.3.41" +version = "0.3.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" dependencies = [ "deranged", "itoa", @@ -4190,50 +3264,25 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" [[package]] name = "time-macros" -version = "0.2.22" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" 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", - "tiny-skia-path", -] - -[[package]] -name = "tiny-skia-path" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" -dependencies = [ - "arrayref", - "bytemuck", - "strict-num", -] - [[package]] name = "tinystr" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" dependencies = [ "displaydoc", "zerovec", @@ -4241,17 +3290,31 @@ dependencies = [ [[package]] name = "tokio" -version = "1.45.1" +version = "1.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" dependencies = [ - "backtrace", "bytes", "libc", "mio", + "parking_lot", "pin-project-lite", + "signal-hook-registry", "socket2", - "windows-sys 0.52.0", + "tokio-macros", + "tracing", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -4266,9 +3329,9 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ "rustls", "tokio", @@ -4276,9 +3339,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.15" +version = "0.7.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" +checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" dependencies = [ "bytes", "futures-core", @@ -4294,11 +3357,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", - "serde_spanned", - "toml_datetime", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", "toml_edit 0.22.27", ] +[[package]] +name = "toml" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned 1.0.3", + "toml_datetime 0.7.3", + "toml_parser", + "toml_writer", + "winnow", +] + [[package]] name = "toml_datetime" version = "0.6.11" @@ -4309,46 +3387,53 @@ dependencies = [ ] [[package]] -name = "toml_edit" -version = "0.19.15" +name = "toml_datetime" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" dependencies = [ - "indexmap", - "toml_datetime", - "winnow 0.5.40", + "serde_core", ] [[package]] name = "toml_edit" -version = "0.20.7" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap", - "toml_datetime", - "winnow 0.5.40", + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "winnow", ] [[package]] name = "toml_edit" -version = "0.22.27" +version = "0.23.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" dependencies = [ "indexmap", - "serde", - "serde_spanned", - "toml_datetime", - "toml_write", - "winnow 0.7.11", + "toml_datetime 0.7.3", + "toml_parser", + "winnow", ] [[package]] -name = "toml_write" -version = "0.1.2" +name = "toml_parser" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" +checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" [[package]] name = "tower" @@ -4371,7 +3456,7 @@ version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.10.0", "bytes", "futures-util", "http", @@ -4414,7 +3499,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn", ] [[package]] @@ -4440,9 +3525,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.19" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" dependencies = [ "nu-ansi-term", "sharded-slab", @@ -4452,50 +3537,12 @@ dependencies = [ "tracing-log", ] -[[package]] -name = "tray-icon" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7eee98ec5c90daf179d55c20a49d8c0d043054ce7c26336c09a24d31f14fa0" -dependencies = [ - "crossbeam-channel", - "dirs", - "libappindicator", - "muda", - "objc2 0.6.1", - "objc2-app-kit 0.3.1", - "objc2-core-foundation", - "objc2-core-graphics", - "objc2-foundation 0.3.1", - "once_cell", - "png", - "thiserror 2.0.12", - "windows-sys 0.59.0", -] - -[[package]] -name = "triomphe" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef8f7726da4807b58ea5c96fdc122f80702030edc33b35aff9190a51148ccc85" -dependencies = [ - "arc-swap", - "serde", - "stable_deref_trait", -] - [[package]] name = "try-lock" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" -[[package]] -name = "ttf-parser" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" - [[package]] name = "uds_windows" version = "1.1.0" @@ -4509,33 +3556,21 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" - -[[package]] -name = "unicode-segmentation" -version = "1.12.0" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "unicode-width" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] name = "unit-prefix" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "323402cff2dd658f39ca17c789b502021b3f18707c91cdf22e3838e1b4023817" - -[[package]] -name = "unsafe-libyaml" -version = "0.2.11" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" +checksum = "81e544489bf3d8ef66c953931f56617f423cd4b5494be343d9b9d3dda037b9a3" [[package]] name = "untrusted" @@ -4545,9 +3580,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "ureq" -version = "3.0.12" +version = "3.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f0fde9bc91026e381155f8c67cb354bcd35260b2f4a29bcc84639f762760c39" +checksum = "d39cb1dbab692d82a977c0392ffac19e188bd9186a9f32806f0aaa859d75585a" dependencies = [ "base64", "cookie_store", @@ -4555,21 +3590,20 @@ dependencies = [ "log", "percent-encoding", "rustls", - "rustls-pemfile", "rustls-pki-types", "serde", "serde_json", "socks", "ureq-proto", "utf-8", - "webpki-roots 0.26.11", + "webpki-roots", ] [[package]] name = "ureq-proto" -version = "0.4.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59db78ad1923f2b1be62b6da81fe80b173605ca0d57f85da2e005382adf693f7" +checksum = "d81f9efa9df032be5934a46a068815a10a042b494b6a58cb0a1a97bb5467ed6f" dependencies = [ "base64", "http", @@ -4579,9 +3613,9 @@ dependencies = [ [[package]] name = "url" -version = "2.5.4" +version = "2.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" dependencies = [ "form_urlencoded", "idna", @@ -4607,6 +3641,17 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "uuid" +version = "1.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + [[package]] name = "v_frame" version = "0.3.9" @@ -4632,25 +3677,15 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version-compare" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" +checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e" [[package]] name = "version_check" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "want" @@ -4668,45 +3703,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] -name = "wasi" -version = "0.14.2+wasi-0.2.4" +name = "wasip2" +version = "1.0.1+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" dependencies = [ - "wit-bindgen-rt", + "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.100" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" 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.103", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.50" +version = "0.4.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +checksum = "551f88106c6d5e7ccc7cd9a16f312dd3b5d36ea8b4954304657d5dfba115d4a0" dependencies = [ "cfg-if", "js-sys", @@ -4717,9 +3739,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4727,140 +3749,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" dependencies = [ + "bumpalo", "proc-macro2", "quote", - "syn 2.0.103", - "wasm-bindgen-backend", + "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" dependencies = [ "unicode-ident", ] -[[package]] -name = "wayland-backend" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe770181423e5fc79d3e2a7f4410b7799d5aab1de4372853de3c6aa13ca24121" -dependencies = [ - "cc", - "downcast-rs", - "rustix 0.38.44", - "scoped-tls", - "smallvec", - "wayland-sys", -] - -[[package]] -name = "wayland-client" -version = "0.31.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978fa7c67b0847dbd6a9f350ca2569174974cd4082737054dbb7fbb79d7d9a61" -dependencies = [ - "bitflags 2.9.1", - "rustix 0.38.44", - "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.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a65317158dec28d00416cb16705934070aef4f8393353d41126c54264ae0f182" -dependencies = [ - "rustix 0.38.44", - "wayland-client", - "xcursor", -] - -[[package]] -name = "wayland-protocols" -version = "0.32.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "779075454e1e9a521794fed15886323ea0feda3f8b0fc1390f5398141310422a" -dependencies = [ - "bitflags 2.9.1", - "wayland-backend", - "wayland-client", - "wayland-scanner", -] - -[[package]] -name = "wayland-protocols-plasma" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fd38cdad69b56ace413c6bcc1fbf5acc5e2ef4af9d5f8f1f9570c0c83eae175" -dependencies = [ - "bitflags 2.9.1", - "wayland-backend", - "wayland-client", - "wayland-protocols", - "wayland-scanner", -] - -[[package]] -name = "wayland-protocols-wlr" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cb6cdc73399c0e06504c437fe3cf886f25568dd5454473d565085b36d6a8bbf" -dependencies = [ - "bitflags 2.9.1", - "wayland-backend", - "wayland-client", - "wayland-protocols", - "wayland-scanner", -] - -[[package]] -name = "wayland-scanner" -version = "0.31.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "896fdafd5d28145fce7958917d69f2fd44469b1d4e861cb5961bcbeebc6d1484" -dependencies = [ - "proc-macro2", - "quick-xml", - "quote", -] - -[[package]] -name = "wayland-sys" -version = "0.31.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbcebb399c77d5aa9fa5db874806ee7b4eba4e73650948e8f93963f128896615" -dependencies = [ - "dlib", - "log", - "once_cell", - "pkg-config", -] - [[package]] name = "web-sys" -version = "0.3.77" +version = "0.3.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" dependencies = [ "js-sys", "wasm-bindgen", @@ -4878,27 +3791,18 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.26.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" -dependencies = [ - "webpki-roots 1.0.0", -] - -[[package]] -name = "webpki-roots" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb" +checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" dependencies = [ "rustls-pki-types", ] [[package]] name = "weezl" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3" +checksum = "009936b22a61d342859b5f0ea64681cbb35a358ab548e2a44a8cf0dac2d980b8" [[package]] name = "winapi" @@ -4917,19 +3821,45 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] -name = "winapi-util" -version = "0.1.9" +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ - "windows-sys 0.59.0", + "windows-implement", + "windows-interface", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", ] [[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" +name = "windows-implement" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "windows-link" @@ -4937,15 +3867,21 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + [[package]] name = "windows-registry" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3bab093bdd303a1240bb99b8aba8ea8a69ee19d34c9e2ef9594e708a4878820" +checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" dependencies = [ - "windows-link", - "windows-result", - "windows-strings", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", ] [[package]] @@ -4954,41 +3890,41 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] -name = "windows-strings" -version = "0.4.2" +name = "windows-result" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-link", + "windows-link 0.2.1", ] [[package]] -name = "windows-sys" -version = "0.45.0" +name = "windows-strings" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ - "windows-targets 0.42.2", + "windows-link 0.1.3", ] [[package]] -name = "windows-sys" -version = "0.52.0" +name = "windows-strings" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows-targets 0.52.6", + "windows-link 0.2.1", ] [[package]] name = "windows-sys" -version = "0.59.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets 0.52.6", ] @@ -4999,37 +3935,16 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.2", -] - -[[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", + "windows-targets 0.53.5", ] [[package]] -name = "windows-targets" -version = "0.48.5" +name = "windows-sys" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" 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", + "windows-link 0.2.1", ] [[package]] @@ -5050,32 +3965,21 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.2" +version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "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", + "windows-link 0.2.1", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.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" @@ -5084,21 +3988,9 @@ 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" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" [[package]] name = "windows_aarch64_msvc" @@ -5108,21 +4000,9 @@ 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" -version = "0.48.5" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" [[package]] name = "windows_i686_gnu" @@ -5132,9 +4012,9 @@ checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" [[package]] name = "windows_i686_gnullvm" @@ -5144,21 +4024,9 @@ 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 = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" [[package]] name = "windows_i686_msvc" @@ -5168,21 +4036,9 @@ 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 = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" [[package]] name = "windows_x86_64_gnu" @@ -5192,21 +4048,9 @@ 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 = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" [[package]] name = "windows_x86_64_gnullvm" @@ -5216,21 +4060,9 @@ 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 = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" [[package]] name = "windows_x86_64_msvc" @@ -5240,175 +4072,66 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "windows_x86_64_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" - -[[package]] -name = "winit" -version = "0.30.11" -source = "git+https://github.com/Stremio/winit?branch=feat%2Fwayland-file-window-events#dbb55e126896887283c269c4460d0d7f80349012" -dependencies = [ - "ahash", - "android-activity", - "atomic-waker", - "bitflags 2.9.1", - "block2 0.5.1", - "bytemuck", - "calloop", - "cfg_aliases", - "concurrent-queue", - "core-foundation", - "core-graphics", - "cursor-icon", - "dpi 0.1.1", - "js-sys", - "libc", - "memmap2", - "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", - "url", - "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" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" -dependencies = [ - "memchr", -] +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winnow" -version = "0.7.11" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" dependencies = [ "memchr", ] [[package]] -name = "wit-bindgen-rt" -version = "0.39.0" +name = "wit-bindgen" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" -dependencies = [ - "bitflags 2.9.1", -] +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "writeable" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] -name = "x11-dl" +name = "x11" version = "2.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" 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 0.8.8", - "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 = "xattr" -version = "1.5.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d65cbf2f12c15564212d48f4e3dfb87923d25d611f2aed18f4cb23f0413d89e" +checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" dependencies = [ "libc", - "rustix 1.0.7", + "rustix", ] [[package]] -name = "xcursor" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ef33da6b1660b4ddbfb3aef0ade110c8b8a781a3b6382fa5f2b5b040fd55f61" - -[[package]] -name = "xkbcommon-dl" -version = "0.4.2" +name = "xml-rs" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" +checksum = "3c1cb601d29fe2c2ac60a2b2e5e293994d87a1f6fa9687a31a15270f909be9c2" dependencies = [ - "bitflags 2.9.1", - "dlib", - "log", - "once_cell", - "xkeysym", + "bitflags 1.3.2", ] -[[package]] -name = "xkeysym" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" - -[[package]] -name = "xml-rs" -version = "0.8.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62ce76d9b56901b19a74f19431b0d8b3bc7ca4ad685a746dfd78ca8f4fc6bda" - [[package]] name = "yoke" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" dependencies = [ - "serde", "stable_deref_trait", "yoke-derive", "zerofrom", @@ -5416,31 +4139,25 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn", "synstructure", ] [[package]] name = "zbus" -version = "5.7.1" +version = "5.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3a7c7cee313d044fca3f48fa782cb750c79e4ca76ba7bc7718cd4024cdf6f68" +checksum = "b622b18155f7a93d1cd2dc8c01d2d6a44e08fb9ebb7b3f9e6ed101488bad6c91" dependencies = [ "async-broadcast", - "async-executor", - "async-io", - "async-lock", - "async-process", "async-recursion", - "async-task", "async-trait", - "blocking", "enumflags2", "event-listener", "futures-core", @@ -5450,10 +4167,12 @@ dependencies = [ "ordered-stream", "serde", "serde_repr", + "tokio", "tracing", "uds_windows", - "windows-sys 0.59.0", - "winnow 0.7.11", + "uuid", + "windows-sys 0.61.2", + "winnow", "zbus_macros", "zbus_names", "zvariant", @@ -5461,14 +4180,14 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "5.7.1" +version = "5.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17e7e5eec1550f747e71a058df81a9a83813ba0f6a95f39c4e218bdc7ba366a" +checksum = "1cdb94821ca8a87ca9c298b5d1cbd80e2a8b67115d99f6e4551ac49e42b6a314" dependencies = [ - "proc-macro-crate 3.3.0", + "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.103", + "syn", "zbus_names", "zvariant", "zvariant_utils", @@ -5482,28 +4201,28 @@ checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97" dependencies = [ "serde", "static_assertions", - "winnow 0.7.11", + "winnow", "zvariant", ] [[package]] name = "zerocopy" -version = "0.8.26" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.26" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn", ] [[package]] @@ -5523,21 +4242,21 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn", "synstructure", ] [[package]] name = "zeroize" -version = "1.8.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" [[package]] name = "zerotrie" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" dependencies = [ "displaydoc", "yoke", @@ -5546,9 +4265,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.2" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" dependencies = [ "yoke", "zerofrom", @@ -5557,13 +4276,13 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn", ] [[package]] @@ -5583,51 +4302,50 @@ dependencies = [ [[package]] name = "zune-jpeg" -version = "0.4.17" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f6fe2e33d02a98ee64423802e16df3de99c43e5cf5ff983767e1128b394c8ac" +checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713" dependencies = [ "zune-core", ] [[package]] name = "zvariant" -version = "5.5.3" +version = "5.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d30786f75e393ee63a21de4f9074d4c038d52c5b1bb4471f955db249f9dffb1" +checksum = "2be61892e4f2b1772727be11630a62664a1826b62efa43a6fe7449521cb8744c" dependencies = [ "endi", "enumflags2", "serde", "url", - "winnow 0.7.11", + "winnow", "zvariant_derive", "zvariant_utils", ] [[package]] name = "zvariant_derive" -version = "5.5.3" +version = "5.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75fda702cd42d735ccd48117b1630432219c0e9616bf6cb0f8350844ee4d9580" +checksum = "da58575a1b2b20766513b1ec59d8e2e68db2745379f961f86650655e862d2006" dependencies = [ - "proc-macro-crate 3.3.0", + "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.103", + "syn", "zvariant_utils", ] [[package]] name = "zvariant_utils" -version = "3.2.0" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16edfee43e5d7b553b77872d99bc36afdda75c223ca7ad5e3fbecd82ca5fc34" +checksum = "c6949d142f89f6916deca2232cf26a8afacf2b9fdc35ce766105e104478be599" dependencies = [ "proc-macro2", "quote", "serde", - "static_assertions", - "syn 2.0.103", - "winnow 0.7.11", + "syn", + "winnow", ] diff --git a/Cargo.toml b/Cargo.toml index 072fdf3..f6b4ef3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,51 +1,49 @@ [package] name = "stremio-linux-shell" -authors = ["Stremio"] version = "1.0.0-beta.12" -description = "Freedom to Stream" -edition = "2024" +authors = ["Stremio"] +homepage = "https://stremio.com" +repository = "https://github.com/Stremio/stremio-linux-shell" license = "GPL-3.0-only" +edition = "2024" [features] offline-build = [] [dependencies] -anyhow = "1.0.98" -ashpd = { version = "0.11.0", features = ["async-std", "raw-window-handle", "raw_handle", "wayland"], default-features = false } -bytes = "1.10.1" -cef = { version = "138.0.21", git = "https://github.com/Stremio/cef-rs" } -cef-dll-sys = { version = "138.0.21", git = "https://github.com/Stremio/cef-rs" } -clap = { version = "4.5.40", features = ["derive"] } -crossbeam-channel = "0.5.15" -dirs = "6.0.0" -futures = "0.3.31" -gl = "0.14.0" -glutin = { git = "https://github.com/Stremio/glutin", branch = "feat/wayland-file-window-events" } -glutin-winit = { git = "https://github.com/Stremio/glutin", branch = "feat/wayland-file-window-events" } -gtk = "0.18.2" -image = "0.25.6" -itertools = "0.14.0" -libc = "0.2.174" -libmpv2 = { git = "https://github.com/Stremio/libmpv2-rs" } -once_cell = "1.21.3" -paste = "1.0.15" -reqwest = { version = "0.12.20", features = ["blocking", "json"] } -rust-i18n = "3.1.5" -serde = { version = "1.0.219", features = ["derive"] } -serde_json = "1.0.140" +gtk = { package = "gtk4", version = "0.10.3", features = ["v4_20"] } +adw = { package = "libadwaita", version = "0.8.1", features = ["v1_8"] } +cef = { version = "143.1.0+143.0.10", git = "https://github.com/Stremio/cef-rs" } +tokio = { version = "1.48.0", features = ["full"] } +ashpd = { version = "0.12.0", features = ["gtk4"] } +ksni = "0.3.1" +libloading = "0.9.0" +epoxy = "0.1.0" +serde_json = "1.0.145" tracing = "0.1.41" -tracing-subscriber = "0.3.19" -tray-icon = { version = "0.20.1", default-features = false } -url = "2.5.4" -winit = { git = "https://github.com/Stremio/winit", branch = "feat/wayland-file-window-events" } +serde = "1.0.228" +libc = "0.2.177" +itertools = "0.14.0" +tracing-subscriber = "0.3.20" +rand = "0.9.2" +libmpv2 = "5.0.1" +clap = { version = "4.5.51", features = ["derive"] } +image = "0.25.8" +chrono = "0.4.42" +reqwest = { version = "0.12.24", features = ["json"] } +anyhow = "1.0.100" +url = "2.5.7" +dirs = "6.0.0" +gettext-rs = { version = "0.7.7", features = ["gettext-system"] } +flume = "0.11.1" +crossbeam-queue = "0.3.12" [build-dependencies] -anyhow = "1.0.98" -bzip2 = "0.6.0" -dircpy = "0.3.19" -globset = "0.4.16" -serde = "1.0.219" +anyhow = "1.0.100" +serde = "1.0.228" +serde_json = "1.0.145" +toml = "0.9.8" +ureq = { version = "3.1.4", features = ["json"] } +bzip2 = "0.6.1" tar = "0.4.44" -toml = "0.8.23" -ureq = { version = "3.0.12", features = ["json"] } - +globset = "0.4.18" diff --git a/README.md b/README.md index 6193ca2..4a714ea 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ ![Stremio icon](data/icons/com.stremio.Stremio.svg "Stremio icon") # Stremio on Linux -Client for Stremio on Linux using [`winit`](https://github.com/rust-windowing/winit) + [`glutin`](https://github.com/rust-windowing/glutin) with [`libmpv`](https://github.com/mpv-player/mpv/blob/master/DOCS/man/libmpv.rst) and [`CEF`](https://github.com/chromiumembedded/cef) +Client for Stremio on Linux using [`gtk4`](https://docs.gtk.org/gtk4/) + [`libadwaita`](https://gnome.pages.gitlab.gnome.org/libadwaita/doc/1.8/) + [`CEF`](https://github.com/chromiumembedded/cef) + [`libmpv`](https://github.com/mpv-player/mpv/blob/master/DOCS/man/libmpv.rst) @@ -26,7 +26,7 @@ git clone --recurse-submodules https://github.com/Stremio/stremio-linux-shell #### Fedora ```bash -dnf install mpv-devel flatpak-builder gtk3-devel libappindicator-gtk3-devel +dnf install gtk4-devel libadwaita-devel mpv-devel libepoxy-devel flatpak-builder ``` ```bash @@ -35,7 +35,7 @@ cargo build --release #### Ubuntu ```bash -apt install build-essential libssl-dev libnss3 libmpv-dev flatpak-builder libgtk-3-dev libappindicator3-dev +apt install build-essential pkg-config libgtk-4-dev libadwaita-1-dev libmpv-dev gettext nodejs flatpak-builder ``` ```bash @@ -45,14 +45,14 @@ cargo build --release #### Flatpak ```bash flatpak install -y \ - org.freedesktop.Sdk//24.08 \ - org.freedesktop.Platform//24.08 \ - org.freedesktop.Sdk.Extension.rust-stable//24.08 \ - org.freedesktop.Platform.ffmpeg-full//24.08 \ - org.freedesktop.Platform.VAAPI.Intel//24.08 -python3 -m pip install toml aiohttp + org.gnome.Sdk//49 \ + org.gnome.Platform//49 \ + org.freedesktop.Sdk.Extension.rust-stable//25.08 \ + org.freedesktop.Platform.ffmpeg-full//25.08 \ + org.freedesktop.Platform.VAAPI.Intel//25.08 +python3 -m pip install aiohttp tomlkit ``` ```bash ./flatpak/build.sh -``` +``` \ No newline at end of file diff --git a/build.rs b/build.rs index dc57057..d043603 100644 --- a/build.rs +++ b/build.rs @@ -2,14 +2,56 @@ use std::{ fs::{self, File}, io::BufReader, path::{Path, PathBuf}, + process::Command, }; -use anyhow::{Error, Ok, Result}; +use anyhow::{Error, Result}; use bzip2::bufread::BzDecoder; use globset::GlobBuilder; use serde::{Deserialize, Serialize}; +use tar::Archive; use toml::Value; +fn main() -> Result<()> { + setup_cef()?; + setup_po()?; + + Ok(()) +} + +pub const GETTEXT_DOMAIN: &str = "stremio"; +pub const GETTEXT_DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/po"); + +fn setup_po() -> Result<()> { + println!("cargo:rerun-if-changed={GETTEXT_DIR}"); + + let po_dir = Path::new(GETTEXT_DIR); + + for entry in fs::read_dir(po_dir)? { + let entry = entry?; + let path = entry.path(); + + if let Some(extension) = path.extension() + && extension == "po" + && let Some(po_lang) = path.file_stem() + { + let mo_dir = po_dir.join(po_lang).join("LC_MESSAGES"); + + fs::create_dir_all(&mo_dir)?; + + let mo_path = mo_dir.join(format!("{GETTEXT_DOMAIN}.mo")); + + Command::new("msgfmt") + .arg("-o") + .arg(mo_path) + .arg(path) + .spawn()?; + } + } + + Ok(()) +} + const CEF_CDN: &str = "https://cef-builds.spotifycdn.com"; const CEF_CDN_INDEX: &str = "index.json"; const CEF_FILE_TYPE: &str = "minimal"; @@ -48,7 +90,7 @@ pub struct CefIndex { pub linux64: CefPlatform, } -fn main() -> Result<()> { +fn setup_cef() -> Result<()> { let cef_path = PathBuf::from(std::env::var("CEF_PATH")?); #[cfg(not(feature = "offline-build"))] @@ -87,6 +129,7 @@ fn get_version() -> Result { } }) .and_then(|v| v.as_str()) + .and_then(|s| s.split("+").last()) .map(|s| s.to_string()) .ok_or(Error::msg("Failed to get cef version")) } @@ -134,7 +177,7 @@ fn unpack_archive(path: &Path, out: &Path) -> Result<()> { if path.exists() { let decoder = BzDecoder::new(BufReader::new(File::open(path)?)); - let mut archive = tar::Archive::new(decoder); + let mut archive = Archive::new(decoder); for entry in archive.entries()? { let mut entry = entry?; diff --git a/data/com.stremio.Stremio.desktop b/data/com.stremio.Stremio.desktop index db2e5bb..695a024 100644 --- a/data/com.stremio.Stremio.desktop +++ b/data/com.stremio.Stremio.desktop @@ -2,10 +2,11 @@ Name=Stremio Comment=Freedom To Stream Icon=com.stremio.Stremio +Categories=Utility;AudioVideo;Video;Player; +Keywords=Stremio;Media;Play; +Type=Application +Exec=stremio %u +MimeType=x-scheme-handler/stremio; Terminal=false StartupNotify=true -Exec=sh -c "stremio -o '%u'" -MimeType=x-scheme-handler/stremio; -Type=Application -Categories=Utility;AudioVideo;Video;Player; -Keywords=Stremio;Media;Play; \ No newline at end of file +DBusActivatable=true \ No newline at end of file diff --git a/data/com.stremio.Stremio.metainfo.xml b/data/com.stremio.Stremio.metainfo.xml index d8428d8..72f3d47 100644 --- a/data/com.stremio.Stremio.metainfo.xml +++ b/data/com.stremio.Stremio.metainfo.xml @@ -13,8 +13,8 @@ - #1a173e - #0c0b11 + #9C97D8 + #2E286D @@ -45,6 +45,8 @@ https://github.com/Stremio/stremio-linux-shell https://github.com/Stremio/stremio-linux-shell/issues + stremio + com.stremio.Stremio.desktop diff --git a/data/com.stremio.Stremio.service b/data/com.stremio.Stremio.service new file mode 100644 index 0000000..7cfb139 --- /dev/null +++ b/data/com.stremio.Stremio.service @@ -0,0 +1,3 @@ +[D-BUS Service] +Name=com.stremio.Stremio +Exec=/app/bin/stremio --gapplication-service diff --git a/data/screenshots/screenshot1.png b/data/screenshots/screenshot1.png index 362260c..ce6acd0 100644 Binary files a/data/screenshots/screenshot1.png and b/data/screenshots/screenshot1.png differ diff --git a/data/screenshots/screenshot2.png b/data/screenshots/screenshot2.png index 1090460..83f7458 100644 Binary files a/data/screenshots/screenshot2.png and b/data/screenshots/screenshot2.png differ diff --git a/data/screenshots/screenshot3.png b/data/screenshots/screenshot3.png index aeb8aac..a5497ec 100644 Binary files a/data/screenshots/screenshot3.png and b/data/screenshots/screenshot3.png differ diff --git a/data/stremio b/data/stremio deleted file mode 100644 index d8174a0..0000000 --- a/data/stremio +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -/app/share/stremio/stremio "$@" \ No newline at end of file diff --git a/flatpak/build.sh b/flatpak/build.sh index f88a9fa..949925f 100755 --- a/flatpak/build.sh +++ b/flatpak/build.sh @@ -1,9 +1,9 @@ #!/bin/sh -package_id="com.stremio.Stremio.Devel" +app_id="com.stremio.Stremio.Devel" cwd="flatpak" python3 $cwd/flatpak-builder-tools/cargo/flatpak-cargo-generator.py Cargo.lock -o $cwd/cargo-sources.json -flatpak-builder --repo=$cwd/repo --force-clean $cwd/build $cwd/$package_id.json -flatpak build-bundle $cwd/repo $cwd/$package_id.flatpak $package_id \ No newline at end of file +flatpak-builder --repo=$cwd/repo --force-clean $cwd/build $cwd/$app_id.json +flatpak build-bundle $cwd/repo $cwd/$app_id.flatpak $app_id \ No newline at end of file diff --git a/flatpak/com.stremio.Stremio.Devel.json b/flatpak/com.stremio.Stremio.Devel.json index 26680d9..9769933 100644 --- a/flatpak/com.stremio.Stremio.Devel.json +++ b/flatpak/com.stremio.Stremio.Devel.json @@ -1,19 +1,19 @@ { "app-id": "com.stremio.Stremio.Devel", - "runtime": "org.freedesktop.Platform", - "runtime-version": "24.08", - "sdk": "org.freedesktop.Sdk", + "runtime": "org.gnome.Platform", + "runtime-version": "49", + "sdk": "org.gnome.Sdk", "sdk-extensions": [ "org.freedesktop.Sdk.Extension.rust-stable" ], "add-extensions": { "org.freedesktop.Platform.ffmpeg-full": { - "version": "24.08", + "version": "25.08", "directory": "lib/ffmpeg", "add-ld-path": "." }, "org.freedesktop.Platform.VAAPI.Intel": { - "version": "24.08", + "version": "25.08", "directory": "lib/intel-vaapi-driver", "add-ld-path": ".", "download-if": "have-intel-gpu", @@ -23,19 +23,18 @@ "command": "stremio", "finish-args": [ "--socket=wayland", - "--socket=x11", + "--socket=fallback-x11", "--socket=pulseaudio", "--share=ipc", "--share=network", "--device=dri", "--persist=.stremio-server", "--filesystem=xdg-download", - "--filesystem=xdg-run/stremio:create", - "--env=LD_LIBRARY_PATH=/app/lib/:/app/lib64/:/app/share/stremio", - "--system-talk-name=org.freedesktop.systemd1", - "--system-talk-name=org.freedesktop.UPower", "--talk-name=org.kde.StatusNotifierWatcher", - "--talk-name=org.kde.kwalletd6" + "--own-name=com.stremio.Stremio.Devel", + "--env=LD_LIBRARY_PATH=/app/lib/:/app/lib64/:/app/share/stremio", + "--env=SERVER_PATH=/app/bin/server.js", + "--env=RUST_LOG=debug" ], "build-options": { "append-path": "/usr/lib/sdk/rust-stable/bin", @@ -56,17 +55,20 @@ }, "build-commands": [ "cargo --offline fetch --manifest-path Cargo.toml --verbose", - "cargo --offline build --verbose -F offline-build" + "cargo --offline build --verbose" ], "post-install": [ - "install -Dm755 data/stremio -t /app/bin/", - "install -Dm755 target/debug/stremio-linux-shell /app/share/stremio/stremio", - "install -Dm644 data/server.js -t /app/share/stremio/", + "install -Dm755 target/debug/stremio-linux-shell /app/bin/stremio", + "install -Dm644 data/server.js -t /app/bin/", "install -Dm644 data/icons/com.stremio.Stremio.Devel.svg -t /app/share/icons/hicolor/scalable/apps/", "install -Dm644 data/com.stremio.Stremio.desktop /app/share/applications/com.stremio.Stremio.Devel.desktop", "sed -i -e 's/com.stremio.Stremio/com.stremio.Stremio.Devel/g' /app/share/applications/com.stremio.Stremio.Devel.desktop", "install -Dm644 data/com.stremio.Stremio.metainfo.xml /app/share/metainfo/com.stremio.Stremio.Devel.metainfo.xml", "sed -i -e 's/com.stremio.Stremio/com.stremio.Stremio.Devel/g' /app/share/metainfo/com.stremio.Stremio.Devel.metainfo.xml", + "install -Dm644 data/com.stremio.Stremio.service /app/share/dbus-1/services/com.stremio.Stremio.Devel.service", + "sed -i -e 's/com.stremio.Stremio/com.stremio.Stremio.Devel/g' /app/share/dbus-1/services/com.stremio.Stremio.Devel.service", + "install -Dm644 po/es/LC_MESSAGES/stremio.mo -t /app/share/locale/es/LC_MESSAGES/", + "install -Dm644 po/fr/LC_MESSAGES/stremio.mo -t /app/share/locale/fr/LC_MESSAGES/", "mkdir -p /app/lib/ffmpeg", "mkdir -p /app/lib/intel-vaapi-driver" ], @@ -113,7 +115,7 @@ { "type": "git", "url": "https://github.com/haasn/libplacebo.git", - "tag": "v7.349.0" + "branch": "master" } ] }, @@ -150,8 +152,8 @@ "sources": [ { "type": "archive", - "url": "https://cef-builds.spotifycdn.com/cef_binary_138.0.21+g54811fe+chromium-138.0.7204.101_linux64_minimal.tar.bz2", - "sha256": "2a86ffe653dd65cf4920fc73aa225735231ac4bb80bcd40a75dfc0643897bcd2" + "url": "https://cef-builds.spotifycdn.com/cef_binary_143.0.9+ge88e818+chromium-143.0.7499.40_linux64_minimal.tar.bz2", + "sha256": "f94065a9ff31bab8a2aa8b1089afef528b614a949f928ba9c19d644c290d964f" } ] } @@ -170,7 +172,6 @@ "sha256": "dafe2e8f82cb97de1bd10db9e2ec4c07bbf53389b0799b1e095a918951e78fd4" } ] - }, - "shared-modules/libappindicator/libappindicator-gtk3-12.10.json" + } ] } \ No newline at end of file diff --git a/flatpak/shared-modules b/flatpak/shared-modules deleted file mode 160000 index a34a31e..0000000 --- a/flatpak/shared-modules +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a34a31e4dd50a0b38d1725f6881d1bedb0948b38 diff --git a/locales/en.yml b/locales/en.yml deleted file mode 100644 index cfb5e4f..0000000 --- a/locales/en.yml +++ /dev/null @@ -1,6 +0,0 @@ -show: Show -hide: Hide -quit: Quit - -player_error_quit: The player had to quit -player_error_general: The player was unable to read this file diff --git a/locales/es.yml b/locales/es.yml deleted file mode 100644 index c487d7b..0000000 --- a/locales/es.yml +++ /dev/null @@ -1,6 +0,0 @@ -show: Mostrar -hide: Ocultar -quit: Salir - -player_error_quit: El reproductor tuvo que terminar -player_error_general: El reproductor no pudo leer este archivo diff --git a/locales/fr.yml b/locales/fr.yml deleted file mode 100644 index 8bd90d8..0000000 --- a/locales/fr.yml +++ /dev/null @@ -1,6 +0,0 @@ -show: Afficher -hide: Masquer -quit: Quitter - -player_error_quit: Le lecteur a dû quitter -player_error_general: Le lecteur n'a pas pu lire ce fichier diff --git a/po/.gitignore b/po/.gitignore new file mode 100644 index 0000000..4db6541 --- /dev/null +++ b/po/.gitignore @@ -0,0 +1 @@ +*/LC_MESSAGES \ No newline at end of file diff --git a/po/es.po b/po/es.po new file mode 100644 index 0000000..f09415e --- /dev/null +++ b/po/es.po @@ -0,0 +1,19 @@ +msgid "" +msgstr "" +"Language: es\n" +"Content-Type: text/plain; charset=UTF-8\n" + +msgid "Main menu" +msgstr "Menú principal" + +msgid "_About Stremio" +msgstr "_Acerca de Stremio" + +msgid "Show" +msgstr "Mostrar" + +msgid "Hide" +msgstr "Ocultar" + +msgid "Quit" +msgstr "Salir" \ No newline at end of file diff --git a/po/fr.po b/po/fr.po new file mode 100644 index 0000000..131bdf7 --- /dev/null +++ b/po/fr.po @@ -0,0 +1,19 @@ +msgid "" +msgstr "" +"Language: fr\n" +"Content-Type: text/plain; charset=UTF-8\n" + +msgid "Main menu" +msgstr "Menu principal" + +msgid "_About Stremio" +msgstr "_À propos de Stremio" + +msgid "Show" +msgstr "Afficher" + +msgid "Hide" +msgstr "Masquer" + +msgid "Quit" +msgstr "Quitter" \ No newline at end of file diff --git a/src/app/about/mod.rs b/src/app/about/mod.rs new file mode 100644 index 0000000..987660f --- /dev/null +++ b/src/app/about/mod.rs @@ -0,0 +1,35 @@ +use adw::prelude::AdwDialogExt; +use chrono::{Datelike, Utc}; +use gtk::glib::object::IsA; +use itertools::Itertools; + +use crate::app::config::{APP_ID, APP_NAME}; + +pub struct AboutDialog { + dialog: adw::AboutDialog, +} + +impl AboutDialog { + pub fn new() -> Self { + let authors = env!("CARGO_PKG_AUTHORS").split(':').collect_vec(); + let copyright = format!("© {} {}", Utc::now().year(), authors[0]); + + let dialog = adw::AboutDialog::builder() + .application_icon(APP_ID) + .application_name(APP_NAME) + .version(env!("CARGO_PKG_VERSION")) + .website(env!("CARGO_PKG_HOMEPAGE")) + .issue_url(env!("CARGO_PKG_REPOSITORY")) + .license_type(gtk::License::Gpl30Only) + .copyright(copyright) + .developers(&*authors) + .designers(&*authors) + .build(); + + Self { dialog } + } + + pub fn show(&self, parent: &impl IsA) { + self.dialog.present(Some(parent)); + } +} diff --git a/src/app/adapters.rs b/src/app/adapters.rs deleted file mode 100644 index 279f4c3..0000000 --- a/src/app/adapters.rs +++ /dev/null @@ -1,45 +0,0 @@ -use winit::{ - dpi::{PhysicalPosition, PhysicalSize}, - event::MouseScrollDelta, - window::CursorIcon, -}; - -use crate::shared::types::{Cursor, MouseDelta, MousePosition, WindowSize}; - -impl From for MouseDelta { - fn from(value: MouseScrollDelta) -> Self { - match value { - MouseScrollDelta::LineDelta(x, y) => Self((x * 100.0) as i32, (y * 100.0) as i32), - MouseScrollDelta::PixelDelta(position) => Self(position.x as i32, position.y as i32), - } - } -} - -impl From> for WindowSize { - fn from(value: PhysicalSize) -> Self { - Self(value.width as i32, value.height as i32) - } -} - -impl From> for MousePosition { - fn from(value: PhysicalPosition) -> Self { - Self(value.x as i32, value.y as i32) - } -} - -impl TryFrom for CursorIcon { - type Error = &'static str; - - fn try_from(value: Cursor) -> Result { - match value { - Cursor::Default => Ok(Self::Default), - Cursor::Pointer => Ok(Self::Pointer), - Cursor::Text => Ok(Self::Text), - Cursor::Move => Ok(Self::Move), - Cursor::ZoomIn => Ok(Self::ZoomIn), - Cursor::ZoomOut => Ok(Self::ZoomOut), - Cursor::Wait => Ok(Self::Wait), - _ => Err("Failed to convert Cursor to CursorIcon"), - } - } -} diff --git a/src/app/config.rs b/src/app/config.rs new file mode 100644 index 0000000..62a4703 --- /dev/null +++ b/src/app/config.rs @@ -0,0 +1,7 @@ +pub const APP_ID: &str = match cfg!(debug_assertions) { + true => "com.stremio.Stremio.Devel", + false => "com.stremio.Stremio", +}; +pub const APP_NAME: &str = "Stremio"; + +pub const URI_SCHEME: &str = "stremio://"; diff --git a/src/app/imp.rs b/src/app/imp.rs new file mode 100644 index 0000000..cc73f10 --- /dev/null +++ b/src/app/imp.rs @@ -0,0 +1,369 @@ +use std::{ + cell::{Cell, RefCell}, + rc::Rc, +}; + +use adw::{prelude::*, subclass::prelude::*}; +use gtk::glib::{self, ControlFlow, Properties, clone}; + +use crate::{ + app::{config::URI_SCHEME, tray::Tray, video::Video, webview::WebView, window::Window}, + chromium::{Chromium, ChromiumEvent}, + shared::ipc::{ + self, + event::{IpcEvent, IpcEventMpv}, + }, +}; + +#[derive(Properties, Default)] +#[properties(wrapper_type = super::Application)] +pub struct Application { + #[property(get, set)] + dev_mode: Cell, + #[property(get, set)] + startup_url: RefCell, + #[property(get, set)] + open_uri: RefCell>, + #[property(get, set)] + decorations: Cell, + tray: RefCell>, + browser: Rc>>, + deeplink: Rc>>, +} + +impl Application { + pub fn set_browser(&self, browser: Chromium) { + *self.browser.borrow_mut() = Some(browser); + } +} + +#[glib::object_subclass] +impl ObjectSubclass for Application { + const NAME: &'static str = "Application"; + type Type = super::Application; + type ParentType = adw::Application; +} + +#[glib::derived_properties] +impl ObjectImpl for Application {} + +impl ApplicationImpl for Application { + fn startup(&self) { + self.parent_startup(); + + let app = self.obj(); + app.setup_actions(); + app.setup_accels(); + + if let Some(ref mut browser) = *self.browser.borrow_mut() { + browser.start(); + } + } + + fn activate(&self) { + self.parent_activate(); + + let app = self.obj(); + + if let Some(window) = app.active_window() { + window.present(); + return; + } + + let tray = Tray::default(); + let video = Video::default(); + let webview = WebView::default(); + + let window = Window::new(&app); + window.set_property("decorations", self.decorations.get()); + window.set_underlay(&video); + window.set_overlay(&webview); + + let browser = self.browser.clone(); + window.connect_monitor_info(clone!( + #[weak] + video, + #[weak] + webview, + move |refresh_rate, scale_factor| { + if let Some(ref browser) = *browser.borrow() { + browser.set_monitor_info(refresh_rate, scale_factor); + video.set_property("scale-factor", scale_factor); + webview.set_property("scale-factor", scale_factor); + } + } + )); + + video.connect_playback_started(clone!( + #[weak] + window, + move || { + window.disable_idling(); + } + )); + + video.connect_playback_ended(clone!( + #[weak] + window, + move || { + window.enable_idling(); + } + )); + + let browser = self.browser.clone(); + video.connect_mpv_property_change(move |name, value| { + if let Some(ref browser) = *browser.borrow() { + let message = ipc::create_response(IpcEvent::Mpv(IpcEventMpv::Change(( + name.to_string(), + value, + )))); + + browser.post_message(message); + } + }); + + let browser = self.browser.clone(); + let dev_mode = self.dev_mode.get(); + let startup_url = self.startup_url.clone(); + let open_uri = self.open_uri.clone(); + let deeplink = self.deeplink.clone(); + glib::idle_add_local(clone!( + #[weak] + webview, + #[weak] + video, + #[weak] + window, + #[weak] + app, + #[upgrade_or] + ControlFlow::Continue, + move || { + if let Some(ref browser) = *browser.borrow() { + browser.on_event(|event| match event { + ChromiumEvent::Ready => { + browser.dev_tools(dev_mode); + browser.load_url(&startup_url.borrow()); + } + ChromiumEvent::Loaded => { + if let Some(ref uri) = *open_uri.borrow() + && uri.starts_with(URI_SCHEME) + { + let message = + ipc::create_response(IpcEvent::OpenMedia(uri.to_string())); + browser.post_message(message); + } + } + ChromiumEvent::Fullscreen(state) => window.set_fullscreen(state), + ChromiumEvent::Render(frame) => webview.render(frame), + ChromiumEvent::Open(url) => window.open_uri(url), + ChromiumEvent::Ipc(message) => { + if let Ok(event) = ipc::parse_request(&message) { + match event { + IpcEvent::Init => { + let message = ipc::create_response(IpcEvent::Init); + browser.post_message(message); + } + IpcEvent::Ready => { + if let Some(ref uri) = *deeplink.borrow() { + let message = ipc::create_response( + IpcEvent::OpenMedia(uri.to_string()), + ); + browser.post_message(message); + } + } + IpcEvent::Quit => { + app.quit(); + } + IpcEvent::Fullscreen(state) => { + window.set_fullscreen(state); + + let message = + ipc::create_response(IpcEvent::Fullscreen(state)); + browser.post_message(message); + } + IpcEvent::Mpv(event) => match event { + IpcEventMpv::Observe(name) => { + video.observe_mpv_property(name) + } + IpcEventMpv::Command((name, args)) => { + video.send_command(name, args) + } + IpcEventMpv::Set((name, value)) => { + video.set_mpv_property(name, value) + } + _ => {} + }, + _ => {} + } + } + } + }); + } + + ControlFlow::Continue + } + )); + + let browser = self.browser.clone(); + window.connect_visibility(clone!( + #[weak] + tray, + move |state| { + if let Some(ref browser) = *browser.borrow() { + browser.hidden(!state); + + let message = ipc::create_response(IpcEvent::Visibility(state)); + browser.post_message(message); + } + + tray.update(state); + } + )); + + let browser = self.browser.clone(); + webview.connect_has_focus_notify(move |_| { + if let Some(ref browser) = *browser.borrow() { + browser.focus(true); + } + }); + + let browser = self.browser.clone(); + webview.connect_resized(move |width, height| { + if let Some(ref browser) = *browser.borrow() { + browser.resize(width, height); + } + }); + + let browser = self.browser.clone(); + webview.connect_motion(move |pointer_state| { + if let Some(ref browser) = *browser.borrow() { + browser.forward_motion(&pointer_state); + } + }); + + let browser = self.browser.clone(); + webview.connect_scroll(move |pointer_state, delta_x, delta_y| { + if let Some(ref browser) = *browser.borrow() { + browser.forward_scroll(&pointer_state, delta_x, delta_y); + } + }); + + let browser = self.browser.clone(); + webview.connect_click(clone!( + #[weak] + webview, + move |pointer_state, count| { + if let Some(ref browser) = *browser.borrow() { + webview.grab_focus(); + browser.forward_click(&pointer_state, count); + } + } + )); + + let browser = self.browser.clone(); + webview.connect_keys(clone!( + #[weak] + webview, + move |keyboard_state| { + if let Some(ref browser) = *browser.borrow() { + webview.grab_focus(); + browser.forward_key(&keyboard_state); + } + } + )); + + let browser = self.browser.clone(); + webview.connect_clipboard(move |text| { + if let Some(ref browser) = *browser.borrow() { + browser.clipboard(text); + } + }); + + let browser = self.browser.clone(); + webview.connect_file_enter(move |pointer_state, path| { + if let Some(ref browser) = *browser.borrow() { + browser.forward_file_enter(pointer_state.as_ref(), path); + } + }); + + let browser = self.browser.clone(); + webview.connect_file_leave(move || { + if let Some(ref browser) = *browser.borrow() { + browser.forward_file_leave(); + } + }); + + let browser = self.browser.clone(); + webview.connect_file_motion(move |pointer_state| { + if let Some(ref browser) = *browser.borrow() { + browser.forward_file_hover(pointer_state.as_ref()); + } + }); + + let browser = self.browser.clone(); + webview.connect_file_drop(move |pointer_state| { + if let Some(ref browser) = *browser.borrow() { + browser.forward_file_drop(pointer_state.as_ref()); + } + }); + + tray.connect_show(clone!( + #[weak] + window, + move || { + window.set_visible(true); + } + )); + + tray.connect_hide(clone!( + #[weak] + window, + move || { + window.set_visible(false); + } + )); + + tray.connect_quit(clone!( + #[weak] + app, + move || { + app.quit(); + } + )); + + *self.tray.borrow_mut() = Some(tray); + + window.present(); + } + + fn open(&self, files: &[gtk::gio::File], hint: &str) { + self.parent_open(files, hint); + + self.activate(); + + if let Some(file) = files.first() { + let uri = file.uri().to_string(); + if uri.starts_with(URI_SCHEME) { + let mut deeplink = self.deeplink.borrow_mut(); + *deeplink = Some(uri.clone()); + + if let Some(ref browser) = *self.browser.borrow() { + let message = ipc::create_response(IpcEvent::OpenMedia(uri)); + browser.post_message(message); + } + } + } + } + + fn shutdown(&self) { + if let Some(browser) = self.browser.take() { + browser.stop(); + } + + self.parent_shutdown(); + } +} + +impl GtkApplicationImpl for Application {} +impl AdwApplicationImpl for Application {} diff --git a/src/app/mod.rs b/src/app/mod.rs index c1fcfad..3e95490 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -1,356 +1,77 @@ -mod adapters; -mod utils; - -use std::{ffi::CString, path::PathBuf}; - -use ashpd::{ - WindowIdentifier, - desktop::{ - Request, - background::Background, - inhibit::{InhibitFlags, InhibitProxy}, - open_uri::OpenFileRequest, - }, - enumflags2::BitFlags, -}; -use crossbeam_channel::{Receiver, Sender, unbounded}; -use glutin::{ - context::{ContextApi, Version}, - display::GetGlDisplay, - prelude::GlDisplay, -}; -use tracing::error; -use url::Url; -use winit::{ - application::ApplicationHandler, - dpi::PhysicalSize, - event::{ElementState, KeyEvent, Touch, WindowEvent}, - event_loop::ActiveEventLoop, - keyboard::ModifiersState, - platform::wayland::WindowAttributesExtWayland, - raw_window_handle::{HasDisplayHandle, HasWindowHandle}, - window::{CursorIcon, Fullscreen, UserAttentionType, Window, WindowAttributes}, +mod about; +mod config; +mod imp; +mod tray; +mod video; +mod webview; +mod window; + +use adw::subclass::prelude::ObjectSubclassIsExt; +use gtk::{ + gio::{self, ActionEntry, ApplicationFlags, prelude::*}, + glib::{self, ExitCode, Object}, + prelude::*, }; use crate::{ - constants::{APP_ID, APP_NAME, WINDOW_SIZE}, - shared::{ - self, - types::{Cursor, MouseState, UserEvent, WindowSize}, + app::{ + about::AboutDialog, + config::{APP_ID, APP_NAME}, }, + chromium::Chromium, }; -const CONTEXT_API: ContextApi = ContextApi::OpenGl(Some(Version::new(3, 3))); - -#[derive(Debug)] -pub enum AppEvent { - Init, - Ready, - Resized(WindowSize), - Focused(bool), - Visibility(bool), - Minimized(bool), - Fullscreen(bool), - MouseMoved(MouseState), - MouseWheel(MouseState), - MouseInput(MouseState), - TouchInput(Touch), - KeyboardInput((KeyEvent, ModifiersState)), - FileHover((PathBuf, MouseState)), - FileDrop(MouseState), - FileCancel, +glib::wrapper! { + pub struct Application(ObjectSubclass) + @extends gio::Application, gtk::Application, adw::Application, + @implements gio::ActionGroup, gio::ActionMap; } -pub struct App { - window: Option, - sender: Sender, - receiver: Receiver, - maximized: bool, - modifiers_state: ModifiersState, - mouse_state: MouseState, - inhibit_request: Option>, +impl Default for Application { + fn default() -> Self { + Self::new() + } } -impl App { +impl Application { pub fn new() -> Self { - let (sender, receiver) = unbounded::(); - - Self { - window: None, - sender, - receiver, - maximized: false, - modifiers_state: ModifiersState::empty(), - mouse_state: MouseState::default(), - inhibit_request: None, - } - } - - pub fn events(&self, handler: T) { - self.receiver.try_iter().for_each(handler); - } - - pub fn create_window(&mut self, event_loop: &ActiveEventLoop) { - let window_attributes = WindowAttributes::default() - .with_title(APP_NAME) - .with_name(APP_ID, APP_ID) - .with_decorations(true) - .with_resizable(true) - .with_maximized(self.maximized) - .with_min_inner_size(PhysicalSize::new(900, 600)) - .with_inner_size(PhysicalSize::::from(WINDOW_SIZE)); - - let (window, config) = utils::create_window(event_loop, window_attributes); - let surface = utils::create_surface(&config, &window); - let context = utils::create_context(&config, CONTEXT_API); + glib::set_application_name(APP_NAME); - gl::load_with(|name| { - let name = CString::new(name).unwrap(); - context.display().get_proc_address(&name) as _ - }); - - self.window = window; - self.sender.send(AppEvent::Visibility(true)).ok(); - - shared::create_gl(surface, context); - shared::with_gl(|_, _| { - let refresh_rate = self.get_refresh_rate(); - shared::create_renderer(WINDOW_SIZE, refresh_rate); - }); - - self.sender.send(AppEvent::Ready).ok(); + Object::builder() + .property("application-id", APP_ID) + .property("flags", ApplicationFlags::HANDLES_OPEN) + .build() } - pub fn destroy_window(&mut self) { - shared::drop_renderer(); - shared::drop_gl(); - - self.window.take(); - self.sender.send(AppEvent::Visibility(false)).ok(); + pub fn set_browser(&self, browser: Chromium) { + self.imp().set_browser(browser); } - pub fn notify(&self) { - if let Some(window) = self.window.as_ref() { - window.request_user_attention(Some(UserAttentionType::Informational)); - } + pub async fn run(&self) -> ExitCode { + let args: Vec = vec![]; + self.run_with_args(&args) } - pub fn set_cursor(&self, cursor: Cursor) { - if let Some(window) = self.window.as_ref() { - if let Ok(icon) = TryInto::::try_into(cursor) { - window.set_cursor(icon); - window.set_cursor_visible(true); - } else { - window.set_cursor_visible(false); - } - } - } - - pub fn set_fullscreen(&self, state: bool) { - if let Some(window) = self.window.as_ref() { - let fullscreen = match state { - true => Some(Fullscreen::Borderless(None)), - false => None, - }; + fn setup_actions(&self) { + let quit_action = ActionEntry::builder("quit") + .activate(|app: &Self, _, _| { + app.quit(); + }) + .build(); - window.set_fullscreen(fullscreen); - } - } - - pub fn get_refresh_rate(&self) -> u32 { - if let Some(window) = self.window.as_ref() { - for monitor in window.available_monitors() { - if let Some(m_hz) = monitor.refresh_rate_millihertz() { - return m_hz / 1000; + let show_about_action = ActionEntry::builder("show-about") + .activate(|app: &Self, _, _| { + if let Some(window) = app.active_window() { + let dialog = AboutDialog::new(); + dialog.show(&window); } - } - } - - 30 - } - - pub async fn disable_idling(&mut self) { - if let Some(identifier) = self.window_identifier().await - && let Ok(proxy) = InhibitProxy::new().await - { - let mut flags = BitFlags::empty(); - flags.insert(InhibitFlags::Idle); - - let reason = "Prevent screen from going blank during media playback"; - - self.inhibit_request = proxy - .inhibit(Some(&identifier), flags, reason) - .await - .map_err(|e| error!("Failed to prevent idling: {e}")) - .ok(); - } - } - - pub async fn enable_idling(&mut self) { - if let Some(request) = self.inhibit_request.take() { - request - .close() - .await - .map_err(|e| error!("Failed to allow idling: {e}")) - .ok(); - } - } - - pub async fn open_url>(&self, input: T) { - if let Ok(url) = Url::parse(&input.into()) - && let Some(identifier) = self.window_identifier().await - { - let request = OpenFileRequest::default().identifier(identifier); - - request - .send_uri(&url) - .await - .map_err(|e| error!("Failed to open uri: {e}")) - .ok(); - } - } - - async fn request_background(&self) { - if let Some(identifier) = self.window_identifier().await { - let request = Background::request().identifier(identifier); - - request - .send() - .await - .map_err(|e| error!("Failed to set background mode: {e}")) - .ok(); - } - } - - async fn window_identifier(&self) -> Option { - if let Some(window) = self.window.as_ref() - && let (Ok(window), Ok(display)) = (window.window_handle(), window.display_handle()) - { - let window_handle = window.as_raw(); - let display_handle = display.as_raw(); - - return WindowIdentifier::from_raw_handle(&window_handle, Some(&display_handle)).await; - } - - None - } -} - -impl Drop for App { - fn drop(&mut self) { - self.window.take(); - } -} + }) + .build(); -impl ApplicationHandler for App { - fn resumed(&mut self, event_loop: &ActiveEventLoop) { - self.create_window(event_loop); - - futures::executor::block_on(self.request_background()); - - self.sender.send(AppEvent::Init).ok(); - } - - fn window_event( - &mut self, - _event_loop: &ActiveEventLoop, - _window_id: winit::window::WindowId, - event: winit::event::WindowEvent, - ) { - match event { - WindowEvent::ModifiersChanged(modifiers) => { - self.modifiers_state = modifiers.state(); - } - WindowEvent::KeyboardInput { event, .. } => { - self.sender - .send(AppEvent::KeyboardInput((event, self.modifiers_state))) - .ok(); - } - WindowEvent::CursorMoved { position, .. } => { - self.mouse_state.over = true; - self.mouse_state.position = position.into(); - self.sender - .send(AppEvent::MouseMoved(self.mouse_state)) - .ok(); - } - WindowEvent::CursorLeft { .. } => { - self.mouse_state.over = false; - self.sender - .send(AppEvent::MouseMoved(self.mouse_state)) - .ok(); - } - WindowEvent::MouseWheel { delta, .. } => { - self.mouse_state.delta = delta.into(); - self.sender - .send(AppEvent::MouseWheel(self.mouse_state)) - .ok(); - } - WindowEvent::MouseInput { button, state, .. } => { - self.mouse_state.button = button; - self.mouse_state.pressed = match state { - ElementState::Pressed => true, - ElementState::Released => false, - }; - - self.sender - .send(AppEvent::MouseInput(self.mouse_state)) - .ok(); - } - WindowEvent::Touch(touch) => { - self.sender.send(AppEvent::TouchInput(touch)).ok(); - } - WindowEvent::HoveredFile(path) => { - self.sender - .send(AppEvent::FileHover((path, self.mouse_state))) - .ok(); - } - WindowEvent::DroppedFile(_) => { - self.sender.send(AppEvent::FileDrop(self.mouse_state)).ok(); - } - WindowEvent::HoveredFileCancelled => { - self.sender.send(AppEvent::FileCancel).ok(); - } - WindowEvent::Resized(size) => { - self.sender.send(AppEvent::Resized(size.into())).ok(); - } - WindowEvent::Focused(state) => { - self.sender.send(AppEvent::Focused(state)).ok(); - - if let Some(window) = self.window.as_ref() { - self.maximized = window.is_maximized(); - - let minimized = window.is_minimized().unwrap_or(false); - self.sender.send(AppEvent::Minimized(minimized)).ok(); - } - } - WindowEvent::RedrawRequested => { - if let Some(window) = self.window.as_ref() { - let fullscreen = window.fullscreen().is_some(); - self.sender.send(AppEvent::Fullscreen(fullscreen)).ok(); - } - } - WindowEvent::CloseRequested => { - self.destroy_window(); - } - _ => (), - } + self.add_action_entries([quit_action, show_about_action]); } - fn user_event(&mut self, event_loop: &ActiveEventLoop, event: UserEvent) { - match event { - UserEvent::Raise => match self.window.is_some() { - true => self.notify(), - false => self.create_window(event_loop), - }, - UserEvent::Show => { - self.create_window(event_loop); - } - UserEvent::Hide => { - self.destroy_window(); - } - UserEvent::Quit => { - event_loop.exit(); - } - } + fn setup_accels(&self) { + self.set_accels_for_action("app.quit", &["q"]); } } diff --git a/src/app/tray/config.rs b/src/app/tray/config.rs new file mode 100644 index 0000000..257cc2b --- /dev/null +++ b/src/app/tray/config.rs @@ -0,0 +1,4 @@ +pub const ICON_FILE: &[u8] = include_bytes!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/data/icons/symbolic.png" +)); diff --git a/src/app/tray/imp.rs b/src/app/tray/imp.rs new file mode 100644 index 0000000..3c5fcf8 --- /dev/null +++ b/src/app/tray/imp.rs @@ -0,0 +1,176 @@ +use std::sync::{ + Arc, LazyLock, OnceLock, + mpsc::{Sender, channel}, +}; + +use gettextrs::gettext; +use gtk::{ + glib::{self, object::ObjectExt, subclass::Signal}, + subclass::prelude::*, +}; +use ksni::{Handle, MenuItem, TrayMethods, menu::StandardItem}; +use tokio::sync::Mutex; + +use crate::app::{ + config::{APP_ID, APP_NAME}, + tray::config::ICON_FILE, +}; + +#[derive(Default)] +pub struct Tray { + handle: Arc>>>, +} + +impl Tray { + pub fn update(&self, state: bool) { + let local_handle = self.handle.clone(); + tokio::spawn(async move { + let handle_guard = local_handle.lock().await; + if let Some(handle) = handle_guard.as_ref() { + handle.update(|tray| tray.window_visible = state).await; + } + }); + } +} + +#[glib::object_subclass] +impl ObjectSubclass for Tray { + const NAME: &'static str = "Tray"; + type Type = super::Tray; + type ParentType = glib::Object; +} + +impl ObjectImpl for Tray { + fn signals() -> &'static [Signal] { + static SIGNALS: OnceLock> = OnceLock::new(); + SIGNALS.get_or_init(|| { + vec![ + Signal::builder("show").build(), + Signal::builder("hide").build(), + Signal::builder("quit").build(), + ] + }) + } + + fn constructed(&self) { + self.parent_constructed(); + + let (sender, receiver) = channel::(); + + let tray_icon = TrayIcon { + sender, + window_visible: true, + }; + + let local_handle = self.handle.clone(); + tokio::spawn(async move { + let mut handle_guard = local_handle.lock().await; + let handle = tray_icon + .spawn_without_dbus_name() + .await + .expect("Failed to create tray icon"); + *handle_guard = Some(handle); + }); + + let object_weak = self.obj().downgrade(); + glib::idle_add_local(move || { + receiver.try_iter().for_each(|event| { + if let Some(object) = object_weak.upgrade() { + match event { + TrayEvent::Show => object.emit_by_name::<()>("show", &[]), + TrayEvent::Hide => object.emit_by_name::<()>("hide", &[]), + TrayEvent::Quit => object.emit_by_name::<()>("quit", &[]), + } + } + }); + + glib::ControlFlow::Continue + }); + } +} + +#[derive(Debug)] +pub enum TrayEvent { + Show, + Hide, + Quit, +} + +pub struct TrayIcon { + sender: Sender, + window_visible: bool, +} + +impl ksni::Tray for TrayIcon { + fn id(&self) -> String { + APP_ID.into() + } + + fn title(&self) -> String { + APP_NAME.into() + } + + fn icon_pixmap(&self) -> Vec { + static ICON: LazyLock = LazyLock::new(|| { + let (data, width, height) = load_image(ICON_FILE); + + ksni::Icon { + width: width as i32, + height: height as i32, + data, + } + }); + + vec![ICON.clone()] + } + + fn menu(&self) -> Vec> { + let sender_show = self.sender.clone(); + let sender_hide = self.sender.clone(); + let sender_quit = self.sender.clone(); + + vec![ + StandardItem { + label: gettext("Show"), + visible: !self.window_visible, + activate: Box::new(move |_| { + sender_show.send(TrayEvent::Show).ok(); + }), + ..Default::default() + } + .into(), + StandardItem { + label: gettext("Hide"), + visible: self.window_visible, + activate: Box::new(move |_| { + sender_hide.send(TrayEvent::Hide).ok(); + }), + ..Default::default() + } + .into(), + StandardItem { + label: gettext("Quit"), + activate: Box::new(move |_| { + sender_quit.send(TrayEvent::Quit).ok(); + }), + ..Default::default() + } + .into(), + ] + } +} + +fn load_image(buffer: &[u8]) -> (Vec, u32, u32) { + let image = image::load_from_memory(buffer) + .expect("Failed to open icon path") + .into_rgba8(); + + let (width, height) = image.dimensions(); + let mut data = image.into_raw(); + + for pixel in data.chunks_exact_mut(4) { + pixel.rotate_right(1) // rgba to argb + } + + (data, width, height) +} diff --git a/src/app/tray/mod.rs b/src/app/tray/mod.rs new file mode 100644 index 0000000..1dbcbaa --- /dev/null +++ b/src/app/tray/mod.rs @@ -0,0 +1,51 @@ +mod config; +mod imp; + +use adw::subclass::prelude::ObjectSubclassIsExt; +use gtk::glib::{self, closure_local, object::ObjectExt}; + +glib::wrapper! { + pub struct Tray(ObjectSubclass); +} + +impl Default for Tray { + fn default() -> Self { + glib::Object::builder().build() + } +} + +impl Tray { + pub fn update(&self, state: bool) { + self.imp().update(state); + } + + pub fn connect_show(&self, callback: T) { + self.connect_closure( + "show", + false, + closure_local!(move |_: Tray| { + callback(); + }), + ); + } + + pub fn connect_hide(&self, callback: T) { + self.connect_closure( + "hide", + false, + closure_local!(move |_: Tray| { + callback(); + }), + ); + } + + pub fn connect_quit(&self, callback: T) { + self.connect_closure( + "quit", + false, + closure_local!(move |_: Tray| { + callback(); + }), + ); + } +} diff --git a/src/app/utils.rs b/src/app/utils.rs deleted file mode 100644 index efd51cf..0000000 --- a/src/app/utils.rs +++ /dev/null @@ -1,64 +0,0 @@ -use glutin::{ - config::{Config, ConfigTemplateBuilder, GlConfig}, - context::{ContextApi, ContextAttributesBuilder, NotCurrentContext}, - display::GetGlDisplay, - prelude::GlDisplay, - surface::{Surface, WindowSurface}, -}; -use glutin_winit::{DisplayBuilder, GlWindow}; -use winit::{ - event_loop::ActiveEventLoop, - window::{Window, WindowAttributes}, -}; - -fn config_picker(configs: Box + '_>) -> Config { - configs - .reduce(|accum, config| { - if config.num_samples() > accum.num_samples() { - config - } else { - accum - } - }) - .expect("Failed to pick a config") -} - -pub fn create_window( - event_loop: &ActiveEventLoop, - window_attributes: WindowAttributes, -) -> (Option, Config) { - let template_builder = ConfigTemplateBuilder::new(); - - DisplayBuilder::new() - .with_window_attributes(Some(window_attributes)) - .build(event_loop, template_builder, config_picker) - .expect("Failed to build display") -} - -pub fn create_surface(config: &Config, window: &Option) -> Surface { - let surface_attributes = window - .as_ref() - .expect("Failed to get window") - .build_surface_attributes(Default::default()) - .expect("Failed to build surface attributes"); - - unsafe { - config - .display() - .create_window_surface(config, &surface_attributes) - .expect("Failed to create surface") - } -} - -pub fn create_context(config: &Config, api: ContextApi) -> NotCurrentContext { - let context_attributes = ContextAttributesBuilder::new() - .with_context_api(api) - .build(None); - - unsafe { - config - .display() - .create_context(config, &context_attributes) - .expect("Failed to create shared context") - } -} diff --git a/src/player/constants.rs b/src/app/video/config.rs similarity index 83% rename from src/player/constants.rs rename to src/app/video/config.rs index 3226c94..d793e5e 100644 --- a/src/player/constants.rs +++ b/src/app/video/config.rs @@ -6,6 +6,7 @@ pub const FLOAT_PROPERTIES: &[&str] = &[ "sub-pos", "sub-scale", "sub-delay", + "cache-buffering-state", ]; pub const BOOL_PROPERTIES: &[&str] = &[ @@ -15,6 +16,8 @@ pub const BOOL_PROPERTIES: &[&str] = &[ "osc", "input-default-bindings", "input-vo-keyboard", + "eof-reached", + "paused-for-cache", ]; pub const STRING_PROPERTIES: &[&str] = &[ @@ -30,4 +33,6 @@ pub const STRING_PROPERTIES: &[&str] = &[ "sid", "aid", "mute", + "metadata", + "video-params", ]; diff --git a/src/app/video/imp.rs b/src/app/video/imp.rs new file mode 100644 index 0000000..6e86f31 --- /dev/null +++ b/src/app/video/imp.rs @@ -0,0 +1,253 @@ +use gtk::{ + gdk::GLContext, + glib::{self, Propagation, Properties, Variant, clone, subclass::Signal}, + prelude::*, + subclass::prelude::*, +}; +use libc::{LC_NUMERIC, setlocale}; +use libmpv2::{ + Format, Mpv, SetData, + events::{Event, PropertyData}, + render::{OpenGLInitParams, RenderContext, RenderParam, RenderParamApiType}, +}; +use std::{ + cell::{Cell, RefCell}, + env, + os::raw::c_void, + sync::{OnceLock, mpsc::channel}, +}; +use tracing::error; + +fn get_proc_address(_context: &GLContext, name: &str) -> *mut c_void { + epoxy::get_proc_addr(name) as _ +} + +#[derive(Properties)] +#[properties(wrapper_type = super::Video)] +pub struct Video { + #[property(get, set)] + scale_factor: Cell, + mpv: RefCell, + render_context: RefCell>, + fbo: Cell, +} + +impl Default for Video { + fn default() -> Self { + // Required for libmpv to work alongside gtk + unsafe { + setlocale(LC_NUMERIC, c"C".as_ptr()); + } + + let log = env::var("RUST_LOG"); + let msg_level = match log { + Ok(scope) => &format!("all={}", scope.as_str()), + _ => "all=no", + }; + + let mpv = Mpv::with_initializer(|init| { + init.set_property("vo", "libmpv")?; + init.set_property("video-timing-offset", "0")?; + init.set_property("terminal", "yes")?; + init.set_property("msg-level", msg_level)?; + Ok(()) + }) + .expect("Failed to create mpv"); + + mpv.disable_deprecated_events().ok(); + + Self { + scale_factor: Cell::new(1), + mpv: RefCell::new(mpv), + render_context: Default::default(), + fbo: Default::default(), + } + } +} + +impl Video { + fn fbo(&self) -> i32 { + let mut fbo = self.fbo.get(); + + if fbo == 0 { + let mut current_fbo = 0; + + unsafe { + epoxy::GetIntegerv(epoxy::FRAMEBUFFER_BINDING, &mut current_fbo); + } + + fbo = current_fbo as u32; + self.fbo.set(fbo); + } + + fbo as i32 + } + + fn on_event(&self, callback: T) { + if let Some(result) = self.mpv.borrow_mut().wait_event(0.0) { + match result { + Ok(event) => callback(event), + Err(e) => error!("Failed to wait for event: {e}"), + } + }; + } + + pub fn send_command(&self, name: &str, args: &[&str]) { + if let Err(e) = self.mpv.borrow().command(name, args) { + error!("Failed to send command {name}: {e}"); + } + } + + pub fn observe_property(&self, name: &str, format: Format) { + if let Err(e) = self.mpv.borrow().observe_property(name, format, 0) { + error!("Failed to observe property {name}: {e}"); + } + } + + pub fn set_property(&self, name: &str, value: T) { + if let Err(e) = self.mpv.borrow().set_property(name, value) { + error!("Failed to set property {name}: {e}"); + } + } +} + +#[glib::object_subclass] +impl ObjectSubclass for Video { + const NAME: &'static str = "Video"; + type Type = super::Video; + type ParentType = gtk::GLArea; +} + +#[glib::derived_properties] +impl ObjectImpl for Video { + fn signals() -> &'static [Signal] { + static SIGNALS: OnceLock> = OnceLock::new(); + SIGNALS.get_or_init(|| { + vec![ + Signal::builder("property-changed") + .param_types([str::static_type(), Variant::static_type()]) + .build(), + Signal::builder("playback-started").build(), + Signal::builder("playback-ended").build(), + ] + }) + } + + fn constructed(&self) { + self.parent_constructed(); + + glib::idle_add_local(clone!( + #[weak(rename_to = video)] + self, + #[weak(rename_to = object)] + self.obj(), + #[upgrade_or] + glib::ControlFlow::Continue, + move || { + video.on_event(|event| match event { + Event::PropertyChange { name, change, .. } => { + let value = match change { + PropertyData::Str(v) => Some(v.to_variant()), + PropertyData::Flag(v) => Some(v.to_variant()), + PropertyData::Double(v) => Some(v.to_variant()), + _ => None, + }; + + if let Some(value) = value { + object.emit_by_name::<()>("property-changed", &[&name, &value]); + } + } + Event::StartFile => { + object.emit_by_name::<()>("playback-started", &[]); + } + Event::EndFile(_) => { + object.emit_by_name::<()>("playback-ended", &[]); + } + _ => {} + }); + + glib::ControlFlow::Continue + } + )); + } +} + +impl WidgetImpl for Video { + fn realize(&self) { + self.parent_realize(); + + let object = self.obj(); + object.make_current(); + + if object.error().is_some() { + return; + } + + if let Some(context) = object.context() { + let mut mpv = self.mpv.borrow_mut(); + let mpv_handle = unsafe { mpv.ctx.as_mut() }; + + let mut render_context = RenderContext::new( + mpv_handle, + vec![ + RenderParam::ApiType(RenderParamApiType::OpenGl), + RenderParam::InitParams(OpenGLInitParams { + get_proc_address, + ctx: context, + }), + RenderParam::BlockForTargetTime(false), + ], + ) + .expect("Failed to create render context"); + + let (sender, receiver) = channel::<()>(); + + glib::idle_add_local(clone!( + #[weak] + object, + #[upgrade_or] + glib::ControlFlow::Continue, + move || { + if let Ok(()) = receiver.try_recv() { + object.queue_render(); + } + + glib::ControlFlow::Continue + } + )); + + render_context.set_update_callback(move || { + sender.send(()).ok(); + }); + + *self.render_context.borrow_mut() = Some(render_context); + } + } + + fn unrealize(&self) { + if let Some(render_context) = self.render_context.borrow_mut().take() { + drop(render_context); + } + + self.parent_unrealize(); + } +} + +impl GLAreaImpl for Video { + fn render(&self, _context: &GLContext) -> Propagation { + let object = self.obj(); + + let fbo = self.fbo(); + let scale_factor = self.scale_factor.get(); + let width = object.width(); + let height = object.height(); + + if let Some(ref render_context) = *self.render_context.borrow() { + render_context + .render::(fbo, width * scale_factor, height * scale_factor, true) + .expect("Failed to render"); + } + + Propagation::Stop + } +} diff --git a/src/app/video/mod.rs b/src/app/video/mod.rs new file mode 100644 index 0000000..1c491d2 --- /dev/null +++ b/src/app/video/mod.rs @@ -0,0 +1,126 @@ +mod config; +mod imp; + +use adw::subclass::prelude::ObjectSubclassIsExt; +use gtk::glib::{self, Variant, closure_local, object::ObjectExt}; +use itertools::Itertools; +use libmpv2::Format; +use serde_json::{Number, Value}; +use tracing::error; + +use crate::app::video::config::{BOOL_PROPERTIES, FLOAT_PROPERTIES, STRING_PROPERTIES}; + +glib::wrapper! { + pub struct Video(ObjectSubclass) + @extends gtk::GLArea, gtk::Widget, + @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget; +} + +impl Default for Video { + fn default() -> Self { + glib::Object::builder() + .property("hexpand", true) + .property("vexpand", true) + .build() + } +} + +impl Video { + pub fn connect_mpv_property_change(&self, callback: T) { + self.connect_closure( + "property-changed", + false, + closure_local!(move |_: Video, name: &str, value: Variant| { + match name { + name if FLOAT_PROPERTIES.contains(&name) => { + if let Some(value) = value.get::() { + callback(name, Value::Number(Number::from_f64(value).unwrap())) + } + } + name if BOOL_PROPERTIES.contains(&name) => { + if let Some(value) = value.get::() { + callback(name, Value::Bool(value)) + } + } + name if STRING_PROPERTIES.contains(&name) => { + if let Some(value) = value.get::() { + if let Ok(json_value) = serde_json::from_str::(&value) { + callback(name, json_value) + } else { + callback(name, Value::String(value)) + } + } + } + _ => {} + }; + }), + ); + } + + pub fn connect_playback_started(&self, callback: T) { + self.connect_closure( + "playback-started", + false, + closure_local!(move |_: Video| { + callback(); + }), + ); + } + + pub fn connect_playback_ended(&self, callback: T) { + self.connect_closure( + "playback-ended", + false, + closure_local!(move |_: Video| { + callback(); + }), + ); + } + + pub fn send_command(&self, name: String, args: Vec) { + let widget = self.imp(); + + let args = args.iter().map(String::as_ref).collect_vec(); + widget.send_command(&name, &args); + } + + pub fn observe_mpv_property(&self, name: String) { + let widget = self.imp(); + + match name.as_str() { + name if FLOAT_PROPERTIES.contains(&name) => { + widget.observe_property(name, Format::Double); + } + name if BOOL_PROPERTIES.contains(&name) => { + widget.observe_property(name, Format::Flag); + } + name if STRING_PROPERTIES.contains(&name) => { + widget.observe_property(name, Format::String); + } + _ => error!("Failed to observe property {name}: Unsupported"), + }; + } + + pub fn set_mpv_property(&self, name: String, value: Value) { + let widget = self.imp(); + + match name.as_str() { + name if FLOAT_PROPERTIES.contains(&name) => { + if let Some(value) = value.as_f64() { + widget.set_property(name, value); + } + } + name if BOOL_PROPERTIES.contains(&name) => { + if let Some(value) = value.as_bool() { + widget.set_property(name, value); + } + } + name if STRING_PROPERTIES.contains(&name) => { + if let Some(value) = value.as_str() { + widget.set_property(name, value); + } + } + name => error!("Failed to set property {name}: Unsupported"), + }; + } +} diff --git a/src/app/webview/gl.rs b/src/app/webview/gl.rs new file mode 100644 index 0000000..a2e9500 --- /dev/null +++ b/src/app/webview/gl.rs @@ -0,0 +1,199 @@ +use std::{ffi::CString, mem, ptr}; + +use epoxy::{types::*, *}; + +pub fn create_geometry(program: u32) -> (GLuint, GLuint) { + unsafe { + let vertices: [f32; 16] = [ + -1.0, -1.0, 0.0, 0.0, 1.0, -1.0, 1.0, 0.0, -1.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, + ]; + + let mut vbo = 0; + GenBuffers(1, &mut vbo); + BindBuffer(ARRAY_BUFFER, vbo); + + BufferData( + ARRAY_BUFFER, + (vertices.len() * mem::size_of::()) as GLsizeiptr, + vertices.as_ptr() as _, + STATIC_DRAW, + ); + + let mut vao = 0; + GenVertexArrays(1, &mut vao); + BindVertexArray(vao); + + let pos_attrib = GetAttribLocation(program, c"position".as_ptr() as _); + EnableVertexAttribArray(pos_attrib as GLuint); + VertexAttribPointer( + pos_attrib as GLuint, + 2, + FLOAT, + FALSE, + (4 * mem::size_of::()) as GLsizei, + ptr::null(), + ); + + let tex_attrib = GetAttribLocation(program, c"texcoord".as_ptr() as _); + EnableVertexAttribArray(tex_attrib as GLuint); + VertexAttribPointer( + tex_attrib as GLuint, + 2, + FLOAT, + FALSE, + (4 * mem::size_of::()) as GLsizei, + (2 * mem::size_of::()) as _, + ); + + (vao, vbo) + } +} + +pub fn compile_shader(kind: GLenum, src: &str) -> GLuint { + unsafe { + let shader = CreateShader(kind); + let c_str = CString::new(src.as_bytes()).unwrap(); + ShaderSource(shader, 1, &c_str.as_ptr(), ptr::null()); + CompileShader(shader); + + let mut success = 0; + GetShaderiv(shader, COMPILE_STATUS, &mut success); + if success == 0 { + let mut len = 0; + GetShaderiv(shader, INFO_LOG_LENGTH, &mut len); + + let mut buffer = vec![0u8; len as usize]; + GetShaderInfoLog(shader, len, ptr::null_mut(), buffer.as_mut_ptr() as *mut i8); + + panic!("Shader compile error: {}", str::from_utf8(&buffer).unwrap()); + } + + shader + } +} + +pub fn compile_vertex_shader(src: &str) -> GLuint { + compile_shader(VERTEX_SHADER, src) +} + +pub fn compile_fragment_shader(src: &str) -> GLuint { + compile_shader(FRAGMENT_SHADER, src) +} + +pub fn create_program(vertex_shader: GLuint, fragment_shader: GLuint) -> GLuint { + unsafe { + let program = CreateProgram(); + + AttachShader(program, vertex_shader); + AttachShader(program, fragment_shader); + + LinkProgram(program); + UseProgram(program); + + DeleteShader(vertex_shader); + DeleteShader(fragment_shader); + + program + } +} + +pub fn create_texture(program: GLuint, uniform_name: &str) -> (GLuint, GLint) { + unsafe { + let mut texture = 0; + GenTextures(1, &mut texture); + BindTexture(TEXTURE_2D, texture); + + TexImage2D( + TEXTURE_2D, + 0, + BGRA as GLint, + 1, + 1, + 0, + BGRA, + UNSIGNED_BYTE, + ptr::null(), + ); + + TexParameteri(TEXTURE_2D, TEXTURE_MAX_LEVEL, 0); + TexParameteri(TEXTURE_2D, TEXTURE_MIN_FILTER, LINEAR as GLint); + TexParameteri(TEXTURE_2D, TEXTURE_MAG_FILTER, LINEAR as GLint); + TexParameteri(TEXTURE_2D, TEXTURE_WRAP_S, CLAMP_TO_EDGE as GLint); + TexParameteri(TEXTURE_2D, TEXTURE_WRAP_T, CLAMP_TO_EDGE as GLint); + + let name = CString::new(uniform_name).unwrap(); + let texture_uniform = GetUniformLocation(program, name.as_ptr() as _); + + (texture, texture_uniform) + } +} + +pub fn resize_texture(texture: GLuint, width: i32, height: i32) { + unsafe { + BindTexture(TEXTURE_2D, texture); + TexImage2D( + TEXTURE_2D, + 0, + BGRA as GLint, + width, + height, + 0, + BGRA, + UNSIGNED_BYTE, + ptr::null(), + ); + + BindTexture(TEXTURE_2D, 0); + } +} + +pub fn update_texture( + texture: GLuint, + x: GLint, + y: GLint, + width: GLint, + height: GLint, + stride: GLint, + buffer: &[u8], +) { + unsafe { + BindTexture(TEXTURE_2D, texture); + PixelStorei(UNPACK_ROW_LENGTH, stride); + + let offset = ((y * stride + x) * 4) as usize; + let pixels = buffer.as_ptr().add(offset); + + TexSubImage2D( + TEXTURE_2D, + 0, + x, + y, + width, + height, + BGRA, + UNSIGNED_BYTE, + pixels as _, + ); + + PixelStorei(UNPACK_ROW_LENGTH, 0); + BindTexture(TEXTURE_2D, 0); + } +} + +pub fn resize_viewport(width: i32, height: i32) { + unsafe { + epoxy::Viewport(0, 0, width, height); + } +} + +pub fn draw_texture(program: GLuint, texture: GLuint, texture_uniform: GLint, vao: GLuint) { + unsafe { + epoxy::UseProgram(program); + epoxy::ActiveTexture(epoxy::TEXTURE0); + epoxy::BindTexture(epoxy::TEXTURE_2D, texture); + epoxy::Uniform1i(texture_uniform, 0); + + epoxy::BindVertexArray(vao); + epoxy::DrawArrays(epoxy::TRIANGLE_STRIP, 0, 4); + } +} diff --git a/src/app/webview/imp.rs b/src/app/webview/imp.rs new file mode 100644 index 0000000..b9f4be4 --- /dev/null +++ b/src/app/webview/imp.rs @@ -0,0 +1,142 @@ +use std::{cell::Cell, rc::Rc}; + +use adw::subclass::prelude::*; +use crossbeam_queue::SegQueue; +use epoxy::types::{GLint, GLuint}; +use gtk::{ + DropTarget, + gdk::{DragAction, FileList, GLContext}, + glib::{self, ControlFlow, Propagation, Properties}, + prelude::*, +}; + +use crate::{ + app::webview::gl, + shared::{ + Frame, + states::{KeyboardState, PointerState}, + }, +}; + +pub const FRAGMENT_SRC: &str = include_str!("shader.frag"); +pub const VERTEX_SRC: &str = include_str!("shader.vert"); +pub const UPDATES_PER_RENDER: i32 = 8; + +#[derive(Default, Properties)] +#[properties(wrapper_type = super::WebView)] +pub struct WebView { + #[property(get, set)] + scale_factor: Cell, + program: Cell, + vao: Cell, + vbo: Cell, + texture: Cell, + texture_uniform: Cell, + pub pointer_state: Rc, + pub keyboard_state: Rc, + pub frames: Box>, +} + +#[glib::object_subclass] +impl ObjectSubclass for WebView { + const NAME: &'static str = "WebView"; + type Type = super::WebView; + type ParentType = gtk::GLArea; +} + +#[glib::derived_properties] +impl ObjectImpl for WebView { + fn constructed(&self) { + self.parent_constructed(); + + let drop_target = DropTarget::new(FileList::static_type(), DragAction::COPY); + self.obj().add_controller(drop_target); + } +} + +impl WidgetImpl for WebView { + fn realize(&self) { + self.parent_realize(); + + let gl_area = self.obj(); + gl_area.make_current(); + + if gl_area.error().is_some() { + return; + } + + let vertex_shader = gl::compile_vertex_shader(VERTEX_SRC); + let fragment_shader = gl::compile_fragment_shader(FRAGMENT_SRC); + let program = gl::create_program(vertex_shader, fragment_shader); + let (vao, vbo) = gl::create_geometry(program); + let (texture, texture_uniform) = gl::create_texture(program, "text_uniform"); + + self.program.set(program); + self.vao.set(vao); + self.vbo.set(vbo); + self.texture.set(texture); + self.texture_uniform.set(texture_uniform); + + self.obj().add_tick_callback(|webview, _| { + if !webview.imp().frames.is_empty() { + webview.queue_render(); + } + + ControlFlow::Continue + }); + } + + fn unrealize(&self) { + unsafe { + epoxy::DeleteProgram(self.program.get()); + epoxy::DeleteTextures(1, &self.texture.get()); + epoxy::DeleteBuffers(1, &self.vbo.get()); + epoxy::DeleteVertexArrays(1, &self.vao.get()); + } + + self.program.take(); + self.vao.take(); + self.vbo.take(); + self.texture.take(); + self.texture_uniform.take(); + + self.parent_unrealize(); + } +} + +impl GLAreaImpl for WebView { + fn render(&self, _: &GLContext) -> Propagation { + let scale_factor = self.scale_factor.get(); + + for _ in 0..UPDATES_PER_RENDER { + if let Some(frame) = self.frames.pop() { + gl::resize_texture(self.texture.get(), frame.full_width, frame.full_height); + gl::update_texture( + self.texture.get(), + frame.x, + frame.y, + frame.width, + frame.height, + frame.full_width, + &frame.buffer, + ); + + gl::resize_viewport( + frame.full_width * scale_factor, + frame.full_height * scale_factor, + ); + + gl::draw_texture( + self.program.get(), + self.texture.get(), + self.texture_uniform.get(), + self.vao.get(), + ); + } else { + break; + } + } + + Propagation::Proceed + } +} diff --git a/src/app/webview/mod.rs b/src/app/webview/mod.rs new file mode 100644 index 0000000..3594757 --- /dev/null +++ b/src/app/webview/mod.rs @@ -0,0 +1,266 @@ +mod gl; +mod imp; + +use std::{path::PathBuf, rc::Rc}; + +use adw::subclass::prelude::ObjectSubclassIsExt; +use gtk::{ + DropTarget, EventControllerKey, EventControllerMotion, EventControllerScroll, + EventControllerScrollFlags, GestureClick, + gdk::{Display, DragAction, FileList, Key, ModifierType, ScrollUnit, prelude::DisplayExt}, + gio::{Cancellable, prelude::FileExt}, + glib::{ + self, Object, Priority, Propagation, + object::{Cast, IsA}, + types::StaticType, + }, + prelude::*, +}; + +use crate::shared::{ + Frame, + states::{KeyboardState, PointerState}, +}; + +glib::wrapper! { + pub struct WebView(ObjectSubclass) + @extends gtk::GLArea, gtk::Widget, + @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget; +} + +impl Default for WebView { + fn default() -> Self { + glib::Object::builder() + .property("hexpand", true) + .property("vexpand", true) + .property("focusable", true) + .property("can-focus", true) + .build() + } +} + +impl WebView { + pub fn render(&self, frame: Frame) { + self.imp().frames.push(frame); + } + + pub fn connect_resized(&self, callback: T) { + self.connect_resize(move |_, width, height| { + callback(width, height); + }); + } + + pub fn connect_motion) + 'static>(&self, callback: T) { + let callback = Rc::new(callback); + + let event_controller_motion = EventControllerMotion::new(); + + let motion_callback = callback.clone(); + let pointer_state = self.imp().pointer_state.clone(); + event_controller_motion.connect_motion(move |_, x, y| { + pointer_state.set_over(true); + pointer_state.set_position(x, y); + + motion_callback(pointer_state.clone()); + }); + + let leave_callback = callback.clone(); + let pointer_state = self.imp().pointer_state.clone(); + event_controller_motion.connect_leave(move |_| { + pointer_state.set_over(false); + + leave_callback(pointer_state.clone()) + }); + + self.add_controller(event_controller_motion); + } + + pub fn connect_scroll, f64, f64) + 'static>(&self, callback: T) { + let pointer_state = self.imp().pointer_state.clone(); + + let flags = EventControllerScrollFlags::BOTH_AXES | EventControllerScrollFlags::KINETIC; + let event_controller_motion = EventControllerScroll::new(flags); + + event_controller_motion.connect_scroll(move |controller, delta_x, delta_y| { + match controller.unit() { + ScrollUnit::Wheel => { + callback(pointer_state.clone(), delta_x * -100.0, delta_y * -100.0); + } + ScrollUnit::Surface => { + callback(pointer_state.clone(), delta_x - 2.0, delta_y - 2.0); + } + _ => {} + } + + Propagation::Proceed + }); + + self.add_controller(event_controller_motion); + } + + pub fn connect_click, i32) + 'static>(&self, callback: T) { + let callback = Rc::new(callback); + let gesture_click = GestureClick::builder().button(0).build(); + + let pressed_callback = callback.clone(); + let pressed_pointer_state = self.imp().pointer_state.clone(); + + gesture_click.connect_pressed(move |gesture, count, x, y| { + pressed_pointer_state.set_position(x, y); + pressed_pointer_state.set_pressed(true); + pressed_pointer_state.set_button(gesture.current_button()); + + pressed_callback(pressed_pointer_state.clone(), count); + }); + + let released_callback = callback.clone(); + let released_pointer_state = self.imp().pointer_state.clone(); + + gesture_click.connect_released(move |gesture, count, x, y| { + released_pointer_state.set_position(x, y); + released_pointer_state.set_pressed(false); + released_pointer_state.set_button(gesture.current_button()); + + released_callback(released_pointer_state.clone(), count); + }); + + self.add_controller(gesture_click); + } + + pub fn connect_keys) + 'static>(&self, callback: T) { + let callback = Rc::new(callback); + let event_controller_key = EventControllerKey::new(); + + let pressed_callback = callback.clone(); + let kayboard_state_pressed = self.imp().keyboard_state.clone(); + event_controller_key.connect_key_pressed(move |_, key, code, modifiers| { + let character = key.to_unicode(); + kayboard_state_pressed.set_character(character); + kayboard_state_pressed.set_pressed(true); + kayboard_state_pressed.set_code(code); + kayboard_state_pressed.set_modifiers(modifiers); + + pressed_callback(kayboard_state_pressed.clone()); + + Propagation::Proceed + }); + + let released_callback = callback.clone(); + let kayboard_state_released = self.imp().keyboard_state.clone(); + event_controller_key.connect_key_released(move |_, key, code, modifiers| { + let character = key.to_unicode(); + kayboard_state_released.set_character(character); + kayboard_state_released.set_pressed(false); + kayboard_state_released.set_code(code); + kayboard_state_released.set_modifiers(modifiers); + + released_callback(kayboard_state_released.clone()); + }); + + self.add_controller(event_controller_key); + } + + pub fn connect_clipboard(&self, callback: T) { + let callback = Rc::new(callback); + let event_controller_key = EventControllerKey::new(); + + event_controller_key.connect_key_pressed(move |_, key, _, modifiers| { + let ctrl_modifier = modifiers.contains(ModifierType::CONTROL_MASK); + + if ctrl_modifier && key == Key::v { + if let Some(display) = Display::default() { + let clipboard = display.clipboard(); + + let callback = callback.clone(); + clipboard.read_text_async(None::<&Cancellable>, move |result| { + if let Ok(Some(text)) = result { + callback(text.to_string()); + } + }); + } + + return Propagation::Stop; + } + + Propagation::Proceed + }); + + self.add_controller(event_controller_key); + } + + pub fn connect_file_enter, PathBuf) + 'static>(&self, callback: F) { + if let Some(drop_target) = self.controller::() { + let callback = Rc::new(callback); + let pointer_state = self.imp().pointer_state.clone(); + + let enter_callback = callback.clone(); + drop_target.connect_enter(move |target, _, _| { + if let Some(drop) = target.current_drop() { + let pointer_state = pointer_state.clone(); + let enter_callback = enter_callback.clone(); + + drop.read_value_async( + FileList::static_type(), + Priority::DEFAULT, + None::<&Cancellable>, + move |result| { + if let Ok(value) = result + && let Ok(file_list) = value.get::() + { + for file in file_list.files() { + if let Some(path) = file.path() { + enter_callback(pointer_state.clone(), path); + } + } + } + }, + ); + } + + DragAction::COPY + }); + } + } + + pub fn connect_file_leave(&self, callback: F) { + if let Some(drop_target) = self.controller::() { + drop_target.connect_leave(move |_| { + callback(); + }); + } + } + + pub fn connect_file_motion) + 'static>(&self, callback: F) { + if let Some(drop_target) = self.controller::() { + let pointer_state = self.imp().pointer_state.clone(); + + drop_target.connect_motion(move |_, _, _| { + callback(pointer_state.clone()); + DragAction::COPY + }); + } + } + + pub fn connect_file_drop) + 'static>(&self, callback: F) { + if let Some(drop_target) = self.controller::() { + let pointer_state = self.imp().pointer_state.clone(); + + drop_target.connect_drop(move |_, _, _, _| { + callback(pointer_state.clone()); + true + }); + } + } + + fn controller>(&self) -> Option { + for controller in &self.observe_controllers() { + if let Ok(controller) = controller + && let Ok(contoller) = controller.downcast::() + { + return Some(contoller); + } + } + + None + } +} diff --git a/src/app/webview/shader.frag b/src/app/webview/shader.frag new file mode 100644 index 0000000..bf5808f --- /dev/null +++ b/src/app/webview/shader.frag @@ -0,0 +1,10 @@ +#version 300 es +precision highp float; + +uniform sampler2D text_uniform; +in vec2 v_texcoord; +out vec4 frag_color; + +void main() { + frag_color = texture(text_uniform, vec2(v_texcoord.x, 1.0 - v_texcoord.y)); +} diff --git a/src/shared/renderer/shader.vert b/src/app/webview/shader.vert similarity index 91% rename from src/shared/renderer/shader.vert rename to src/app/webview/shader.vert index 8bf82ea..2dd4b6b 100644 --- a/src/shared/renderer/shader.vert +++ b/src/app/webview/shader.vert @@ -1,4 +1,4 @@ -#version 330 core +#version 300 es layout(location = 0) in vec2 position; layout(location = 1) in vec2 texcoord; diff --git a/src/app/window/imp.rs b/src/app/window/imp.rs new file mode 100644 index 0000000..8b98726 --- /dev/null +++ b/src/app/window/imp.rs @@ -0,0 +1,181 @@ +use std::{cell::Cell, sync::Arc}; + +use adw::prelude::*; +use adw::subclass::prelude::*; +use ashpd::{ + WindowIdentifier, + desktop::{ + Request, + background::Background, + inhibit::{InhibitFlags, InhibitProxy}, + open_uri::OpenFileRequest, + }, + enumflags2::BitFlags, +}; +use gtk::{ + glib::{self, clone, subclass::InitializingObject}, + prelude::WidgetExt, +}; +use tokio::sync::Mutex; +use tracing::error; +use url::Url; + +use crate::spawn_local; + +#[derive(Default, glib::Properties, gtk::CompositeTemplate)] +#[properties(wrapper_type = super::Window)] +#[template(file = "window.ui")] +pub struct Window { + #[property(get, set)] + decorations: Cell, + #[template_child] + header: TemplateChild, + #[template_child] + pub overlay: TemplateChild, + pub inhibit_request: Arc>>>, +} + +impl Window { + pub fn request_backgound(&self) { + let object = self.obj(); + + spawn_local!(clone!( + #[weak] + object, + async move { + if let Some(identifier) = WindowIdentifier::from_native(&object).await { + let request = Background::request().identifier(identifier); + request + .send() + .await + .map_err(|e| error!("Failed to set background mode: {e}")) + .ok(); + } + } + )); + } + + pub fn disable_idling(&self) { + let object = self.obj(); + let inhibit_request = self.inhibit_request.clone(); + + spawn_local!(clone!( + #[weak] + object, + async move { + if let Some(identifier) = WindowIdentifier::from_native(&object).await + && let Ok(proxy) = InhibitProxy::new().await + { + let mut flags = BitFlags::empty(); + flags.insert(InhibitFlags::Idle); + + let reason = "Prevent screen from going blank during media playback"; + + let mut inhibit_request = inhibit_request.lock().await; + *inhibit_request = proxy + .inhibit(Some(&identifier), flags, reason) + .await + .map_err(|e| error!("Failed to prevent idling: {e}")) + .ok(); + } + } + )); + } + + pub fn enable_idling(&self) { + let inhibit_request = self.inhibit_request.clone(); + + spawn_local!(async move { + let mut inhibit_request = inhibit_request.lock().await; + if let Some(request) = inhibit_request.take() { + request + .close() + .await + .map_err(|e| error!("Failed to allow idling: {e}")) + .ok(); + } + }); + } + + pub fn open_uri(&self, uri: Url) { + let object = self.obj(); + + spawn_local!(clone!( + #[weak] + object, + async move { + if let Some(identifier) = WindowIdentifier::from_native(&object).await { + let request = OpenFileRequest::default().identifier(identifier); + + request + .send_uri(&uri) + .await + .map_err(|e| error!("Failed to open uri: {e}")) + .ok(); + } + } + )); + } + + pub fn show_header(&self, state: bool) { + self.header.set_visible(self.decorations.get() && state); + } +} + +#[glib::object_subclass] +impl ObjectSubclass for Window { + const NAME: &'static str = "Window"; + type Type = super::Window; + type ParentType = adw::ApplicationWindow; + + fn class_init(klass: &mut Self::Class) { + klass.bind_template(); + } + + fn instance_init(obj: &InitializingObject) { + obj.init_template(); + } +} + +#[glib::derived_properties] +impl ObjectImpl for Window { + fn constructed(&self) { + self.parent_constructed(); + + if cfg!(debug_assertions) { + self.obj().add_css_class("devel"); + } + } +} + +impl WidgetImpl for Window { + fn realize(&self) { + self.parent_realize(); + + if !self.decorations.get() { + self.show_header(false); + self.obj().remove_css_class("csd"); + } + } +} + +impl WindowImpl for Window { + fn activate_default(&self) { + self.parent_activate_default(); + + let widget = self.obj(); + widget.request_backgound(); + } + + fn close_request(&self) -> glib::Propagation { + self.parent_close_request(); + + let widget = self.obj(); + widget.set_visible(false); + + glib::Propagation::Stop + } +} + +impl ApplicationWindowImpl for Window {} +impl AdwApplicationWindowImpl for Window {} diff --git a/src/app/window/mod.rs b/src/app/window/mod.rs new file mode 100644 index 0000000..0cdcb8e --- /dev/null +++ b/src/app/window/mod.rs @@ -0,0 +1,90 @@ +mod imp; + +use adw::subclass::prelude::*; +use gtk::{ + Widget, + gdk::prelude::{DisplayExt, MonitorExt}, + gio, + glib::{self, object::IsA}, + prelude::{GtkWindowExt, NativeExt, WidgetExt}, +}; +use url::Url; + +use crate::app::Application; + +glib::wrapper! { + pub struct Window(ObjectSubclass) + @extends gtk::Window, gtk::ApplicationWindow, adw::ApplicationWindow, gtk::Widget, + @implements gio::ActionGroup, gio::ActionMap, gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget, gtk::ShortcutManager, gtk::Native, gtk::Root; +} + +impl Window { + pub fn new(application: &Application) -> Self { + glib::Object::builder() + .property("application", application) + .build() + } + + pub fn set_underlay(&self, widget: &impl IsA) { + let window = self.imp(); + + window.overlay.set_child(Some(&graphics_offload(widget))); + } + + pub fn set_overlay(&self, widget: &impl IsA) { + let window = self.imp(); + + window.overlay.add_overlay(&graphics_offload(widget)); + } + + pub fn set_fullscreen(&self, fullscreen: bool) { + self.imp().show_header(!fullscreen); + self.set_fullscreened(fullscreen); + } + + pub fn connect_monitor_info(&self, callback: F) { + self.connect_realize(move |window| { + let display = window.display(); + let surface = window.surface(); + + if let Some(surface) = surface + && let Some(monitor) = display.monitor_at_surface(&surface) + { + let refresh_rate = monitor.refresh_rate() as f64 / 1000.0; + let scale_factor = monitor.scale_factor(); + + callback(refresh_rate, scale_factor); + } + }); + } + + pub fn connect_visibility(&self, callback: T) { + self.connect_visible_notify(move |window| { + callback(window.is_visible()); + }); + } + + fn request_backgound(&self) { + self.imp().request_backgound(); + } + + pub fn disable_idling(&self) { + self.imp().disable_idling(); + } + + pub fn enable_idling(&self) { + self.imp().enable_idling(); + } + + pub fn open_uri(&self, uri: Url) { + self.imp().open_uri(uri); + } +} + +fn graphics_offload(widget: &impl IsA) -> gtk::GraphicsOffload { + gtk::GraphicsOffload::builder() + .vexpand(true) + .hexpand(true) + .child(widget) + .build() +} diff --git a/src/app/window/window.ui b/src/app/window/window.ui new file mode 100644 index 0000000..778be0e --- /dev/null +++ b/src/app/window/window.ui @@ -0,0 +1,47 @@ + + + + + + +
+ + _About Stremio + app.show-about + +
+
+ + +
\ No newline at end of file diff --git a/src/chromium/app/browser_process_handler.rs b/src/chromium/app/browser_process_handler.rs new file mode 100644 index 0000000..6267a3a --- /dev/null +++ b/src/chromium/app/browser_process_handler.rs @@ -0,0 +1,47 @@ +use std::sync::{Arc, Mutex, RwLock}; + +use cef::sys::cef_state_t::STATE_ENABLED; +use cef::{rc::*, *}; +use flume::Sender; + +use crate::chromium::{ChromiumEvent, app::client::ChromiumClient, types::Viewport}; + +wrap_browser_process_handler! { + pub struct ChromiumBrowserProcessHandler { + browser: Arc>>, + viewport: Arc>, + sender: Sender, + } + + impl BrowserProcessHandler { + fn on_context_initialized(&self) { + let mut client = ChromiumClient::new(self.viewport.clone(), self.sender.clone()); + let url = CefString::from("about:blank"); + + let window_info = WindowInfo { + windowless_rendering_enabled: true.into(), + shared_texture_enabled: false.into(), + ..Default::default() + }; + + let settings = BrowserSettings { + javascript_access_clipboard: STATE_ENABLED.into(), + javascript_dom_paste: STATE_ENABLED.into(), + ..Default::default() + }; + + let browser_result = browser_host_create_browser_sync( + Some(&window_info), + Some(&mut client), + Some(&url), + Some(&settings), + None, + None, + ); + + if let Ok(mut browser) = self.browser.lock() { + *browser = browser_result; + } + } + } +} diff --git a/src/chromium/app/client/display_handler.rs b/src/chromium/app/client/display_handler.rs new file mode 100644 index 0000000..f481435 --- /dev/null +++ b/src/chromium/app/client/display_handler.rs @@ -0,0 +1,22 @@ +use cef::{rc::*, *}; +use flume::Sender; + +use crate::chromium::ChromiumEvent; + +wrap_display_handler! { + pub struct ChromiumDisplayHandler { + sender: Sender, + } + + impl DisplayHandler { + fn on_fullscreen_mode_change( + &self, + _browser: Option<&mut Browser>, + fullscreen: i32, + ) { + let state = fullscreen == 1; + + self.sender.send(ChromiumEvent::Fullscreen(state)).ok(); + } + } +} diff --git a/src/chromium/app/client/lifespan_handler.rs b/src/chromium/app/client/lifespan_handler.rs new file mode 100644 index 0000000..a2e098b --- /dev/null +++ b/src/chromium/app/client/lifespan_handler.rs @@ -0,0 +1,40 @@ +use cef::{rc::*, *}; +use flume::Sender; +use url::Url; + +use crate::chromium::ChromiumEvent; + +wrap_life_span_handler! { + pub struct ChromiumLifeSpanHandler { + sender: Sender, + } + + impl LifeSpanHandler { + fn on_before_popup( + &self, + _browser: Option<&mut Browser>, + _frame: Option<&mut Frame>, + _popup_id: i32, + target_url: Option<&CefString>, + _target_frame_name: Option<&CefString>, + _target_disposition: WindowOpenDisposition, + _user_gesture: i32, + _popup_features: Option<&PopupFeatures>, + _window_info: Option<&mut WindowInfo>, + _client: Option<&mut Option>, + _settings: Option<&mut BrowserSettings>, + _extra_info: Option<&mut Option>, + _no_javascript_access: Option<&mut i32>, + ) -> i32 { + if let Some(target_url) = target_url { + let target_url = target_url.to_string(); + + if let Ok(url) = Url::parse(&target_url) { + self.sender.send(ChromiumEvent::Open(url)).ok(); + } + } + + true.into() + } + } +} diff --git a/src/webview/app/client/load_handler.rs b/src/chromium/app/client/load_handler.rs similarity index 64% rename from src/webview/app/client/load_handler.rs rename to src/chromium/app/client/load_handler.rs index 728c466..cc4eb48 100644 --- a/src/webview/app/client/load_handler.rs +++ b/src/chromium/app/client/load_handler.rs @@ -1,18 +1,17 @@ -use std::os::raw::c_int; +use cef::{rc::*, *}; +use flume::Sender; -use crate::{ - cef_impl, - webview::{ - SENDER, WebViewEvent, - constants::{IPC_RECEIVER, IPC_SENDER, PRELOAD_SCRIPT}, - }, +use crate::chromium::{ + ChromiumEvent, + config::{IPC_RECEIVER, IPC_SCRIPT, IPC_SENDER}, }; -cef_impl!( - prefix = "WebView", - name = LoadHandler, - sys_type = cef_dll_sys::cef_load_handler_t, - { +wrap_load_handler! { + pub struct ChromiumLoadHandler { + sender: Sender, + } + + impl LoadHandler { fn on_load_start( &self, _browser: Option<&mut Browser>, @@ -22,7 +21,7 @@ cef_impl!( if let Some(frame) = frame && frame.is_main() == 1 { - let script = PRELOAD_SCRIPT + let script = IPC_SCRIPT .replace("IPC_SENDER", IPC_SENDER) .replace("IPC_RECEIVER", IPC_RECEIVER); let code = CefString::from(script.as_str()); @@ -34,15 +33,14 @@ cef_impl!( &self, _browser: Option<&mut Browser>, frame: Option<&mut Frame>, - http_status_code: c_int, + http_status_code: i32, ) { if let Some(frame) = frame && frame.is_main() == 1 && http_status_code == 200 - && let Some(sender) = SENDER.get() { - sender.send(WebViewEvent::Loaded).ok(); + self.sender.send(ChromiumEvent::Loaded).ok(); } } } -); +} diff --git a/src/webview/app/client/mod.rs b/src/chromium/app/client/mod.rs similarity index 50% rename from src/webview/app/client/mod.rs rename to src/chromium/app/client/mod.rs index 503fe9f..e5978f7 100644 --- a/src/webview/app/client/mod.rs +++ b/src/chromium/app/client/mod.rs @@ -1,48 +1,53 @@ mod display_handler; -mod keyboard_handler; mod lifespan_handler; mod load_handler; +mod permission_handler; mod render_handler; -use std::os::raw::c_int; +use std::sync::{Arc, RwLock}; -use display_handler::WebViewDisplayHandler; -use lifespan_handler::WebViewLifeSpanHandler; -use load_handler::WebViewLoadHandler; -use render_handler::WebViewRenderHandler; +use cef::{rc::*, *}; +use flume::Sender; -use crate::{ - WebViewEvent, cef_impl, - webview::{ - SENDER, - app::client::keyboard_handler::WebViewKeyboardHandler, - constants::{IPC_MESSAGE, READY_MESSAGE}, +use crate::chromium::{ + ChromiumEvent, + app::client::{ + display_handler::ChromiumDisplayHandler, lifespan_handler::ChromiumLifeSpanHandler, + load_handler::ChromiumLoadHandler, permission_handler::ChromiumPermissionHandler, }, + config::{IPC_MESSAGE, READY_MESSAGE}, + types::Viewport, }; +use render_handler::ChromiumRenderHandler; -cef_impl!( - prefix = "WebView", - name = Client, - sys_type = cef_dll_sys::cef_client_t, - { - fn display_handler(&self) -> Option { - Some(WebViewDisplayHandler::new()) - } +wrap_client! { + pub struct ChromiumClient { + viewport: Arc>, + sender: Sender, + } + impl Client { fn render_handler(&self) -> Option { - Some(WebViewRenderHandler::new()) + Some(ChromiumRenderHandler::new( + self.viewport.clone(), + self.sender.clone(), + )) + } + + fn load_handler(&self) -> Option { + Some(ChromiumLoadHandler::new(self.sender.clone())) } fn life_span_handler(&self) -> Option { - Some(WebViewLifeSpanHandler::new()) + Some(ChromiumLifeSpanHandler::new(self.sender.clone())) } - fn load_handler(&self) -> Option { - Some(WebViewLoadHandler::new()) + fn permission_handler(&self) -> Option { + Some(ChromiumPermissionHandler::new()) } - fn keyboard_handler(&self) -> Option { - Some(WebViewKeyboardHandler::new()) + fn display_handler(&self) -> Option { + Some(ChromiumDisplayHandler::new(self.sender.clone())) } fn on_process_message_received( @@ -51,15 +56,13 @@ cef_impl!( _frame: Option<&mut Frame>, _source_process: ProcessId, message: Option<&mut ProcessMessage>, - ) -> c_int { + ) -> i32 { if let Some(message) = message { let name = CefString::from(&message.name()); let ready_message_name = CefString::from(READY_MESSAGE); - if name.as_slice() == ready_message_name.as_slice() - && let Some(sender) = SENDER.get() - { - sender.send(WebViewEvent::Ready).ok(); + if name.as_slice() == ready_message_name.as_slice() { + self.sender.send(ChromiumEvent::Ready).ok(); } let ipc_message_name = CefString::from(IPC_MESSAGE); @@ -67,13 +70,11 @@ cef_impl!( let arguments = message.argument_list().unwrap(); let data = CefString::from(&arguments.string(0)); - if let Some(sender) = SENDER.get() { - sender.send(WebViewEvent::Ipc(data.to_string())).ok(); - } + self.sender.send(ChromiumEvent::Ipc(data.to_string())).ok(); } } Default::default() } } -); +} diff --git a/src/chromium/app/client/permission_handler.rs b/src/chromium/app/client/permission_handler.rs new file mode 100644 index 0000000..94ec638 --- /dev/null +++ b/src/chromium/app/client/permission_handler.rs @@ -0,0 +1,29 @@ +use cef::sys::{ + cef_permission_request_result_t::CEF_PERMISSION_RESULT_ACCEPT, + cef_permission_request_types_t::CEF_PERMISSION_TYPE_LOCAL_NETWORK_ACCESS, +}; +use cef::{rc::*, *}; + +wrap_permission_handler! { + pub struct ChromiumPermissionHandler; + + impl PermissionHandler { + fn on_show_permission_prompt( + &self, + _browser: Option<&mut Browser>, + _prompt_id: u64, + _requesting_origin: Option<&CefString>, + requested_permissions: u32, + callback: Option<&mut PermissionPromptCallback>, + ) -> i32 { + println!("{}", requested_permissions); + if requested_permissions == CEF_PERMISSION_TYPE_LOCAL_NETWORK_ACCESS as u32 + && let Some(callback) = callback { + callback.cont(CEF_PERMISSION_RESULT_ACCEPT.into()); + return true.into(); + } + + Default::default() + } + } +} diff --git a/src/chromium/app/client/render_handler.rs b/src/chromium/app/client/render_handler.rs new file mode 100644 index 0000000..2d54732 --- /dev/null +++ b/src/chromium/app/client/render_handler.rs @@ -0,0 +1,80 @@ +use std::{ + slice, + sync::{Arc, RwLock}, +}; + +use cef::{rc::*, *}; +use flume::Sender; + +use crate::{ + chromium::{ChromiumEvent, types::Viewport}, + shared::Frame, +}; + +wrap_render_handler! { + pub struct ChromiumRenderHandler { + viewport: Arc>, + sender: Sender, + } + + impl RenderHandler { + fn screen_info(&self, _browser: Option<&mut Browser>, screen_info: Option<&mut ScreenInfo>) -> i32 { + if let Ok(viewport) = self.viewport.read() + && let Some(screen_info) = screen_info { + screen_info.device_scale_factor = viewport.scale_factor as f32; + + return true.into(); + } + + false.into() + } + + fn screen_point( + &self, + _browser: Option<&mut Browser>, + _view_x: i32, + _view_y: i32, + _screen_x: Option<&mut i32>, + _screen_y: Option<&mut i32>, + ) -> i32 { + false.into() + } + + fn view_rect(&self, _browser: Option<&mut Browser>, rect: Option<&mut Rect>) { + if let Some(rect) = rect + && let Ok(viewport) = self.viewport.read() { + rect.width = viewport.width / viewport.scale_factor; + rect.height = viewport.height / viewport.scale_factor; + } + } + + fn on_paint( + &self, + _browser: Option<&mut Browser>, + _type: PaintElementType, + dirty_rects: Option<&[Rect]>, + buffer: *const u8, + width: i32, + height: i32, + ) { + if let Some(dirty_rects) = dirty_rects + && let Some(dirty_rect) = dirty_rects.first() { + let size = (width * height * 4) as usize; + let buffer_slice = unsafe { slice::from_raw_parts(buffer, size) }; + let buffer = Arc::from(buffer_slice); + + let frame = Frame { + x: dirty_rect.x, + y: dirty_rect.y, + width: dirty_rect.width, + height: dirty_rect.height, + full_width: width, + full_height: height, + buffer, + }; + + self.sender.send(ChromiumEvent::Render(frame)).ok(); + } + } + } +} diff --git a/src/chromium/app/mod.rs b/src/chromium/app/mod.rs new file mode 100644 index 0000000..f0e6f97 --- /dev/null +++ b/src/chromium/app/mod.rs @@ -0,0 +1,49 @@ +mod browser_process_handler; +mod client; +mod process; +mod render_process_handler; + +use std::sync::{Arc, Mutex, RwLock}; + +use cef::{rc::*, *}; +use flume::Sender; + +use crate::chromium::{ + ChromiumEvent, app::render_process_handler::ChromiumRenderProcessHandler, config::CMD_SWITCHES, + types::Viewport, +}; +use browser_process_handler::ChromiumBrowserProcessHandler; + +wrap_app! { + pub struct ChromiumApp { + browser: Arc>>, + viewport: Arc>, + sender: Sender, + } + + impl App { + fn on_before_command_line_processing( + &self, + _process_type: Option<&CefString>, + command_line: Option<&mut CommandLine>, + ) { + if let Some(line) = command_line { + CMD_SWITCHES.iter().for_each(|switch| { + line.append_switch(Some(&CefString::from(switch.to_owned()))); + }); + } + } + + fn browser_process_handler(&self) -> Option { + Some(ChromiumBrowserProcessHandler::new( + self.browser.clone(), + self.viewport.clone(), + self.sender.clone(), + )) + } + + fn render_process_handler(&self) -> Option { + Some(ChromiumRenderProcessHandler::new()) + } + } +} diff --git a/src/webview/app/utils.rs b/src/chromium/app/process.rs similarity index 53% rename from src/webview/app/utils.rs rename to src/chromium/app/process.rs index 12379fc..4aec93f 100644 --- a/src/webview/app/utils.rs +++ b/src/chromium/app/process.rs @@ -1,17 +1,12 @@ use cef::{ Browser, CefString, CefStringUtf16, ImplBrowser, ImplFrame, ImplListValue, ImplProcessMessage, - process_message_create, + sys::cef_process_id_t::PID_BROWSER, }; -use cef_dll_sys::cef_process_id_t; -pub fn send_process_message( - browser: Option<&mut Browser>, - name: &str, - arg: Option<&CefStringUtf16>, -) { +pub fn send_message(browser: Option<&mut Browser>, name: &str, arg: Option<&CefStringUtf16>) { let name = CefString::from(name); let mut message = - process_message_create(Some(&name)).expect("Failed to create process message"); + cef::process_message_create(Some(&name)).expect("Failed to create process message"); if let Some(arg) = arg { let arguments = message.argument_list().unwrap(); @@ -21,6 +16,6 @@ pub fn send_process_message( if let Some(browser) = browser && let Some(main_frame) = browser.main_frame() { - main_frame.send_process_message(cef_process_id_t::PID_BROWSER.into(), Some(&mut message)); + main_frame.send_process_message(PID_BROWSER.into(), Some(&mut message)); } } diff --git a/src/chromium/app/render_process_handler.rs b/src/chromium/app/render_process_handler.rs new file mode 100644 index 0000000..3af580e --- /dev/null +++ b/src/chromium/app/render_process_handler.rs @@ -0,0 +1,94 @@ +use cef::{rc::*, *}; + +use crate::chromium::{ + app::process, + config::{IPC_MESSAGE, IPC_RECEIVER, READY_MESSAGE}, +}; + +wrap_render_process_handler! { + pub struct ChromiumRenderProcessHandler; + + impl RenderProcessHandler { + fn on_browser_created( + &self, + browser: Option<&mut Browser>, + _extra_info: Option<&mut DictionaryValue>, + ) { + process::send_message(browser, READY_MESSAGE, None); + } + + fn on_context_created( + &self, + _browser: Option<&mut Browser>, + _frame: Option<&mut Frame>, + context: Option<&mut V8Context>, + ) { + let name = CefString::from(IPC_RECEIVER); + let mut handler = ChromiumV8Handler::new(); + + let mut value = v8_value_create_function(Some(&name), Some(&mut handler)) + .expect("Failed to create a value for function"); + + if let Some(context) = context + && let Some(global) = context.global() + { + global.set_value_bykey( + Some(&name), + Some(&mut value), + V8Propertyattribute::default(), + ); + } + } + } +} + +wrap_v8_handler! { + struct ChromiumV8Handler; + + impl V8Handler { + fn execute( + &self, + name: Option<&CefString>, + _object: Option<&mut V8Value>, + arguments: Option<&[Option]>, + _retval: Option<&mut Option>, + _exception: Option<&mut CefString>, + ) -> i32 { + if is_handler(name, IPC_RECEIVER) + && let Some(data) = handler_data(arguments) + { + send_ipc_message(data); + + return 1; + } + + 0 + } + } +} + +fn is_handler(name: Option<&CefString>, value: &str) -> bool { + name.is_some_and(|name| { + let handler_name = CefString::from(value); + name.as_slice() == handler_name.as_slice() + }) +} + +fn handler_data(arguments: Option<&[Option]>) -> Option { + arguments.and_then(|arguments| { + arguments.first().and_then(|value| { + value + .as_ref() + .map(|value| value.string_value()) + .map(|value| CefString::from(&value)) + }) + }) +} + +fn send_ipc_message(data: CefStringUtf16) { + if let Some(context) = v8_context_get_current_context() + && let Some(mut browser) = context.browser() + { + process::send_message(Some(&mut browser), IPC_MESSAGE, Some(&data)); + } +} diff --git a/src/webview/constants.rs b/src/chromium/config.rs similarity index 51% rename from src/webview/constants.rs rename to src/chromium/config.rs index 37ad0ec..c01efb4 100644 --- a/src/webview/constants.rs +++ b/src/chromium/config.rs @@ -1,9 +1,13 @@ -pub const PRELOAD_SCRIPT: &str = include_str!("preload.js"); +pub const MAX_FRAME_RATE: f64 = 60.0; + pub const IPC_SENDER: &str = "__postMessage"; pub const IPC_RECEIVER: &str = "__onMessage"; -// Process messages pub const IPC_MESSAGE: &str = "IPC"; pub const READY_MESSAGE: &str = "READY"; -pub const ZOOM_AMOUNT: f64 = 0.2; +pub const IPC_SCRIPT: &str = include_str!("ipc.js"); + +pub const CMD_SWITCHES: &[&str] = &[ + // "disable-web-security", +]; diff --git a/src/webview/preload.js b/src/chromium/ipc.js similarity index 97% rename from src/webview/preload.js rename to src/chromium/ipc.js index bdee2c5..4f1f190 100644 --- a/src/webview/preload.js +++ b/src/chromium/ipc.js @@ -57,4 +57,4 @@ window.ipc.addEventListener('message', (message) => { window.qt.webChannelTransport.onmessage(message); }); -console.log('preload'); +console.log('IPC script injected'); diff --git a/src/chromium/mod.rs b/src/chromium/mod.rs new file mode 100644 index 0000000..2bf04fd --- /dev/null +++ b/src/chromium/mod.rs @@ -0,0 +1,347 @@ +mod app; +mod config; +mod types; + +use std::path::{Path, PathBuf}; +use std::ptr; +use std::sync::{Arc, Mutex, RwLock}; + +use cef::sys::{ + cef_drag_operations_mask_t as OperationsMask, cef_key_event_type_t::KEYEVENT_CHAR, + cef_log_severity_t::LOGSEVERITY_VERBOSE, cef_mouse_button_type_t::MBT_LEFT, + cef_mouse_button_type_t::MBT_MIDDLE, cef_mouse_button_type_t::MBT_RIGHT, + cef_paint_element_type_t::PET_VIEW, +}; +use cef::{Frame as MainFrame, args::Args, *}; +use flume::Receiver; + +use app::ChromiumApp; +use config::IPC_SENDER; +use types::Viewport; +use url::Url; + +use crate::chromium::config::MAX_FRAME_RATE; +use crate::shared::{ + Frame, + states::{KeyboardState, PointerState}, +}; + +#[derive(Debug)] +pub enum ChromiumEvent { + Ready, + Loaded, + Fullscreen(bool), + Render(Frame), + Open(Url), + Ipc(String), +} + +pub struct Chromium { + args: Args, + app: App, + settings: Settings, + browser: Arc>>, + viewport: Arc>, + receiver: Receiver, +} + +impl Chromium { + pub fn new(data_dir: &Path) -> Self { + let _ = api_hash(sys::CEF_API_VERSION_LAST, 0); + + let args = Args::new(); + + let browser = Arc::new(Mutex::new(None)); + let viewport = Arc::new(RwLock::new(Viewport::default())); + + let (sender, receiver) = flume::unbounded(); + let app = ChromiumApp::new(browser.clone(), viewport.clone(), sender); + + let cache_path = data_dir.join("cache"); + let log_path = data_dir.join("log"); + + let settings = Settings { + no_sandbox: true.into(), + windowless_rendering_enabled: true.into(), + multi_threaded_message_loop: true.into(), + root_cache_path: cache_path.to_str().unwrap().into(), + cache_path: cache_path.to_str().unwrap().into(), + log_file: log_path.to_str().unwrap().into(), + log_severity: LogSeverity::from(LOGSEVERITY_VERBOSE), + ..Default::default() + }; + + Self { + args, + app, + settings, + browser, + viewport, + receiver, + } + } + + pub fn execute(&mut self) -> Option { + let exit_code = cef::execute_process( + Some(self.args.as_main_args()), + Some(&mut self.app), + ptr::null_mut(), + ); + + if exit_code >= 0 { + return Some(exit_code); + } + + None + } + + pub fn start(&mut self) { + cef::initialize( + Some(self.args.as_main_args()), + Some(&self.settings), + Some(&mut self.app), + ptr::null_mut(), + ); + } + + pub fn stop(&self) { + if let Ok(mut browser) = self.browser.lock() + && let Some(browser) = browser.take() + && let Some(browser_host) = browser.host() + { + browser_host.close_browser(0); + } + } + + pub fn dev_tools(&self, state: bool) { + if let Some(host) = self.browser_host() { + if state { + host.show_dev_tools( + None, + Option::<&mut Client>::None, + Option::<&BrowserSettings>::None, + None, + ); + } else { + host.close_dev_tools(); + } + } + } + + pub fn load_url(&self, url: &str) { + if let Some(main_frame) = self.main_frame() { + let url = CefString::from(url); + main_frame.load_url(Some(&url)); + } + } + + pub fn on_event(&self, handler: F) { + self.receiver.try_iter().for_each(handler); + } + + pub fn set_monitor_info(&self, refresh_rate: f64, scale_factor: i32) { + if let Ok(mut viewport) = self.viewport.write() { + viewport.scale_factor = scale_factor; + } + + if let Some(browser_host) = self.browser_host() { + browser_host.set_windowless_frame_rate(refresh_rate.min(MAX_FRAME_RATE) as i32); + browser_host.notify_screen_info_changed(); + } + } + + pub fn resize(&self, width: i32, height: i32) { + if let Ok(mut viewport) = self.viewport.write() { + viewport.width = width; + viewport.height = height; + + if let Some(browser_host) = self.browser_host() { + browser_host.was_resized(); + browser_host.invalidate(PET_VIEW.into()); + } + } + } + + pub fn hidden(&self, state: bool) { + if let Some(browser_host) = self.browser_host() { + browser_host.was_hidden(state.into()); + } + } + + pub fn focus(&self, state: bool) { + if let Some(browser_host) = self.browser_host() { + browser_host.set_focus(state.into()); + } + } + + pub fn forward_motion(&self, pointer_state: &PointerState) { + if let Some(browser_host) = self.browser_host() { + let mouse_event = MouseEvent::from(pointer_state); + let mouse_leave = (!pointer_state.over()).into(); + browser_host.send_mouse_move_event(Some(&mouse_event), mouse_leave); + } + } + + pub fn forward_scroll(&self, pointer_state: &PointerState, delta_x: f64, delta_y: f64) { + if let Some(browser_host) = self.browser_host() { + let mouse_event = MouseEvent::from(pointer_state); + + browser_host.send_mouse_wheel_event(Some(&mouse_event), delta_x as i32, delta_y as i32); + } + } + + pub fn forward_click(&self, pointer_state: &PointerState, count: i32) { + if let Some(browser_host) = self.browser_host() { + let pressed = pointer_state.pressed(); + let button = pointer_state.button(); + + let mouse_event = MouseEvent::from(pointer_state); + + let r#type = match button { + 1 => Some(MBT_LEFT.into()), + 2 => Some(MBT_MIDDLE.into()), + 3 => Some(MBT_RIGHT.into()), + 8 if self.go_back() => None, + 9 if self.go_forward() => None, + _ => None, + }; + + let mouse_up = (!pressed).into(); + + if let Some(r#type) = r#type { + browser_host.send_mouse_click_event(Some(&mouse_event), r#type, mouse_up, count); + } + } + } + + pub fn forward_key(&self, keyboard_state: &KeyboardState) { + if let Some(browser_host) = self.browser_host() { + if let Some(character) = keyboard_state.character() + && keyboard_state.pressed() + && !keyboard_state.control_modifier() + { + let event = cef::KeyEvent { + type_: KEYEVENT_CHAR.into(), + character: character as u16, + ..Default::default() + }; + + browser_host.send_key_event(Some(&event)); + } + + let key_event = KeyEvent::from(keyboard_state); + browser_host.send_key_event(Some(&key_event)); + } + } + + pub fn forward_file_enter(&self, pointer_state: &PointerState, path: PathBuf) { + if let Some(browser_host) = self.browser_host() { + let file_path = path.to_str().map(CefString::from); + let file_name = path + .file_name() + .and_then(|name| name.to_str()) + .map(CefString::from); + + if let Some(mut drag_data) = cef::drag_data_create() { + drag_data.add_file(file_path.as_ref(), file_name.as_ref()); + + let mouse_event = MouseEvent::from(pointer_state); + + browser_host.drag_target_drag_enter( + Some(&mut drag_data), + Some(&mouse_event), + OperationsMask::DRAG_OPERATION_MOVE.into(), + ); + } + } + } + + pub fn forward_file_hover(&self, pointer_state: &PointerState) { + if let Some(browser_host) = self.browser_host() { + let mouse_event = MouseEvent::from(pointer_state); + + browser_host.drag_target_drag_over( + Some(&mouse_event), + OperationsMask::DRAG_OPERATION_MOVE.into(), + ); + } + } + + pub fn forward_file_drop(&self, pointer_state: &PointerState) { + if let Some(browser_host) = self.browser_host() { + let mouse_event = MouseEvent::from(pointer_state); + + browser_host.drag_target_drop(Some(&mouse_event)); + } + } + + pub fn forward_file_leave(&self) { + if let Some(browser_host) = self.browser_host() { + browser_host.drag_target_drag_leave(); + } + } + + pub fn clipboard(&self, data: String) { + if let Some(browser_host) = self.browser_host() { + for character in data.chars() { + let event = cef::KeyEvent { + type_: KEYEVENT_CHAR.into(), + character: character as u16, + ..Default::default() + }; + + browser_host.send_key_event(Some(&event)); + } + } + } + + pub fn post_message(&self, message: String) { + if let Some(main_frame) = self.main_frame() { + let serialized_message = + serde_json::to_string(&message).expect("Failed to serialize as JSON string"); + let script = format!("{IPC_SENDER}({serialized_message})"); + let code = CefString::from(script.as_str()); + main_frame.execute_java_script(Some(&code), None, 0); + } + } + + fn go_back(&self) -> bool { + if let Ok(browser) = self.browser.lock() + && let Some(browser) = browser.as_ref() + { + browser.go_back(); + } + + true + } + + fn go_forward(&self) -> bool { + if let Ok(browser) = self.browser.lock() + && let Some(browser) = browser.as_ref() + { + browser.go_forward(); + } + + true + } + + fn browser_host(&self) -> Option { + if let Ok(browser) = self.browser.lock() + && let Some(browser) = browser.as_ref() + { + return browser.host(); + } + + None + } + + fn main_frame(&self) -> Option { + if let Ok(browser) = self.browser.lock() + && let Some(browser) = browser.as_ref() + { + return browser.main_frame(); + } + + None + } +} diff --git a/src/chromium/types.rs b/src/chromium/types.rs new file mode 100644 index 0000000..1f0244e --- /dev/null +++ b/src/chromium/types.rs @@ -0,0 +1,16 @@ +#[derive(Debug)] +pub struct Viewport { + pub width: i32, + pub height: i32, + pub scale_factor: i32, +} + +impl Default for Viewport { + fn default() -> Self { + Self { + width: 1700, + height: 1004, + scale_factor: 1, + } + } +} diff --git a/src/config.rs b/src/config.rs index 4eda395..2878924 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,121 +1,7 @@ -use std::{ - env, fs, - path::{Path, PathBuf}, -}; +pub const DATA_DIR: &str = "stremio"; -use crate::constants::DATA_DIR; +pub const GETTEXT_DOMAIN: &str = "stremio"; +pub const GETTEXT_DIR_DEV: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/po"); +pub const GETTEXT_DIR_FLATPAK: &str = "/app/share/locale"; -pub struct Config { - pub instance: InstanceConfig, - pub server: ServerConfig, - pub webview: WebViewConfig, - pub tray: TrayConfig, -} - -impl Config { - pub fn new() -> Self { - let data_dir = dirs::data_dir() - .expect("Failed to get data dir") - .join(DATA_DIR); - - let runtime_dir = dirs::runtime_dir() - .expect("Failed to get runtime dir") - .join(DATA_DIR); - - fs::create_dir_all(&data_dir).expect("Failed to create data directory"); - fs::create_dir_all(&runtime_dir).expect("Failed to create runtime directory"); - - let current_exe_path = env::current_exe().expect("Failed to get current exe path"); - let current_dir = current_exe_path - .parent() - .expect("Failed to get current directory"); - - let instance = InstanceConfig::new(&runtime_dir); - let server = ServerConfig::new(current_dir); - let webview = WebViewConfig::new(&data_dir); - let tray = TrayConfig::new(&runtime_dir); - - Self { - instance, - server, - webview, - tray, - } - } -} - -const INSTANCE_SOCKET_FILE: &str = "stremio.sock"; - -pub struct InstanceConfig { - pub socket_file: PathBuf, -} - -impl InstanceConfig { - pub fn new(runtime_dir: &Path) -> Self { - let socket_file = runtime_dir.join(INSTANCE_SOCKET_FILE); - - Self { socket_file } - } - - pub fn remove_socket_file(&self) { - let _ = fs::remove_file(&self.socket_file); - } -} - -const SERVER_FILE: &str = "server.js"; - -pub struct ServerConfig { - pub file: PathBuf, -} - -impl ServerConfig { - pub fn new(current_dir: &Path) -> Self { - let file = current_dir.join(SERVER_FILE); - - Self { file } - } -} - -const CEF_DIR: &str = "cef"; -const CEF_CACHE_DIR: &str = "cache"; -const CEF_LOG_FILE: &str = "log"; -const CEF_LOCK_FILE: &str = "SingletonLock"; - -pub struct WebViewConfig { - pub cache_dir: PathBuf, - pub log_file: PathBuf, - pub lock_file: PathBuf, -} - -impl WebViewConfig { - pub fn new(data_dir: &Path) -> Self { - let cef_dir = data_dir.join(CEF_DIR); - let cache_dir = cef_dir.join(CEF_CACHE_DIR); - let log_file = cef_dir.join(CEF_LOG_FILE); - let lock_file = cache_dir.join(CEF_LOCK_FILE); - - Self { - cache_dir, - log_file, - lock_file, - } - } - - pub fn remove_lock_file(&self) { - let _ = fs::remove_file(&self.lock_file); - } -} - -const TRAY_ICON_DIR: &str = "tray"; - -pub struct TrayConfig { - pub icon_path: PathBuf, -} - -impl TrayConfig { - pub fn new(runtime_path: &Path) -> Self { - let icon_path = runtime_path.join(TRAY_ICON_DIR); - - Self { icon_path } - } -} +pub const STARTUP_URL: &str = "https://web.stremio.com"; diff --git a/src/constants.rs b/src/constants.rs deleted file mode 100644 index bccb7cf..0000000 --- a/src/constants.rs +++ /dev/null @@ -1,21 +0,0 @@ -pub const APP_ID: &str = match cfg!(debug_assertions) { - true => "com.stremio.Stremio.Devel", - false => "com.stremio.Stremio", -}; - -pub const APP_NAME: &str = "Stremio"; -pub const WINDOW_SIZE: (i32, i32) = (1700, 1050); -pub const STARTUP_URL: &str = "https:/web.stremio.com"; -pub const URI_SCHEME: &str = "stremio://"; -pub const DATA_DIR: &str = "stremio"; - -pub const CMD_SWITCHES: &[&str] = &[ - // "enable-begin-frame-scheduling", - // "enable-gpu", - // "enable-gpu-rasterization", - // "disable-frame-rate-limit", - // "disable-gpu-vsync", - // "disable-gpu-compositing", - // "disable-direct-composition", - // "disable-gpu", -]; diff --git a/src/instance.rs b/src/instance.rs deleted file mode 100644 index d1e4c6f..0000000 --- a/src/instance.rs +++ /dev/null @@ -1,71 +0,0 @@ -use std::{ - io::{Read, Write}, - os::unix::net::{UnixListener, UnixStream}, - thread, -}; - -use crossbeam_channel::{Receiver, Sender, unbounded}; - -use crate::config::InstanceConfig; - -pub enum InstanceEvent { - Open(String), -} - -pub struct Instance { - sender: Sender, - receiver: Receiver, - socket: Option, - config: InstanceConfig, -} - -impl Instance { - pub fn new(config: InstanceConfig) -> Self { - let (sender, receiver) = unbounded::(); - let socket = UnixStream::connect(&config.socket_file).ok(); - - Self { - sender, - receiver, - socket, - config, - } - } - - pub fn running(&self) -> bool { - self.socket.is_some() - } - - pub fn send(&self, data: String) { - if let Some(mut stream) = self.socket.as_ref() { - stream - .write_all(data.as_bytes()) - .expect("Failed to write to stream"); - } - } - - pub fn start(&self) { - self.config.remove_socket_file(); - - let listener = - UnixListener::bind(&self.config.socket_file).expect("Failed to create socket"); - - let sender = self.sender.clone(); - thread::spawn(move || { - for mut stream in listener.incoming().flatten() { - let mut buffer = String::new(); - if stream.read_to_string(&mut buffer).is_ok() { - sender.send(InstanceEvent::Open(buffer)).ok(); - } - } - }); - } - - pub fn stop(&self) { - self.config.remove_socket_file(); - } - - pub fn events(&self, handler: F) { - self.receiver.try_iter().for_each(handler); - } -} diff --git a/src/ipc.rs b/src/ipc.rs deleted file mode 100644 index 8fd4b66..0000000 --- a/src/ipc.rs +++ /dev/null @@ -1,238 +0,0 @@ -use serde::{Deserialize, Serialize}; -use serde_json::{Value, json}; - -use crate::player::MpvProperty; - -const VERSION: &str = env!("CARGO_PKG_VERSION"); -const TRANSPORT_NAME: &str = "transport"; - -#[derive(Deserialize, Debug)] -pub enum IpcEventMpv { - Observe(String), - Command((String, Vec)), - Set(MpvProperty), - Change(MpvProperty), - Ended(Option), -} - -#[derive(Deserialize, Debug)] -pub enum IpcEvent { - Init(u64), - Quit, - Fullscreen(bool), - Minimized(bool), - Visibility(bool), - OpenMedia(String), - OpenExternal(String), - Mpv(IpcEventMpv), -} - -#[derive(Deserialize, Debug)] -pub struct IpcMessageRequestWinSetVisilibty { - fullscreen: bool, -} - -#[derive(Deserialize, Debug)] -pub struct IpcMessageRequest { - id: u64, - r#type: u8, - #[serde(skip_serializing_if = "Option::is_none")] - args: Option, -} - -impl TryFrom for IpcEvent { - type Error = &'static str; - - fn try_from(value: IpcMessageRequest) -> Result { - match value.r#type { - 3 => Ok(IpcEvent::Init(value.id)), - 6 => match value.args { - Some(args) => { - let args: Vec = serde_json::from_value(args).expect("Invalid arguments"); - let name = args.first().and_then(Value::as_str).ok_or("Invalid name")?; - let data = args.get(1).cloned(); - - match data { - Some(data) => match name { - "win-set-visibility" => { - let data: IpcMessageRequestWinSetVisilibty = - serde_json::from_value(data) - .expect("Invalid win-set-visibility object"); - - Ok(IpcEvent::Fullscreen(data.fullscreen)) - } - "open-external" => { - let data: String = serde_json::from_value(data) - .expect("Invalid open-external argument"); - - Ok(IpcEvent::OpenExternal(data)) - } - "mpv-command" => { - let data: Vec = serde_json::from_value(data) - .expect("Invalid mpv-command arguments"); - let name = data[0].clone(); - - let mut args = vec![]; - for arg in data.iter().skip(1) { - args.push(arg.clone()); - } - - Ok(IpcEvent::Mpv(IpcEventMpv::Command((name, args)))) - } - "mpv-observe-prop" => { - let name = data.as_str().expect("Invalid mpv-observe-prop name"); - Ok(IpcEvent::Mpv(IpcEventMpv::Observe(name.to_owned()))) - } - "mpv-set-prop" => { - let key_value: Vec = serde_json::from_value(data) - .expect("Invalid mpv-set-prop arguments"); - - let name = key_value[0] - .as_str() - .expect("Invalid mpv-set-prop name") - .to_owned(); - - let value = key_value.get(1).cloned(); - - Ok(IpcEvent::Mpv(IpcEventMpv::Set(MpvProperty(name, value)))) - } - _ => Err("Unknown method"), - }, - None => match name { - "quit" => Ok(IpcEvent::Quit), - _ => Err("Unknown method"), - }, - } - } - None => Err("Missing args"), - }, - _ => Err("Unknown type"), - } - } -} - -impl TryFrom for IpcEvent { - type Error = String; - - fn try_from(value: String) -> Result { - serde_json::from_str::(&value) - .map_err(|e| format!("Failed to convert String to IpcEvent: {e}"))? - .try_into() - .map_err(|e| format!("Failed to convert IpcEvent to IpcMessageRequest: {e}")) - } -} - -#[derive(Serialize, Debug)] -pub struct IpcMessageResponse { - id: u64, - r#type: u8, - object: String, - #[serde(skip_serializing_if = "Option::is_none")] - data: Option, - #[serde(skip_serializing_if = "Option::is_none")] - args: Option, -} - -impl TryFrom for IpcMessageResponse { - type Error = &'static str; - - fn try_from(value: IpcEvent) -> Result { - match value { - IpcEvent::Init(id) => Ok(IpcMessageResponse { - id, - r#type: 3, - object: TRANSPORT_NAME.to_owned(), - args: None, - data: Some(json!({ - "transport": { - "properties": [[], ["", "shellVersion", "", VERSION]], - "signals": [], - "methods": [["onEvent"]] - } - })), - }), - IpcEvent::Fullscreen(state) => Ok(IpcMessageResponse { - id: 1, - r#type: 1, - object: TRANSPORT_NAME.to_owned(), - data: None, - args: Some(json!([ - "win-visibility-changed", - { - "visible": true, - "visibility": 1, - "isFullscreen": state, - } - ])), - }), - IpcEvent::Visibility(state) => Ok(IpcMessageResponse { - id: 1, - r#type: 1, - object: TRANSPORT_NAME.to_owned(), - data: None, - args: Some(json!([ - "win-visibility-changed", - { - "visible": state, - "visibility": state as u32, - "isFullscreen": false, - } - ])), - }), - IpcEvent::Minimized(state) => Ok(IpcMessageResponse { - id: 1, - r#type: 1, - object: TRANSPORT_NAME.to_owned(), - data: None, - args: Some(json!([ - "win-state-changed", - { - "state": match state { - true => 9, - false => 8, - }, - } - ])), - }), - IpcEvent::OpenMedia(deeplink) => Ok(IpcMessageResponse { - id: 1, - r#type: 1, - object: TRANSPORT_NAME.to_owned(), - data: None, - args: Some(json!(["open-media", deeplink])), - }), - IpcEvent::Mpv(IpcEventMpv::Change(property)) => Ok(IpcMessageResponse { - id: 1, - r#type: 1, - object: TRANSPORT_NAME.to_owned(), - data: None, - args: Some(json!(["mpv-prop-change", property])), - }), - IpcEvent::Mpv(IpcEventMpv::Ended(error)) => Ok(IpcMessageResponse { - id: 1, - r#type: 1, - object: TRANSPORT_NAME.to_owned(), - data: None, - args: Some(json!([ - "mpv-event-ended", - { - "error": error, - } - ])), - }), - _ => Err("Failed to convert IpcEvent to IpcMessageResponse"), - } - } -} - -pub fn parse_request(data: String, handler: T) { - IpcEvent::try_from(data) - .map(handler) - .map_err(|e| eprintln!("{e}")) - .ok(); -} - -pub fn create_response(event: IpcEvent) -> String { - let message = IpcMessageResponse::try_from(event).ok(); - serde_json::to_string(&message).expect("Failed to convert IpcMessage to string") -} diff --git a/src/main.rs b/src/main.rs index 3ce3af2..f1d0a67 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,35 +1,23 @@ mod app; +mod chromium; mod config; -mod constants; -mod instance; -mod ipc; -mod player; mod server; mod shared; -mod tray; -mod webview; +mod utils; + +use std::{env, fs, ptr}; -use app::{App, AppEvent}; use clap::Parser; -use config::Config; -use constants::{STARTUP_URL, URI_SCHEME}; -use glutin::{display::GetGlDisplay, surface::GlSurface}; -use instance::{Instance, InstanceEvent}; -use ipc::{IpcEvent, IpcEventMpv}; -use player::{Player, PlayerEvent}; -use rust_i18n::i18n; -use server::Server; -use shared::{types::UserEvent, with_gl, with_renderer_read, with_renderer_write}; -use std::{num::NonZeroU32, process::ExitCode, rc::Rc, time::Duration}; -use tray::Tray; -use webview::{WebView, WebViewEvent}; -use winit::{ - event_loop::{ControlFlow, EventLoop}, - platform::pump_events::{EventLoopExtPumpEvents, PumpStatus}, +use gtk::glib::{ExitCode, object::ObjectExt}; +use tokio::runtime::Runtime; + +use crate::{ + app::Application, + chromium::Chromium, + config::{DATA_DIR, GETTEXT_DIR_DEV, GETTEXT_DIR_FLATPAK, GETTEXT_DOMAIN, STARTUP_URL}, + server::Server, }; -i18n!("locales", fallback = "en"); - #[derive(Parser, Debug)] #[command(version, ignore_errors(true))] struct Args { @@ -42,243 +30,57 @@ struct Args { /// Open a deeplink #[arg(short, long)] open: Option, - /// Disable server + /// Disable window decorations #[arg(short, long)] - no_server: bool, + no_window_decorations: bool, } fn main() -> ExitCode { tracing_subscriber::fmt::init(); - let args = Args::parse(); - let config = Config::new(); - - let mut webview = WebView::new(config.webview); - if webview.should_exit() { - return ExitCode::SUCCESS; - } - - let instance = Instance::new(config.instance); - if instance.running() { - if let Some(deeplink) = args.open { - instance.send(deeplink); - } + let data_dir = dirs::data_dir() + .expect("Failed to get data dir") + .join(DATA_DIR); - return ExitCode::SUCCESS; - } - - instance.start(); + fs::create_dir_all(&data_dir).expect("Failed to create data directory"); - let mut server = Server::new(config.server); - if !args.no_server { - server.start(args.dev).expect("Failed to start server"); + let mut chromium = Chromium::new(&data_dir); + if let Some(exit_code) = chromium.execute() { + return ExitCode::from(exit_code as u8); } - let tray = Tray::new(config.tray); - let mut app = App::new(); - let mut player = Player::new(); - - let mut event_loop = EventLoop::::with_user_event() - .build() - .expect("Failed to create event loop"); - - event_loop.set_control_flow(ControlFlow::Poll); - - let event_loop_proxy = event_loop.create_proxy(); - - let mut needs_redraw = false; + let gettext_dir = match env::var("FLATPAK_ID") { + Ok(_) => GETTEXT_DIR_FLATPAK, + Err(_) => GETTEXT_DIR_DEV, + }; - loop { - let timeout = match needs_redraw { - true => Some(Duration::ZERO), - false => None, - }; + gettextrs::bindtextdomain(GETTEXT_DOMAIN, gettext_dir).expect("Failed to bind text domain"); + gettextrs::bind_textdomain_codeset(GETTEXT_DOMAIN, "UTF-8") + .expect("Failed to set the text domain encoding"); + gettextrs::textdomain(GETTEXT_DOMAIN).expect("Failed to switch text domain"); - let status = event_loop.pump_app_events(timeout, &mut app); + let library = unsafe { libloading::os::unix::Library::new("libepoxy.so.0") } + .expect("Failed to load libepoxy"); - if let PumpStatus::Exit(exit_code) = status { - server.stop().expect("Failed to stop server"); - webview.stop(); - instance.stop(); - shared::drop_renderer(); - shared::drop_gl(); + epoxy::load_with(|name| { + unsafe { library.get::<_>(name.as_bytes()) } + .map(|symbol| *symbol) + .unwrap_or(ptr::null()) + }); - break ExitCode::from(exit_code as u8); - } - - if needs_redraw { - with_gl(|surface, context| { - with_renderer_read(|renderer| { - player.render(renderer.fbo, renderer.width, renderer.height); - renderer.draw(); - }); - - surface - .swap_buffers(context) - .expect("Failed to swap buffers"); - - player.report_swap(); - }); - - needs_redraw = false; - } - - instance.events(|event| match event { - InstanceEvent::Open(deeplink) => { - event_loop_proxy.send_event(UserEvent::Raise).ok(); - - if deeplink.starts_with(URI_SCHEME) { - let message = ipc::create_response(IpcEvent::OpenMedia(deeplink.to_string())); - webview.post_message(message); - } - } - }); - - tray.events(|event| { - event_loop_proxy.send_event(event).ok(); - }); - - app.events(|event| match event { - AppEvent::Init => { - webview.start(); - } - AppEvent::Ready => { - shared::with_gl(|surface, _| { - player.setup(Rc::new(surface.display())); - }); - } - AppEvent::Resized(size) => { - with_gl(|surface, context| { - surface.resize( - context, - NonZeroU32::new(size.0 as u32).unwrap(), - NonZeroU32::new(size.1 as u32).unwrap(), - ); - - with_renderer_write(|renderer| { - renderer.resize(size.0, size.1); - }); - - webview.update(); - needs_redraw = true; - }); - } - AppEvent::Focused(state) => { - webview.focused(state); - } - AppEvent::Visibility(visible) => { - let message = ipc::create_response(IpcEvent::Visibility(visible)); - webview.post_message(message); + let args = Args::parse(); - tray.update(visible); - } - AppEvent::Minimized(minimized) => { - let message = ipc::create_response(IpcEvent::Minimized(minimized)); - webview.post_message(message); - } - AppEvent::Fullscreen(fullscreen) => { - let message = ipc::create_response(IpcEvent::Fullscreen(fullscreen)); - webview.post_message(message); - } - AppEvent::MouseMoved(state) => { - webview.mouse_moved(state); - } - AppEvent::MouseWheel(state) => { - webview.mouse_wheel(state); - } - AppEvent::MouseInput(state) => { - webview.mouse_input(state); - } - AppEvent::TouchInput(touch) => { - webview.touch_input(touch); - } - AppEvent::KeyboardInput((key_event, modifiers)) => { - webview.keyboard_input(key_event, modifiers); - } - AppEvent::FileHover((path, state)) => { - webview.file_hover(path, state); - } - AppEvent::FileDrop(state) => { - webview.file_drop(state); - } - AppEvent::FileCancel => { - webview.file_cancel(); - } - }); + let runtime = Runtime::new().expect("Failed to create Tokio runtime"); - webview.events(|event| match event { - WebViewEvent::Ready => { - webview.navigate(&args.url); - webview.dev_tools(args.dev); - } - WebViewEvent::Loaded => { - if let Some(deeplink) = &args.open - && deeplink.starts_with(URI_SCHEME) - { - let message = ipc::create_response(IpcEvent::OpenMedia(deeplink.to_string())); - webview.post_message(message); - } - } - WebViewEvent::Paint => { - needs_redraw = true; - } - WebViewEvent::Resized => { - webview.update(); - needs_redraw = true; - } - WebViewEvent::Cursor(cursor) => { - app.set_cursor(cursor); - } - WebViewEvent::Open(url) => { - futures::executor::block_on(app.open_url(url)); - } - WebViewEvent::Ipc(data) => ipc::parse_request(data, |event| match event { - IpcEvent::Init(id) => { - let message = ipc::create_response(IpcEvent::Init(id)); - webview.post_message(message); - } - IpcEvent::Fullscreen(state) => { - app.set_fullscreen(state); - } - IpcEvent::OpenExternal(url) => { - futures::executor::block_on(app.open_url(url)); - } - IpcEvent::Quit => { - event_loop_proxy.send_event(UserEvent::Quit).ok(); - } - IpcEvent::Mpv(event) => match event { - IpcEventMpv::Observe(name) => { - player.observe_property(name); - } - IpcEventMpv::Command((name, args)) => { - player.command(name, args); - } - IpcEventMpv::Set(property) => { - player.set_property(property); - } - _ => {} - }, - _ => {} - }), - }); + let mut server = Server::new(); + server.start(args.dev).expect("Failed to start server"); - player.events(|event| match event { - PlayerEvent::Start => { - futures::executor::block_on(app.disable_idling()); - } - PlayerEvent::Stop(error) => { - futures::executor::block_on(app.enable_idling()); + let app = Application::new(); + app.set_property("dev-mode", args.dev); + app.set_property("startup-url", args.url); + app.set_property("open-uri", args.open); + app.set_property("decorations", !args.no_window_decorations); + app.set_browser(chromium); - let message = ipc::create_response(IpcEvent::Mpv(IpcEventMpv::Ended(error))); - webview.post_message(message); - } - PlayerEvent::Update => { - needs_redraw = true; - } - PlayerEvent::PropertyChange(property) => { - let message = ipc::create_response(IpcEvent::Mpv(IpcEventMpv::Change(property))); - webview.post_message(message); - } - }); - } + runtime.block_on(app.run()) } diff --git a/src/player/mod.rs b/src/player/mod.rs deleted file mode 100644 index 44b8a85..0000000 --- a/src/player/mod.rs +++ /dev/null @@ -1,306 +0,0 @@ -mod constants; - -use std::{env, ffi::CString, os::raw::c_void, rc::Rc}; - -use constants::{BOOL_PROPERTIES, FLOAT_PROPERTIES, STRING_PROPERTIES}; -use crossbeam_channel::{Receiver, Sender, unbounded}; -use glutin::{display::Display, prelude::GlDisplay}; -use itertools::Itertools; -use libc::{LC_NUMERIC, setlocale}; -use libmpv2::{ - Format, Mpv, - events::{Event, EventContext, PropertyData}, - render::{OpenGLInitParams, RenderContext, RenderParam, RenderParamApiType}, -}; -use rust_i18n::t; -use serde::{Deserialize, Serialize, Serializer, ser::SerializeStruct}; -use serde_json::{Number, Value}; -use tracing::error; - -pub type GLContext = Rc; - -#[derive(Debug)] -pub enum MpvPropertyValue { - Float(f64), - Bool(bool), - String(String), -} - -impl Serialize for MpvPropertyValue { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - match self { - MpvPropertyValue::Float(value) => serializer.serialize_f64(*value), - MpvPropertyValue::Bool(value) => serializer.serialize_bool(*value), - MpvPropertyValue::String(value) => { - if let Ok(json_value) = serde_json::from_str::(value) { - json_value.serialize(serializer) - } else { - serializer.serialize_str(value) - } - } - } - } -} - -#[derive(Deserialize, Debug)] -pub struct MpvProperty(pub String, pub Option); - -impl MpvProperty { - pub fn name(&self) -> &str { - self.0.as_ref() - } - - pub fn value(&self) -> Result { - if let Some(value) = self.1.clone() { - if FLOAT_PROPERTIES.contains(&self.name()) { - return serde_json::from_value::(value) - .map(MpvPropertyValue::Float) - .map_err(|_| "Failed to get f64 from Value"); - } - - if BOOL_PROPERTIES.contains(&self.name()) { - return serde_json::from_value::(value) - .map(MpvPropertyValue::Bool) - .map_err(|_| "Failed to get bool from Value"); - } - - if STRING_PROPERTIES.contains(&self.name()) { - return serde_json::from_value::(value) - .map(MpvPropertyValue::String) - .map_err(|_| "Failed to get String from Value"); - } - } - - Err("Failed to get value of MpvProperty") - } -} - -impl Serialize for MpvProperty { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut state = serializer.serialize_struct("MpvProperty", 2)?; - state.serialize_field("name", self.name())?; - - if let Ok(value) = self.value() { - state.serialize_field("data", &value)?; - } - - state.end() - } -} - -#[derive(Debug)] -pub enum PlayerEvent { - Start, - Stop(Option), - Update, - PropertyChange(MpvProperty), -} - -impl<'a> TryFrom> for PlayerEvent { - type Error = &'static str; - - fn try_from(value: Event<'a>) -> Result { - match value { - Event::StartFile => Ok(PlayerEvent::Start), - Event::EndFile(code) => { - let error = match code { - 3 => Some(t!("player_error_quit")), - 4 => Some(t!("player_error_general")), - _ => None, - }; - - Ok(PlayerEvent::Stop(error.map(String::from))) - } - Event::PropertyChange { name, change, .. } => { - let property = match change { - PropertyData::Double(value) => MpvProperty( - name.to_owned(), - Some(Value::Number(Number::from_f64(value).unwrap())), - ), - PropertyData::Flag(value) => { - MpvProperty(name.to_owned(), Some(Value::Bool(value))) - } - PropertyData::Str(value) => { - MpvProperty(name.to_owned(), Some(Value::String(value.to_owned()))) - } - _ => return Err("Property not supported"), - }; - - Ok(PlayerEvent::PropertyChange(property)) - } - _ => Err("Event not supported"), - } - } -} - -pub struct Player { - mpv: Mpv, - event_context: EventContext, - render_context: Option, - sender: Sender, - receiver: Receiver, -} - -impl Player { - pub fn new() -> Self { - // Required for libmpv to work alongside gtk - unsafe { - setlocale(LC_NUMERIC, c"C".as_ptr()); - } - - let log = env::var("RUST_LOG"); - let msg_level = match log { - Ok(scope) => &format!("all={}", scope.as_str()), - _ => "all=no", - }; - - let mpv = Mpv::with_initializer(|init| { - init.set_property("vo", "libmpv")?; - init.set_property("video-timing-offset", "0")?; - init.set_property("terminal", "yes")?; - init.set_property("msg-level", msg_level)?; - Ok(()) - }) - .expect("Failed to create mpv"); - - let event_context = EventContext::new(mpv.ctx); - event_context - .disable_deprecated_events() - .expect("Failed to disable deprecated events"); - - let (sender, receiver) = unbounded::(); - - Self { - mpv, - event_context, - render_context: None, - sender, - receiver, - } - } - - pub fn setup(&mut self, context: GLContext) { - self.render_context.take(); - - fn get_proc_address(context: &GLContext, name: &str) -> *mut c_void { - let procname = CString::new(name).unwrap(); - context.get_proc_address(procname.as_c_str()) as _ - } - - let mpv_handle = unsafe { self.mpv.ctx.as_mut() }; - - let mut render_context = RenderContext::new( - mpv_handle, - vec![ - RenderParam::ApiType(RenderParamApiType::OpenGl), - RenderParam::InitParams(OpenGLInitParams { - get_proc_address, - ctx: context, - }), - RenderParam::BlockForTargetTime(false), - // RenderParam::AdvancedControl(true), - ], - ) - .expect("Failed to create render context"); - - let sender = self.sender.clone(); - render_context.set_update_callback(move || { - sender.send(PlayerEvent::Update).ok(); - }); - - self.render_context = Some(render_context); - } - - pub fn render(&self, fbo: u32, width: i32, height: i32) { - if let Some(render_context) = self.render_context.as_ref() { - render_context - .render::(fbo as i32, width, height, false) - .expect("Failed to draw on glutin window"); - } - } - - pub fn report_swap(&self) { - if let Some(render_context) = self.render_context.as_ref() { - render_context.report_swap(); - } - } - - pub fn events(&mut self, handler: T) { - self.receiver.try_iter().for_each(handler); - - let sender = self.sender.clone(); - if let Some(result) = self.event_context.wait_event(0.0) { - match result { - Ok(event) => { - if let Ok(player_event) = PlayerEvent::try_from(event) { - sender.send(player_event).ok(); - } - } - Err(e) => { - eprintln!("Mpv error: {e}") - } - } - }; - } - - pub fn command(&self, name: String, args: Vec) { - let args = args.iter().map(String::as_ref).collect_vec(); - if let Err(e) = self.mpv.command(&name, &args) { - error!("Failed to use command {name}: {e}"); - } - } - - pub fn observe_property(&self, name: String) { - let format = match name.as_str() { - name if FLOAT_PROPERTIES.contains(&name) => Some(Format::Double), - name if BOOL_PROPERTIES.contains(&name) => Some(Format::Flag), - name if STRING_PROPERTIES.contains(&name) => Some(Format::String), - _ => None, - }; - - if let Some(format) = format - && let Err(e) = self.event_context.observe_property(&name, format, 0) - { - error!("Failed to observe property {name}: {e}"); - } - } - - pub fn set_property(&self, property: MpvProperty) { - match property.name() { - name if FLOAT_PROPERTIES.contains(&name) => { - if let Ok(MpvPropertyValue::Float(value)) = property.value() - && let Err(e) = self.mpv.set_property(name, value) - { - error!("Failed to set property {name}: {e}"); - } - } - name if BOOL_PROPERTIES.contains(&name) => { - if let Ok(MpvPropertyValue::Bool(value)) = property.value() - && let Err(e) = self.mpv.set_property(name, value) - { - error!("Failed to set property {name}: {e}"); - } - } - name if STRING_PROPERTIES.contains(&name) => { - if let Ok(MpvPropertyValue::String(value)) = property.value() - && let Err(e) = self.mpv.set_property(name, value) - { - error!("Failed to set property {name}: {e}"); - } - } - name => error!("Failed to set property {name}: Unsupported"), - }; - } -} - -impl Drop for Player { - fn drop(&mut self) { - self.render_context.take(); - } -} diff --git a/src/server.rs b/src/server.rs index a782427..7b0e99a 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,5 +1,7 @@ use std::{ + env, io::{BufRead, BufReader}, + path::PathBuf, process::{self, Child, Command}, thread, }; @@ -7,25 +9,26 @@ use std::{ use anyhow::{Context, Ok}; use tracing::debug; -use crate::config::ServerConfig; - pub struct Server { - config: ServerConfig, process: Option, + file: PathBuf, } impl Server { - pub fn new(config: ServerConfig) -> Self { + pub fn new() -> Self { + let server_path = env::var("SERVER_PATH").expect("Failed to read SERVER_PATH env"); + let file = PathBuf::from(&server_path); + Self { - config, process: None, + file, } } pub fn start(&mut self, dev: bool) -> anyhow::Result<()> { let mut child = Command::new("node") .env("NO_CORS", (dev as i32).to_string()) - .arg(self.config.file.as_os_str()) + .arg(self.file.as_os_str()) .stdout(process::Stdio::piped()) .spawn() .context("Failed to start server")?; diff --git a/src/shared/ipc/event.rs b/src/shared/ipc/event.rs new file mode 100644 index 0000000..5302f90 --- /dev/null +++ b/src/shared/ipc/event.rs @@ -0,0 +1,35 @@ +use serde::Deserialize; +use serde_json::Value; + +use super::request::IpcMessageRequest; + +#[derive(Deserialize, Debug)] +pub enum IpcEventMpv { + Observe(String), + Command((String, Vec)), + Set((String, Value)), + Change((String, Value)), + Ended(Option), +} + +#[derive(Deserialize, Debug)] +pub enum IpcEvent { + Init, + Ready, + Quit, + Fullscreen(bool), + Visibility(bool), + OpenMedia(String), + Mpv(IpcEventMpv), +} + +impl TryFrom<&str> for IpcEvent { + type Error = String; + + fn try_from(value: &str) -> Result { + serde_json::from_str::(value) + .map_err(|e| format!("Failed to convert String to IpcEvent: {e}"))? + .try_into() + .map_err(|e| format!("Failed to convert IpcEvent to IpcMessageRequest: {e}")) + } +} diff --git a/src/shared/ipc/mod.rs b/src/shared/ipc/mod.rs new file mode 100644 index 0000000..783ebe0 --- /dev/null +++ b/src/shared/ipc/mod.rs @@ -0,0 +1,18 @@ +pub mod event; +mod request; +mod response; + +use event::IpcEvent; +use response::IpcMessageResponse; + +const VERSION: &str = env!("CARGO_PKG_VERSION"); +const TRANSPORT_NAME: &str = "transport"; + +pub fn parse_request(data: &str) -> Result { + IpcEvent::try_from(data).map_err(|e| eprintln!("{e}")) +} + +pub fn create_response(event: IpcEvent) -> String { + let message = IpcMessageResponse::try_from(event).ok(); + serde_json::to_string(&message).expect("Failed to convert IpcMessage to string") +} diff --git a/src/shared/ipc/request.rs b/src/shared/ipc/request.rs new file mode 100644 index 0000000..5a0100d --- /dev/null +++ b/src/shared/ipc/request.rs @@ -0,0 +1,85 @@ +use serde::Deserialize; +use serde_json::Value; + +use super::event::{IpcEvent, IpcEventMpv}; + +#[derive(Deserialize, Debug)] +pub struct IpcMessageRequest { + r#type: u8, + #[serde(skip_serializing_if = "Option::is_none")] + args: Option, +} + +#[derive(Deserialize, Debug)] +pub struct IpcMessageRequestWinSetVisilibty { + fullscreen: bool, +} + +impl TryFrom for IpcEvent { + type Error = &'static str; + + fn try_from(value: IpcMessageRequest) -> Result { + match value.r#type { + 3 => Ok(IpcEvent::Init), + 6 => match value.args { + Some(args) => { + let args: Vec = serde_json::from_value(args).expect("Invalid arguments"); + let name = args.first().and_then(Value::as_str).ok_or("Invalid name")?; + let data = args.get(1).cloned(); + + match data { + Some(data) => match name { + "app-ready" => Ok(IpcEvent::Ready), + "win-set-visibility" => { + let data: IpcMessageRequestWinSetVisilibty = + serde_json::from_value(data) + .expect("Invalid win-set-visibility object"); + + Ok(IpcEvent::Fullscreen(data.fullscreen)) + } + "mpv-command" => { + let data: Vec = serde_json::from_value(data) + .expect("Invalid mpv-command arguments"); + let name = data[0].clone(); + + let mut args = vec![]; + for arg in data.iter().skip(1) { + args.push(arg.clone()); + } + + Ok(IpcEvent::Mpv(IpcEventMpv::Command((name, args)))) + } + "mpv-observe-prop" => { + let name = data.as_str().expect("Invalid mpv-observe-prop name"); + Ok(IpcEvent::Mpv(IpcEventMpv::Observe(name.to_owned()))) + } + "mpv-set-prop" => { + let key_value: Vec = serde_json::from_value(data) + .expect("Invalid mpv-set-prop arguments"); + + let name = key_value[0] + .as_str() + .expect("Invalid mpv-set-prop name") + .to_owned(); + + let value = key_value + .get(1) + .expect("Invalid mpv-set-prop value") + .to_owned(); + + Ok(IpcEvent::Mpv(IpcEventMpv::Set((name, value)))) + } + _ => Err("Unknown method"), + }, + None => match name { + "quit" => Ok(IpcEvent::Quit), + _ => Err("Unknown method"), + }, + } + } + None => Err("Missing args"), + }, + _ => Err("Unknown type"), + } + } +} diff --git a/src/shared/ipc/response.rs b/src/shared/ipc/response.rs new file mode 100644 index 0000000..cfbe5a7 --- /dev/null +++ b/src/shared/ipc/response.rs @@ -0,0 +1,98 @@ +use serde::Serialize; +use serde_json::json; + +use super::{ + TRANSPORT_NAME, VERSION, + event::{IpcEvent, IpcEventMpv}, +}; + +#[derive(Serialize, Debug)] +pub struct IpcMessageResponse { + id: u64, + r#type: u8, + object: String, + #[serde(skip_serializing_if = "Option::is_none")] + data: Option, + #[serde(skip_serializing_if = "Option::is_none")] + args: Option, +} + +impl TryFrom for IpcMessageResponse { + type Error = &'static str; + + fn try_from(value: IpcEvent) -> Result { + match value { + IpcEvent::Init => Ok(IpcMessageResponse { + id: 0, + r#type: 3, + object: TRANSPORT_NAME.to_owned(), + args: None, + data: Some(json!({ + "transport": { + "properties": [[], ["", "shellVersion", "", VERSION]], + "signals": [], + "methods": [["onEvent"]] + } + })), + }), + IpcEvent::Fullscreen(state) => Ok(IpcMessageResponse { + id: 1, + r#type: 1, + object: TRANSPORT_NAME.to_owned(), + data: None, + args: Some(json!([ + "win-visibility-changed", + { + "visible": true, + "visibility": 1, + "isFullscreen": state, + } + ])), + }), + IpcEvent::Visibility(state) => Ok(IpcMessageResponse { + id: 1, + r#type: 1, + object: TRANSPORT_NAME.to_owned(), + data: None, + args: Some(json!([ + "win-visibility-changed", + { + "visible": state, + "visibility": state as u32, + "isFullscreen": false, + } + ])), + }), + IpcEvent::OpenMedia(deeplink) => Ok(IpcMessageResponse { + id: 1, + r#type: 1, + object: TRANSPORT_NAME.to_owned(), + data: None, + args: Some(json!(["open-media", deeplink])), + }), + IpcEvent::Mpv(IpcEventMpv::Change((name, value))) => Ok(IpcMessageResponse { + id: 1, + r#type: 1, + object: TRANSPORT_NAME.to_owned(), + data: None, + args: Some(json!(["mpv-prop-change", { + "name": name, + "data": value, + }])), + }), + IpcEvent::Mpv(IpcEventMpv::Ended(error)) => Ok(IpcMessageResponse { + id: 1, + r#type: 1, + object: TRANSPORT_NAME.to_owned(), + data: None, + args: Some(json!([ + "mpv-event-ended", + { + "error": error, + } + ])), + }), + _ => Err("Failed to convert IpcEvent to IpcMessageResponse"), + } + } +} diff --git a/src/shared/mod.rs b/src/shared/mod.rs index 9d81cc4..7e0af44 100644 --- a/src/shared/mod.rs +++ b/src/shared/mod.rs @@ -1,102 +1,15 @@ -mod renderer; -pub mod types; - -use std::{ - num::NonZeroU32, - sync::{Mutex, RwLock}, -}; - -use glutin::{ - context::{NotCurrentContext, PossiblyCurrentContext}, - prelude::{NotCurrentGlContext, PossiblyCurrentGlContext}, - surface::{GlSurface, Surface, SwapInterval, WindowSurface}, -}; -use renderer::Renderer; -use tracing::warn; - -pub static RENDERER: RwLock> = RwLock::new(None); - -pub fn create_renderer(default_size: (i32, i32), refresh_rate: u32) { - if let Ok(mut guard) = RENDERER.write() { - *guard = Some(Renderer::new(default_size, refresh_rate)); - } -} - -pub fn with_renderer_read(handler: T) { - if let Ok(renderer) = RENDERER.read() - && let Some(renderer) = renderer.as_ref() - { - handler(renderer) - } -} - -pub fn with_renderer_write(handler: T) { - if let Ok(mut renderer) = RENDERER.write() - && let Some(renderer) = renderer.as_mut() - { - handler(renderer) - } -} - -pub fn drop_renderer() { - if let Ok(mut renderer) = RENDERER.write() { - renderer.take(); - } -} - -pub static GL_SURFACE: Mutex>> = Mutex::new(None); -pub static GL_CONTEXT: Mutex> = Mutex::new(None); - -pub fn create_gl(surface: Surface, context: NotCurrentContext) { - let current_context = context - .make_current(&surface) - .expect("Failed to make context current"); - - let swap_interval = SwapInterval::Wait(NonZeroU32::new(1).unwrap()); - surface - .set_swap_interval(¤t_context, swap_interval) - .map_err(|e| warn!("Failed to enable VSync: {e}")) - .ok(); - - let not_current_context = current_context - .make_not_current() - .expect("Failed to make context not current"); - - if let Ok(mut guard) = GL_SURFACE.lock() { - *guard = Some(surface); - } - - if let Ok(mut guard) = GL_CONTEXT.lock() { - *guard = Some(not_current_context); - } -} - -pub fn with_gl, &PossiblyCurrentContext)>(mut handler: T) { - if let Ok(surface) = GL_SURFACE.lock() - && let Some(surface) = surface.as_ref() - && let Ok(mut guard) = GL_CONTEXT.lock() - && let Some(context) = guard.take() - { - let current_context = context - .make_current(surface) - .expect("Failed to make context current"); - - handler(surface, ¤t_context); - - let not_current_context = current_context - .make_not_current() - .expect("Failed to make context not current"); - - *guard = Some(not_current_context); - }; -} - -pub fn drop_gl() { - if let Ok(mut surface) = GL_SURFACE.lock() { - surface.take(); - } - - if let Ok(mut context) = GL_CONTEXT.lock() { - context.take(); - } +pub mod ipc; +pub mod states; + +use std::sync::Arc; + +#[derive(Default, Debug)] +pub struct Frame { + pub x: i32, + pub y: i32, + pub width: i32, + pub height: i32, + pub full_width: i32, + pub full_height: i32, + pub buffer: Arc<[u8]>, } diff --git a/src/shared/renderer/constants.rs b/src/shared/renderer/constants.rs deleted file mode 100644 index 3c4da04..0000000 --- a/src/shared/renderer/constants.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub const FRAGMENT_SRC: &str = include_str!("shader.frag"); -pub const VERTEX_SRC: &str = include_str!("shader.vert"); -pub const BYTES_PER_PIXEL: i32 = 4; diff --git a/src/shared/renderer/mod.rs b/src/shared/renderer/mod.rs deleted file mode 100644 index 518a727..0000000 --- a/src/shared/renderer/mod.rs +++ /dev/null @@ -1,174 +0,0 @@ -mod constants; -mod utils; - -use std::ptr; - -use constants::{BYTES_PER_PIXEL, FRAGMENT_SRC, VERTEX_SRC}; -use gl::types::{GLint, GLuint}; - -#[derive(Debug)] -pub struct Renderer { - pub program: GLuint, - pub front_texture: GLuint, - pub front_uniform: GLint, - pub back_texture: GLuint, - pub back_uniform: GLint, - pub vao: GLuint, - pub vbo: GLuint, - pub fbo: GLuint, - pub pbo: GLuint, - pub width: i32, - pub height: i32, - pub refresh_rate: u32, -} - -impl Renderer { - pub fn new((width, height): (i32, i32), refresh_rate: u32) -> Self { - unsafe { - let vertex_shader = utils::compile_shader(gl::VERTEX_SHADER, VERTEX_SRC); - let fragment_shader = utils::compile_shader(gl::FRAGMENT_SHADER, FRAGMENT_SRC); - let program = gl::CreateProgram(); - - gl::AttachShader(program, vertex_shader); - gl::AttachShader(program, fragment_shader); - - gl::LinkProgram(program); - gl::UseProgram(program); - - gl::DeleteShader(vertex_shader); - gl::DeleteShader(fragment_shader); - - let front_texture = utils::create_texture(width, height); - let front_uniform = gl::GetUniformLocation(program, c"front_texture".as_ptr() as _); - - let back_texture = utils::create_texture(width, height); - let back_uniform = gl::GetUniformLocation(program, c"back_texture".as_ptr() as _); - - let (vao, vbo) = utils::create_geometry(program); - let fbo = utils::create_fbo(back_texture); - - let pbo = utils::create_pbo(width, height); - - let status = gl::CheckFramebufferStatus(gl::FRAMEBUFFER); - if status != gl::FRAMEBUFFER_COMPLETE { - panic!("Framebuffer not complete: {status}"); - } - - Self { - program, - front_texture, - front_uniform, - back_texture, - back_uniform, - vao, - vbo, - fbo, - pbo, - width, - height, - refresh_rate, - } - } - } - - pub fn resize(&mut self, width: i32, height: i32) { - unsafe { - self.width = width; - self.height = height; - - gl::Viewport(0, 0, width, height); - - utils::resize_texture(self.back_texture, width, height); - } - } - - // A Pixel Buffer Object (PBO) is used to upload the buffer directly to the GPU, - // offering better performance than direct texture uploads. - // This helps reduce the time the current GL context remains locked. - pub fn paint( - &self, - x: i32, - y: i32, - width: i32, - height: i32, - buffer: *const u8, - full_width: i32, - ) { - utils::resize_pbo(self.pbo, self.width, self.height); - utils::resize_texture(self.front_texture, self.width, self.height); - - unsafe { - gl::BindBuffer(gl::PIXEL_UNPACK_BUFFER, self.pbo); - - let row_bytes = width * BYTES_PER_PIXEL; - let stride = full_width * BYTES_PER_PIXEL; - - let ptr = gl::MapBuffer(gl::PIXEL_UNPACK_BUFFER, gl::WRITE_ONLY) as *mut u8; - if !ptr.is_null() { - for row in 0..height { - let src_offset = (y + row) * stride + (x * BYTES_PER_PIXEL); - let dst_offset = row * row_bytes; - - let src_ptr = buffer.add(src_offset as usize); - let dst_ptr = ptr.add(dst_offset as usize); - - ptr::copy_nonoverlapping(src_ptr, dst_ptr, row_bytes as usize); - } - - gl::UnmapBuffer(gl::PIXEL_UNPACK_BUFFER); - } - - gl::BindTexture(gl::TEXTURE_2D, self.front_texture); - gl::TexSubImage2D( - gl::TEXTURE_2D, - 0, - x, - y, - width, - height, - gl::BGRA, - gl::UNSIGNED_BYTE, - std::ptr::null(), - ); - - gl::BindBuffer(gl::PIXEL_UNPACK_BUFFER, 0); - } - } - - pub fn draw(&self) { - unsafe { - gl::Enable(gl::BLEND); - gl::BlendFunc(gl::ONE, gl::ONE_MINUS_SRC_ALPHA); - gl::BlendEquation(gl::FUNC_ADD); - - gl::UseProgram(self.program); - - gl::ActiveTexture(gl::TEXTURE0); - gl::BindTexture(gl::TEXTURE_2D, self.back_texture); - gl::Uniform1i(self.back_uniform, 0); - - gl::ActiveTexture(gl::TEXTURE1); - gl::BindTexture(gl::TEXTURE_2D, self.front_texture); - gl::Uniform1i(self.front_uniform, 1); - - gl::BindVertexArray(self.vao); - gl::ClearColor(0.0, 0.0, 0.0, 1.0); - gl::Clear(gl::COLOR_BUFFER_BIT); - gl::DrawArrays(gl::TRIANGLE_STRIP, 0, 4); - } - } -} - -impl Drop for Renderer { - fn drop(&mut self) { - unsafe { - gl::DeleteProgram(self.program); - gl::DeleteTextures(1, &self.front_texture); - gl::DeleteTextures(1, &self.back_texture); - gl::DeleteBuffers(1, &self.vbo); - gl::DeleteVertexArrays(1, &self.vao); - gl::DeleteBuffers(1, &self.pbo); - gl::DeleteBuffers(1, &self.fbo); - } - } -} diff --git a/src/shared/renderer/shader.frag b/src/shared/renderer/shader.frag deleted file mode 100644 index ce14dff..0000000 --- a/src/shared/renderer/shader.frag +++ /dev/null @@ -1,14 +0,0 @@ -#version 330 core - -uniform sampler2D back_texture; -uniform sampler2D front_texture; - -in vec2 v_texcoord; -out vec4 frag_color; - -void main() { - vec4 back_color = texture(back_texture, vec2(v_texcoord.x, 1.0 - v_texcoord.y)); - vec4 front_color = texture(front_texture, vec2(v_texcoord.x, 1.0 - v_texcoord.y)); - - frag_color = mix(back_color, front_color, front_color.a); -} \ No newline at end of file diff --git a/src/shared/renderer/utils.rs b/src/shared/renderer/utils.rs deleted file mode 100644 index be4a65d..0000000 --- a/src/shared/renderer/utils.rs +++ /dev/null @@ -1,192 +0,0 @@ -use std::{mem, ptr}; - -use gl::types::{GLenum, GLfloat, GLint, GLsizei, GLsizeiptr, GLuint}; - -use super::constants::BYTES_PER_PIXEL; - -pub fn create_fbo(texture: GLuint) -> GLuint { - unsafe { - let mut fbo = 0; - gl::GenFramebuffers(1, &mut fbo); - gl::BindFramebuffer(gl::FRAMEBUFFER, fbo); - gl::FramebufferTexture2D( - gl::FRAMEBUFFER, - gl::COLOR_ATTACHMENT0, - gl::TEXTURE_2D, - texture, - 0, - ); - - gl::BindFramebuffer(gl::FRAMEBUFFER, 0); - - fbo - } -} - -pub fn create_pbo(width: i32, height: i32) -> GLuint { - unsafe { - let mut pbo = 0; - gl::GenBuffers(1, &mut pbo); - - gl::BindBuffer(gl::PIXEL_UNPACK_BUFFER, pbo); - gl::BufferData( - gl::PIXEL_UNPACK_BUFFER, - (width * height * BYTES_PER_PIXEL) as GLsizeiptr, - ptr::null(), - gl::STREAM_DRAW, - ); - - gl::BindBuffer(gl::PIXEL_UNPACK_BUFFER, 0); - - pbo - } -} - -pub fn create_geometry(program: u32) -> (GLuint, GLuint) { - unsafe { - let vertices: [f32; 16] = [ - -1.0, -1.0, 0.0, 0.0, 1.0, -1.0, 1.0, 0.0, -1.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, - ]; - - let mut vbo = 0; - gl::GenBuffers(1, &mut vbo); - gl::BindBuffer(gl::ARRAY_BUFFER, vbo); - - gl::BufferData( - gl::ARRAY_BUFFER, - (vertices.len() * mem::size_of::()) as GLsizeiptr, - vertices.as_ptr() as _, - gl::STATIC_DRAW, - ); - - let mut vao = 0; - gl::GenVertexArrays(1, &mut vao); - gl::BindVertexArray(vao); - - let pos_attrib = gl::GetAttribLocation(program, c"position".as_ptr() as _); - gl::EnableVertexAttribArray(pos_attrib as GLuint); - gl::VertexAttribPointer( - pos_attrib as GLuint, - 2, - gl::FLOAT, - gl::FALSE, - (4 * mem::size_of::()) as GLsizei, - ptr::null(), - ); - - let tex_attrib = gl::GetAttribLocation(program, c"texcoord".as_ptr() as _); - gl::EnableVertexAttribArray(tex_attrib as GLuint); - gl::VertexAttribPointer( - tex_attrib as GLuint, - 2, - gl::FLOAT, - gl::FALSE, - (4 * mem::size_of::()) as GLsizei, - (2 * mem::size_of::()) as _, - ); - - (vao, vbo) - } -} - -pub fn create_texture(width: i32, height: i32) -> GLuint { - unsafe { - let mut texture = 0; - gl::GenTextures(1, &mut texture); - gl::BindTexture(gl::TEXTURE_2D, texture); - - gl::TexImage2D( - gl::TEXTURE_2D, - 0, - gl::RGBA8 as GLint, - width, - height, - 0, - gl::BGRA, - gl::UNSIGNED_BYTE, - ptr::null(), - ); - - gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAX_LEVEL, 0); - gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as GLint); - gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as GLint); - gl::TexParameteri( - gl::TEXTURE_2D, - gl::TEXTURE_WRAP_S, - gl::CLAMP_TO_EDGE as GLint, - ); - gl::TexParameteri( - gl::TEXTURE_2D, - gl::TEXTURE_WRAP_T, - gl::CLAMP_TO_EDGE as GLint, - ); - - texture - } -} - -pub fn compile_shader(kind: GLenum, src: &str) -> GLuint { - unsafe { - let shader = gl::CreateShader(kind); - let c_str = std::ffi::CString::new(src.as_bytes()).unwrap(); - gl::ShaderSource(shader, 1, &c_str.as_ptr(), ptr::null()); - gl::CompileShader(shader); - - let mut success = 0; - gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut success); - if success == 0 { - let mut len = 0; - gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len); - - let mut buffer = vec![0u8; len as usize]; - gl::GetShaderInfoLog(shader, len, ptr::null_mut(), buffer.as_mut_ptr() as *mut i8); - - panic!( - "Shader compile error: {}", - std::str::from_utf8(&buffer).unwrap() - ); - } - - shader - } -} - -pub fn resize_pbo(pbo: GLuint, width: i32, height: i32) { - unsafe { - gl::BindBuffer(gl::PIXEL_UNPACK_BUFFER, pbo); - - let mut pbo_size = 0; - gl::GetBufferParameteriv(gl::PIXEL_UNPACK_BUFFER, gl::BUFFER_SIZE, &mut pbo_size); - - let new_size = width * height * BYTES_PER_PIXEL; - if new_size > pbo_size { - gl::BufferData( - gl::PIXEL_UNPACK_BUFFER, - new_size as GLsizeiptr, - ptr::null(), - gl::STREAM_DRAW, - ); - } - - gl::BindBuffer(gl::PIXEL_UNPACK_BUFFER, 0); - } -} - -pub fn resize_texture(texture: GLuint, width: i32, height: i32) { - unsafe { - gl::BindTexture(gl::TEXTURE_2D, texture); - gl::TexImage2D( - gl::TEXTURE_2D, - 0, - gl::RGBA8 as GLint, - width, - height, - 0, - gl::BGRA, - gl::UNSIGNED_BYTE, - ptr::null(), - ); - - gl::BindTexture(gl::TEXTURE_2D, 0); - } -} diff --git a/src/shared/states.rs b/src/shared/states.rs new file mode 100644 index 0000000..1878e2f --- /dev/null +++ b/src/shared/states.rs @@ -0,0 +1,237 @@ +use std::cell::Cell; + +use cef::sys::{ + cef_event_flags_t as EventFlags, cef_key_event_type_t::KEYEVENT_KEYDOWN, + cef_key_event_type_t::KEYEVENT_KEYUP, +}; +use cef::{KeyEvent, MouseEvent}; +use gtk::gdk::ModifierType; + +#[derive(Default, Debug)] +pub struct PointerState { + position: Cell<(f64, f64)>, + pressed: Cell, + button: Cell, + over: Cell, +} + +impl PointerState { + pub fn position(&self) -> (f64, f64) { + self.position.get() + } + + pub fn set_position(&self, x: f64, y: f64) { + self.position.set((x, y)); + } + + pub fn pressed(&self) -> bool { + self.pressed.get() + } + + pub fn set_pressed(&self, pressed: bool) { + self.pressed.set(pressed); + } + + pub fn button(&self) -> u32 { + self.button.get() + } + + pub fn set_button(&self, r#type: u32) { + self.button.set(r#type); + } + + pub fn over(&self) -> bool { + self.over.get() + } + + pub fn set_over(&self, state: bool) { + self.over.set(state); + } +} + +impl From<&PointerState> for MouseEvent { + fn from(pointer_state: &PointerState) -> Self { + let (x, y) = pointer_state.position(); + let pressed = pointer_state.pressed(); + let button = pointer_state.button(); + + MouseEvent { + x: x as i32, + y: y as i32, + modifiers: match button { + 1 if pressed => EventFlags::EVENTFLAG_LEFT_MOUSE_BUTTON.0, + 3 if pressed => EventFlags::EVENTFLAG_MIDDLE_MOUSE_BUTTON.0, + 2 if pressed => EventFlags::EVENTFLAG_RIGHT_MOUSE_BUTTON.0, + _ => 0, + }, + } + } +} + +enum KeyCode { + None, + Backspace, + Tab, + Enter, + Control, + Escape, + Space, + PageUp, + PageDown, + End, + Home, + ArrowLeft, + ArrowUp, + ArrowRight, + ArrowDown, + Equal, + Minus, + A, + C, + V, + X, +} + +impl KeyCode { + fn windows(&self) -> u32 { + match self { + KeyCode::Backspace => 8, + KeyCode::Tab => 9, + KeyCode::Enter => 13, + KeyCode::Control => 17, + KeyCode::Escape => 27, + KeyCode::Space => 32, + KeyCode::PageUp => 33, + KeyCode::PageDown => 34, + KeyCode::End => 35, + KeyCode::Home => 36, + KeyCode::ArrowLeft => 37, + KeyCode::ArrowUp => 38, + KeyCode::ArrowRight => 39, + KeyCode::ArrowDown => 40, + KeyCode::Equal => 61, + KeyCode::A => 65, + KeyCode::C => 67, + KeyCode::V => 86, + KeyCode::X => 88, + KeyCode::Minus => 173, + KeyCode::None => 0, + } + } +} + +impl From for KeyCode { + fn from(value: u32) -> Self { + match value { + 22 => KeyCode::Backspace, + 23 => KeyCode::Tab, + 36 => KeyCode::Enter, + 37 => KeyCode::Control, + 9 => KeyCode::Escape, + 65 => KeyCode::Space, + 112 => KeyCode::PageUp, + 117 => KeyCode::PageDown, + 115 => KeyCode::End, + 110 => KeyCode::Home, + 113 => KeyCode::ArrowLeft, + 111 => KeyCode::ArrowUp, + 114 => KeyCode::ArrowRight, + 116 => KeyCode::ArrowDown, + 21 => KeyCode::Equal, + 38 => KeyCode::A, + 54 => KeyCode::C, + 55 => KeyCode::V, + 53 => KeyCode::X, + 20 => KeyCode::Minus, + _ => KeyCode::None, + } + } +} + +#[derive(Default, Debug)] +pub struct KeyboardState { + character: Cell>, + pressed: Cell, + code: Cell, + control_modifier: Cell, + shift_modifier: Cell, +} + +impl KeyboardState { + pub fn character(&self) -> Option { + self.character.get() + } + + pub fn set_character(&self, character: Option) { + self.character.set(character); + } + + pub fn pressed(&self) -> bool { + self.pressed.get() + } + + pub fn set_pressed(&self, pressed: bool) { + self.pressed.set(pressed); + } + + pub fn code(&self) -> u32 { + self.code.get() + } + + pub fn windows_code(&self) -> u32 { + let code = self.code.get(); + let native_code = KeyCode::from(code); + native_code.windows() + } + + pub fn set_code(&self, code: u32) { + self.code.set(code); + } + + pub fn control_modifier(&self) -> bool { + self.control_modifier.get() + } + + pub fn shift_modifier(&self) -> bool { + self.shift_modifier.get() + } + + pub fn set_modifiers(&self, modifiers: ModifierType) { + let control_modifier = modifiers.contains(ModifierType::CONTROL_MASK); + self.control_modifier.set(control_modifier); + + let shift_modifier = modifiers.contains(ModifierType::SHIFT_MASK); + self.shift_modifier.set(shift_modifier); + } +} + +impl From<&KeyboardState> for KeyEvent { + fn from(keyboard_state: &KeyboardState) -> Self { + let pressed = keyboard_state.pressed(); + let code = keyboard_state.code(); + let windows_code = keyboard_state.windows_code(); + + let event_type = match pressed { + true => KEYEVENT_KEYDOWN.into(), + false => KEYEVENT_KEYUP.into(), + }; + + let mut modifiers = EventFlags::EVENTFLAG_NONE.0; + + if keyboard_state.shift_modifier() { + modifiers |= EventFlags::EVENTFLAG_SHIFT_DOWN.0; + } + + if keyboard_state.control_modifier() { + modifiers |= EventFlags::EVENTFLAG_CONTROL_DOWN.0; + } + + KeyEvent { + type_: event_type, + native_key_code: code as i32, + windows_key_code: windows_code as i32, + modifiers, + ..Default::default() + } + } +} diff --git a/src/shared/types.rs b/src/shared/types.rs deleted file mode 100644 index e57b0d7..0000000 --- a/src/shared/types.rs +++ /dev/null @@ -1,50 +0,0 @@ -use winit::event::MouseButton; - -#[derive(Debug, Clone, Copy)] -pub enum Cursor { - Default, - Pointer, - Text, - Move, - ZoomIn, - ZoomOut, - Wait, - None, -} - -#[derive(Debug, Default, Clone, Copy)] -pub struct MousePosition(pub i32, pub i32); - -#[derive(Debug, Default, Clone, Copy)] -pub struct MouseDelta(pub i32, pub i32); - -#[derive(Debug, Clone, Copy)] -pub struct WindowSize(pub i32, pub i32); - -#[derive(Debug, Clone, Copy)] -pub struct MouseState { - pub button: MouseButton, - pub pressed: bool, - pub position: MousePosition, - pub delta: MouseDelta, - pub over: bool, -} - -impl Default for MouseState { - fn default() -> Self { - Self { - button: MouseButton::Left, - pressed: Default::default(), - position: Default::default(), - delta: Default::default(), - over: Default::default(), - } - } -} - -pub enum UserEvent { - Raise, - Show, - Hide, - Quit, -} diff --git a/src/tray.rs b/src/tray.rs deleted file mode 100644 index ea1a540..0000000 --- a/src/tray.rs +++ /dev/null @@ -1,140 +0,0 @@ -use std::{thread, time::Duration}; - -use crossbeam_channel::{Receiver, Sender, unbounded}; -use gtk::glib; -use rust_i18n::t; -use tray_icon::{ - Icon, TrayIcon, TrayIconBuilder, - menu::{Menu, MenuEvent, MenuItem}, -}; - -use crate::{config::TrayConfig, shared::types::UserEvent}; - -const ICON: &[u8] = include_bytes!(concat!( - env!("CARGO_MANIFEST_DIR"), - "/data/icons/symbolic.png" -)); - -enum TrayEvent { - Visibility(bool), -} - -pub struct Tray { - receiver: Receiver, - tray_sender: Sender, -} - -impl Tray { - pub fn new(config: TrayConfig) -> Self { - let (sender, receiver) = unbounded::(); - let (tray_sender, tray_receiver) = unbounded::(); - - thread::spawn(|| { - gtk::init().expect("Failed to initialize gtk"); - - let language = gtk::default_language(); - if let Some(language) = language { - rust_i18n::set_locale(&language.to_str()); - } - - let menu = Self::create_menu(); - let tray = Self::create(menu, config); - - glib::timeout_add_local(Duration::from_millis(16), move || { - tray_receiver.try_iter().for_each(|event| match event { - TrayEvent::Visibility(state) => { - let menu = Self::create_menu(); - menu.remove_at(0); - - if state { - let hide_item = Self::create_menu_item("hide"); - menu.prepend(&hide_item).ok(); - } else { - let show_item = Self::create_menu_item("show"); - menu.prepend(&show_item).ok(); - } - - tray.set_menu(Some(menu)); - } - }); - - glib::ControlFlow::Continue - }); - - gtk::main(); - }); - - MenuEvent::set_event_handler(Some(move |event: MenuEvent| { - if event.id == "hide" { - sender.send(UserEvent::Hide).ok(); - } - - if event.id == "show" { - sender.send(UserEvent::Show).ok(); - } - - if event.id == "quit" { - sender.send(UserEvent::Quit).ok(); - } - })); - - Self { - receiver, - tray_sender, - } - } - - fn create_menu_item(id: &str) -> MenuItem { - MenuItem::with_id(id, t!(id), true, None) - } - - fn create_menu() -> Box { - let empty_item = Self::create_menu_item(""); - let quit_item = Self::create_menu_item("quit"); - - let version_label = format!("v{}", env!("CARGO_PKG_VERSION")); - let version_item = MenuItem::new(version_label.as_str(), false, None); - - let menu = Menu::new(); - menu.append_items(&[&empty_item, &quit_item, &version_item]) - .expect("Failed to append menu items"); - - Box::new(menu) - } - - fn create(menu: Box, config: TrayConfig) -> TrayIcon { - let icon = load_icon(ICON); - - TrayIconBuilder::new() - .with_menu(menu) - .with_icon(icon) - .with_temp_dir_path(config.icon_path) - .build() - .expect("Failed to build tray icon") - } - - pub fn update(&self, visibility: bool) { - self.tray_sender - .send(TrayEvent::Visibility(visibility)) - .ok(); - } - - pub fn events(&self, handler: F) { - self.receiver.try_iter().for_each(handler); - } -} - -fn load_icon(buffer: &[u8]) -> Icon { - let (icon_rgba, icon_width, icon_height) = { - let image = image::load_from_memory(buffer) - .expect("Failed to open icon path") - .into_rgba8(); - - let (width, height) = image.dimensions(); - let rgba = image.into_raw(); - - (rgba, width, height) - }; - - Icon::from_rgba(icon_rgba, icon_width, icon_height).expect("Failed to open icon") -} diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..f107433 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,6 @@ +#[macro_export] +macro_rules! spawn_local { + ($body:expr) => { + glib::MainContext::default().spawn_local($body) + }; +} diff --git a/src/webview/adapters.rs b/src/webview/adapters.rs deleted file mode 100644 index 9471092..0000000 --- a/src/webview/adapters.rs +++ /dev/null @@ -1,135 +0,0 @@ -use cef_dll_sys::cef_cursor_type_t; -use winit::{event::MouseButton, keyboard::KeyCode}; - -use crate::shared::types::{Cursor, MouseState}; - -impl From for cef::MouseEvent { - fn from(state: MouseState) -> Self { - let modifiers = match state.button { - MouseButton::Left if state.pressed => 16, - MouseButton::Right if state.pressed => 32, - MouseButton::Middle if state.pressed => 64, - _ => 0, - }; - - Self { - x: state.position.0, - y: state.position.1, - modifiers, - } - } -} - -impl From for Cursor { - fn from(value: cef::CursorType) -> Self { - match value.as_ref() { - cef_cursor_type_t::CT_POINTER => Cursor::Default, - cef_cursor_type_t::CT_HAND => Cursor::Pointer, - cef_cursor_type_t::CT_IBEAM => Cursor::Text, - cef_cursor_type_t::CT_MOVE => Cursor::Move, - cef_cursor_type_t::CT_ZOOMIN => Cursor::ZoomIn, - cef_cursor_type_t::CT_ZOOMOUT => Cursor::ZoomOut, - cef_cursor_type_t::CT_WAIT => Cursor::Wait, - cef_cursor_type_t::CT_NONE => Cursor::None, - _ => Cursor::Default, - } - } -} - -pub struct WindowsKeyCode(pub i32); - -impl TryFrom for WindowsKeyCode { - type Error = &'static str; - - fn try_from(value: KeyCode) -> Result { - match value { - KeyCode::Backspace => Ok(Self(8)), - KeyCode::Tab => Ok(Self(9)), - KeyCode::Enter => Ok(Self(13)), - KeyCode::Escape => Ok(Self(27)), - KeyCode::Space => Ok(Self(32)), - KeyCode::PageUp => Ok(Self(33)), - KeyCode::PageDown => Ok(Self(34)), - KeyCode::End => Ok(Self(35)), - KeyCode::Home => Ok(Self(36)), - KeyCode::ArrowLeft => Ok(Self(37)), - KeyCode::ArrowUp => Ok(Self(38)), - KeyCode::ArrowRight => Ok(Self(39)), - KeyCode::ArrowDown => Ok(Self(40)), - KeyCode::Digit0 => Ok(Self(48)), - KeyCode::Digit1 => Ok(Self(49)), - KeyCode::Digit2 => Ok(Self(50)), - KeyCode::Digit3 => Ok(Self(51)), - KeyCode::Digit4 => Ok(Self(52)), - KeyCode::Digit5 => Ok(Self(53)), - KeyCode::Digit6 => Ok(Self(54)), - KeyCode::Digit7 => Ok(Self(55)), - KeyCode::Digit8 => Ok(Self(56)), - KeyCode::Digit9 => Ok(Self(57)), - KeyCode::Equal => Ok(Self(61)), - KeyCode::KeyA => Ok(Self(65)), - KeyCode::KeyC => Ok(Self(67)), - KeyCode::KeyD => Ok(Self(68)), - KeyCode::KeyF => Ok(Self(70)), - KeyCode::KeyG => Ok(Self(71)), - KeyCode::KeyH => Ok(Self(72)), - KeyCode::KeyI => Ok(Self(73)), - KeyCode::KeyR => Ok(Self(82)), - KeyCode::KeyS => Ok(Self(83)), - KeyCode::KeyV => Ok(Self(86)), - KeyCode::KeyX => Ok(Self(88)), - KeyCode::F11 => Ok(Self(122)), - KeyCode::Minus => Ok(Self(173)), - _ => Err("Failed to convert KeyCode to WindowsKeyCode"), - } - } -} - -pub struct NativeKeyCode(pub i32); - -impl TryFrom for NativeKeyCode { - type Error = &'static str; - - fn try_from(value: KeyCode) -> Result { - match value { - KeyCode::Backspace => Ok(Self(22)), - KeyCode::Tab => Ok(Self(23)), - KeyCode::Enter => Ok(Self(36)), - KeyCode::Escape => Ok(Self(9)), - KeyCode::Space => Ok(Self(65)), - KeyCode::PageUp => Ok(Self(112)), - KeyCode::PageDown => Ok(Self(117)), - KeyCode::End => Ok(Self(115)), - KeyCode::Home => Ok(Self(110)), - KeyCode::ArrowLeft => Ok(Self(113)), - KeyCode::ArrowUp => Ok(Self(111)), - KeyCode::ArrowRight => Ok(Self(114)), - KeyCode::ArrowDown => Ok(Self(116)), - KeyCode::Digit0 => Ok(Self(19)), - KeyCode::Digit1 => Ok(Self(10)), - KeyCode::Digit2 => Ok(Self(11)), - KeyCode::Digit3 => Ok(Self(12)), - KeyCode::Digit4 => Ok(Self(13)), - KeyCode::Digit5 => Ok(Self(14)), - KeyCode::Digit6 => Ok(Self(15)), - KeyCode::Digit7 => Ok(Self(16)), - KeyCode::Digit8 => Ok(Self(17)), - KeyCode::Digit9 => Ok(Self(18)), - KeyCode::Equal => Ok(Self(21)), - KeyCode::KeyA => Ok(Self(38)), - KeyCode::KeyC => Ok(Self(54)), - KeyCode::KeyD => Ok(Self(40)), - KeyCode::KeyF => Ok(Self(41)), - KeyCode::KeyG => Ok(Self(42)), - KeyCode::KeyH => Ok(Self(43)), - KeyCode::KeyI => Ok(Self(31)), - KeyCode::KeyR => Ok(Self(27)), - KeyCode::KeyS => Ok(Self(39)), - KeyCode::KeyV => Ok(Self(55)), - KeyCode::KeyX => Ok(Self(53)), - KeyCode::F11 => Ok(Self(95)), - KeyCode::Minus => Ok(Self(20)), - _ => Err("Failed to convert KeyCode to NativeKeyCode"), - } - } -} diff --git a/src/webview/app/browser_process_handler.rs b/src/webview/app/browser_process_handler.rs deleted file mode 100644 index f534d6b..0000000 --- a/src/webview/app/browser_process_handler.rs +++ /dev/null @@ -1,42 +0,0 @@ -use crate::{ - cef_impl, - shared::with_renderer_read, - webview::{BROWSER, app::client::WebViewClient}, -}; - -cef_impl!( - prefix = "WebView", - name = BrowserProcessHandler, - sys_type = cef_dll_sys::cef_browser_process_handler_t, - { - fn on_context_initialized(&self) { - with_renderer_read(|renderer| { - let mut client = WebViewClient::new(); - - let url = CefString::from("about:blank"); - - let window_info = WindowInfo { - windowless_rendering_enabled: 1, - ..Default::default() - }; - - let settings = BrowserSettings { - windowless_frame_rate: renderer.refresh_rate as i32, - ..Default::default() - }; - - BROWSER.get_or_init(|| { - browser_host_create_browser_sync( - Some(&window_info), - Some(&mut client), - Some(&url), - Some(&settings), - Option::<&mut DictionaryValue>::None, - Option::<&mut RequestContext>::None, - ) - .expect("Failed to create browser sync") - }); - }); - } - } -); diff --git a/src/webview/app/client/display_handler.rs b/src/webview/app/client/display_handler.rs deleted file mode 100644 index 076143d..0000000 --- a/src/webview/app/client/display_handler.rs +++ /dev/null @@ -1,28 +0,0 @@ -use std::os::raw::{c_int, c_ulong}; - -use crate::{ - cef_impl, - webview::{SENDER, WebViewEvent}, -}; - -cef_impl!( - prefix = "WebView", - name = DisplayHandler, - sys_type = cef_dll_sys::cef_display_handler_t, - { - fn on_cursor_change( - &self, - _browser: Option<&mut Browser>, - _cursor: c_ulong, - type_: CursorType, - _custom_cursor_info: Option<&CursorInfo>, - ) -> c_int { - if let Some(sender) = SENDER.get() { - sender.send(WebViewEvent::Cursor(type_.into())).ok(); - return 1; - } - - 0 - } - } -); diff --git a/src/webview/app/client/keyboard_handler.rs b/src/webview/app/client/keyboard_handler.rs deleted file mode 100644 index d24e16e..0000000 --- a/src/webview/app/client/keyboard_handler.rs +++ /dev/null @@ -1,48 +0,0 @@ -use std::os::raw::c_int; - -use cef_dll_sys::{cef_event_flags_t, cef_key_event_type_t}; - -use crate::{cef_impl, webview::constants::ZOOM_AMOUNT}; - -const MINUS_KEY_CODE: c_int = 61; -const EQUAL_KEY_CODE: c_int = 173; - -cef_impl!( - prefix = "WebView", - name = KeyboardHandler, - sys_type = cef_dll_sys::cef_keyboard_handler_t, - { - fn on_key_event( - &self, - browser: Option<&mut Browser>, - event: Option<&KeyEvent>, - _os_event: Option<&mut cef_dll_sys::XEvent>, - ) -> c_int { - if let Some(event) = event - && event.type_ == cef_key_event_type_t::KEYEVENT_RAWKEYDOWN.into() - && event.modifiers == cef_event_flags_t::EVENTFLAG_CONTROL_DOWN as u32 - { - if event.windows_key_code == MINUS_KEY_CODE { - set_zoom_level(browser, ZOOM_AMOUNT); - return true.into(); - } - - if event.windows_key_code == EQUAL_KEY_CODE { - set_zoom_level(browser, -ZOOM_AMOUNT); - return true.into(); - } - } - - false.into() - } - } -); - -fn set_zoom_level(browser: Option<&mut Browser>, amount: f64) { - if let Some(browser) = browser - && let Some(host) = browser.host() - { - let zoom_level = host.zoom_level(); - host.set_zoom_level(zoom_level + amount); - } -} diff --git a/src/webview/app/client/lifespan_handler.rs b/src/webview/app/client/lifespan_handler.rs deleted file mode 100644 index e2df87d..0000000 --- a/src/webview/app/client/lifespan_handler.rs +++ /dev/null @@ -1,48 +0,0 @@ -use std::os::raw::c_int; - -use url::Url; - -use crate::{ - cef_impl, - webview::{SENDER, WebViewEvent}, -}; - -cef_impl!( - prefix = "WebView", - name = LifeSpanHandler, - sys_type = cef_dll_sys::cef_life_span_handler_t, - { - fn on_before_close(&self, _browser: Option<&mut Browser>) { - shutdown(); - } - - fn on_before_popup( - &self, - _browser: Option<&mut Browser>, - _frame: Option<&mut Frame>, - _popup_id: c_int, - target_url: Option<&CefString>, - _target_frame_name: Option<&CefString>, - _target_disposition: WindowOpenDisposition, - _user_gesture: c_int, - _popup_features: Option<&PopupFeatures>, - _window_info: Option<&mut WindowInfo>, - _client: Option<&mut Option>, - _settings: Option<&mut BrowserSettings>, - _extra_info: Option<&mut Option>, - _no_javascript_access: Option<&mut c_int>, - ) -> c_int { - if let Some(target_url) = target_url { - let target_url = target_url.to_string(); - - if let Ok(url) = Url::parse(&target_url) - && let Some(sender) = SENDER.get() - { - sender.send(WebViewEvent::Open(url)).ok(); - } - } - - true.into() - } - } -); diff --git a/src/webview/app/client/render_handler.rs b/src/webview/app/client/render_handler.rs deleted file mode 100644 index 5c7df69..0000000 --- a/src/webview/app/client/render_handler.rs +++ /dev/null @@ -1,66 +0,0 @@ -use std::os::raw::c_int; - -use crate::{ - WebViewEvent, cef_impl, - shared::{with_gl, with_renderer_read}, - webview::SENDER, -}; - -cef_impl!( - prefix = "WebView", - name = RenderHandler, - sys_type = cef_dll_sys::cef_render_handler_t, - { - fn view_rect(&self, _browser: Option<&mut Browser>, rect: Option<&mut Rect>) { - with_renderer_read(|renderer| { - if let Some(rect) = rect { - *rect = Rect { - x: 0, - y: 0, - width: renderer.width, - height: renderer.height, - }; - } - }); - } - - // The `width` and `height` parameters may be outdated due to asynchronous updates from `on_paint` and `view_rect`. - // We compare them against the current renderer dimensions before painting. - // If they don't match, send a Resized event to ask for a repaint. - fn on_paint( - &self, - _browser: Option<&mut Browser>, - _type_: PaintElementType, - _dirty_rects_count: usize, - dirty_rects: Option<&Rect>, - buffer: *const u8, - width: c_int, - height: c_int, - ) { - with_gl(|_, _| { - with_renderer_read(|renderer| { - if renderer.width == width && renderer.height == height { - if let Some(dirty) = dirty_rects { - renderer.paint( - dirty.x, - dirty.y, - dirty.width, - dirty.height, - buffer, - width, - ); - } else { - renderer.paint(0, 0, width, height, buffer, width); - } - - if let Some(sender) = SENDER.get() { - sender.send(WebViewEvent::Paint).ok(); - } - } else if let Some(sender) = SENDER.get() { - sender.send(WebViewEvent::Resized).ok(); - } - }); - }); - } - } -); diff --git a/src/webview/app/mod.rs b/src/webview/app/mod.rs deleted file mode 100644 index 2c6125e..0000000 --- a/src/webview/app/mod.rs +++ /dev/null @@ -1,37 +0,0 @@ -mod browser_process_handler; -mod client; -mod render_process_handler; -mod utils; -mod v8_handler; - -use browser_process_handler::WebViewBrowserProcessHandler; -use render_process_handler::WebViewRenderProcessHandler; - -use crate::{cef_impl, constants::CMD_SWITCHES}; - -cef_impl!( - prefix = "WebView", - name = App, - sys_type = cef_dll_sys::cef_app_t, - { - fn on_before_command_line_processing( - &self, - _process_type: Option<&CefString>, - command_line: Option<&mut CommandLine>, - ) { - if let Some(line) = command_line { - CMD_SWITCHES.iter().for_each(|switch| { - line.append_switch(Some(&CefString::from(switch.to_owned()))); - }); - } - } - - fn browser_process_handler(&self) -> Option { - Some(WebViewBrowserProcessHandler::new()) - } - - fn render_process_handler(&self) -> Option { - Some(WebViewRenderProcessHandler::new()) - } - } -); diff --git a/src/webview/app/render_process_handler.rs b/src/webview/app/render_process_handler.rs deleted file mode 100644 index 366fe1c..0000000 --- a/src/webview/app/render_process_handler.rs +++ /dev/null @@ -1,47 +0,0 @@ -use crate::{ - cef_impl, - webview::{ - app::v8_handler::WebViewV8Handler, - constants::{IPC_RECEIVER, READY_MESSAGE}, - }, -}; - -use super::utils; - -cef_impl!( - prefix = "WebView", - name = RenderProcessHandler, - sys_type = cef_dll_sys::cef_render_process_handler_t, - { - fn on_browser_created( - &self, - browser: Option<&mut Browser>, - _extra_info: Option<&mut DictionaryValue>, - ) { - utils::send_process_message(browser, READY_MESSAGE, None); - } - - fn on_context_created( - &self, - _browser: Option<&mut Browser>, - _frame: Option<&mut Frame>, - context: Option<&mut V8Context>, - ) { - let name = CefString::from(IPC_RECEIVER); - let mut handler = WebViewV8Handler::new(); - - let mut value = v8_value_create_function(Some(&name), Some(&mut handler)) - .expect("Failed to create a value for function"); - - if let Some(context) = context - && let Some(global) = context.global() - { - global.set_value_bykey( - Some(&name), - Some(&mut value), - V8Propertyattribute::default(), - ); - } - } - } -); diff --git a/src/webview/app/v8_handler.rs b/src/webview/app/v8_handler.rs deleted file mode 100644 index e1dce79..0000000 --- a/src/webview/app/v8_handler.rs +++ /dev/null @@ -1,60 +0,0 @@ -use std::os::raw::c_int; - -use crate::{ - cef_impl, - webview::constants::{IPC_MESSAGE, IPC_RECEIVER}, -}; - -use super::utils; - -cef_impl!( - prefix = "WebView", - name = V8Handler, - sys_type = cef_dll_sys::cef_v8_handler_t, - { - fn execute( - &self, - name: Option<&CefString>, - _object: Option<&mut V8Value>, - arguments: Option<&[Option]>, - _retval: Option<&mut Option>, - _exception: Option<&mut CefString>, - ) -> c_int { - if is_handler(name, IPC_RECEIVER) - && let Some(data) = handler_data(arguments) - { - send_ipc_message(data); - - return 1; - } - - 0 - } - } -); - -fn is_handler(name: Option<&CefString>, value: &str) -> bool { - name.is_some_and(|name| { - let handler_name = CefString::from(value); - name.as_slice() == handler_name.as_slice() - }) -} - -fn handler_data(arguments: Option<&[Option]>) -> Option { - arguments.and_then(|arguments| { - arguments.first().and_then(|value| { - value - .as_ref() - .map(|value| value.string_value()) - .map(|value| CefString::from(&value)) - }) - }) -} - -fn send_ipc_message(data: CefStringUtf16) { - if let Some(context) = v8_context_get_current_context() - && let Some(mut browser) = context.browser() - { - utils::send_process_message(Some(&mut browser), IPC_MESSAGE, Some(&data)); - } -} diff --git a/src/webview/cef_impl.rs b/src/webview/cef_impl.rs deleted file mode 100644 index ca74e69..0000000 --- a/src/webview/cef_impl.rs +++ /dev/null @@ -1,61 +0,0 @@ -#[macro_export] -macro_rules! cef_impl { - ( - prefix = $prefix:literal, - name = $name:ident, - sys_type = $sys:ty, - { $($body:tt)* } - ) => { - paste::paste! { - use cef::{rc::*, *}; - - pub struct [<$prefix $name>] { - object: *mut RcImpl<$sys, Self>, - } - - impl [<$prefix $name>] { - #[allow(clippy::new_ret_no_self)] - pub fn new() -> $name { - $name::new(Self { - object: std::ptr::null_mut(), - }) - } - } - - impl [<"Wrap" $name>] for [<$prefix $name>] { - fn wrap_rc(&mut self, object: *mut RcImpl<$sys, Self>) { - self.object = object; - } - } - - impl Clone for [<$prefix $name>] { - fn clone(&self) -> Self { - let object = unsafe { - let rc_impl = &mut *self.object; - rc_impl.interface.add_ref(); - rc_impl - }; - - Self { object } - } - } - - impl Rc for [<$prefix $name>] { - fn as_base(&self) -> &cef_dll_sys::cef_base_ref_counted_t { - unsafe { - let base = &*self.object; - std::mem::transmute(&base.cef_object) - } - } - } - - impl [<"Impl" $name>] for [<$prefix $name>] { - fn get_raw(&self) -> *mut $sys { - self.object.cast() - } - - $($body)* - } - } - }; -} diff --git a/src/webview/mod.rs b/src/webview/mod.rs deleted file mode 100644 index 3f803b8..0000000 --- a/src/webview/mod.rs +++ /dev/null @@ -1,337 +0,0 @@ -mod adapters; -mod app; -mod cef_impl; -mod constants; - -use std::path::PathBuf; - -use adapters::{NativeKeyCode, WindowsKeyCode}; -use app::WebViewApp; -use cef::{ - App, Browser, BrowserHost, BrowserSettings, CefString, Client, Frame, ImplBrowser, - ImplBrowserHost, ImplCommandLine, ImplDragData, ImplFrame, LogSeverity, Settings, api_hash, - args::Args, execute_process, initialize, -}; -use cef_dll_sys::{ - cef_drag_operations_mask_t, cef_event_flags_t, cef_key_event_type_t, cef_log_severity_t, - cef_mouse_button_type_t, cef_paint_element_type_t, cef_pointer_type_t, cef_touch_event_type_t, -}; -use constants::IPC_SENDER; -use crossbeam_channel::{Receiver, Sender, unbounded}; -use once_cell::sync::OnceCell; -use url::Url; -use winit::{ - event::{KeyEvent, MouseButton, Touch, TouchPhase}, - keyboard::{ModifiersState, PhysicalKey}, -}; - -use crate::{ - config::WebViewConfig, - shared::types::{Cursor, MouseState}, -}; - -static SENDER: OnceCell> = OnceCell::new(); -static BROWSER: OnceCell = OnceCell::new(); - -pub enum WebViewEvent { - Ready, - Loaded, - Paint, - Resized, - Cursor(Cursor), - Open(Url), - Ipc(String), -} - -pub struct WebView { - args: Args, - settings: Settings, - app: App, - receiver: Receiver, -} - -impl WebView { - pub fn new(config: WebViewConfig) -> Self { - let _ = api_hash(cef_dll_sys::CEF_API_VERSION_LAST, 0); - - let args = Args::new(); - - let (sender, receiver) = unbounded::(); - SENDER.get_or_init(|| sender); - - let app = WebViewApp::new(); - - config.remove_lock_file(); - - let settings = Settings { - no_sandbox: 1, - windowless_rendering_enabled: 1, - multi_threaded_message_loop: 1, - cache_path: config.cache_dir.to_str().unwrap().into(), - log_file: config.log_file.to_str().unwrap().into(), - log_severity: LogSeverity::from(cef_log_severity_t::LOGSEVERITY_VERBOSE), - ..Default::default() - }; - - Self { - args, - settings, - app, - receiver, - } - } - - fn browser_host(&self) -> Option { - if let Some(browser) = BROWSER.get() { - return browser.host(); - } - - None - } - - fn main_frame(&self) -> Option { - if let Some(browser) = BROWSER.get() { - return browser.main_frame(); - } - - None - } - - pub fn should_exit(&mut self) -> bool { - let ret = execute_process( - Some(self.args.as_main_args()), - Some(&mut self.app), - std::ptr::null_mut(), - ); - - let cmd = self.args.as_cmd_line().unwrap(); - - let switch = CefString::from("type"); - let is_browser_process = cmd.has_switch(Some(&switch)) != 1; - - if is_browser_process { - assert!(ret == -1, "cannot execute browser process"); - false - } else { - assert!(ret >= 0, "cannot execute non-browser process"); - true - } - } - - pub fn start(&mut self) { - assert_eq!( - initialize( - Some(self.args.as_main_args()), - Some(&self.settings), - Some(&mut self.app), - std::ptr::null_mut(), - ), - 1 - ); - } - - pub fn stop(&self) { - if let Some(host) = self.browser_host() { - host.close_browser(0); - } - } - - pub fn events(&self, handler: T) { - self.receiver.try_iter().for_each(handler); - } - - pub fn navigate(&self, url: &str) { - if let Some(main_frame) = self.main_frame() { - let url = CefString::from(url); - main_frame.load_url(Some(&url)); - } - } - - pub fn dev_tools(&self, state: bool) { - if let Some(host) = self.browser_host() { - if state { - host.show_dev_tools( - None, - Option::<&mut Client>::None, - Option::<&BrowserSettings>::None, - None, - ); - } else { - host.close_dev_tools(); - } - } - } - - pub fn post_message(&self, message: String) { - if let Some(main_frame) = self.main_frame() { - let serialized_message = - serde_json::to_string(&message).expect("Failed to serialize as JSON string"); - let script = format!("{IPC_SENDER}({serialized_message})"); - let code = CefString::from(script.as_str()); - main_frame.execute_java_script(Some(&code), None, 0); - } - } - - /// Tells the webview to update its bounds and repaint. - pub fn update(&self) { - if let Some(host) = self.browser_host() { - host.was_resized(); - // Invalidate the view to ensure the `view_rect` callback is triggered, - // since repeated calls to `was_resized` may fail to invoke it consistently. - host.invalidate(cef_paint_element_type_t::PET_VIEW.into()); - } - } - - pub fn focused(&mut self, state: bool) { - if let Some(host) = self.browser_host() { - host.set_focus(state.into()); - } - } - - pub fn mouse_moved(&mut self, state: MouseState) { - if let Some(host) = self.browser_host() { - let event = state.into(); - let mouse_leave = (!state.over).into(); - host.send_mouse_move_event(Some(&event), mouse_leave); - } - } - - pub fn mouse_wheel(&self, state: MouseState) { - if let Some(host) = self.browser_host() { - let event = state.into(); - host.send_mouse_wheel_event(Some(&event), state.delta.0, state.delta.1); - } - } - - pub fn mouse_input(&self, state: MouseState) { - if let Some(browser) = BROWSER.get() { - let mouse_up = !state.pressed; - - let button_type = match state.button { - MouseButton::Back if mouse_up => { - browser.go_back(); - None - } - MouseButton::Forward if mouse_up => { - browser.go_forward(); - None - } - MouseButton::Left => Some(cef_mouse_button_type_t::MBT_LEFT), - MouseButton::Right => Some(cef_mouse_button_type_t::MBT_RIGHT), - MouseButton::Middle => Some(cef_mouse_button_type_t::MBT_MIDDLE), - _ => None, - }; - - if let Some(button_type) = button_type - && let Some(host) = browser.host() - { - let event = state.into(); - - host.send_mouse_click_event(Some(&event), button_type.into(), mouse_up.into(), 1); - } - } - } - - pub fn touch_input(&self, touch: Touch) { - if let Some(host) = self.browser_host() { - let event_type = match touch.phase { - TouchPhase::Started => cef_touch_event_type_t::CEF_TET_PRESSED, - TouchPhase::Ended => cef_touch_event_type_t::CEF_TET_RELEASED, - TouchPhase::Moved => cef_touch_event_type_t::CEF_TET_MOVED, - TouchPhase::Cancelled => cef_touch_event_type_t::CEF_TET_CANCELLED, - }; - - let event = cef::TouchEvent { - type_: event_type.into(), - pointer_type: cef_pointer_type_t::CEF_POINTER_TYPE_TOUCH.into(), - x: touch.location.x as f32, - y: touch.location.y as f32, - ..Default::default() - }; - - host.send_touch_event(Some(&event)); - } - } - - pub fn keyboard_input(&self, key_event: KeyEvent, modifiers: ModifiersState) { - if let Some(host) = self.browser_host() { - if let PhysicalKey::Code(code) = key_event.physical_key - && let (Ok(WindowsKeyCode(windows_key_code)), Ok(NativeKeyCode(native_key_code))) = - (code.try_into(), code.try_into()) - { - let event_type = match key_event.state.is_pressed() { - true => cef_key_event_type_t::KEYEVENT_KEYDOWN.into(), - false => cef_key_event_type_t::KEYEVENT_KEYUP.into(), - }; - - let modifiers = if modifiers.control_key() { - cef_event_flags_t::EVENTFLAG_CONTROL_DOWN as u32 - } else { - cef_event_flags_t::EVENTFLAG_NONE as u32 - }; - - let event = cef::KeyEvent { - type_: event_type, - windows_key_code, - native_key_code, - modifiers, - ..Default::default() - }; - - host.send_key_event(Some(&event)); - } - - if key_event.state.is_pressed() - && let Some(text) = key_event.text - { - let event = cef::KeyEvent { - type_: cef_key_event_type_t::KEYEVENT_CHAR.into(), - character: text.as_str().chars().next().map(|c| c as u16).unwrap(), - ..Default::default() - }; - - host.send_key_event(Some(&event)); - } - } - } - - pub fn file_hover(&self, path: PathBuf, state: MouseState) { - if let Some(host) = self.browser_host() { - let event = state.into(); - - let file_path = path.to_str().map(CefString::from); - let file_name = path - .file_name() - .and_then(|name| name.to_str()) - .map(CefString::from); - - if let Some(mut drag_data) = cef::drag_data_create() { - drag_data.add_file(file_path.as_ref(), file_name.as_ref()); - - host.drag_target_drag_enter( - Some(&mut drag_data), - Some(&event), - cef_drag_operations_mask_t::DRAG_OPERATION_MOVE.into(), - ); - - host.drag_target_drag_over( - Some(&event), - cef_drag_operations_mask_t::DRAG_OPERATION_MOVE.into(), - ); - } - } - } - - pub fn file_drop(&self, state: MouseState) { - if let Some(host) = self.browser_host() { - let event = state.into(); - host.drag_target_drop(Some(&event)); - } - } - - pub fn file_cancel(&self) { - if let Some(host) = self.browser_host() { - host.drag_target_drag_leave(); - } - } -}