chore(release): v0.2.18 - Windows cross-compile support, Forgejo updater, hardware capture startup resilience, and GTK3 retirement

This commit is contained in:
katR 2026-10-11 12:40:09 +05:30
parent 71e95e656c
commit 836bf0bf76
13 changed files with 222 additions and 2708 deletions

617
Cargo.lock generated
View file

@ -414,29 +414,6 @@ dependencies = [
"syn 3.0.4", "syn 3.0.4",
] ]
[[package]]
name = "atk"
version = "0.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "241b621213072e993be4f6f3a9e4b45f65b7e6faad43001be957184b7bb1824b"
dependencies = [
"atk-sys",
"glib 0.18.5",
"libc",
]
[[package]]
name = "atk-sys"
version = "0.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5e48b684b0ca77d2bbadeef17424c2ea3c897d44d566a1617e7e8f30614d086"
dependencies = [
"glib-sys 0.18.1",
"gobject-sys 0.18.0",
"libc",
"system-deps 6.2.2",
]
[[package]] [[package]]
name = "atomic-waker" name = "atomic-waker"
version = "1.1.2" version = "1.1.2"
@ -672,31 +649,6 @@ version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
[[package]]
name = "cairo-rs"
version = "0.18.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2"
dependencies = [
"bitflags 2.13.1",
"cairo-sys-rs",
"glib 0.18.5",
"libc",
"once_cell",
"thiserror 1.0.69",
]
[[package]]
name = "cairo-sys-rs"
version = "0.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51"
dependencies = [
"glib-sys 0.18.1",
"libc",
"system-deps 6.2.2",
]
[[package]] [[package]]
name = "calloop" name = "calloop"
version = "0.13.0" version = "0.13.0"
@ -769,16 +721,6 @@ dependencies = [
"nom 7.1.3", "nom 7.1.3",
] ]
[[package]]
name = "cfg-expr"
version = "0.15.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02"
dependencies = [
"smallvec",
"target-lexicon 0.12.16",
]
[[package]] [[package]]
name = "cfg-expr" name = "cfg-expr"
version = "0.20.9" version = "0.20.9"
@ -786,7 +728,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe4ece8474b5f766c63426647e7b4b316b67431ade1036a8313cee24a03ae917" checksum = "fe4ece8474b5f766c63426647e7b4b316b67431ade1036a8313cee24a03ae917"
dependencies = [ dependencies = [
"smallvec", "smallvec",
"target-lexicon 0.13.5", "target-lexicon",
] ]
[[package]] [[package]]
@ -1372,16 +1314,6 @@ dependencies = [
"vcpkg", "vcpkg",
] ]
[[package]]
name = "field-offset"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f"
dependencies = [
"memoffset",
"rustc_version",
]
[[package]] [[package]]
name = "find-msvc-tools" name = "find-msvc-tools"
version = "0.1.11" version = "0.1.11"
@ -1441,32 +1373,12 @@ dependencies = [
"percent-encoding", "percent-encoding",
] ]
[[package]]
name = "futures-channel"
version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4"
dependencies = [
"futures-core",
]
[[package]] [[package]]
name = "futures-core" name = "futures-core"
version = "0.3.34" version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e"
[[package]]
name = "futures-executor"
version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "031b47cf1a3c6cc8bc2fc76cd437f521619387907d469316e7c0bc278f1f5432"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]] [[package]]
name = "futures-io" name = "futures-io"
version = "0.3.34" version = "0.3.34"
@ -1525,64 +1437,6 @@ dependencies = [
"slab", "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 0.18.5",
"libc",
"pango",
]
[[package]]
name = "gdk-pixbuf"
version = "0.18.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec"
dependencies = [
"gdk-pixbuf-sys",
"gio",
"glib 0.18.5",
"libc",
"once_cell",
]
[[package]]
name = "gdk-pixbuf-sys"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7"
dependencies = [
"gio-sys 0.18.1",
"glib-sys 0.18.1",
"gobject-sys 0.18.0",
"libc",
"system-deps 6.2.2",
]
[[package]]
name = "gdk-sys"
version = "0.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c2d13f38594ac1e66619e188c6d5a1adb98d11b2fcf7894fc416ad76aa2f3f7"
dependencies = [
"cairo-sys-rs",
"gdk-pixbuf-sys",
"gio-sys 0.18.1",
"glib-sys 0.18.1",
"gobject-sys 0.18.0",
"libc",
"pango-sys",
"pkg-config",
"system-deps 6.2.2",
]
[[package]] [[package]]
name = "generic-array" name = "generic-array"
version = "0.14.7" version = "0.14.7"
@ -1637,51 +1491,6 @@ dependencies = [
"r-efi 6.0.0", "r-efi 6.0.0",
] ]
[[package]]
name = "gio"
version = "0.18.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-util",
"gio-sys 0.18.1",
"glib 0.18.5",
"libc",
"once_cell",
"pin-project-lite",
"smallvec",
"thiserror 1.0.69",
]
[[package]]
name = "gio-sys"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2"
dependencies = [
"glib-sys 0.18.1",
"gobject-sys 0.18.0",
"libc",
"system-deps 6.2.2",
"winapi",
]
[[package]]
name = "gio-sys"
version = "0.22.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c28739f914c15b87a9856000bed9cbd5b3a8afbca5e982d9a299e1601422cb5"
dependencies = [
"glib-sys 0.22.9",
"gobject-sys 0.22.9",
"libc",
"system-deps 9.0.0",
"windows-sys 0.61.2",
]
[[package]] [[package]]
name = "gl_generator" name = "gl_generator"
version = "0.14.0" version = "0.14.0"
@ -1693,96 +1502,6 @@ dependencies = [
"xml-rs", "xml-rs",
] ]
[[package]]
name = "glib"
version = "0.18.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5"
dependencies = [
"bitflags 2.13.1",
"futures-channel",
"futures-core",
"futures-executor",
"futures-task",
"futures-util",
"gio-sys 0.18.1",
"glib-macros 0.18.5",
"glib-sys 0.18.1",
"gobject-sys 0.18.0",
"libc",
"memchr",
"once_cell",
"smallvec",
"thiserror 1.0.69",
]
[[package]]
name = "glib"
version = "0.22.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18b9b8d350db41f690ec1b87109f202782748bbd8ab2f2ede17cb40d29e2838c"
dependencies = [
"bitflags 2.13.1",
"futures-channel",
"futures-core",
"futures-executor",
"futures-task",
"futures-util",
"gio-sys 0.22.9",
"glib-macros 0.22.9",
"glib-sys 0.22.9",
"gobject-sys 0.22.9",
"libc",
"memchr",
"smallvec",
]
[[package]]
name = "glib-macros"
version = "0.18.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc"
dependencies = [
"heck 0.4.1",
"proc-macro-crate 2.0.2",
"proc-macro-error",
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]]
name = "glib-macros"
version = "0.22.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9597c68fa7bdf4154a3079642cd21c4dccac0b0bdd2897d82861523bf91e7b9"
dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote",
"syn 3.0.4",
]
[[package]]
name = "glib-sys"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898"
dependencies = [
"libc",
"system-deps 6.2.2",
]
[[package]]
name = "glib-sys"
version = "0.22.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99b38907e67e40dec9b60f858bcada952598719cb512a13eb13d6cdcd16f8295"
dependencies = [
"libc",
"system-deps 9.0.0",
]
[[package]] [[package]]
name = "glob" name = "glob"
version = "0.3.4" version = "0.3.4"
@ -1884,28 +1603,6 @@ dependencies = [
"gl_generator", "gl_generator",
] ]
[[package]]
name = "gobject-sys"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44"
dependencies = [
"glib-sys 0.18.1",
"libc",
"system-deps 6.2.2",
]
[[package]]
name = "gobject-sys"
version = "0.22.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07595c9ba696bd9819cb6a8df39f08078f3dd679508fe052dd558492c2053834"
dependencies = [
"glib-sys 0.22.9",
"libc",
"system-deps 9.0.0",
]
[[package]] [[package]]
name = "gpu-alloc" name = "gpu-alloc"
version = "0.6.2" version = "0.6.2"
@ -1945,86 +1642,6 @@ dependencies = [
"bitflags 2.13.1", "bitflags 2.13.1",
] ]
[[package]]
name = "gtk"
version = "0.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd56fb197bfc42bd5d2751f4f017d44ff59fbb58140c6b49f9b3b2bdab08506a"
dependencies = [
"atk",
"cairo-rs",
"field-offset",
"futures-channel",
"gdk",
"gdk-pixbuf",
"gio",
"glib 0.18.5",
"gtk-sys",
"gtk3-macros",
"libc",
"pango",
"pkg-config",
]
[[package]]
name = "gtk-layer-shell"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc759b3184830a547b31549ab40c4b54450ab702bba79ba23f049bc1d1e3ca98"
dependencies = [
"bitflags 2.13.1",
"gdk",
"glib 0.18.5",
"glib-sys 0.18.1",
"gtk",
"gtk-layer-shell-sys",
"libc",
]
[[package]]
name = "gtk-layer-shell-sys"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4eee067e022416d53a70de69d3d3929d8a6e687f3278b8934faa671750fa6eb"
dependencies = [
"gdk-sys",
"glib-sys 0.18.1",
"gtk-sys",
"libc",
"system-deps 7.0.8",
]
[[package]]
name = "gtk-sys"
version = "0.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f29a1c21c59553eb7dd40e918be54dccd60c52b049b75119d5d96ce6b624414"
dependencies = [
"atk-sys",
"cairo-sys-rs",
"gdk-pixbuf-sys",
"gdk-sys",
"gio-sys 0.18.1",
"glib-sys 0.18.1",
"gobject-sys 0.18.0",
"libc",
"pango-sys",
"system-deps 6.2.2",
]
[[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.119",
]
[[package]] [[package]]
name = "half" name = "half"
version = "2.7.1" version = "2.7.1"
@ -2051,12 +1668,6 @@ version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]] [[package]]
name = "heck" name = "heck"
version = "0.5.0" version = "0.5.0"
@ -2381,7 +1992,7 @@ dependencies = [
"libspa-sys", "libspa-sys",
"nom 8.0.0", "nom 8.0.0",
"rustix 1.1.4", "rustix 1.1.4",
"system-deps 7.0.8", "system-deps",
] ]
[[package]] [[package]]
@ -2392,7 +2003,7 @@ checksum = "3b6e17bdaf63ed0d5e4144022624032b41fd9733112e8c74ac26fc9bf1291924"
dependencies = [ dependencies = [
"bindgen", "bindgen",
"cc", "cc",
"system-deps 7.0.8", "system-deps",
] ]
[[package]] [[package]]
@ -2734,7 +2345,7 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8"
dependencies = [ dependencies = [
"proc-macro-crate 3.5.0", "proc-macro-crate",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.119", "syn 2.0.119",
@ -3123,31 +2734,6 @@ dependencies = [
"ttf-parser", "ttf-parser",
] ]
[[package]]
name = "pango"
version = "0.18.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4"
dependencies = [
"gio",
"glib 0.18.5",
"libc",
"once_cell",
"pango-sys",
]
[[package]]
name = "pango-sys"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5"
dependencies = [
"glib-sys 0.18.1",
"gobject-sys 0.18.0",
"libc",
"system-deps 6.2.2",
]
[[package]] [[package]]
name = "parking" name = "parking"
version = "2.2.1" version = "2.2.1"
@ -3248,7 +2834,7 @@ checksum = "9ce653f53e63e5b93853218092ee9a8906a5d082c92f3f1db26316955dd63ce0"
dependencies = [ dependencies = [
"bindgen", "bindgen",
"libspa-sys", "libspa-sys",
"system-deps 7.0.8", "system-deps",
] ]
[[package]] [[package]]
@ -3332,57 +2918,13 @@ dependencies = [
"num-integer", "num-integer",
] ]
[[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.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24"
dependencies = [
"toml_datetime 0.6.3",
"toml_edit 0.20.2",
]
[[package]] [[package]]
name = "proc-macro-crate" name = "proc-macro-crate"
version = "3.5.0" version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f"
dependencies = [ dependencies = [
"toml_edit 0.25.13+spec-1.1.0", "toml_edit",
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
] ]
[[package]] [[package]]
@ -3700,7 +3242,7 @@ dependencies = [
[[package]] [[package]]
name = "scythe" name = "scythe"
version = "0.2.17" version = "0.2.18"
dependencies = [ dependencies = [
"ashpd", "ashpd",
"cpal", "cpal",
@ -3710,11 +3252,7 @@ dependencies = [
"egui", "egui",
"ffmpeg-next", "ffmpeg-next",
"ffmpeg-sys-next", "ffmpeg-sys-next",
"gdk",
"glib 0.22.9",
"global-hotkey", "global-hotkey",
"gtk",
"gtk-layer-shell",
"image", "image",
"libc", "libc",
"memmap2", "memmap2",
@ -3789,15 +3327,6 @@ dependencies = [
"syn 3.0.4", "syn 3.0.4",
] ]
[[package]]
name = "serde_spanned"
version = "0.6.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "serde_spanned" name = "serde_spanned"
version = "1.1.1" version = "1.1.1"
@ -4013,23 +3542,13 @@ version = "0.26.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
dependencies = [ dependencies = [
"heck 0.5.0", "heck",
"proc-macro2", "proc-macro2",
"quote", "quote",
"rustversion", "rustversion",
"syn 2.0.119", "syn 2.0.119",
] ]
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"unicode-ident",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.119" version = "2.0.119"
@ -4063,51 +3582,19 @@ dependencies = [
"syn 2.0.119", "syn 2.0.119",
] ]
[[package]]
name = "system-deps"
version = "6.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349"
dependencies = [
"cfg-expr 0.15.8",
"heck 0.5.0",
"pkg-config",
"toml 0.8.2",
"version-compare",
]
[[package]] [[package]]
name = "system-deps" name = "system-deps"
version = "7.0.8" version = "7.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "396a35feb67335377e0251fcbc1092fc85c484bd4e3a7a54319399da127796e7" checksum = "396a35feb67335377e0251fcbc1092fc85c484bd4e3a7a54319399da127796e7"
dependencies = [ dependencies = [
"cfg-expr 0.20.9", "cfg-expr",
"heck 0.5.0", "heck",
"pkg-config", "pkg-config",
"toml 1.1.4+spec-1.1.0", "toml",
"version-compare", "version-compare",
] ]
[[package]]
name = "system-deps"
version = "9.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a0dae2cbca1f0ff93795713cfe0a4c02f760e3c0b8ae2ab4ec4045808ff429f"
dependencies = [
"cfg-expr 0.20.9",
"heck 0.5.0",
"pkg-config",
"toml 1.1.4+spec-1.1.0",
"version-compare",
]
[[package]]
name = "target-lexicon"
version = "0.12.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
[[package]] [[package]]
name = "target-lexicon" name = "target-lexicon"
version = "0.13.5" version = "0.13.5"
@ -4253,18 +3740,6 @@ dependencies = [
"syn 3.0.4", "syn 3.0.4",
] ]
[[package]]
name = "toml"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d"
dependencies = [
"serde",
"serde_spanned 0.6.9",
"toml_datetime 0.6.3",
"toml_edit 0.20.2",
]
[[package]] [[package]]
name = "toml" name = "toml"
version = "1.1.4+spec-1.1.0" version = "1.1.4+spec-1.1.0"
@ -4273,20 +3748,11 @@ checksum = "3aace63f4bbcdfc2c965b059de67119c89c4017a70d633be6c104910f67056f5"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"serde_core", "serde_core",
"serde_spanned 1.1.1", "serde_spanned",
"toml_datetime 1.1.1+spec-1.1.0", "toml_datetime",
"toml_parser", "toml_parser",
"toml_writer", "toml_writer",
"winnow 1.0.4", "winnow",
]
[[package]]
name = "toml_datetime"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
dependencies = [
"serde",
] ]
[[package]] [[package]]
@ -4298,30 +3764,6 @@ dependencies = [
"serde_core", "serde_core",
] ]
[[package]]
name = "toml_edit"
version = "0.19.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
dependencies = [
"indexmap",
"toml_datetime 0.6.3",
"winnow 0.5.40",
]
[[package]]
name = "toml_edit"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338"
dependencies = [
"indexmap",
"serde",
"serde_spanned 0.6.9",
"toml_datetime 0.6.3",
"winnow 0.5.40",
]
[[package]] [[package]]
name = "toml_edit" name = "toml_edit"
version = "0.25.13+spec-1.1.0" version = "0.25.13+spec-1.1.0"
@ -4329,9 +3771,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6975367e4d2ef766d86af01ffad14b622fecc8d4357a998fbc4deb6e9bacaf9b" checksum = "6975367e4d2ef766d86af01ffad14b622fecc8d4357a998fbc4deb6e9bacaf9b"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"toml_datetime 1.1.1+spec-1.1.0", "toml_datetime",
"toml_parser", "toml_parser",
"winnow 1.0.4", "winnow",
] ]
[[package]] [[package]]
@ -4340,7 +3782,7 @@ version = "1.1.3+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56" checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56"
dependencies = [ dependencies = [
"winnow 1.0.4", "winnow",
] ]
[[package]] [[package]]
@ -5228,15 +4670,6 @@ dependencies = [
"xkbcommon-dl", "xkbcommon-dl",
] ]
[[package]]
name = "winnow"
version = "0.5.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "winnow" name = "winnow"
version = "1.0.4" version = "1.0.4"
@ -5427,7 +4860,7 @@ dependencies = [
"uds_windows", "uds_windows",
"uuid", "uuid",
"windows-sys 0.61.2", "windows-sys 0.61.2",
"winnow 1.0.4", "winnow",
"zbus_macros 5.19.0", "zbus_macros 5.19.0",
"zbus_names 4.3.4", "zbus_names 4.3.4",
"zvariant 5.15.0", "zvariant 5.15.0",
@ -5463,7 +4896,7 @@ version = "4.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e" checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e"
dependencies = [ dependencies = [
"proc-macro-crate 3.5.0", "proc-macro-crate",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.119", "syn 2.0.119",
@ -5476,7 +4909,7 @@ version = "5.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2990635d09ade6df1868f72f8cac69a876a90981e8bd3c40b1be413f8dc88f40" checksum = "2990635d09ade6df1868f72f8cac69a876a90981e8bd3c40b1be413f8dc88f40"
dependencies = [ dependencies = [
"proc-macro-crate 3.5.0", "proc-macro-crate",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.4", "syn 3.0.4",
@ -5503,7 +4936,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8bf88b4a3ff53e883001e0e0115b297a9d53c31b9c1edd2bfdd853e3428624e" checksum = "d8bf88b4a3ff53e883001e0e0115b297a9d53c31b9c1edd2bfdd853e3428624e"
dependencies = [ dependencies = [
"serde", "serde",
"winnow 1.0.4", "winnow",
"zvariant 5.15.0", "zvariant 5.15.0",
] ]
@ -5652,7 +5085,7 @@ dependencies = [
"endi", "endi",
"enumflags2", "enumflags2",
"serde", "serde",
"winnow 1.0.4", "winnow",
"zcheapstr", "zcheapstr",
"zvariant_derive 5.15.0", "zvariant_derive 5.15.0",
"zvariant_utils 4.2.0", "zvariant_utils 4.2.0",
@ -5664,7 +5097,7 @@ version = "4.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449" checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449"
dependencies = [ dependencies = [
"proc-macro-crate 3.5.0", "proc-macro-crate",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.119", "syn 2.0.119",
@ -5677,7 +5110,7 @@ version = "5.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "864155e69b4352db0c7f374917bf45d1e0c8d17659c8b3dbf9795f3673f8c497" checksum = "864155e69b4352db0c7f374917bf45d1e0c8d17659c8b3dbf9795f3673f8c497"
dependencies = [ dependencies = [
"proc-macro-crate 3.5.0", "proc-macro-crate",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.4", "syn 3.0.4",
@ -5705,5 +5138,5 @@ dependencies = [
"quote", "quote",
"serde", "serde",
"syn 3.0.4", "syn 3.0.4",
"winnow 1.0.4", "winnow",
] ]

View file

@ -1,6 +1,6 @@
[package] [package]
name = "scythe" name = "scythe"
version = "0.2.17" version = "0.2.18"
edition = "2024" edition = "2024"
[[bin]] [[bin]]
@ -15,10 +15,7 @@ path = "src/bin/scythe-ui.rs"
crossbeam-channel = "0.5.16" crossbeam-channel = "0.5.16"
ffmpeg-next = "9.0.0" ffmpeg-next = "9.0.0"
ffmpeg-sys-next = "9.0.0" ffmpeg-sys-next = "9.0.0"
gdk = "0.18.2"
glib = "0.22.9"
global-hotkey = "0.8.0" global-hotkey = "0.8.0"
gtk = { version = "0.18.2", package = "gtk" }
libc = "0.2.189" libc = "0.2.189"
mp4 = "0.14.0" mp4 = "0.14.0"
ringbuf = "0.5.1" ringbuf = "0.5.1"
@ -34,11 +31,17 @@ image = { version = "0.25", default-features = false, features = ["png"] }
[target.'cfg(target_os = "linux")'.dependencies] [target.'cfg(target_os = "linux")'.dependencies]
ashpd = { version = "0.13.13", features = ["screencast"] } ashpd = { version = "0.13.13", features = ["screencast"] }
gtk-layer-shell = { version = "0.8.2", features = ["v0_5"] }
pipewire = "0.10.1" pipewire = "0.10.1"
xcb = { version = "1.7.1", features = ["shm", "xfixes"] } xcb = { version = "1.7.1", features = ["shm", "xfixes"] }
memmap2 = "0.9.11" memmap2 = "0.9.11"
[profile.release]
opt-level = 3
lto = "thin"
codegen-units = 4
panic = "abort"
strip = true
[target.'cfg(target_os = "windows")'.dependencies] [target.'cfg(target_os = "windows")'.dependencies]
windows = { version = "0.58.0", features = [ windows = { version = "0.58.0", features = [
"Win32_Foundation", "Win32_Foundation",

View file

@ -26,6 +26,27 @@ ln -sf "$BIN_DIR/scythe-ui" "$BIN_DIR/vrec-ui"
ln -sf "$BIN_DIR/scythe-ui" "$BIN_DIR/vrec" ln -sf "$BIN_DIR/scythe-ui" "$BIN_DIR/vrec"
SYSTEMD_USER_DIR="$HOME/.config/systemd/user" SYSTEMD_USER_DIR="$HOME/.config/systemd/user"
mkdir -p "$SYSTEMD_USER_DIR"
cat > "$SYSTEMD_USER_DIR/scythe-daemon.service" << EOF
[Unit]
Description=Scythe Screen Recorder Background Engine
After=graphical-session.target xdg-desktop-portal.service xdg-desktop-portal-hyprland.service
Wants=xdg-desktop-portal.service
[Service]
Type=simple
ExecStart=$BIN_DIR/scythe-daemon
Restart=always
RestartSec=2
Environment="XDG_RUNTIME_DIR=/run/user/1000"
Environment="XDG_CURRENT_DESKTOP=Hyprland"
Environment="XDG_SESSION_TYPE=wayland"
[Install]
WantedBy=default.target
EOF
systemctl --user daemon-reload 2>/dev/null || true
if [ -f "$SYSTEMD_USER_DIR/vrec-daemon.service" ] || systemctl --user is-active --quiet vrec-daemon.service 2>/dev/null; then if [ -f "$SYSTEMD_USER_DIR/vrec-daemon.service" ] || systemctl --user is-active --quiet vrec-daemon.service 2>/dev/null; then
echo "Migrating legacy vrec-daemon systemd service to scythe-daemon.service..." echo "Migrating legacy vrec-daemon systemd service to scythe-daemon.service..."
systemctl --user stop vrec-daemon.service 2>/dev/null || true systemctl --user stop vrec-daemon.service 2>/dev/null || true

View file

@ -381,11 +381,17 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
} }
} }
let is_mock_cli = std::env::args().any(|a| a == "--mock");
let mut source: Box<dyn FrameSource> = match source_opt { let mut source: Box<dyn FrameSource> = match source_opt {
Some(s) => s, Some(s) => s,
None => { None => {
eprintln!("Warning: Hardware capture unavailable; falling back to mock capture to preserve daemon IPC."); if is_mock_cli {
Box::new(scythe::capture::mock::MockCapture::new()) eprintln!("Warning: Mock capture mode active via --mock flag.");
Box::new(scythe::capture::mock::MockCapture::new())
} else {
eprintln!("Error: Hardware capture failed to initialize on {} session. Exiting for restart.", session_type);
std::process::exit(1);
}
} }
}; };

View file

@ -120,8 +120,8 @@ fn check_and_toggle_overlay() -> bool {
} }
} }
#[cfg(windows)] #[cfg(windows)]
if let Ok(pid) = content.trim().parse::<u32>() { if let Ok(pid) = content.trim().parse::<u32>()
if pid != std::process::id() { && pid != std::process::id() {
unsafe { unsafe {
use windows::Win32::UI::WindowsAndMessaging::{ use windows::Win32::UI::WindowsAndMessaging::{
EnumWindows, GetWindowThreadProcessId, PostMessageW, WM_CLOSE, EnumWindows, GetWindowThreadProcessId, PostMessageW, WM_CLOSE,
@ -132,13 +132,15 @@ fn check_and_toggle_overlay() -> bool {
}; };
unsafe extern "system" fn close_proc(hwnd: HWND, lparam: LPARAM) -> BOOL { unsafe extern "system" fn close_proc(hwnd: HWND, lparam: LPARAM) -> BOOL {
let target = lparam.0 as u32; unsafe {
let mut pid = 0u32; let target = lparam.0 as u32;
GetWindowThreadProcessId(hwnd, Some(&mut pid)); let mut pid = 0u32;
if pid == target { GetWindowThreadProcessId(hwnd, Some(&mut pid));
let _ = PostMessageW(hwnd, WM_CLOSE, None, None); if pid == target {
let _ = PostMessageW(hwnd, WM_CLOSE, None, None);
}
BOOL(1)
} }
BOOL(1)
} }
let _ = EnumWindows(Some(close_proc), LPARAM(pid as isize)); let _ = EnumWindows(Some(close_proc), LPARAM(pid as isize));
@ -154,7 +156,6 @@ fn check_and_toggle_overlay() -> bool {
let _ = std::fs::remove_file(&pid_path); let _ = std::fs::remove_file(&pid_path);
return true; return true;
} }
}
} }
} }
let _ = std::fs::remove_file(&pid_path); let _ = std::fs::remove_file(&pid_path);
@ -480,11 +481,11 @@ fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
return Ok(()); return Ok(());
} }
"--gtk" => { "--gtk" => {
ensure_daemon_running(); ensure_daemon_running_async();
ensure_hotkeys_running(); ensure_hotkeys_running();
#[cfg(target_os = "linux")] if check_and_toggle_overlay() {
scythe::overlay::show_menu_overlay(); return Ok(());
#[cfg(not(target_os = "linux"))] }
scythe::overlay_egui::run_egui_overlay(); scythe::overlay_egui::run_egui_overlay();
return Ok(()); return Ok(());
} }
@ -767,7 +768,7 @@ fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
use windows::Win32::UI::WindowsAndMessaging::{ use windows::Win32::UI::WindowsAndMessaging::{
MsgWaitForMultipleObjectsEx, QS_ALLINPUT, MWMO_INPUTAVAILABLE, MsgWaitForMultipleObjectsEx, QS_ALLINPUT, MWMO_INPUTAVAILABLE,
}; };
let _ = MsgWaitForMultipleObjectsEx(None, 200, QS_ALLINPUT.0, MWMO_INPUTAVAILABLE); let _ = MsgWaitForMultipleObjectsEx(None, 200, QS_ALLINPUT, MWMO_INPUTAVAILABLE);
} }
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
std::thread::sleep(std::time::Duration::from_millis(30)); std::thread::sleep(std::time::Duration::from_millis(30));

View file

@ -55,20 +55,20 @@ impl WindowsCapture {
'adapter_loop: while let Ok(adapter) = factory.EnumAdapters1(a_idx) { 'adapter_loop: while let Ok(adapter) = factory.EnumAdapters1(a_idx) {
if let Ok(desc1) = adapter.GetDesc1() { if let Ok(desc1) = adapter.GetDesc1() {
// Skip software rasterizers (e.g. Microsoft Basic Render Driver) when hardware GPU exists // Skip software rasterizers (e.g. Microsoft Basic Render Driver) when hardware GPU exists
if (desc1.Flags & DXGI_ADAPTER_FLAG_SOFTWARE.0) != 0 { if (desc1.Flags & (DXGI_ADAPTER_FLAG_SOFTWARE.0 as u32)) != 0 {
a_idx += 1; a_idx += 1;
continue; continue;
} }
} }
let mut o_idx = 0; let mut o_idx = 0;
while let Ok(output) = adapter.EnumOutputs(o_idx) { while let Ok(output) = adapter.EnumOutputs(o_idx) {
if let Ok(output1) = output.cast::<IDXGIOutput1>() { if let Ok(output1) = output.cast::<IDXGIOutput1>()
if let Ok(desc) = output1.GetDesc() { && let Ok(desc) = output1.GetDesc() {
let is_attached = desc.AttachedToDesktop.as_bool(); let is_attached = desc.AttachedToDesktop.as_bool();
let width = (desc.DesktopCoordinates.right - desc.DesktopCoordinates.left).unsigned_abs(); let width = (desc.DesktopCoordinates.right - desc.DesktopCoordinates.left).unsigned_abs();
let height = (desc.DesktopCoordinates.bottom - desc.DesktopCoordinates.top).unsigned_abs(); let height = (desc.DesktopCoordinates.bottom - desc.DesktopCoordinates.top).unsigned_abs();
if is_attached && width > 0 && height > 0 { if is_attached && width > 0 && height > 0
if let Ok(adapter_base) = adapter.cast::<IDXGIAdapter>() { && let Ok(adapter_base) = adapter.cast::<IDXGIAdapter>() {
let mut device: Option<ID3D11Device> = None; let mut device: Option<ID3D11Device> = None;
let mut context: Option<ID3D11DeviceContext> = None; let mut context: Option<ID3D11DeviceContext> = None;
let mut feature_level = D3D_FEATURE_LEVEL_11_0; let mut feature_level = D3D_FEATURE_LEVEL_11_0;
@ -90,8 +90,8 @@ impl WindowsCapture {
Some(&mut context), Some(&mut context),
); );
if create_res.is_ok() { if create_res.is_ok()
if let (Some(dev), Some(ctx)) = (device, context) { && let (Some(dev), Some(ctx)) = (device, context) {
match output1.DuplicateOutput(&dev) { match output1.DuplicateOutput(&dev) {
Ok(dup) => { Ok(dup) => {
successful_session = Some((dev, ctx, dup, width, height)); successful_session = Some((dev, ctx, dup, width, height));
@ -101,11 +101,8 @@ impl WindowsCapture {
eprintln!("Adapter {} Output {} DuplicateOutput failed: {:?}", a_idx, o_idx, e); eprintln!("Adapter {} Output {} DuplicateOutput failed: {:?}", a_idx, o_idx, e);
} }
} }
}
} }
}
} }
}
} }
o_idx += 1; o_idx += 1;
} }
@ -178,7 +175,7 @@ impl FrameSource for WindowsCapture {
let _ = self.reinit_duplication(); let _ = self.reinit_duplication();
} }
let mut dup = match self.duplication.clone() { let dup = match self.duplication.clone() {
Some(d) => d, Some(d) => d,
None => { None => {
return Ok(Frame::Raw { return Ok(Frame::Raw {

View file

@ -1,5 +1,6 @@
use ffmpeg_next::ffi::*; use ffmpeg_next::ffi::*;
use std::ptr; use std::ptr;
#[cfg(unix)]
use std::collections::HashMap; use std::collections::HashMap;
use crate::capture::Frame; use crate::capture::Frame;
@ -294,9 +295,11 @@ impl VaapiEncoder {
self.has_frame = true; self.has_frame = true;
self.send_staged_frame(&mut packets, pts)?; self.send_staged_frame(&mut packets, pts)?;
} }
#[allow(unused_variables)]
Frame::DmaBuf { width: dma_width, height: dma_height, format, modifier, fd, stride, offset } => { Frame::DmaBuf { width: dma_width, height: dma_height, format, modifier, fd, stride, offset } => {
#[allow(unused_variables)] #[allow(unused_variables)]
let mmap_size = (*offset as usize) + (*stride as usize) * (*dma_height as usize); let mmap_size = (*offset as usize) + (*stride as usize) * (*dma_height as usize);
#[allow(unused_mut)]
let mut mapped_direct = false; let mut mapped_direct = false;
#[cfg(unix)] #[cfg(unix)]
@ -465,6 +468,7 @@ impl Drop for VaapiEncoder {
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
pub struct WindowsHwEncoder { pub struct WindowsHwEncoder {
codec_ctx: *mut AVCodecContext, codec_ctx: *mut AVCodecContext,
#[allow(dead_code)]
encoder_name: String, encoder_name: String,
sws_ctx: *mut SwsContext, sws_ctx: *mut SwsContext,
sw_frame: *mut AVFrame, sw_frame: *mut AVFrame,
@ -697,46 +701,43 @@ impl WindowsHwEncoder {
pub fn encode_frame(&mut self, frame: &Frame, pts: i64) -> Result<Vec<crate::ring::Packet>, String> { pub fn encode_frame(&mut self, frame: &Frame, pts: i64) -> Result<Vec<crate::ring::Packet>, String> {
let mut packets = Vec::new(); let mut packets = Vec::new();
unsafe { unsafe {
match frame { if let Frame::Raw { width: _, height, stride, data } = frame {
Frame::Raw { width: _, height, stride, data } => { if av_frame_make_writable(self.sw_frame) < 0 {
if av_frame_make_writable(self.sw_frame) < 0 { let _ = av_frame_get_buffer(self.sw_frame, 32);
let _ = av_frame_get_buffer(self.sw_frame, 32); }
}
if !self.sws_ctx.is_null() {
if !self.sws_ctx.is_null() { let src_data = [data.as_ptr(), ptr::null(), ptr::null(), ptr::null()];
let src_data = [data.as_ptr(), ptr::null(), ptr::null(), ptr::null()]; let src_linesize = [*stride as i32, 0, 0, 0];
let src_linesize = [*stride as i32, 0, 0, 0]; sws_scale(
sws_scale( self.sws_ctx,
self.sws_ctx, src_data.as_ptr(),
src_data.as_ptr(), src_linesize.as_ptr(),
src_linesize.as_ptr(), 0,
0, *height as i32,
*height as i32, (*self.sw_frame).data.as_mut_ptr(),
(*self.sw_frame).data.as_mut_ptr(), (*self.sw_frame).linesize.as_mut_ptr(),
(*self.sw_frame).linesize.as_mut_ptr(), );
); }
}
(*self.sw_frame).pts = pts;
(*self.sw_frame).pts = pts; if self.force_idr {
if self.force_idr { (*self.sw_frame).pict_type = AVPictureType::AV_PICTURE_TYPE_I;
(*self.sw_frame).pict_type = AVPictureType::AV_PICTURE_TYPE_I; self.force_idr = false;
self.force_idr = false; } else {
} else { (*self.sw_frame).pict_type = AVPictureType::AV_PICTURE_TYPE_NONE;
(*self.sw_frame).pict_type = AVPictureType::AV_PICTURE_TYPE_NONE; }
}
if avcodec_send_frame(self.codec_ctx, self.sw_frame) >= 0 {
if avcodec_send_frame(self.codec_ctx, self.sw_frame) >= 0 { let mut pkt = av_packet_alloc();
let mut pkt = av_packet_alloc(); while avcodec_receive_packet(self.codec_ctx, pkt) >= 0 {
while avcodec_receive_packet(self.codec_ctx, pkt) >= 0 { let new_pkt = av_packet_alloc();
let new_pkt = av_packet_alloc(); av_packet_move_ref(new_pkt, pkt);
av_packet_move_ref(new_pkt, pkt); packets.push(crate::ring::Packet::new(new_pkt));
packets.push(crate::ring::Packet::new(new_pkt)); }
} av_packet_free(&mut pkt);
av_packet_free(&mut pkt); self.has_frame = true;
self.has_frame = true;
}
} }
_ => {}
} }
} }
Ok(packets) Ok(packets)

View file

@ -1,5 +1,30 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
pub enum ToastIcon {
Info,
Record,
Save,
Replay,
Cursor,
Screenshot,
Error,
}
impl ToastIcon {
pub fn from_name(name: &str) -> Self {
match name.to_lowercase().as_str() {
"replay" | "save_replay" => ToastIcon::Replay,
"record" | "recording" | "start" => ToastIcon::Record,
"save" | "saved" | "stop" => ToastIcon::Save,
"cursor" => ToastIcon::Cursor,
"screenshot" => ToastIcon::Screenshot,
"error" => ToastIcon::Error,
_ => ToastIcon::Info,
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub enum Command { pub enum Command {
SaveReplay, SaveReplay,
@ -79,16 +104,14 @@ pub fn get_ipc_port_path() -> std::path::PathBuf {
#[cfg(windows)] #[cfg(windows)]
pub fn read_active_ipc_port() -> u16 { pub fn read_active_ipc_port() -> u16 {
if let Ok(content) = std::fs::read_to_string(get_ipc_port_path()) { if let Ok(content) = std::fs::read_to_string(get_ipc_port_path())
if let Ok(port) = content.trim().parse::<u16>() { && let Ok(port) = content.trim().parse::<u16>() {
return port; return port;
}
} }
let legacy = std::env::temp_dir().join("vrec-ipc.port"); let legacy = std::env::temp_dir().join("vrec-ipc.port");
if let Ok(content) = std::fs::read_to_string(legacy) { if let Ok(content) = std::fs::read_to_string(legacy)
if let Ok(port) = content.trim().parse::<u16>() { && let Ok(port) = content.trim().parse::<u16>() {
return port; return port;
}
} }
42069 42069
} }
@ -147,14 +170,13 @@ pub fn send_command(cmd: Command) -> Result<(), Box<dyn std::error::Error + Send
use std::net::{SocketAddr, TcpStream}; use std::net::{SocketAddr, TcpStream};
let candidate_ports = get_candidate_ipc_ports(); let candidate_ports = get_candidate_ipc_ports();
for &port in &candidate_ports { for &port in &candidate_ports {
if let Ok(addr) = format!("127.0.0.1:{}", port).parse::<SocketAddr>() { if let Ok(addr) = format!("127.0.0.1:{}", port).parse::<SocketAddr>()
if let Ok(mut stream) = TcpStream::connect_timeout(&addr, Duration::from_millis(150)) { && let Ok(mut stream) = TcpStream::connect_timeout(&addr, Duration::from_millis(150)) {
let _ = stream.set_write_timeout(Some(Duration::from_millis(600))); let _ = stream.set_write_timeout(Some(Duration::from_millis(600)));
let _ = stream.set_read_timeout(Some(Duration::from_millis(600))); let _ = stream.set_read_timeout(Some(Duration::from_millis(600)));
if stream.write_all(&len_buf).is_ok() && stream.write_all(&payload).is_ok() { if stream.write_all(&len_buf).is_ok() && stream.write_all(&payload).is_ok() {
return Ok(()); return Ok(());
} }
}
} }
} }
last_err = Some("Could not connect to daemon on any TCP IPC port".into()); last_err = Some("Could not connect to daemon on any TCP IPC port".into());
@ -217,11 +239,10 @@ pub fn query_status() -> Result<DaemonStatus, Box<dyn std::error::Error + Send +
let candidate_ports = get_candidate_ipc_ports(); let candidate_ports = get_candidate_ipc_ports();
let mut conn = None; let mut conn = None;
for &port in &candidate_ports { for &port in &candidate_ports {
if let Ok(addr) = format!("127.0.0.1:{}", port).parse::<SocketAddr>() { if let Ok(addr) = format!("127.0.0.1:{}", port).parse::<SocketAddr>()
if let Ok(stream) = TcpStream::connect_timeout(&addr, Duration::from_millis(150)) { && let Ok(stream) = TcpStream::connect_timeout(&addr, Duration::from_millis(150)) {
conn = Some(stream); conn = Some(stream);
break; break;
}
} }
} }
conn.ok_or_else(|| std::io::Error::new(std::io::ErrorKind::NotConnected, "No active daemon port reachable")) conn.ok_or_else(|| std::io::Error::new(std::io::ErrorKind::NotConnected, "No active daemon port reachable"))

View file

@ -8,7 +8,6 @@ pub mod hotkey;
pub mod ipc; pub mod ipc;
pub mod overlay; pub mod overlay;
pub mod config; pub mod config;
pub mod settings_ui;
pub mod overlay_egui; pub mod overlay_egui;
pub mod hyprland_binds; pub mod hyprland_binds;
pub mod updater; pub mod updater;

File diff suppressed because it is too large Load diff

View file

@ -484,7 +484,7 @@ fn pick_folder(current_dir: &str, tx: Sender<String>, is_active: Arc<AtomicBool>
let _ = unsafe { CoInitializeEx(None, COINIT_APARTMENTTHREADED) }; let _ = unsafe { CoInitializeEx(None, COINIT_APARTMENTTHREADED) };
let mut picked = None; let mut picked = None;
if let Ok(dialog) = (unsafe { CoCreateInstance::<_, IFileOpenDialog>(&FileOpenDialog, None, CLSCTX_INPROC_SERVER) }) { if let Ok(dialog) = unsafe { CoCreateInstance::<_, IFileOpenDialog>(&FileOpenDialog, None, CLSCTX_INPROC_SERVER) } {
unsafe { unsafe {
let _ = dialog.SetTitle(windows::core::w!("Select Recordings Directory")); let _ = dialog.SetTitle(windows::core::w!("Select Recordings Directory"));
if dialog.SetOptions(FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM).is_ok() if dialog.SetOptions(FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM).is_ok()
@ -2115,7 +2115,7 @@ impl ScytheOverlayApp {
ui.with_layout(egui::Layout::bottom_up(egui::Align::Center), |ui| { ui.with_layout(egui::Layout::bottom_up(egui::Align::Center), |ui| {
ui.add_space(4.0); ui.add_space(4.0);
ui.label( ui.label(
egui::RichText::new("Scythe v0.2.17") egui::RichText::new(format!("Scythe v{}", crate::updater::CURRENT_VERSION))
.size(10.0) .size(10.0)
.color(Color32::from_rgb(100, 108, 120)), .color(Color32::from_rgb(100, 108, 120)),
); );
@ -3022,7 +3022,7 @@ impl ScytheOverlayApp {
render_section_card(ui, "Software Updates", accent, |ui| { render_section_card(ui, "Software Updates", accent, |ui| {
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.label(egui::RichText::new("Scythe Version:").size(12.0).color(Color32::WHITE)); ui.label(egui::RichText::new("Scythe Version:").size(12.0).color(Color32::WHITE));
ui.label(egui::RichText::new("v0.2.17 (Latest)").size(12.0).color(accent).strong()); ui.label(egui::RichText::new(format!("v{} (Latest)", crate::updater::CURRENT_VERSION)).size(12.0).color(accent).strong());
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
if squared_button_sized(ui, "Check for Updates", false, accent, Some(egui::vec2(130.0, 26.0))) { if squared_button_sized(ui, "Check for Updates", false, accent, Some(egui::vec2(130.0, 26.0))) {
@ -3934,7 +3934,7 @@ impl eframe::App for ScytheOverlayApp {
} }
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
pub fn apply_windows_transparency(title: &str) { pub fn apply_windows_transparency(_title: &str) {
unsafe { unsafe {
use windows::Win32::UI::WindowsAndMessaging::{ use windows::Win32::UI::WindowsAndMessaging::{
EnumWindows, GetWindowThreadProcessId, EnumWindows, GetWindowThreadProcessId,
@ -4202,13 +4202,15 @@ pub fn run_egui_toast(title: &str, subtitle: &str, icon: crate::overlay::ToastIc
}; };
unsafe extern "system" fn close_toast(hwnd: HWND, lparam: LPARAM) -> BOOL { unsafe extern "system" fn close_toast(hwnd: HWND, lparam: LPARAM) -> BOOL {
let target = lparam.0 as u32; unsafe {
let mut pid = 0u32; let target = lparam.0 as u32;
GetWindowThreadProcessId(hwnd, Some(&mut pid)); let mut pid = 0u32;
if pid == target { GetWindowThreadProcessId(hwnd, Some(&mut pid));
let _ = PostMessageW(hwnd, WM_CLOSE, None, None); if pid == target {
let _ = PostMessageW(hwnd, WM_CLOSE, None, None);
}
BOOL(1)
} }
BOOL(1)
} }
let _ = EnumWindows(Some(close_toast), LPARAM(prev_pid as isize)); let _ = EnumWindows(Some(close_toast), LPARAM(prev_pid as isize));

View file

@ -1,187 +0,0 @@
use gtk::prelude::*;
use gtk::{Window, WindowType, Box, Orientation, Label, Entry, Button, Switch, ComboBoxText};
use crate::config::ScytheConfig;
pub fn open_replay_settings(config_in: &ScytheConfig) {
let window = Window::new(WindowType::Toplevel);
window.set_title("Replay Settings");
window.set_default_size(320, 200);
let vbox = Box::new(Orientation::Vertical, 10);
vbox.set_margin(15);
let duration_label = Label::new(Some("Replay Duration (seconds):"));
duration_label.set_halign(gtk::Align::Start);
let duration_entry = Entry::new();
duration_entry.set_text(&config_in.replay_duration_sec.to_string());
let bitrate_label = Label::new(Some("Bitrate (kbps):"));
bitrate_label.set_halign(gtk::Align::Start);
let bitrate_entry = Entry::new();
bitrate_entry.set_text(&config_in.replay_bitrate_kbps.to_string());
let save_btn = Button::with_label("Save");
vbox.pack_start(&duration_label, false, false, 0);
vbox.pack_start(&duration_entry, false, false, 0);
vbox.pack_start(&bitrate_label, false, false, 0);
vbox.pack_start(&bitrate_entry, false, false, 0);
vbox.pack_start(&save_btn, false, false, 0);
window.add(&vbox);
let window_clone = window.clone();
save_btn.connect_clicked(move |_| {
let mut cfg = ScytheConfig::load();
if let Ok(val) = duration_entry.text().parse() {
cfg.replay_duration_sec = val;
}
if let Ok(val) = bitrate_entry.text().parse() {
cfg.replay_bitrate_kbps = val;
}
let _ = cfg.save();
ScytheConfig::notify_daemon_reload();
window_clone.close();
});
let window_esc = window.clone();
window.connect_key_press_event(move |_, key| {
if key.keyval() == gdk::keys::constants::Escape {
window_esc.close();
gtk::glib::Propagation::Stop
} else {
gtk::glib::Propagation::Proceed
}
});
window.show_all();
}
pub fn open_record_settings(config_in: &ScytheConfig) {
let window = Window::new(WindowType::Toplevel);
window.set_title("Record Settings");
window.set_default_size(320, 150);
let vbox = Box::new(Orientation::Vertical, 10);
vbox.set_margin(15);
let bitrate_label = Label::new(Some("Recording Bitrate (kbps):"));
bitrate_label.set_halign(gtk::Align::Start);
let bitrate_entry = Entry::new();
bitrate_entry.set_text(&config_in.record_bitrate_kbps.to_string());
let save_btn = Button::with_label("Save");
vbox.pack_start(&bitrate_label, false, false, 0);
vbox.pack_start(&bitrate_entry, false, false, 0);
vbox.pack_start(&save_btn, false, false, 0);
window.add(&vbox);
let window_clone = window.clone();
save_btn.connect_clicked(move |_| {
let mut cfg = ScytheConfig::load();
if let Ok(val) = bitrate_entry.text().parse() {
cfg.record_bitrate_kbps = val;
}
let _ = cfg.save();
ScytheConfig::notify_daemon_reload();
window_clone.close();
});
let window_esc = window.clone();
window.connect_key_press_event(move |_, key| {
if key.keyval() == gdk::keys::constants::Escape {
window_esc.close();
gtk::glib::Propagation::Stop
} else {
gtk::glib::Propagation::Proceed
}
});
window.show_all();
}
pub fn open_general_settings(config_in: &ScytheConfig) {
let window = Window::new(WindowType::Toplevel);
window.set_title("General Settings");
window.set_default_size(380, 320);
let vbox = Box::new(Orientation::Vertical, 10);
vbox.set_margin(15);
// Autostart
let hbox_auto = Box::new(Orientation::Horizontal, 10);
let auto_label = Label::new(Some("Autostart with system:"));
let auto_switch = Switch::new();
auto_switch.set_active(config_in.autostart);
hbox_auto.pack_start(&auto_label, false, false, 0);
hbox_auto.pack_start(&auto_switch, false, false, 0);
// Theme
let theme_label = Label::new(Some("UI Theme:"));
theme_label.set_halign(gtk::Align::Start);
let theme_combo = ComboBoxText::new();
theme_combo.append_text("dark");
theme_combo.append_text("light");
theme_combo.set_active(Some(if config_in.ui_color_theme == "dark" { 0 } else { 1 }));
// Language
let lang_label = Label::new(Some("Language Code (e.g. en, fr):"));
lang_label.set_halign(gtk::Align::Start);
let lang_entry = Entry::new();
lang_entry.set_text(&config_in.language);
// Hotkeys
let menu_hk_label = Label::new(Some("Menu Hotkey:"));
menu_hk_label.set_halign(gtk::Align::Start);
let menu_hk_entry = Entry::new();
menu_hk_entry.set_text(&config_in.menu_hotkey);
let save_hk_label = Label::new(Some("Save Replay Hotkey:"));
save_hk_label.set_halign(gtk::Align::Start);
let save_hk_entry = Entry::new();
save_hk_entry.set_text(&config_in.save_hotkey);
let save_btn = Button::with_label("Save General Settings");
vbox.pack_start(&hbox_auto, false, false, 0);
vbox.pack_start(&theme_label, false, false, 0);
vbox.pack_start(&theme_combo, false, false, 0);
vbox.pack_start(&lang_label, false, false, 0);
vbox.pack_start(&lang_entry, false, false, 0);
vbox.pack_start(&menu_hk_label, false, false, 0);
vbox.pack_start(&menu_hk_entry, false, false, 0);
vbox.pack_start(&save_hk_label, false, false, 0);
vbox.pack_start(&save_hk_entry, false, false, 0);
vbox.pack_start(&save_btn, false, false, 0);
window.add(&vbox);
let window_clone = window.clone();
save_btn.connect_clicked(move |_| {
let mut cfg = ScytheConfig::load();
cfg.autostart = auto_switch.is_active();
if let Some(txt) = theme_combo.active_text() {
cfg.ui_color_theme = txt.to_string();
}
cfg.language = lang_entry.text().to_string();
cfg.menu_hotkey = menu_hk_entry.text().to_string();
cfg.save_hotkey = save_hk_entry.text().to_string();
let _ = cfg.save();
ScytheConfig::notify_daemon_reload();
window_clone.close();
});
let window_esc = window.clone();
window.connect_key_press_event(move |_, key| {
if key.keyval() == gdk::keys::constants::Escape {
window_esc.close();
gtk::glib::Propagation::Stop
} else {
gtk::glib::Propagation::Proceed
}
});
window.show_all();
}

View file

@ -2,6 +2,8 @@ use std::sync::{Arc, Mutex};
use serde::Deserialize; use serde::Deserialize;
pub const CURRENT_VERSION: &str = env!("CARGO_PKG_VERSION"); pub const CURRENT_VERSION: &str = env!("CARGO_PKG_VERSION");
pub const FORGEJO_HOST: &str = "https://git.oiupoyt.space";
pub const REPO_OWNER_NAME: &str = "oiupoyt/scythe";
pub const GITHUB_REPO: &str = "oiupoyt/scythe"; pub const GITHUB_REPO: &str = "oiupoyt/scythe";
#[derive(Deserialize, Debug, Clone, PartialEq, Eq, Default)] #[derive(Deserialize, Debug, Clone, PartialEq, Eq, Default)]
@ -24,7 +26,7 @@ pub struct ReleaseInfo {
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
struct GitHubRelease { struct ForgejoRelease {
tag_name: String, tag_name: String,
name: Option<String>, name: Option<String>,
html_url: String, html_url: String,
@ -54,41 +56,43 @@ pub fn is_newer(latest: &str, current: &str) -> bool {
} }
pub fn check_for_updates() -> Option<ReleaseInfo> { pub fn check_for_updates() -> Option<ReleaseInfo> {
let url = format!("https://api.github.com/repos/{}/releases/latest", GITHUB_REPO); let urls = [
format!("{}/api/v1/repos/{}/releases/latest", FORGEJO_HOST, REPO_OWNER_NAME),
format!("https://api.github.com/repos/{}/releases/latest", REPO_OWNER_NAME),
];
// Run curl with a 6-second timeout to avoid blocking for url in urls {
let mut cmd = std::process::Command::new("curl"); let mut cmd = std::process::Command::new("curl");
cmd.args([ cmd.args([
"-s", "-s",
"-H", "User-Agent: scythe-updater", "-H", "User-Agent: scythe-updater",
"-H", "Accept: application/vnd.github.v3+json", "-H", "Accept: application/json",
"--max-time", "6", "--max-time", "6",
&url, &url,
]); ]);
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
{ {
use std::os::windows::process::CommandExt; use std::os::windows::process::CommandExt;
cmd.creation_flags(0x08000000); cmd.creation_flags(0x08000000);
}
if let Ok(output) = cmd.output()
&& output.status.success()
&& let Ok(release) = serde_json::from_slice::<ForgejoRelease>(&output.stdout) {
let clean_ver = release.tag_name.trim().trim_start_matches('v').to_string();
let has_update = is_newer(&clean_ver, CURRENT_VERSION);
return Some(ReleaseInfo {
tag_name: release.tag_name,
version: clean_ver,
name: release.name.unwrap_or_else(|| format!("v{}", CURRENT_VERSION)),
html_url: release.html_url,
release_notes: release.body.unwrap_or_default(),
has_update,
assets: release.assets,
});
}
} }
let output = cmd.output().ok()?; None
if !output.status.success() {
return None;
}
let release: GitHubRelease = serde_json::from_slice(&output.stdout).ok()?;
let clean_ver = release.tag_name.trim().trim_start_matches('v').to_string();
let has_update = is_newer(&clean_ver, CURRENT_VERSION);
Some(ReleaseInfo {
tag_name: release.tag_name,
version: clean_ver,
name: release.name.unwrap_or_else(|| format!("Scythe v{}", CURRENT_VERSION)),
html_url: release.html_url,
release_notes: release.body.unwrap_or_default(),
has_update,
assets: release.assets,
})
} }
#[derive(Debug, Clone, PartialEq, Eq, Default)] #[derive(Debug, Clone, PartialEq, Eq, Default)]