diff --git a/Cargo.lock b/Cargo.lock index 9be63cae..305e468e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,7 +11,7 @@ dependencies = [ "macroific", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.74", ] [[package]] @@ -108,6 +108,55 @@ dependencies = [ "yansi-term", ] +[[package]] +name = "anstream" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "anstyle-parse" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + [[package]] name = "anyhow" version = "1.0.86" @@ -131,7 +180,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.74", ] [[package]] @@ -148,14 +197,14 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.74", ] [[package]] name = "arrayref" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" +checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" [[package]] name = "arrayvec" @@ -223,9 +272,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd066d0b4ef8ecb03a55319dc13aa6910616d0f44008a045bb1835af830abff5" +checksum = "fec134f64e2bc57411226dfc4e52dec859ddfc7e711fc5e07b612584f000e4aa" dependencies = [ "flate2", "futures-core", @@ -236,9 +285,9 @@ dependencies = [ [[package]] name = "async-io" -version = "2.3.3" +version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d6baa8f0178795da0e71bc42c9e5d13261aac7ee549853162e66a241ba17964" +checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8" dependencies = [ "async-lock", "cfg-if", @@ -250,7 +299,7 @@ dependencies = [ "rustix", "slab", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -272,9 +321,9 @@ checksum = "9338790e78aa95a416786ec8389546c4b6a1dfc3dc36071ed9518a9413a542eb" [[package]] name = "async-process" -version = "2.2.3" +version = "2.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7eda79bbd84e29c2b308d1dc099d7de8dcc7035e48f4bf5dc4a531a44ff5e2a" +checksum = "a8a07789659a4d385b79b18b9127fc27e1a59e1e89117c78c5ea3b806f016374" dependencies = [ "async-channel", "async-io", @@ -287,7 +336,7 @@ dependencies = [ "futures-lite", "rustix", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -298,7 +347,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.74", ] [[package]] @@ -313,9 +362,9 @@ dependencies = [ [[package]] name = "async-signal" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb3634b73397aa844481f814fad23bbf07fdb0eabec10f2eb95e58944b1ec32" +checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" dependencies = [ "async-io", "async-lock", @@ -326,7 +375,7 @@ dependencies = [ "rustix", "signal-hook-registry", "slab", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -348,7 +397,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.74", ] [[package]] @@ -365,7 +414,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.74", ] [[package]] @@ -529,7 +578,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.71", + "syn 2.0.74", ] [[package]] @@ -638,9 +687,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytemuck" -version = "1.16.1" +version = "1.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b236fc92302c97ed75b38da1f4917b5cdda4984745740f153a5d3059e48d725e" +checksum = "102087e286b4677862ea56cf8fc58bb2cdfa8725c40ffb80fe3a008eb7f2fc83" [[package]] name = "byteorder" @@ -656,9 +705,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.6.1" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" [[package]] name = "bytesize" @@ -701,9 +750,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.5" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "324c74f2155653c90b04f25b2a47a8a631360cb908f92a772695f430c7e31052" +checksum = "e9e8aabfac534be767c909e0690571677d49f41bd8465ae876fe043d52ba5292" dependencies = [ "jobserver", "libc", @@ -806,6 +855,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "colorchoice" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -863,9 +918,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" @@ -961,7 +1016,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.74", ] [[package]] @@ -985,7 +1040,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.71", + "syn 2.0.74", ] [[package]] @@ -996,7 +1051,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.71", + "syn 2.0.74", ] [[package]] @@ -1055,7 +1110,7 @@ dependencies = [ "macroific", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.74", ] [[package]] @@ -1078,7 +1133,7 @@ checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.74", ] [[package]] @@ -1108,7 +1163,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.74", ] [[package]] @@ -1118,7 +1173,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" dependencies = [ "derive_builder_core", - "syn 2.0.71", + "syn 2.0.74", ] [[package]] @@ -1138,6 +1193,21 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8cf7d61e627a3b49af8f24f47e57f3788cdd7a0e4f17cd79fda5ada87f08578" +[[package]] +name = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + [[package]] name = "ed25519" version = "2.2.3" @@ -1194,7 +1264,30 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.74", +] + +[[package]] +name = "env_filter" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", ] [[package]] @@ -1309,7 +1402,7 @@ dependencies = [ "macroific", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.74", ] [[package]] @@ -1351,9 +1444,9 @@ checksum = "b3ea1ec5f8307826a5b71094dd91fc04d4ae75d5709b20ad351c7fb4815c86ec" [[package]] name = "flate2" -version = "1.0.30" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +checksum = "7f211bbe8e69bbd0cfdea405084f128ae8b4aaa6b0b522fc8f2b009084797920" dependencies = [ "crc32fast", "miniz_oxide", @@ -1380,7 +1473,28 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ - "foreign-types-shared", + "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.74", ] [[package]] @@ -1389,6 +1503,12 @@ 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" @@ -1408,6 +1528,7 @@ dependencies = [ "futures-util", "geo-uri", "gettext-rs", + "glycin", "gst-plugin-gtk4", "gstreamer", "gstreamer-base", @@ -1526,7 +1647,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.74", ] [[package]] @@ -1570,21 +1691,21 @@ dependencies = [ [[package]] name = "g2gen" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc2c7625b2fc250dd90b63f7887a6bb0f7ec1d714c8278415bea2669ef20820e" +checksum = "dc3e32f911a41e073b8492473c3595a043e1369ab319a2dbf8c89b1fea06457c" dependencies = [ "g2poly", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.74", ] [[package]] name = "g2p" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc36d9bdc3d2da057775a9f4fa7d7b09edab3e0eda7a92cc353358fa63b8519e" +checksum = "1a9afa6efed9af3a5a68ba066429c1497c299d4eafbd948fe630df47a8f2d29f" dependencies = [ "g2gen", "g2poly", @@ -1592,9 +1713,9 @@ dependencies = [ [[package]] name = "g2poly" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af6a86e750338603ea2c14b1c0bfe58cd61f87ca67a0021d9334996024608e12" +checksum = "0fd8b261ccf00df8c5cc60c082bb7d7aa64c33a433cfcc091ca244326c924b2c" [[package]] name = "gdk-pixbuf" @@ -1819,7 +1940,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.74", ] [[package]] @@ -1863,6 +1984,56 @@ dependencies = [ "web-sys", ] +[[package]] +name = "glycin" +version = "2.0.0-beta" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ad0e7b971597fd206fbf0a6fb37608fd7983705ef8adfb8781fcbc155ef8f83" +dependencies = [ + "futures-channel", + "futures-util", + "gdk4", + "gio", + "glycin-utils", + "gufo-common", + "gufo-exif", + "lcms2", + "lcms2-sys", + "libc", + "libseccomp", + "memfd", + "memmap2", + "nix 0.29.0", + "static_assertions", + "thiserror", + "tokio", + "tokio-stream", + "tracing", + "yeslogic-fontconfig-sys", + "zbus", +] + +[[package]] +name = "glycin-utils" +version = "2.0.0-beta" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d32e5bd3f73d2df60927b5eca9cdc9aee064dc7df8720c8bfb73f815f6a17d97" +dependencies = [ + "env_logger", + "gufo-common", + "libc", + "libseccomp", + "log", + "memmap2", + "nix 0.29.0", + "paste", + "rmp-serde", + "serde", + "thiserror", + "tokio", + "zbus", +] + [[package]] name = "gobject-sys" version = "0.20.0" @@ -1965,7 +2136,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e5e874f1660252fd2ec81c602066df3633b3a6fcbe2b196f7f93c27cf069b2a" dependencies = [ "chrono", - "toml_edit 0.22.16", + "toml_edit 0.22.20", ] [[package]] @@ -2210,7 +2381,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.74", ] [[package]] @@ -2232,6 +2403,28 @@ dependencies = [ "system-deps 7.0.1", ] +[[package]] +name = "gufo-common" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1af2c3c6b244761a93e30989fa0868b92e386b6eb817fc6ced405462af4a7db" +dependencies = [ + "once_cell", + "paste", + "serde", +] + +[[package]] +name = "gufo-exif" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2aff5e56872f03d477b55cccef818e6cdae482485c39c130ff9b3b31b69ea10" +dependencies = [ + "gufo-common", + "thiserror", + "tracing", +] + [[package]] name = "half" version = "2.4.1" @@ -2314,7 +2507,7 @@ dependencies = [ "markup5ever", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.74", ] [[package]] @@ -2363,6 +2556,12 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "hyper" version = "1.4.1" @@ -2401,9 +2600,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956" +checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" dependencies = [ "bytes", "futures-channel", @@ -2460,12 +2659,12 @@ dependencies = [ [[package]] name = "image" -version = "0.25.1" +version = "0.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd54d660e773627692c524beaad361aca785a4f9f5730ce91f42aabe5bce3d11" +checksum = "99314c8a2152b8ddb211f924cdae532d8c5e4c8bb54728e12fff1b0cd5963a10" dependencies = [ "bytemuck", - "byteorder", + "byteorder-lite", "color_quant", "exr", "gif", @@ -2541,9 +2740,9 @@ dependencies = [ [[package]] name = "indexed_db_futures" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cc2083760572ee02385ab8b7c02c20925d2dd1f97a1a25a8737a238608f1152" +checksum = "0704b71f13f81b5933d791abf2de26b33c40935143985220299a357721166706" dependencies = [ "accessory", "cfg-if", @@ -2558,9 +2757,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.6" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" dependencies = [ "equivalent", "hashbrown", @@ -2597,7 +2796,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.74", ] [[package]] @@ -2606,6 +2805,12 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +[[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.10.5" @@ -2641,9 +2846,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jobserver" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ "libc", ] @@ -2716,6 +2921,29 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +[[package]] +name = "lcms2" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "680ec3fa42c36e0af9ca02f20a3742a82229c7f1ee0e6754294de46a80be6f74" +dependencies = [ + "bytemuck", + "foreign-types 0.5.0", + "lcms2-sys", +] + +[[package]] +name = "lcms2-sys" +version = "4.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "593265f9a3172180024fb62580ee31348f31be924b19416da174ebb7fb623d2e" +dependencies = [ + "cc", + "dunce", + "libc", + "pkg-config", +] + [[package]] name = "lebe" version = "0.5.2" @@ -2772,9 +3000,9 @@ dependencies = [ [[package]] name = "libloading" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", "windows-targets 0.52.6", @@ -2786,6 +3014,24 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +[[package]] +name = "libseccomp" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21c57fd8981a80019807b7b68118618d29a87177c63d704fc96e6ecd003ae5b3" +dependencies = [ + "bitflags 1.3.2", + "libc", + "libseccomp-sys", + "pkg-config", +] + +[[package]] +name = "libseccomp-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a7cbbd4ad467251987c6e5b47d53b11a5a05add08f2447a9e2d70aef1e0d138" + [[package]] name = "libshumate" version = "0.6.0" @@ -2910,9 +3156,9 @@ dependencies = [ [[package]] name = "lru" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" +checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" dependencies = [ "hashbrown", ] @@ -2943,7 +3189,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.74", ] [[package]] @@ -2954,7 +3200,7 @@ checksum = "13198c120864097a565ccb3ff947672d969932b7975ebd4085732c9f09435e55" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.74", ] [[package]] @@ -2967,7 +3213,7 @@ dependencies = [ "macroific_core", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.74", ] [[package]] @@ -3034,7 +3280,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.74", ] [[package]] @@ -3299,7 +3545,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" dependencies = [ "cfg-if", - "rayon", ] [[package]] @@ -3308,6 +3553,24 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "memfd" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64" +dependencies = [ + "rustix", +] + +[[package]] +name = "memmap2" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" +dependencies = [ + "libc", +] + [[package]] name = "memoffset" version = "0.9.1" @@ -3357,13 +3620,14 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.11" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" dependencies = [ + "hermit-abi 0.3.9", "libc", "wasi", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -3510,7 +3774,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.74", ] [[package]] @@ -3594,9 +3858,9 @@ dependencies = [ [[package]] name = "object" -version = "0.36.1" +version = "0.36.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" +checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9" dependencies = [ "memchr", ] @@ -3635,13 +3899,13 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.64" +version = "0.10.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" dependencies = [ "bitflags 2.6.0", "cfg-if", - "foreign-types", + "foreign-types 0.3.2", "libc", "once_cell", "openssl-macros", @@ -3656,7 +3920,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.74", ] [[package]] @@ -3667,9 +3931,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.102" +version = "0.9.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" dependencies = [ "cc", "libc", @@ -3827,7 +4091,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.74", ] [[package]] @@ -3865,7 +4129,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.74", ] [[package]] @@ -3882,9 +4146,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "piper" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1d5c74c9876f070d3e8fd503d748c7d974c3e48da8f41350fa5222ef9b4391" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" dependencies = [ "atomic-waker", "fastrand", @@ -3961,9 +4225,9 @@ dependencies = [ [[package]] name = "polling" -version = "3.7.2" +version = "3.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3ed00ed3fbf728b5816498ecd316d1716eecaced9c0c8d2c5a6740ca214985b" +checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511" dependencies = [ "cfg-if", "concurrent-queue", @@ -3971,7 +4235,7 @@ dependencies = [ "pin-project-lite", "rustix", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3993,9 +4257,12 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] [[package]] name = "precomputed-hash" @@ -4061,7 +4328,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd" dependencies = [ "quote", - "syn 2.0.71", + "syn 2.0.74", ] [[package]] @@ -4084,7 +4351,7 @@ dependencies = [ "itertools 0.13.0", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.74", ] [[package]] @@ -4215,16 +4482,15 @@ dependencies = [ [[package]] name = "ravif" -version = "0.11.8" +version = "0.11.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6ba61c28ba24c0cf8406e025cb29a742637e3f70776e61c27a8a8b72a042d12" +checksum = "a8f0bfd976333248de2078d350bfdf182ff96e168a24d23d2436cef320dd4bdd" dependencies = [ "avif-serialize", "imgref", "loop9", "quick-error", "rav1e", - "rayon", "rgb", ] @@ -4265,9 +4531,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.5" +version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", @@ -4352,9 +4618,9 @@ dependencies = [ [[package]] name = "rgb" -version = "0.8.45" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade4539f42266ded9e755c605bdddf546242b2c961b03b06a7375260788a0523" +checksum = "0f86ae463694029097b846d8f99fd5536740602ae00022c0c50c5600720b2f71" dependencies = [ "bytemuck", ] @@ -4534,7 +4800,7 @@ dependencies = [ "quote", "ruma-identifiers-validation", "serde", - "syn 2.0.71", + "syn 2.0.74", "toml", ] @@ -4600,9 +4866,9 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" +checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" dependencies = [ "base64", "rustls-pki-types", @@ -4610,9 +4876,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" +checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" [[package]] name = "rustversion" @@ -4681,9 +4947,9 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.204" +version = "1.0.206" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +checksum = "5b3e4cd94123dd520a128bcd11e34d9e9e423e7e3e50425cb1b4b1e3549d0284" dependencies = [ "serde_derive", ] @@ -4710,13 +4976,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.204" +version = "1.0.206" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +checksum = "fabfb6138d2383ea8208cf98ccf69cdfb1aff4088460681d84189aa259762f97" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.74", ] [[package]] @@ -4734,11 +5000,12 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.120" +version = "1.0.124" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" +checksum = "66ad62847a56b3dba58cc891acd13884b9c61138d330c0d7b6181713d4fce38d" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] @@ -4761,14 +5028,14 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.74", ] [[package]] name = "serde_spanned" -version = "0.6.6" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" dependencies = [ "serde", ] @@ -4997,7 +5264,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.71", + "syn 2.0.74", ] [[package]] @@ -5013,15 +5280,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", - "quote", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.71" +version = "2.0.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462" +checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7" dependencies = [ "proc-macro2", "quote", @@ -5068,9 +5334,9 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.15" +version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4873307b7c257eddcb50c9bedf158eb669578359fb28428bef438fec8e6ba7c2" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "temp-dir" @@ -5080,14 +5346,15 @@ checksum = "1f227968ec00f0e5322f9b8173c7a0cbcff6181a0a5b28e9892491c286277231" [[package]] name = "tempfile" -version = "3.10.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" dependencies = [ "cfg-if", "fastrand", + "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -5118,7 +5385,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.74", ] [[package]] @@ -5190,9 +5457,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tld" -version = "2.35.0" +version = "2.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73ca5fc340fcb4f52570c502cf443fc22d5521e9ef2bb03528e3634254016cf7" +checksum = "6672b01e3831c99e7e65d2617dbbc4cdf603e60dfc7120a3b6df7fc97ebe3096" dependencies = [ "phf", "phf_codegen", @@ -5200,32 +5467,31 @@ dependencies = [ [[package]] name = "tokio" -version = "1.38.1" +version = "1.39.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb2caba9f80616f438e09748d5acda951967e1ea58508ef53d9c6402485a46df" +checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1" dependencies = [ "backtrace", "bytes", "libc", "mio", - "num_cpus", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", "tracing", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "tokio-macros" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.74", ] [[package]] @@ -5240,9 +5506,9 @@ dependencies = [ [[package]] name = "tokio-socks" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51165dfa029d2a65969413a6cc96f354b86b464498702f174a4efa13608fd8c0" +checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f" dependencies = [ "either", "futures-util", @@ -5277,21 +5543,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.15" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac2caab0bf757388c6c0ae23b3293fdb463fee59434529014f85e3263b995c28" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.16", + "toml_edit 0.22.20", ] [[package]] name = "toml_datetime" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] @@ -5309,15 +5575,15 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.16" +version = "0.22.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "278f3d518e152219c994ce877758516bca5e118eaed6996192a774fb9fbf0788" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.13", + "winnow 0.6.18", ] [[package]] @@ -5368,7 +5634,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.74", ] [[package]] @@ -5535,11 +5801,17 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "uuid" -version = "1.6.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" dependencies = [ "getrandom", "wasm-bindgen", @@ -5576,9 +5848,9 @@ checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "vodozemac" @@ -5647,7 +5919,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.74", "wasm-bindgen-shared", ] @@ -5681,7 +5953,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.74", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5786,6 +6058,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -5918,9 +6199,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.13" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" dependencies = [ "memchr", ] @@ -5960,19 +6241,19 @@ dependencies = [ [[package]] name = "xdg-home" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca91dcf8f93db085f3a0a29358cd0b9d670915468f4290e8b85d118a34211ab8" +checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "xxhash-rust" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63658493314859b4dfdf3fb8c1defd61587839def09582db50b8a4e93afca6bb" +checksum = "6a5cbf750400958819fb6178eaa83bee5cd9c29a26a40cc241df8c70fdd46984" [[package]] name = "yansi-term" @@ -5983,11 +6264,22 @@ dependencies = [ "winapi", ] +[[package]] +name = "yeslogic-fontconfig-sys" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "503a066b4c037c440169d995b869046827dbc71263f6e8f3be6d77d4f3229dbd" +dependencies = [ + "dlib", + "once_cell", + "pkg-config", +] + [[package]] name = "zbus" -version = "4.3.1" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "851238c133804e0aa888edf4a0229481c753544ca12a60fd1c3230c8a500fe40" +checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725" dependencies = [ "async-broadcast", "async-process", @@ -6018,14 +6310,14 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "4.3.1" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d5a3f12c20bd473be3194af6b49d50d7bb804ef3192dc70eddedb26b85d9da7" +checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.74", "zvariant_utils", ] @@ -6046,6 +6338,7 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ + "byteorder", "zerocopy-derive", ] @@ -6057,7 +6350,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.74", ] [[package]] @@ -6077,7 +6370,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.74", ] [[package]] @@ -6097,18 +6390,18 @@ dependencies = [ [[package]] name = "zune-jpeg" -version = "0.4.11" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec866b44a2a1fd6133d363f073ca1b179f438f99e7e5bfb1e33f7181facfe448" +checksum = "16099418600b4d8f028622f73ff6e3deaabdff330fb9a2a131dea781ee8b0768" dependencies = [ "zune-core", ] [[package]] name = "zvariant" -version = "4.1.2" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1724a2b330760dc7d2a8402d841119dc869ef120b139d29862d6980e9c75bfc9" +checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe" dependencies = [ "endi", "enumflags2", @@ -6120,24 +6413,24 @@ dependencies = [ [[package]] name = "zvariant_derive" -version = "4.1.2" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55025a7a518ad14518fb243559c058a2e5b848b015e31f1d90414f36e3317859" +checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.74", "zvariant_utils", ] [[package]] name = "zvariant_utils" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc242db087efc22bd9ade7aa7809e4ba828132edc312871584a6b4391bdf8786" +checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.74", ] diff --git a/Cargo.toml b/Cargo.toml index f9925b35..99b4f086 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,6 +52,7 @@ url = "2" # gtk-rs project and dependents. These usually need to be updated together. adw = { package = "libadwaita", version = "0.7", features = ["v1_5"] } +glycin = { version = "2.0.0-beta", default-features = false, features = ["tokio", "gdk4"] } gst = { version = "0.23", package = "gstreamer" } gst_base = { version = "0.23", package = "gstreamer-base" } gst_gtk = { version = "0.13", package = "gst-plugin-gtk4" } diff --git a/build-aux/org.gnome.Fractal.Devel.json b/build-aux/org.gnome.Fractal.Devel.json index 0065212d..5bf75915 100644 --- a/build-aux/org.gnome.Fractal.Devel.json +++ b/build-aux/org.gnome.Fractal.Devel.json @@ -71,6 +71,65 @@ } ] }, + { + "name": "libde265", + "buildsystem": "cmake", + "config-opts": [ + "-DCMAKE_INSTALL_PREFIX=/app/lib/libheif-heic", + "-DENABLE_SDL=Off" + ], + "sources": [ + { + "type": "git", + "url": "https://github.com/strukturag/libde265.git", + "tag": "v1.0.15" + } + ] + }, + { + "name": "libheif", + "buildsystem": "cmake", + "config-opts": [ + "-DWITH_LIBDE265_PLUGIN=On", + "-DPLUGIN_DIRECTORY=/app/lib/libheif-heic/lib", + "-DLIBDE265_INCLUDE_DIR=/app/lib/libheif-heic/include", + "-DLIBDE265_PKGCONF_LIBRARY_DIRS=/app/lib/libheif-heic/lib", + "-DWITH_X265=Off", + "-DWITH_SvtEnc=Off", + "-DWITH_SvtEnc_PLUGIN=Off", + "-DWITH_AOM_ENCODER=Off", + "-DWITH_RAV1E_PLUGIN=Off", + "-DWITH_RAV1E=Off", + "-DWITH_EXAMPLES=Off" + ], + "sources": [ + { + "type": "git", + "url": "https://github.com/strukturag/libheif.git", + "tag": "v1.17.6" + } + ] + }, + { + "name": "glycin-loaders", + "buildsystem": "meson", + "config-opts": [ + "-Dtests=false", + "-Dlibglycin=false", + "-Dintrospection=false", + "-Dvapi=false", + "-Dcapi_docs=false", + "-Dpython_tests=false" + ], + "sources": [ + { + "type": "git", + "url": "https://gitlab.gnome.org/sophie-h/glycin.git", + "tag": "1.1.beta", + "commit": "bb5bf87ed35d3cff5fe1f094c89762e96e60c44a" + } + ] + }, { "name": "fractal", "buildsystem": "meson", diff --git a/meson.options b/meson.options index 3fc973f4..1a054cfa 100644 --- a/meson.options +++ b/meson.options @@ -16,5 +16,12 @@ option( value : false, description: 'Whether the build happens in a sandbox.' + 'When that is the case, cargo will not be able to download the dependencies during' + - 'the build so they are assumed to be in meson.project_source_root()/cargo' + 'the build so they are assumed to be in `{meson.project_source_root()}/cargo`.' +) +option( + 'disable-glycin-sandbox', + type : 'boolean', + value : false, + description: 'Whether the sandbox of glycin should be disabled.' + + 'This is only useful during development.' ) diff --git a/src/components/avatar/editable.rs b/src/components/avatar/editable.rs index 1fec8eb1..4c00cd5f 100644 --- a/src/components/avatar/editable.rs +++ b/src/components/avatar/editable.rs @@ -12,9 +12,9 @@ use tracing::{debug, error}; use super::{AvatarData, AvatarImage}; use crate::{ - components::{ActionButton, ActionState, ImagePaintable}, + components::{ActionButton, ActionState}, toast, - utils::expression, + utils::{expression, media::load_image}, }; /// The state of the editable avatar. @@ -192,7 +192,7 @@ mod imp { obj.set_remove_state(ActionState::Default); obj.set_remove_sensitive(true); - obj.set_temp_image_from_file(None); + obj.set_temp_image(None); } EditableAvatarState::EditInProgress => { obj.show_temp_image(true); @@ -207,7 +207,7 @@ mod imp { obj.set_remove_state(ActionState::Default); obj.set_remove_sensitive(true); - obj.set_temp_image_from_file(None); + obj.set_temp_image(None); // Animation for success. obj.set_edit_state(ActionState::Success); @@ -332,11 +332,13 @@ impl EditableAvatar { self.imp().remove_sensitive.set(sensitive); } - fn set_temp_image_from_file(&self, file: Option<&gio::File>) { - self.imp().temp_image.replace( - file.and_then(|file| ImagePaintable::from_file(file).ok()) - .map(|texture| texture.upcast()), - ); + async fn set_temp_image_from_file(&self, file: gio::File) { + let paintable = load_image(file).await.ok(); + self.set_temp_image(paintable); + } + + fn set_temp_image(&self, temp_image: Option) { + self.imp().temp_image.replace(temp_image); self.notify_temp_image(); } @@ -392,7 +394,7 @@ impl EditableAvatar { .and_then(|info| info.content_type()) { if gio::content_type_is_a(&content_type, "image/*") { - self.set_temp_image_from_file(Some(&file)); + self.set_temp_image_from_file(file.clone()).await; self.emit_by_name::<()>("edit-avatar", &[&file]); } else { error!("The chosen file is not an image"); diff --git a/src/components/avatar/image.rs b/src/components/avatar/image.rs index ec031234..6db264b7 100644 --- a/src/components/avatar/image.rs +++ b/src/components/avatar/image.rs @@ -8,7 +8,11 @@ use matrix_sdk::{ }; use tracing::error; -use crate::{components::ImagePaintable, session::model::Session, spawn, spawn_tokio}; +use crate::{ + session::model::Session, + spawn, spawn_tokio, + utils::{media::load_image, save_data_to_tmp_file}, +}; /// The source of an avatar's URI. #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, glib::Enum)] @@ -93,11 +97,17 @@ mod imp { if has_uri { obj.load(); } else { - obj.set_image_data(None); + self.set_paintable(None); } obj.notify_uri(); } + + /// Set the image content as a paintable + pub(super) fn set_paintable(&self, paintable: Option) { + self.paintable.replace(paintable); + self.obj().notify_paintable(); + } } } @@ -117,12 +127,13 @@ impl AvatarImage { } /// Set the content of the image. - fn set_image_data(&self, data: Option>) { - let paintable = data - .and_then(|data| ImagePaintable::from_bytes(&data, None).ok()) - .map(|texture| texture.upcast()); - self.imp().paintable.replace(paintable); - self.notify_paintable(); + async fn set_image_data(&self, data: Vec) { + let Ok(file) = save_data_to_tmp_file(&data) else { + return; + }; + + let paintable = load_image(file).await.ok(); + self.imp().set_paintable(paintable); } fn load(&self) { @@ -155,7 +166,7 @@ impl AvatarImage { self, async move { match handle.await.unwrap() { - Ok(data) => obj.set_image_data(Some(data)), + Ok(data) => obj.set_image_data(data).await, Err(error) => error!("Could not fetch avatar: {error}"), }; } diff --git a/src/components/media/animated_image_paintable.rs b/src/components/media/animated_image_paintable.rs new file mode 100644 index 00000000..14d169cd --- /dev/null +++ b/src/components/media/animated_image_paintable.rs @@ -0,0 +1,188 @@ +use glycin::{Frame, Image}; +use gtk::{gdk, glib, glib::clone, graphene, prelude::*, subclass::prelude::*}; +use tracing::error; + +use crate::{spawn, spawn_tokio}; + +mod imp { + use std::{ + cell::{OnceCell, RefCell}, + sync::Arc, + }; + + use super::*; + + #[derive(Default)] + pub struct AnimatedImagePaintable { + /// The source image. + image: OnceCell>>, + /// The current frame that is displayed. + pub current_frame: RefCell>, + /// The next frame of the animation, if any. + next_frame: RefCell>, + /// The source ID of the timeout to load the next frame, if any. + timeout_source_id: RefCell>, + } + + #[glib::object_subclass] + impl ObjectSubclass for AnimatedImagePaintable { + const NAME: &'static str = "AnimatedImagePaintable"; + type Type = super::AnimatedImagePaintable; + type Interfaces = (gdk::Paintable,); + } + + impl ObjectImpl for AnimatedImagePaintable {} + + impl PaintableImpl for AnimatedImagePaintable { + fn intrinsic_height(&self) -> i32 { + self.current_frame + .borrow() + .as_ref() + .map(|f| f.height()) + .unwrap_or_else(|| self.image().info().height) as i32 + } + + fn intrinsic_width(&self) -> i32 { + self.current_frame + .borrow() + .as_ref() + .map(|f| f.width()) + .unwrap_or_else(|| self.image().info().width) as i32 + } + + fn snapshot(&self, snapshot: &gdk::Snapshot, width: f64, height: f64) { + if let Some(frame) = &*self.current_frame.borrow() { + frame.texture().snapshot(snapshot, width, height); + } else { + let snapshot = snapshot.downcast_ref::().unwrap(); + snapshot.append_color( + &gdk::RGBA::BLACK, + &graphene::Rect::new(0., 0., width as f32, height as f32), + ); + } + } + + fn flags(&self) -> gdk::PaintableFlags { + gdk::PaintableFlags::SIZE + } + + fn current_image(&self) -> gdk::Paintable { + let snapshot = gtk::Snapshot::new(); + self.snapshot( + snapshot.upcast_ref(), + self.intrinsic_width() as f64, + self.intrinsic_height() as f64, + ); + + snapshot + .to_paintable(None) + .expect("snapshot should always work") + } + } + + impl AnimatedImagePaintable { + /// The source image. + fn image(&self) -> &Arc> { + self.image.get().unwrap() + } + + /// Initialize the image. + pub(super) fn init(&self, image: Image<'static>, first_frame: Frame) { + self.image.set(Arc::new(image)).unwrap(); + self.current_frame.replace(Some(first_frame)); + + self.prepare_next_frame(); + } + + /// Show the next frame of the animation. + fn show_next_frame(&self) { + // Drop the timeout source ID so we know we are not waiting for it. + self.timeout_source_id.take(); + + let Some(next_frame) = self.next_frame.take() else { + // Wait for the next frame to be loaded. + return; + }; + + self.current_frame.replace(Some(next_frame)); + + // Invalidate the contents so that the new frame will be rendered. + self.obj().invalidate_contents(); + + self.prepare_next_frame(); + } + + /// Prepare the next frame of the animation. + fn prepare_next_frame(&self) { + let Some(delay) = self.current_frame.borrow().as_ref().and_then(|f| f.delay()) else { + return; + }; + + // Set the timeout to update the animation. + let source_id = glib::timeout_add_local_once( + delay, + clone!( + #[weak(rename_to = imp)] + self, + move || { + imp.show_next_frame(); + } + ), + ); + self.timeout_source_id.replace(Some(source_id)); + + spawn!(clone!( + #[weak(rename_to = imp)] + self, + async move { + imp.load_next_frame_inner().await; + } + )); + } + + async fn load_next_frame_inner(&self) { + let image = self.image().clone(); + + let result = spawn_tokio!(async move { image.next_frame().await }) + .await + .unwrap(); + + match result { + Ok(next_frame) => { + self.next_frame.replace(Some(next_frame)); + + // In case loading the frame took longer than the delay between frames. + if self.timeout_source_id.borrow().is_none() { + self.show_next_frame(); + } + } + Err(error) => { + error!("Failed to load next frame: {error}"); + // Do nothing, the animation will stop. + } + } + } + } +} + +glib::wrapper! { + /// A paintable to display an animated image. + pub struct AnimatedImagePaintable(ObjectSubclass) + @implements gdk::Paintable; +} + +impl AnimatedImagePaintable { + /// Load an image from the given file. + pub fn new(image: Image<'static>, first_frame: Frame) -> Self { + let obj = glib::Object::new::(); + + obj.imp().init(image, first_frame); + + obj + } + + /// Get the current `GdkTexture` of this paintable, if any. + pub fn current_texture(&self) -> Option { + Some(self.imp().current_frame.borrow().as_ref()?.texture()) + } +} diff --git a/src/components/media/content_viewer.rs b/src/components/media/content_viewer.rs index da766e16..a0d581bf 100644 --- a/src/components/media/content_viewer.rs +++ b/src/components/media/content_viewer.rs @@ -4,8 +4,8 @@ use gettextrs::gettext; use gtk::{gdk, gio, glib, glib::clone, CompositeTemplate}; use tracing::warn; -use super::{AudioPlayer, ImagePaintable, LocationViewer}; -use crate::{components::Spinner, spawn}; +use super::{AnimatedImagePaintable, AudioPlayer, LocationViewer}; +use crate::{components::Spinner, spawn, utils::media::load_image}; #[derive(Debug, Default, Clone, Copy)] pub enum ContentType { @@ -197,13 +197,13 @@ impl MediaContentViewer { .unwrap_or_default(); match content_type { - ContentType::Image => match ImagePaintable::from_file(&file) { + ContentType::Image => match load_image(file).await { Ok(texture) => { self.view_image(&texture); return; } Err(error) => { - warn!("Could not load GdkTexture from file: {error}"); + warn!("Could not load image from file: {error}"); } }, ContentType::Audio => { @@ -265,12 +265,19 @@ impl MediaContentViewer { /// Get the texture displayed by this widget, if any. pub fn texture(&self) -> Option { - self.imp() + let paintable = self + .imp() .viewer .child() .and_downcast::() - .and_then(|p| p.paintable()) - .and_downcast::() - .and_then(|p| p.current_frame()) + .and_then(|p| p.paintable())?; + + if let Some(paintable) = paintable.downcast_ref::() { + paintable.current_texture() + } else if let Ok(texture) = paintable.downcast::() { + Some(texture) + } else { + None + } } } diff --git a/src/components/media/image_paintable.rs b/src/components/media/image_paintable.rs deleted file mode 100644 index 18584ca2..00000000 --- a/src/components/media/image_paintable.rs +++ /dev/null @@ -1,404 +0,0 @@ -use std::{ - io::{BufRead, BufReader, Cursor, Seek}, - time::Duration, -}; - -use gtk::{gdk, gio, glib, graphene, prelude::*, subclass::prelude::*}; -use image::{ - codecs::{gif::GifDecoder, png::PngDecoder}, - flat::SampleLayout, - AnimationDecoder, DynamicImage, ImageFormat, -}; -use tracing::error; - -/// A single frame of an animation. -pub struct Frame { - pub texture: gdk::Texture, - pub duration: Duration, -} - -impl From for Frame { - fn from(f: image::Frame) -> Self { - let mut duration = Duration::from(f.delay()); - - // The convention is to use 100 milliseconds duration if it is defined as 0. - if duration.is_zero() { - duration = Duration::from_millis(100); - } - - let sample = f.into_buffer().into_flat_samples(); - let texture = texture_from_data( - &sample.samples, - sample.layout, - gdk::MemoryFormat::R8g8b8a8, - image::ColorType::Rgba8.bytes_per_pixel(), - ); - - Frame { - texture: texture.upcast(), - duration, - } - } -} - -mod imp { - use std::{ - cell::{Cell, RefCell}, - marker::PhantomData, - }; - - use super::*; - - #[derive(Default, glib::Properties)] - #[properties(wrapper_type = super::ImagePaintable)] - pub struct ImagePaintable { - /// The frames of the animation, if any. - pub frames: RefCell>>, - /// The image if this is not an animation, otherwise this is the next - /// frame to display. - pub frame: RefCell>, - /// The current index in the animation. - pub current_idx: Cell, - /// The source ID of the timeout to load the next frame, if any. - pub timeout_source_id: RefCell>, - /// Whether this image is an animation. - #[property(get = Self::is_animation)] - pub is_animation: PhantomData, - /// The width of this image. - #[property(get = Self::intrinsic_width, default = -1)] - pub width: PhantomData, - /// The height of this image. - #[property(get = Self::intrinsic_height, default = -1)] - pub height: PhantomData, - } - - #[glib::object_subclass] - impl ObjectSubclass for ImagePaintable { - const NAME: &'static str = "ImagePaintable"; - type Type = super::ImagePaintable; - type Interfaces = (gdk::Paintable,); - } - - #[glib::derived_properties] - impl ObjectImpl for ImagePaintable {} - - impl PaintableImpl for ImagePaintable { - fn intrinsic_height(&self) -> i32 { - self.frame - .borrow() - .as_ref() - .map(|texture| texture.height()) - .unwrap_or(-1) - } - - fn intrinsic_width(&self) -> i32 { - self.frame - .borrow() - .as_ref() - .map(|texture| texture.width()) - .unwrap_or(-1) - } - - fn snapshot(&self, snapshot: &gdk::Snapshot, width: f64, height: f64) { - if let Some(texture) = &*self.frame.borrow() { - texture.snapshot(snapshot, width, height); - } else { - let snapshot = snapshot.downcast_ref::().unwrap(); - snapshot.append_color( - &gdk::RGBA::BLACK, - &graphene::Rect::new(0f32, 0f32, width as f32, height as f32), - ); - } - } - - fn flags(&self) -> gdk::PaintableFlags { - if self.obj().is_animation() { - gdk::PaintableFlags::SIZE - } else { - gdk::PaintableFlags::SIZE | gdk::PaintableFlags::CONTENTS - } - } - - fn current_image(&self) -> gdk::Paintable { - self.frame - .borrow() - .clone() - .map(|frame| frame.upcast()) - .or_else(|| { - let snapshot = gtk::Snapshot::new(); - self.obj().snapshot(&snapshot, 1.0, 1.0); - - snapshot.to_paintable(None) - }) - .expect("there should be a fallback paintable") - } - } - - impl ImagePaintable { - /// Whether this image is an animation. - fn is_animation(&self) -> bool { - self.frames.borrow().is_some() - } - } -} - -glib::wrapper! { - /// A paintable that loads images with the `image` crate. - /// - /// It handles more image types than GDK-Pixbuf and can also handle - /// animations from GIF and APNG files. - pub struct ImagePaintable(ObjectSubclass) - @implements gdk::Paintable; -} - -impl ImagePaintable { - /// Load an image from the given reader in the optional format. - /// - /// The actual format will try to be guessed from the content. - pub fn new( - reader: R, - format: Option, - ) -> Result> { - let obj = glib::Object::new::(); - - let mut reader = image::io::Reader::new(reader); - - if let Some(format) = format { - reader.set_format(format); - } - - let reader = reader.with_guessed_format()?; - - obj.load_inner(reader)?; - - Ok(obj) - } - - /// Load an image or animation from the given reader. - fn load_inner( - &self, - reader: image::io::Reader, - ) -> Result<(), Box> { - let imp = self.imp(); - let format = reader.format().ok_or("Could not detect image format")?; - - let read = reader.into_inner(); - - // Handle animations. - match format { - image::ImageFormat::Gif => { - let decoder = GifDecoder::new(read)?; - - let frames = decoder - .into_frames() - .collect_frames()? - .into_iter() - .map(Frame::from) - .collect::>(); - - if frames.len() == 1 { - if let Some(frame) = frames.into_iter().next() { - imp.frame.replace(Some(frame.texture)); - } - } else { - imp.frames.replace(Some(frames)); - self.update_frame(); - } - } - image::ImageFormat::Png => { - let decoder = PngDecoder::new(read)?; - - if decoder.is_apng().unwrap_or_default() { - let decoder = decoder.apng()?; - let frames = decoder - .into_frames() - .collect_frames()? - .into_iter() - .map(Frame::from) - .collect::>(); - imp.frames.replace(Some(frames)); - self.update_frame(); - } else { - let image = DynamicImage::from_decoder(decoder)?; - self.set_image(image); - } - } - _ => { - let image = image::load(read, format)?; - self.set_image(image); - } - } - - Ok(()) - } - - /// Set the image that is displayed by this paintable. - fn set_image(&self, image: DynamicImage) { - let texture = match image.color() { - image::ColorType::L8 | image::ColorType::Rgb8 => { - let sample = image.into_rgb8().into_flat_samples(); - texture_from_data( - &sample.samples, - sample.layout, - gdk::MemoryFormat::R8g8b8, - image::ColorType::Rgb8.bytes_per_pixel(), - ) - } - image::ColorType::La8 | image::ColorType::Rgba8 => { - let sample = image.into_rgba8().into_flat_samples(); - texture_from_data( - &sample.samples, - sample.layout, - gdk::MemoryFormat::R8g8b8a8, - image::ColorType::Rgba8.bytes_per_pixel(), - ) - } - image::ColorType::L16 | image::ColorType::Rgb16 => { - let sample = image.into_rgb16().into_flat_samples(); - let bytes = sample - .samples - .into_iter() - .flat_map(|b| b.to_ne_bytes()) - .collect::>(); - texture_from_data( - &bytes, - sample.layout, - gdk::MemoryFormat::R16g16b16, - image::ColorType::Rgb16.bytes_per_pixel(), - ) - } - image::ColorType::La16 | image::ColorType::Rgba16 => { - let sample = image.into_rgba16().into_flat_samples(); - let bytes = sample - .samples - .into_iter() - .flat_map(|b| b.to_ne_bytes()) - .collect::>(); - texture_from_data( - &bytes, - sample.layout, - gdk::MemoryFormat::R16g16b16a16, - image::ColorType::Rgba16.bytes_per_pixel(), - ) - } - image::ColorType::Rgb32F => { - let sample = image.into_rgb32f().into_flat_samples(); - let bytes = sample - .samples - .into_iter() - .flat_map(|b| b.to_ne_bytes()) - .collect::>(); - texture_from_data( - &bytes, - sample.layout, - gdk::MemoryFormat::R32g32b32Float, - image::ColorType::Rgb32F.bytes_per_pixel(), - ) - } - image::ColorType::Rgba32F => { - let sample = image.into_rgb32f().into_flat_samples(); - let bytes = sample - .samples - .into_iter() - .flat_map(|b| b.to_ne_bytes()) - .collect::>(); - texture_from_data( - &bytes, - sample.layout, - gdk::MemoryFormat::R32g32b32Float, - image::ColorType::Rgb32F.bytes_per_pixel(), - ) - } - c => { - error!("Received image of unsupported color format: {c:?}"); - return; - } - }; - - self.imp().frame.replace(Some(texture.upcast())); - } - - /// Creates a new paintable by loading an image from the given file. - pub fn from_file(file: &gio::File) -> Result> { - let stream = file.read(gio::Cancellable::NONE)?; - let reader = BufReader::new(stream.into_read()); - let format = file - .path() - .and_then(|path| ImageFormat::from_path(path).ok()); - - Self::new(reader, format) - } - - /// Creates a new paintable by loading an image from memory. - pub fn from_bytes( - bytes: &[u8], - content_type: Option<&str>, - ) -> Result> { - let reader = Cursor::new(bytes); - let format = content_type.and_then(ImageFormat::from_mime_type); - - Self::new(reader, format) - } - - /// Update the current frame of the animation. - fn update_frame(&self) { - let imp = self.imp(); - let frames_ref = imp.frames.borrow(); - - // If it's not an animation, we return early. - let frames = match &*frames_ref { - Some(frames) => frames, - None => return, - }; - - let idx = imp.current_idx.get(); - let next_frame = frames.get(idx).unwrap(); - imp.frame.replace(Some(next_frame.texture.clone())); - - // Invalidate the contents so that the new frame will be rendered. - self.invalidate_contents(); - - // Update the frame when the duration is elapsed. - let update_frame_callback = glib::clone!( - #[weak(rename_to = obj)] - self, - move || { - obj.imp().timeout_source_id.take(); - obj.update_frame(); - } - ); - let source_id = glib::timeout_add_local_once(next_frame.duration, update_frame_callback); - imp.timeout_source_id.replace(Some(source_id)); - - // Update the index for the next call. - let mut new_idx = idx + 1; - if new_idx >= frames.len() { - new_idx = 0; - } - imp.current_idx.set(new_idx); - } - - /// Get the current frame of this `ImagePaintable`, if any. - pub fn current_frame(&self) -> Option { - self.imp().frame.borrow().clone() - } -} - -fn texture_from_data( - bytes: &[u8], - layout: SampleLayout, - format: gdk::MemoryFormat, - bpp: u8, -) -> gdk::MemoryTexture { - let bytes = glib::Bytes::from(bytes); - - let stride = layout.width * bpp as u32; - - gdk::MemoryTexture::new( - layout.width as i32, - layout.height as i32, - format, - &bytes, - stride as usize, - ) -} diff --git a/src/components/media/mod.rs b/src/components/media/mod.rs index 0f37b394..490a3665 100644 --- a/src/components/media/mod.rs +++ b/src/components/media/mod.rs @@ -1,14 +1,14 @@ +mod animated_image_paintable; mod audio_player; mod content_viewer; -mod image_paintable; mod location_viewer; mod video_player; mod video_player_renderer; pub use self::{ + animated_image_paintable::AnimatedImagePaintable, audio_player::AudioPlayer, content_viewer::{ContentType, MediaContentViewer}, - image_paintable::ImagePaintable, location_viewer::LocationViewer, video_player::VideoPlayer, }; diff --git a/src/config.rs.in b/src/config.rs.in index 47615f1a..4fba8461 100644 --- a/src/config.rs.in +++ b/src/config.rs.in @@ -1,6 +1,7 @@ use crate::application::AppProfile; pub const APP_ID: &str = @APP_ID@; +pub const DISABLE_GLYCIN_SANDBOX: bool = @DISABLE_GLYCIN_SANDBOX@; pub const GETTEXT_PACKAGE: &str = @GETTEXT_PACKAGE@; pub const LOCALEDIR: &str = @LOCALEDIR@; pub const PKGDATADIR: &str = @PKGDATADIR@; diff --git a/src/meson.build b/src/meson.build index a7c87481..5a30bab3 100644 --- a/src/meson.build +++ b/src/meson.build @@ -9,11 +9,12 @@ ui_resources = gnome.compile_resources( global_conf = configuration_data() global_conf.set_quoted('APP_ID', application_id) +global_conf.set('DISABLE_GLYCIN_SANDBOX', get_option('disable-glycin-sandbox').to_string()) +global_conf.set_quoted('GETTEXT_PACKAGE', gettext_package) +global_conf.set_quoted('LOCALEDIR', localedir) global_conf.set_quoted('PKGDATADIR', pkgdatadir) global_conf.set('PROFILE', profile) global_conf.set_quoted('VERSION', full_version) -global_conf.set_quoted('GETTEXT_PACKAGE', gettext_package) -global_conf.set_quoted('LOCALEDIR', localedir) config = configure_file( input: 'config.rs.in', output: 'config.rs', diff --git a/src/session/view/content/room_details/edit_details_subpage.rs b/src/session/view/content/room_details/edit_details_subpage.rs index 9b06491a..03c33bc7 100644 --- a/src/session/view/content/room_details/edit_details_subpage.rs +++ b/src/session/view/content/room_details/edit_details_subpage.rs @@ -183,7 +183,7 @@ mod imp { } }; - let base_image_info = get_image_info(&file).await; + let base_image_info = get_image_info(file).await; let image_info = assign!(ImageInfo::new(), { width: base_image_info.width, height: base_image_info.height, diff --git a/src/session/view/content/room_details/history_viewer/visual_media_item.rs b/src/session/view/content/room_details/history_viewer/visual_media_item.rs index 28188cc0..d93d6e86 100644 --- a/src/session/view/content/room_details/history_viewer/visual_media_item.rs +++ b/src/session/view/content/room_details/history_viewer/visual_media_item.rs @@ -5,9 +5,8 @@ use tracing::warn; use super::{HistoryViewerEvent, VisualMediaHistoryViewer}; use crate::{ - components::ImagePaintable, spawn, - utils::{add_activate_binding_action, matrix::VisualMediaMessage}, + utils::{add_activate_binding_action, matrix::VisualMediaMessage, media::load_image}, }; /// The default size requested by a thumbnail. @@ -165,21 +164,21 @@ mod imp { ((THUMBNAIL_SIZE * scale_factor) as u32).into(), ); - let data = media_message - .thumbnail(&client, settings) + let file = media_message + .thumbnail_tmp_file(&client, settings) .await .ok() .flatten(); - if data.is_none() && matches!(media_message, VisualMediaMessage::Video(_)) { + if file.is_none() && matches!(media_message, VisualMediaMessage::Video(_)) { // No image to show for the video. return; } - let data = match data { - Some(data) => data, - None => match media_message.into_content(&client).await { - Ok(data) => data, + let file = match file { + Some(file) => file, + None => match media_message.into_tmp_file(&client).await { + Ok(file) => file, Err(error) => { warn!("Could not retrieve media file: {error}"); return; @@ -187,9 +186,9 @@ mod imp { }, }; - match ImagePaintable::from_bytes(&data, None) { - Ok(texture) => { - self.picture.set_paintable(Some(&texture)); + match load_image(file).await { + Ok(paintable) => { + self.picture.set_paintable(Some(&paintable)); } Err(error) => { warn!("Image file not supported: {error}"); diff --git a/src/session/view/content/room_history/message_row/visual_media.rs b/src/session/view/content/room_history/message_row/visual_media.rs index f58fe4e7..5e24b3a9 100644 --- a/src/session/view/content/room_history/message_row/visual_media.rs +++ b/src/session/view/content/room_history/message_row/visual_media.rs @@ -11,11 +11,11 @@ use tracing::warn; use super::ContentFormat; use crate::{ - components::{ImagePaintable, Spinner, VideoPlayer}, + components::{AnimatedImagePaintable, Spinner, VideoPlayer}, gettext_f, session::model::Session, spawn, - utils::{matrix::VisualMediaMessage, LoadingState}, + utils::{matrix::VisualMediaMessage, media::load_image, LoadingState}, }; const MAX_THUMBNAIL_WIDTH: i32 = 600; @@ -313,16 +313,16 @@ impl MessageVisualMedia { ((MAX_THUMBNAIL_HEIGHT * scale_factor) as u32).into(), ); - let data = if let Some(data) = media_message - .thumbnail(client, settings) + let file = if let Some(file) = media_message + .thumbnail_tmp_file(client, settings) .await .ok() .flatten() { - data + file } else { - match media_message.into_content(client).await { - Ok(data) => data, + match media_message.into_tmp_file(client).await { + Ok(file) => file, Err(error) => { warn!("Could not retrieve media file: {error}"); imp.overlay_error @@ -334,8 +334,8 @@ impl MessageVisualMedia { } }; - match ImagePaintable::from_bytes(&data, None) { - Ok(texture) => { + match load_image(file).await { + Ok(paintable) => { let child = if let Some(child) = imp.media.child().and_downcast::() { child @@ -344,7 +344,7 @@ impl MessageVisualMedia { imp.media.set_child(Some(&child)); child }; - child.set_paintable(Some(&texture)); + child.set_paintable(Some(&paintable)); child.set_tooltip_text(Some(&filename)); if is_sticker { @@ -393,13 +393,20 @@ impl MessageVisualMedia { /// Get the texture displayed by this widget, if any. pub fn texture(&self) -> Option { - self.imp() + let paintable = self + .imp() .media .child() .and_downcast::() - .and_then(|p| p.paintable()) - .and_downcast::() - .and_then(|p| p.current_frame()) + .and_then(|p| p.paintable())?; + + if let Some(paintable) = paintable.downcast_ref::() { + paintable.current_texture() + } else if let Ok(texture) = paintable.downcast::() { + Some(texture) + } else { + None + } } } diff --git a/src/session/view/content/room_history/message_toolbar/mod.rs b/src/session/view/content/room_history/message_toolbar/mod.rs index 2c8a23d1..3ee82a49 100644 --- a/src/session/view/content/room_history/message_toolbar/mod.rs +++ b/src/session/view/content/room_history/message_toolbar/mod.rs @@ -914,7 +914,7 @@ impl MessageToolbar { let size = file_info.size.map(Into::into); let info = match file_info.mime.type_() { mime::IMAGE => { - let mut info = get_image_info(&file).await; + let mut info = get_image_info(file).await; info.size = size; AttachmentInfo::Image(info) } diff --git a/src/session/view/media_viewer.rs b/src/session/view/media_viewer.rs index aeea214f..41feb0a2 100644 --- a/src/session/view/media_viewer.rs +++ b/src/session/view/media_viewer.rs @@ -5,7 +5,7 @@ use ruma::OwnedEventId; use tracing::warn; use crate::{ - components::{ContentType, ImagePaintable, MediaContentViewer, ScaleRevealer}, + components::{ContentType, MediaContentViewer, ScaleRevealer}, session::model::Room, spawn, toast, utils::matrix::VisualMediaMessage, @@ -391,35 +391,22 @@ impl MediaViewer { let imp = self.imp(); let client = session.client(); - match &message { - VisualMediaMessage::Image(image) => { - let mimetype = image.info.as_ref().and_then(|info| info.mimetype.clone()); - - match message.into_content(&client).await { - Ok(data) => match ImagePaintable::from_bytes(&data, mimetype.as_deref()) { - Ok(texture) => { - imp.media.view_image(&texture); - return; - } - Err(error) => { - warn!("Could not load GdkTexture from file: {error}") - } - }, - Err(error) => warn!("Could not retrieve image file: {error}"), - } + let is_video = matches!(message, VisualMediaMessage::Video(_)); - imp.media.show_fallback(ContentType::Image); + match message.into_tmp_file(&client).await { + Ok(file) => { + imp.media.view_file(file); + } + Err(error) => { + warn!("Could not retrieve media file: {error}"); + + let content_type = if is_video { + ContentType::Video + } else { + ContentType::Image + }; + imp.media.show_fallback(content_type); } - VisualMediaMessage::Video(_) => match message.into_tmp_file(&client).await { - Ok(file) => { - imp.media.view_file(file); - } - Err(error) => { - warn!("Could not retrieve video file: {error}"); - imp.media.show_fallback(ContentType::Video); - } - }, - VisualMediaMessage::Sticker(_) => unreachable!(), } } diff --git a/src/utils/matrix/media_message.rs b/src/utils/matrix/media_message.rs index 37fcba97..e77f43e0 100644 --- a/src/utils/matrix/media_message.rs +++ b/src/utils/matrix/media_message.rs @@ -13,7 +13,7 @@ use ruma::{ }; use tracing::{debug, error}; -use crate::{prelude::*, toast}; +use crate::{prelude::*, toast, utils::save_data_to_tmp_file}; /// Get the filename of a media message. macro_rules! filename { @@ -134,17 +134,7 @@ impl MediaMessage { /// Returns an error if something occurred while fetching the content. pub async fn into_tmp_file(self, client: &Client) -> Result { let data = self.into_content(client).await?; - - let (file, _) = gio::File::new_tmp(None::)?; - file.replace_contents( - &data, - None, - false, - gio::FileCreateFlags::REPLACE_DESTINATION, - gio::Cancellable::NONE, - )?; - - Ok(file) + Ok(save_data_to_tmp_file(&data)?) } /// Save the content of the media to a file selected by the user. @@ -270,7 +260,7 @@ impl VisualMediaMessage { } } - /// Fetch the content of the media with the given client and thumbnail + /// Fetch a thumbnail of the media with the given client and thumbnail /// settings. /// /// This might not return a thumbnail at the requested size, depending on @@ -309,6 +299,28 @@ impl VisualMediaMessage { } } + /// Fetch a thumbnail of the media with the given client and thumbnail + /// settings and write it to a temporary file. + /// + /// This might not return a thumbnail at the requested size, depending on + /// the homeserver. + /// + /// Returns `Ok(None)` if no thumbnail could be retrieved. Returns an error + /// if something occurred while fetching the content. + pub async fn thumbnail_tmp_file( + &self, + client: &Client, + settings: MediaThumbnailSettings, + ) -> Result, MediaFileError> { + let data = self.thumbnail(client, settings).await?; + + let Some(data) = data else { + return Ok(None); + }; + + Ok(Some(save_data_to_tmp_file(&data)?)) + } + /// Fetch the content of the media with the given client. /// /// Returns an error if something occurred while fetching the content. diff --git a/src/utils/media.rs b/src/utils/media.rs index 3a91e75a..5e944d4e 100644 --- a/src/utils/media.rs +++ b/src/utils/media.rs @@ -3,10 +3,13 @@ use std::{cell::Cell, str::FromStr, sync::Mutex}; use gettextrs::gettext; -use gtk::{gio, glib, prelude::*}; +use glycin::Image; +use gtk::{gdk, gio, glib, prelude::*}; use matrix_sdk::attachment::{BaseAudioInfo, BaseImageInfo, BaseVideoInfo}; use mime::Mime; +use crate::{components::AnimatedImagePaintable, spawn_tokio, DISABLE_GLYCIN_SANDBOX}; + /// Get a default filename for a mime type. /// /// Tries to guess the file extension, but it might not find it. @@ -94,7 +97,40 @@ pub async fn load_file(file: &gio::File) -> Result<(Vec, FileInfo), glib::Er )) } -pub async fn get_image_info(file: &gio::File) -> BaseImageInfo { +/// Get an image reader for the given file. +pub async fn image_reader(file: gio::File) -> Result, glycin::ErrorCtx> { + let mut loader = glycin::Loader::new(file); + + if DISABLE_GLYCIN_SANDBOX { + loader.sandbox_selector(glycin::SandboxSelector::NotSandboxed); + } + + spawn_tokio!(async move { loader.load().await }) + .await + .unwrap() +} + +/// Load the given file as an image into a `GdkPaintable`. +pub async fn load_image(file: gio::File) -> Result { + let image = image_reader(file).await?; + + let (image, first_frame) = spawn_tokio!(async move { + let first_frame = image.next_frame().await?; + Ok((image, first_frame)) + }) + .await + .unwrap()?; + + let paintable = if first_frame.delay().is_some() { + AnimatedImagePaintable::new(image, first_frame).upcast() + } else { + first_frame.texture().upcast() + }; + + Ok(paintable) +} + +pub async fn get_image_info(file: gio::File) -> BaseImageInfo { let mut info = BaseImageInfo { width: None, height: None, @@ -102,17 +138,10 @@ pub async fn get_image_info(file: &gio::File) -> BaseImageInfo { blurhash: None, }; - let path = match file.path() { - Some(path) => path, - None => return info, - }; - - if let Some((w, h)) = image::io::Reader::open(path) - .ok() - .and_then(|reader| reader.into_dimensions().ok()) - { - info.width = Some(w.into()); - info.height = Some(h.into()); + if let Ok(image) = image_reader(file).await { + let image_info = image.info(); + info.width = Some(image_info.width.into()); + info.height = Some(image_info.height.into()); } info diff --git a/src/utils/mod.rs b/src/utils/mod.rs index c0d29bb2..35e0aab6 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -22,7 +22,7 @@ use futures_util::{ future::{self, Either, Future}, pin_mut, }; -use gtk::{gdk, glib, prelude::*, subclass::prelude::*}; +use gtk::{gdk, gio, glib, prelude::*, subclass::prelude::*}; use once_cell::sync::Lazy; use regex::Regex; @@ -489,3 +489,17 @@ pub fn add_activate_binding_action(klass: &mut T, action: &st klass.add_binding_action(*key, gdk::ModifierType::empty(), action); } } + +/// Save the given data to a temporary file. +pub fn save_data_to_tmp_file(data: &[u8]) -> Result { + let (file, _) = gio::File::new_tmp(None::)?; + file.replace_contents( + data, + None, + false, + gio::FileCreateFlags::REPLACE_DESTINATION, + gio::Cancellable::NONE, + )?; + + Ok(file) +}