From cf49892268507b6b3b75cab39186e299357c9f0b Mon Sep 17 00:00:00 2001 From: Marius Pardo Date: Mon, 15 Dec 2025 12:01:20 +0100 Subject: [PATCH 01/16] enhance opened window logs --- src/main.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index 6c5cc10..a9a4393 100644 --- a/src/main.rs +++ b/src/main.rs @@ -76,13 +76,17 @@ fn get_process_names(windows: &HashSet) -> Vec { .map(|w| { let is_focused = w.is_focused().unwrap_or(false); format!( - "{}{}(class: {})", + "{}{}[class: {}][hwnd: {:?}][title: {}]", if is_focused { "[FOCUSED] " } else { "" }, w.process_name() .ok() .unwrap_or_else(|| "[ERROR] Could not get process name".to_string()), w.class() - .unwrap_or_else(|_| "[ERROR] Could not get class name".to_string()) + .unwrap_or_else(|_| "[ERROR] Could not get class name".to_string()), + w.handle(), + w.title() + .unwrap_or_else(|_| Some("[ERROR] Could not get window title".to_string())) + .unwrap_or_else(|| "[UNNAMED]".to_string()), ) }) .collect::>() From 17fc4a221f67c981566075aa365e717ad3741d8e Mon Sep 17 00:00:00 2001 From: Marius Pardo Date: Mon, 15 Dec 2025 12:13:03 +0100 Subject: [PATCH 02/16] bugfix: trying to insert window after end of tiler --- src/tiler.rs | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/tiler.rs b/src/tiler.rs index ab98e84..8182ace 100644 --- a/src/tiler.rs +++ b/src/tiler.rs @@ -216,8 +216,9 @@ impl ScrollTiler { /// If the focused window is tiled, new windows are appended after it. /// Otherwise, they are appended at the end. fn append_new_windows(&mut self, windows_snapshot: &HashSet) { - if let Some(focus_index) = self.focus_index().or(self.previously_focused_window_index) - && !self.windows.is_empty() + if !self.windows.is_empty() + && let Some(focus_index) = self.focus_index().or(self.previously_focused_window_index) + && focus_index < self.windows.len() { for window in windows_snapshot { if !self @@ -226,7 +227,10 @@ impl ScrollTiler { .any(|window_item| window_item.inner == *window) { log::info!("Adding after focused {focus_index}"); - self.insert_window_after(*window, focus_index); + self.windows.insert( + focus_index + 1, + WindowItem::new(*window, self.default_size()), + ); } } } else { @@ -237,7 +241,8 @@ impl ScrollTiler { .any(|window_item| window_item.inner == *window) { log::info!("Appending at end"); - self.append_window(*window); + self.windows + .push(WindowItem::new(*window, self.default_size())); } } } @@ -247,16 +252,6 @@ impl ScrollTiler { self.screen_size.width() / 2 - self.padding * 2 } - fn append_window(&mut self, window: Window) { - self.windows - .push(WindowItem::new(window, self.default_size())); - } - - fn insert_window_after(&mut self, window: Window, index: usize) { - self.windows - .insert(index + 1, WindowItem::new(window, self.default_size())); - } - fn layout_windows(&mut self, windows_positions: &[i32]) { for (window, x) in self.windows.iter_mut().zip(windows_positions) { let y = self.padding.cast(); From e872c00df04c218fbac05a13f85b7b87c5d8001c Mon Sep 17 00:00:00 2001 From: sub07 Date: Mon, 22 Dec 2025 00:57:29 +0100 Subject: [PATCH 03/16] Iced integration (#58) * basics are working * border working * iced rewrite done * Update tiler.rs * update deps --- .github/workflows/check.yml | 2 +- .zed/tasks.json | 10 +- Cargo.lock | 2082 +++++++++++++---- Cargo.toml | 21 +- src/adapter/iced.rs | 31 + src/adapter/mod.rs | 1 + src/{ => app}/action.rs | 6 +- src/app/mod.rs | 183 ++ src/app/model/mod.rs | 10 + src/app/service/mod.rs | 114 + src/app/service/overview/mod.rs | 143 ++ src/app/service/overview/thumbnail.rs | 145 ++ src/app/service/tiler.rs | 105 + src/app/subscription/global/input.rs | 86 + src/app/subscription/global/mod.rs | 32 + .../subscription/global}/window.rs | 73 +- src/app/subscription/mod.rs | 3 + src/app/view/mod.rs | 7 + src/app/view/overlay.rs | 68 + src/hook/key.rs | 82 - src/hook/mod.rs | 35 - src/logger.rs | 20 +- src/main.rs | 448 +--- src/{tiler.rs => scroll_tiler.rs} | 129 +- src/system.rs | 37 +- src/thumbnail.rs | 69 - src/utils/cast.rs | 66 +- src/utils/color.rs | 27 - src/utils/frac.rs | 128 - src/utils/math.rs | 22 + src/utils/mod.rs | 71 +- src/{utils => }/winapi.rs | 6 +- src/window/filter.rs | 15 +- src/window/manager.rs | 632 ----- src/window/mod.rs | 88 +- 35 files changed, 2837 insertions(+), 2160 deletions(-) create mode 100644 src/adapter/iced.rs create mode 100644 src/adapter/mod.rs rename src/{ => app}/action.rs (83%) create mode 100644 src/app/mod.rs create mode 100644 src/app/model/mod.rs create mode 100644 src/app/service/mod.rs create mode 100644 src/app/service/overview/mod.rs create mode 100644 src/app/service/overview/thumbnail.rs create mode 100644 src/app/service/tiler.rs create mode 100644 src/app/subscription/global/input.rs create mode 100644 src/app/subscription/global/mod.rs rename src/{hook => app/subscription/global}/window.rs (52%) create mode 100644 src/app/subscription/mod.rs create mode 100644 src/app/view/mod.rs create mode 100644 src/app/view/overlay.rs delete mode 100644 src/hook/key.rs delete mode 100644 src/hook/mod.rs rename src/{tiler.rs => scroll_tiler.rs} (72%) delete mode 100644 src/thumbnail.rs delete mode 100644 src/utils/color.rs delete mode 100644 src/utils/frac.rs create mode 100644 src/utils/math.rs rename src/{utils => }/winapi.rs (89%) delete mode 100644 src/window/manager.rs diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index e04719d..8aec590 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -23,6 +23,6 @@ jobs: - name: Run rustfmt run: cargo fmt --all -- --check - name: Run Clippy - run: cargo clippy --all-targets --all-features -- -D warnings -W clippy::pedantic -W clippy::nursery -W clippy::dbg_macro -A clippy::missing_errors_doc + run: cargo clippy --all-targets --all-features -- -D warnings - name: Run tests run: cargo test --all --all-features diff --git a/.zed/tasks.json b/.zed/tasks.json index b568184..69b667d 100644 --- a/.zed/tasks.json +++ b/.zed/tasks.json @@ -7,13 +7,13 @@ "command": "cargo", "args": ["run"], "env": { - "RUST_BACKTRACE": "1", - "RUST_LOG": "TRACE" - } + // "RUST_BACKTRACE": "1", + "RUST_LOG": "TRACE", + }, }, { "label": "run c", "command": "cargo", - "args": ["check"] - } + "args": ["check"], + }, ] diff --git a/Cargo.lock b/Cargo.lock index cfd796d..180a994 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,35 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "ab_glyph" -version = "0.2.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01c0457472c38ea5bd1c3b5ada5e368271cb550be7a4ca4a0b4634e9913f6cc2" -dependencies = [ - "ab_glyph_rasterizer", - "owned_ttf_parser", -] - -[[package]] -name = "ab_glyph_rasterizer" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" - -[[package]] -name = "ahash" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" -dependencies = [ - "cfg-if", - "getrandom 0.3.4", - "once_cell", - "version_check", - "zerocopy", -] - [[package]] name = "android-activity" version = "0.6.0" @@ -92,10 +63,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] -name = "as-raw-xcb-connection" -version = "1.0.1" +name = "ash" +version = "0.38.0+1.3.281" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" +checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" +dependencies = [ + "libloading", +] [[package]] name = "atomic-waker" @@ -109,6 +83,30 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + [[package]] name = "bitflags" version = "1.3.2" @@ -136,14 +134,14 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" dependencies = [ - "objc2", + "objc2 0.5.2", ] [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" [[package]] name = "bytemuck" @@ -167,9 +165,9 @@ dependencies = [ [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" [[package]] name = "calloop" @@ -185,23 +183,11 @@ dependencies = [ "thiserror 1.0.69", ] -[[package]] -name = "calloop-wayland-source" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20" -dependencies = [ - "calloop", - "rustix 0.38.44", - "wayland-backend", - "wayland-client", -] - [[package]] name = "cc" -version = "1.2.45" +version = "1.2.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35900b6c8d709fb1d854671ae27aeaa9eec2f8b01b364e1619a40da3e6fe2afe" +checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" dependencies = [ "find-msvc-tools", "jobserver", @@ -227,20 +213,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" -[[package]] -name = "channel-protocol" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cea07cc94d18a7c1a6ad0b27b57e005ffa768d0b45f96e94964dfdfe84edb68" -dependencies = [ - "convert_case", - "itertools", - "oneshot", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "chrono" version = "0.4.42" @@ -252,6 +224,26 @@ dependencies = [ "windows-link", ] +[[package]] +name = "clipboard-win" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4" +dependencies = [ + "error-code", +] + +[[package]] +name = "clipboard_macos" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b7f4aaa047ba3c3630b080bb9860894732ff23e2aee290a418909aa6d5df38f" +dependencies = [ + "objc2 0.5.2", + "objc2-app-kit", + "objc2-foundation 0.2.2", +] + [[package]] name = "cocoa" version = "0.22.0" @@ -267,6 +259,17 @@ dependencies = [ "objc", ] +[[package]] +name = "codespan-reporting" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" +dependencies = [ + "serde", + "termcolor", + "unicode-width", +] + [[package]] name = "combine" version = "4.6.7" @@ -286,15 +289,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "convert_case" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" -dependencies = [ - "unicode-segmentation", -] - [[package]] name = "core-foundation" version = "0.7.0" @@ -374,19 +368,6 @@ dependencies = [ "libc", ] -[[package]] -name = "core-graphics" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" -dependencies = [ - "bitflags 2.10.0", - "core-foundation 0.10.1", - "core-graphics-types 0.2.0", - "foreign-types 0.5.0", - "libc", -] - [[package]] name = "core-graphics-types" version = "0.1.3" @@ -409,6 +390,38 @@ dependencies = [ "libc", ] +[[package]] +name = "core_maths" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77745e017f5edba1a9c1d854f6f3a52dac8a12dd5af5d2f54aecf61e43d80d30" +dependencies = [ + "libm", +] + +[[package]] +name = "cosmic-text" +version = "0.15.0" +source = "git+https://github.com/pop-os/cosmic-text.git?rev=a07a6190548c8e40a55f6b7761387047ff1bf6ff#a07a6190548c8e40a55f6b7761387047ff1bf6ff" +dependencies = [ + "bitflags 2.10.0", + "fontdb", + "harfrust", + "linebender_resource_handle", + "log", + "rangemap", + "rustc-hash 1.1.0", + "self_cell", + "skrifa", + "smol_str", + "swash", + "sys-locale", + "unicode-bidi", + "unicode-linebreak", + "unicode-script", + "unicode-segmentation", +] + [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -416,10 +429,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] -name = "ctor-lite" -version = "0.1.0" +name = "crunchy" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f791803201ab277ace03903de1594460708d2d54df6053f2d9e82f592b19e3b" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "cryoglyph" +version = "0.1.0" +source = "git+https://github.com/iced-rs/cryoglyph.git?rev=89883bcf38b5bed0d7bade788ef738d9facc857c#89883bcf38b5bed0d7bade788ef738d9facc857c" +dependencies = [ + "cosmic-text", + "etagere", + "lru", + "rustc-hash 2.1.1", + "wgpu", +] [[package]] name = "cursor-icon" @@ -429,21 +454,22 @@ checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" [[package]] name = "derive_more" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +checksum = "10b768e943bed7bf2cab53df09f4bc34bfd217cdb57d971e769874c9a6710618" dependencies = [ "derive_more-impl", ] [[package]] name = "derive_more-impl" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +checksum = "6d286bfdaf75e988b4a78e013ecd79c581e06399ab53fbacd2d916c2f904f30b" dependencies = [ "proc-macro2", "quote", + "rustc_version", "syn", "unicode-xid", ] @@ -482,64 +508,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" [[package]] -name = "dlib" -version = "0.5.2" +name = "dispatch2" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" dependencies = [ - "libloading", + "bitflags 2.10.0", + "objc2 0.6.3", ] [[package]] -name = "downcast-rs" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" - -[[package]] -name = "dpi" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" - -[[package]] -name = "drm" -version = "0.12.0" +name = "dlib" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98888c4bbd601524c11a7ed63f814b8825f420514f78e96f752c437ae9cbb5d1" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" dependencies = [ - "bitflags 2.10.0", - "bytemuck", - "drm-ffi", - "drm-fourcc", - "rustix 0.38.44", + "libloading", ] [[package]] -name = "drm-ffi" -version = "0.8.0" +name = "document-features" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97c98727e48b7ccb4f4aea8cfe881e5b07f702d17b7875991881b41af7278d53" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" dependencies = [ - "drm-sys", - "rustix 0.38.44", + "litrs", ] [[package]] -name = "drm-fourcc" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4" - -[[package]] -name = "drm-sys" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd39dde40b6e196c2e8763f23d119ddb1a8714534bf7d77fa97a65b0feda3986" -dependencies = [ - "libc", - "linux-raw-sys 0.6.5", -] +name = "dpi" +version = "0.1.1" +source = "git+https://github.com/iced-rs/winit.git?rev=05b8ff17a06562f0a10bb46e6eaacbe2a95cb5ed#05b8ff17a06562f0a10bb46e6eaacbe2a95cb5ed" [[package]] name = "easy-ext" @@ -579,6 +578,31 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "error-code" +version = "3.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" + +[[package]] +name = "etagere" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc89bf99e5dc15954a60f707c1e09d7540e5cd9af85fa75caa0b510bc08c5342" +dependencies = [ + "euclid", + "svg_fmt", +] + +[[package]] +name = "euclid" +version = "0.22.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad9cdb4b747e485a12abb0e6566612956c7a1bafa3bdb8d682c5b6d403589e48" +dependencies = [ + "num-traits", +] + [[package]] name = "evdev-rs" version = "0.4.0" @@ -603,16 +627,16 @@ dependencies = [ ] [[package]] -name = "fastrand" -version = "2.3.0" +name = "find-msvc-tools" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" [[package]] -name = "find-msvc-tools" -version = "0.1.4" +name = "float_next_after" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" +checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8" [[package]] name = "fnv" @@ -620,6 +644,50 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "font-types" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39a654f404bbcbd48ea58c617c2993ee91d1cb63727a37bf2323a4edeed1b8c5" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "fontconfig-parser" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbc773e24e02d4ddd8395fd30dc147524273a83e54e0f312d986ea30de5f5646" +dependencies = [ + "roxmltree", +] + +[[package]] +name = "fontdb" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "457e789b3d1202543297a350643cf459f836cade38934e7a4cf6a39e7cde2905" +dependencies = [ + "fontconfig-parser", + "log", + "memmap2", + "slotmap", + "tinyvec", + "ttf-parser", +] + [[package]] name = "foreign-types" version = "0.3.2" @@ -663,93 +731,517 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" [[package]] -name = "gethostname" -version = "1.1.0" +name = "futures" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ - "rustix 1.1.2", - "windows-link", + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", ] [[package]] -name = "getrandom" -version = "0.2.16" +name = "futures-channel" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ - "cfg-if", - "libc", - "wasi", + "futures-core", + "futures-sink", ] [[package]] -name = "getrandom" -version = "0.3.4" +name = "futures-core" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ - "cfg-if", - "libc", - "r-efi", - "wasip2", + "futures-core", + "futures-task", + "futures-util", ] [[package]] -name = "hashbrown" -version = "0.16.0" +name = "futures-io" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] -name = "hermit-abi" -version = "0.5.2" +name = "futures-macro" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] -name = "humantime" -version = "2.3.0" +name = "futures-sink" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] -name = "iana-time-zone" -version = "0.1.64" +name = "futures-task" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" -dependencies = [ - "android_system_properties", - "core-foundation-sys 0.8.7", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core", -] +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" +name = "futures-util" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ - "cc", + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", ] [[package]] -name = "indexmap" -version = "2.12.0" +name = "getrandom" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ - "equivalent", - "hashbrown", + "cfg-if", + "libc", + "wasi", ] [[package]] -name = "inotify" -version = "0.8.3" +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "gl_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +dependencies = [ + "khronos_api", + "log", + "xml-rs", +] + +[[package]] +name = "glam" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3" + +[[package]] +name = "glow" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e5ea60d70410161c8bf5da3fdfeaa1c72ed2c15f8bbb9d19fe3a4fad085f08" +dependencies = [ + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "glutin_wgl_sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4ee00b289aba7a9e5306d57c2d05499b2e5dc427f84ac708bd2c090212cf3e" +dependencies = [ + "gl_generator", +] + +[[package]] +name = "gpu-alloc" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" +dependencies = [ + "bitflags 2.10.0", + "gpu-alloc-types", +] + +[[package]] +name = "gpu-alloc-types" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" +dependencies = [ + "bitflags 2.10.0", +] + +[[package]] +name = "gpu-allocator" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c151a2a5ef800297b4e79efa4f4bec035c5f51d5ae587287c9b952bdf734cacd" +dependencies = [ + "log", + "presser", + "thiserror 1.0.69", + "windows 0.58.0", +] + +[[package]] +name = "gpu-descriptor" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" +dependencies = [ + "bitflags 2.10.0", + "gpu-descriptor-types", + "hashbrown 0.15.5", +] + +[[package]] +name = "gpu-descriptor-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" +dependencies = [ + "bitflags 2.10.0", +] + +[[package]] +name = "guillotiere" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62d5865c036cb1393e23c50693df631d3f5d7bcca4c04fe4cc0fd592e74a782" +dependencies = [ + "euclid", + "svg_fmt", +] + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "num-traits", + "zerocopy", +] + +[[package]] +name = "harfrust" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92c020db12c71d8a12a3fe7607873cade3a01a6287e29d540c8723276221b9d8" +dependencies = [ + "bitflags 2.10.0", + "bytemuck", + "core_maths", + "read-fonts", + "smallvec", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "foldhash 0.2.0", +] + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "hexf-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" + +[[package]] +name = "humantime" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" + +[[package]] +name = "iana-time-zone" +version = "0.1.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +dependencies = [ + "android_system_properties", + "core-foundation-sys 0.8.7", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core 0.62.2", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "iced" +version = "0.15.0-dev" +source = "git+https://github.com/sub07/iced.git?branch=custom-winri#17d55805dec08bea6af98b2613250a6f1d1c836b" +dependencies = [ + "iced_core", + "iced_debug", + "iced_devtools", + "iced_futures", + "iced_renderer", + "iced_runtime", + "iced_widget", + "iced_winit", + "thiserror 2.0.17", +] + +[[package]] +name = "iced_beacon" +version = "0.15.0-dev" +source = "git+https://github.com/sub07/iced.git?branch=custom-winri#17d55805dec08bea6af98b2613250a6f1d1c836b" +dependencies = [ + "bincode", + "futures", + "iced_core", + "log", + "semver", + "serde", + "thiserror 2.0.17", + "tokio", +] + +[[package]] +name = "iced_core" +version = "0.15.0-dev" +source = "git+https://github.com/sub07/iced.git?branch=custom-winri#17d55805dec08bea6af98b2613250a6f1d1c836b" +dependencies = [ + "bitflags 2.10.0", + "bytes", + "glam", + "lilt", + "log", + "num-traits", + "rustc-hash 2.1.1", + "serde", + "smol_str", + "thiserror 2.0.17", + "web-time", +] + +[[package]] +name = "iced_debug" +version = "0.15.0-dev" +source = "git+https://github.com/sub07/iced.git?branch=custom-winri#17d55805dec08bea6af98b2613250a6f1d1c836b" +dependencies = [ + "iced_beacon", + "iced_core", + "iced_futures", + "log", +] + +[[package]] +name = "iced_devtools" +version = "0.15.0-dev" +source = "git+https://github.com/sub07/iced.git?branch=custom-winri#17d55805dec08bea6af98b2613250a6f1d1c836b" +dependencies = [ + "iced_debug", + "iced_program", + "iced_widget", + "log", +] + +[[package]] +name = "iced_futures" +version = "0.15.0-dev" +source = "git+https://github.com/sub07/iced.git?branch=custom-winri#17d55805dec08bea6af98b2613250a6f1d1c836b" +dependencies = [ + "futures", + "iced_core", + "log", + "rustc-hash 2.1.1", + "tokio", + "wasm-bindgen-futures", + "wasmtimer", +] + +[[package]] +name = "iced_graphics" +version = "0.15.0-dev" +source = "git+https://github.com/sub07/iced.git?branch=custom-winri#17d55805dec08bea6af98b2613250a6f1d1c836b" +dependencies = [ + "bitflags 2.10.0", + "bytemuck", + "cosmic-text", + "half", + "iced_core", + "iced_futures", + "log", + "lyon_path", + "raw-window-handle", + "rustc-hash 2.1.1", + "thiserror 2.0.17", + "unicode-segmentation", +] + +[[package]] +name = "iced_program" +version = "0.15.0-dev" +source = "git+https://github.com/sub07/iced.git?branch=custom-winri#17d55805dec08bea6af98b2613250a6f1d1c836b" +dependencies = [ + "iced_graphics", + "iced_runtime", +] + +[[package]] +name = "iced_renderer" +version = "0.15.0-dev" +source = "git+https://github.com/sub07/iced.git?branch=custom-winri#17d55805dec08bea6af98b2613250a6f1d1c836b" +dependencies = [ + "iced_graphics", + "iced_tiny_skia", + "iced_wgpu", + "log", + "thiserror 2.0.17", +] + +[[package]] +name = "iced_runtime" +version = "0.15.0-dev" +source = "git+https://github.com/sub07/iced.git?branch=custom-winri#17d55805dec08bea6af98b2613250a6f1d1c836b" +dependencies = [ + "bytes", + "iced_core", + "iced_futures", + "raw-window-handle", + "thiserror 2.0.17", +] + +[[package]] +name = "iced_tiny_skia" +version = "0.15.0-dev" +source = "git+https://github.com/sub07/iced.git?branch=custom-winri#17d55805dec08bea6af98b2613250a6f1d1c836b" +dependencies = [ + "bytemuck", + "cosmic-text", + "iced_debug", + "iced_graphics", + "kurbo", + "log", + "rustc-hash 2.1.1", + "softbuffer", + "tiny-skia", +] + +[[package]] +name = "iced_wgpu" +version = "0.15.0-dev" +source = "git+https://github.com/sub07/iced.git?branch=custom-winri#17d55805dec08bea6af98b2613250a6f1d1c836b" +dependencies = [ + "bitflags 2.10.0", + "bytemuck", + "cryoglyph", + "futures", + "glam", + "guillotiere", + "iced_debug", + "iced_graphics", + "log", + "lyon", + "rustc-hash 2.1.1", + "thiserror 2.0.17", + "wgpu", +] + +[[package]] +name = "iced_widget" +version = "0.15.0-dev" +source = "git+https://github.com/sub07/iced.git?branch=custom-winri#17d55805dec08bea6af98b2613250a6f1d1c836b" +dependencies = [ + "iced_renderer", + "log", + "num-traits", + "rustc-hash 2.1.1", + "thiserror 2.0.17", + "unicode-segmentation", +] + +[[package]] +name = "iced_winit" +version = "0.15.0-dev" +source = "git+https://github.com/sub07/iced.git?branch=custom-winri#17d55805dec08bea6af98b2613250a6f1d1c836b" +dependencies = [ + "iced_debug", + "iced_program", + "log", + "rustc-hash 2.1.1", + "thiserror 2.0.17", + "tracing", + "wasm-bindgen-futures", + "web-sys", + "window_clipboard", + "winit", +] + +[[package]] +name = "indexmap" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", +] + +[[package]] +name = "inotify" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46dd0a94b393c730779ccfd2a872b67b1eb67be3fc33082e733bdb38b5fde4d4" dependencies = [ @@ -831,9 +1323,9 @@ source = "git+https://github.com/sub07/rust-utils#0511d29b84705811e51d249a332749 [[package]] name = "js-sys" -version = "0.3.82" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" dependencies = [ "once_cell", "wasm-bindgen", @@ -849,6 +1341,33 @@ dependencies = [ "serde", ] +[[package]] +name = "khronos-egl" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" +dependencies = [ + "libc", + "libloading", + "pkg-config", +] + +[[package]] +name = "khronos_api" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" + +[[package]] +name = "kurbo" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1618d4ebd923e97d67e7cd363d80aef35fe961005cbbbb3d2dad8bdd1bc63440" +dependencies = [ + "arrayvec", + "smallvec", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -857,9 +1376,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.177" +version = "0.2.178" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" [[package]] name = "libloading" @@ -871,28 +1390,43 @@ dependencies = [ "windows-link", ] +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + [[package]] name = "libredox" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" +checksum = "df15f6eac291ed1cf25865b1ee60399f57e7c227e7f51bdbd4c5270396a9ed50" dependencies = [ "bitflags 2.10.0", "libc", - "redox_syscall 0.5.18", + "redox_syscall 0.6.0", ] [[package]] -name = "linux-raw-sys" -version = "0.4.15" +name = "lilt" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" +checksum = "f67562e5eff6b20553fa9be1c503356768420994e28f67e3eafe6f41910e57ad" +dependencies = [ + "web-time", +] + +[[package]] +name = "linebender_resource_handle" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a5ff6bcca6c4867b1c4fd4ef63e4db7436ef363e0ad7531d1558856bae64f4" [[package]] name = "linux-raw-sys" -version = "0.6.5" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a385b1be4e5c3e362ad2ffa73c392e53f031eaa5b7d648e64cd87f27f6063d7" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "linux-raw-sys" @@ -900,6 +1434,12 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + [[package]] name = "lock_api" version = "0.4.14" @@ -911,11 +1451,11 @@ dependencies = [ [[package]] name = "log" -version = "0.4.28" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" dependencies = [ - "serde", + "serde_core", ] [[package]] @@ -953,6 +1493,64 @@ dependencies = [ "winapi", ] +[[package]] +name = "lru" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96051b46fc183dc9cd4a223960ef37b9af631b55191852a8274bfef064cda20f" + +[[package]] +name = "lyon" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbcb7d54d54c8937364c9d41902d066656817dce1e03a44e5533afebd1ef4352" +dependencies = [ + "lyon_algorithms", + "lyon_tessellation", +] + +[[package]] +name = "lyon_algorithms" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c0829e28c4f336396f250d850c3987e16ce6db057ffe047ce0dd54aab6b647" +dependencies = [ + "lyon_path", + "num-traits", +] + +[[package]] +name = "lyon_geom" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e260b6de923e6e47adfedf6243013a7a874684165a6a277594ee3906021b2343" +dependencies = [ + "arrayvec", + "euclid", + "num-traits", +] + +[[package]] +name = "lyon_path" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aeca86bcfd632a15984ba029b539ffb811e0a70bf55e814ef8b0f54f506fdeb" +dependencies = [ + "lyon_geom", + "num-traits", +] + +[[package]] +name = "lyon_tessellation" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3f586142e1280335b1bc89539f7c97dd80f08fc43e9ab1b74ef0a42b04aa353" +dependencies = [ + "float_next_after", + "lyon_path", + "num-traits", +] + [[package]] name = "malloc_buf" version = "0.0.6" @@ -966,15 +1564,41 @@ dependencies = [ name = "memchr" version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "memmap2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "744133e4a0e0a658e1374cf3bf8e415c4052a15a111acd372764c55b4177d490" +dependencies = [ + "libc", +] + +[[package]] +name = "metal" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00c15a6f673ff72ddcc22394663290f870fb224c1bfce55734a75c414150e605" +dependencies = [ + "bitflags 2.10.0", + "block", + "core-graphics-types 0.2.0", + "foreign-types 0.5.0", + "log", + "objc", + "paste", +] [[package]] -name = "memmap2" -version = "0.9.9" +name = "mio" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "744133e4a0e0a658e1374cf3bf8e415c4052a15a111acd372764c55b4177d490" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", + "wasi", + "windows-sys 0.61.2", ] [[package]] @@ -983,6 +1607,32 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dce6dd36094cac388f119d2e9dc82dc730ef91c32a6222170d630e5414b956e6" +[[package]] +name = "naga" +version = "27.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "066cf25f0e8b11ee0df221219010f213ad429855f57c494f995590c861a9a7d8" +dependencies = [ + "arrayvec", + "bit-set", + "bitflags 2.10.0", + "cfg-if", + "cfg_aliases", + "codespan-reporting", + "half", + "hashbrown 0.16.1", + "hexf-parse", + "indexmap", + "libm", + "log", + "num-traits", + "once_cell", + "rustc-hash 1.1.0", + "spirv", + "thiserror 2.0.17", + "unicode-ident", +] + [[package]] name = "ndk" version = "0.9.0" @@ -1020,6 +1670,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -1069,6 +1720,15 @@ dependencies = [ "objc2-encode", ] +[[package]] +name = "objc2" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05" +dependencies = [ + "objc2-encode", +] + [[package]] name = "objc2-app-kit" version = "0.2.2" @@ -1078,11 +1738,11 @@ dependencies = [ "bitflags 2.10.0", "block2", "libc", - "objc2", + "objc2 0.5.2", "objc2-core-data", "objc2-core-image", - "objc2-foundation", - "objc2-quartz-core", + "objc2-foundation 0.2.2", + "objc2-quartz-core 0.2.2", ] [[package]] @@ -1093,9 +1753,9 @@ checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ "bitflags 2.10.0", "block2", - "objc2", + "objc2 0.5.2", "objc2-core-location", - "objc2-foundation", + "objc2-foundation 0.2.2", ] [[package]] @@ -1105,8 +1765,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" dependencies = [ "block2", - "objc2", - "objc2-foundation", + "objc2 0.5.2", + "objc2-foundation 0.2.2", ] [[package]] @@ -1117,8 +1777,32 @@ checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ "bitflags 2.10.0", "block2", - "objc2", - "objc2-foundation", + "objc2 0.5.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags 2.10.0", + "dispatch2", + "objc2 0.6.3", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags 2.10.0", + "dispatch2", + "objc2 0.6.3", + "objc2-core-foundation", + "objc2-io-surface", ] [[package]] @@ -1128,8 +1812,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" dependencies = [ "block2", - "objc2", - "objc2-foundation", + "objc2 0.5.2", + "objc2-foundation 0.2.2", "objc2-metal", ] @@ -1140,9 +1824,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" dependencies = [ "block2", - "objc2", + "objc2 0.5.2", "objc2-contacts", - "objc2-foundation", + "objc2-foundation 0.2.2", ] [[package]] @@ -1161,7 +1845,29 @@ dependencies = [ "block2", "dispatch", "libc", - "objc2", + "objc2 0.5.2", +] + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags 2.10.0", + "objc2 0.6.3", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" +dependencies = [ + "bitflags 2.10.0", + "objc2 0.6.3", + "objc2-core-foundation", ] [[package]] @@ -1171,9 +1877,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" dependencies = [ "block2", - "objc2", + "objc2 0.5.2", "objc2-app-kit", - "objc2-foundation", + "objc2-foundation 0.2.2", ] [[package]] @@ -1184,8 +1890,8 @@ checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ "bitflags 2.10.0", "block2", - "objc2", - "objc2-foundation", + "objc2 0.5.2", + "objc2-foundation 0.2.2", ] [[package]] @@ -1196,19 +1902,31 @@ checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ "bitflags 2.10.0", "block2", - "objc2", - "objc2-foundation", + "objc2 0.5.2", + "objc2-foundation 0.2.2", "objc2-metal", ] +[[package]] +name = "objc2-quartz-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" +dependencies = [ + "bitflags 2.10.0", + "objc2 0.6.3", + "objc2-core-foundation", + "objc2-foundation 0.3.2", +] + [[package]] name = "objc2-symbols" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" dependencies = [ - "objc2", - "objc2-foundation", + "objc2 0.5.2", + "objc2-foundation 0.2.2", ] [[package]] @@ -1219,14 +1937,14 @@ checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ "bitflags 2.10.0", "block2", - "objc2", + "objc2 0.5.2", "objc2-cloud-kit", "objc2-core-data", "objc2-core-image", "objc2-core-location", - "objc2-foundation", + "objc2-foundation 0.2.2", "objc2-link-presentation", - "objc2-quartz-core", + "objc2-quartz-core 0.2.2", "objc2-symbols", "objc2-uniform-type-identifiers", "objc2-user-notifications", @@ -1239,8 +1957,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" dependencies = [ "block2", - "objc2", - "objc2-foundation", + "objc2 0.5.2", + "objc2-foundation 0.2.2", ] [[package]] @@ -1251,9 +1969,9 @@ checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ "bitflags 2.10.0", "block2", - "objc2", + "objc2 0.5.2", "objc2-core-location", - "objc2-foundation", + "objc2-foundation 0.2.2", ] [[package]] @@ -1262,12 +1980,6 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" -[[package]] -name = "oneshot" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ce411919553d3f9fa53a0880544cda985a112117a0444d5ff1e870a893d6ea" - [[package]] name = "option-ext" version = "0.2.0" @@ -1293,12 +2005,12 @@ dependencies = [ ] [[package]] -name = "owned_ttf_parser" -version = "0.25.1" +name = "ordered-float" +version = "5.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36820e9051aca1014ddc75770aab4d68bc1e9e632f0f5627c4086bc216fb583b" +checksum = "7f4779c6901a562440c3786d08192c6fbda7c1c2060edd10006b05ee35d10f2d" dependencies = [ - "ttf-parser", + "num-traits", ] [[package]] @@ -1325,10 +2037,10 @@ dependencies = [ ] [[package]] -name = "percent-encoding" -version = "2.3.2" +name = "paste" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pin-project" @@ -1356,6 +2068,12 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "pkg-config" version = "0.3.32" @@ -1376,6 +2094,21 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "portable-atomic" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -1385,6 +2118,12 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "presser" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" + [[package]] name = "proc-macro-crate" version = "3.4.0" @@ -1404,13 +2143,10 @@ dependencies = [ ] [[package]] -name = "quick-xml" -version = "0.37.5" +name = "profiling" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" -dependencies = [ - "memchr", -] +checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" [[package]] name = "quote" @@ -1456,6 +2192,18 @@ dependencies = [ "getrandom 0.3.4", ] +[[package]] +name = "range-alloc" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d6831663a5098ea164f89cff59c6284e95f4e3c76ce9848d4529f5ccca9bde" + +[[package]] +name = "rangemap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "973443cf09a9c8656b574a866ab68dfa19f0867d0340648c7d2f6a71b8a8ea68" + [[package]] name = "raw-window-handle" version = "0.6.2" @@ -1481,6 +2229,17 @@ dependencies = [ "x11", ] +[[package]] +name = "read-fonts" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6717cf23b488adf64b9d711329542ba34de147df262370221940dfabc2c91358" +dependencies = [ + "bytemuck", + "core_maths", + "font-types", +] + [[package]] name = "redox_syscall" version = "0.4.1" @@ -1499,6 +2258,15 @@ dependencies = [ "bitflags 2.10.0", ] +[[package]] +name = "redox_syscall" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec96166dafa0886eb81fe1c0a388bece180fbef2135f97c1e2cf8302e74b43b5" +dependencies = [ + "bitflags 2.10.0", +] + [[package]] name = "redox_users" version = "0.5.2" @@ -1510,6 +2278,39 @@ dependencies = [ "thiserror 2.0.17", ] +[[package]] +name = "renderdoc-sys" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" + +[[package]] +name = "roxmltree" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.38.44" @@ -1557,12 +2358,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - [[package]] name = "scopeguard" version = "1.2.0" @@ -1570,16 +2365,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] -name = "sctk-adwaita" -version = "0.10.1" +name = "self_cell" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16c2f82143577edb4921b71ede051dac62ca3c16084e918bf7b40c96ae10eb33" + +[[package]] +name = "semver" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6277f0217056f77f1d8f49f2950ac6c278c0d607c45f5ee99328d792ede24ec" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" dependencies = [ - "ab_glyph", - "log", - "memmap2", - "smithay-client-toolkit", - "tiny-skia", + "serde", + "serde_core", ] [[package]] @@ -1598,7 +2396,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" dependencies = [ - "ordered-float", + "ordered-float 2.10.1", "serde", ] @@ -1654,12 +2452,31 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "skrifa" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c31071dedf532758ecf3fed987cdb4bd9509f900e026ab684b4ecb81ea49841" +dependencies = [ + "bytemuck", + "read-fonts", +] + [[package]] name = "slab" version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" +[[package]] +name = "slotmap" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038" +dependencies = [ + "version_check", +] + [[package]] name = "smallvec" version = "1.15.1" @@ -1667,88 +2484,113 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] -name = "smithay-client-toolkit" -version = "0.19.2" +name = "smol_str" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" +checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" dependencies = [ - "bitflags 2.10.0", - "calloop", - "calloop-wayland-source", - "cursor-icon", - "libc", - "log", - "memmap2", - "rustix 0.38.44", - "thiserror 1.0.69", - "wayland-backend", - "wayland-client", - "wayland-csd-frame", - "wayland-cursor", - "wayland-protocols", - "wayland-protocols-wlr", - "wayland-scanner", - "xkeysym", + "serde", ] [[package]] -name = "smol_str" -version = "0.2.2" +name = "socket2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" dependencies = [ - "serde", + "libc", + "windows-sys 0.60.2", ] [[package]] name = "softbuffer" -version = "0.4.6" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18051cdd562e792cad055119e0cdb2cfc137e44e3987532e0f9659a77931bb08" +checksum = "aac18da81ebbf05109ab275b157c22a653bb3c12cf884450179942f81bcbf6c3" dependencies = [ - "as-raw-xcb-connection", "bytemuck", - "cfg_aliases", - "core-graphics 0.24.0", - "drm", - "fastrand", - "foreign-types 0.5.0", "js-sys", - "log", - "memmap2", - "objc2", - "objc2-foundation", - "objc2-quartz-core", + "ndk", + "objc2 0.6.3", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation 0.3.2", + "objc2-quartz-core 0.3.2", "raw-window-handle", "redox_syscall 0.5.18", - "rustix 0.38.44", - "tiny-xlib", + "tracing", "wasm-bindgen", - "wayland-backend", - "wayland-client", - "wayland-sys", "web-sys", - "windows-sys 0.59.0", - "x11rb", + "windows-sys 0.61.2", +] + +[[package]] +name = "spirv" +version = "0.3.0+sdk-1.3.268.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" +dependencies = [ + "bitflags 2.10.0", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strict-num" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" +[[package]] +name = "svg_fmt" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0193cc4331cfd2f3d2011ef287590868599a2f33c3e69bc22c1a3d3acf9e02fb" + +[[package]] +name = "swash" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47846491253e976bdd07d0f9cc24b7daf24720d11309302ccbbc6e6b6e53550a" +dependencies = [ + "skrifa", + "yazi", + "zeno", +] + [[package]] name = "syn" -version = "2.0.110" +version = "2.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "sys-locale" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" +dependencies = [ + "libc", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -1825,32 +2667,60 @@ dependencies = [ ] [[package]] -name = "tiny-xlib" -version = "0.2.4" +name = "tinyvec" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0324504befd01cab6e0c994f34b2ffa257849ee019d3fb3b64fb2c858887d89e" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" dependencies = [ - "as-raw-xcb-connection", - "ctor-lite", - "libloading", - "pkg-config", - "tracing", + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] name = "toml_datetime" -version = "0.7.3" +version = "0.7.5+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" dependencies = [ "serde_core", ] [[package]] name = "toml_edit" -version = "0.23.7" +version = "0.23.10+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" +checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" dependencies = [ "indexmap", "toml_datetime", @@ -1860,34 +2730,52 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.0.4" +version = "1.0.6+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" dependencies = [ "winnow", ] [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "pin-project-lite", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] [[package]] name = "ttf-parser" version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" +dependencies = [ + "core_maths", +] [[package]] name = "typemap-ors" @@ -1898,18 +2786,42 @@ dependencies = [ "unsafe-any-ors", ] +[[package]] +name = "unicode-bidi" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" + [[package]] name = "unicode-ident" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + +[[package]] +name = "unicode-script" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "383ad40bb927465ec0ce7720e033cb4ca06912855fc35db31b5755d0de75b1ee" + [[package]] name = "unicode-segmentation" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -1964,9 +2876,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" dependencies = [ "cfg-if", "once_cell", @@ -1977,9 +2889,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.55" +version = "0.4.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "551f88106c6d5e7ccc7cd9a16f312dd3b5d36ea8b4954304657d5dfba115d4a0" +checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" dependencies = [ "cfg-if", "js-sys", @@ -1990,9 +2902,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2000,9 +2912,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" dependencies = [ "bumpalo", "proc-macro2", @@ -2013,140 +2925,196 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" dependencies = [ "unicode-ident", ] [[package]] -name = "wayland-backend" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "673a33c33048a5ade91a6b139580fa174e19fb0d23f396dca9fa15f2e1e49b35" -dependencies = [ - "cc", - "downcast-rs", - "rustix 1.1.2", - "scoped-tls", - "smallvec", - "wayland-sys", -] - -[[package]] -name = "wayland-client" -version = "0.31.11" +name = "wasmtimer" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d" +checksum = "1c598d6b99ea013e35844697fc4670d08339d5cda15588f193c6beedd12f644b" dependencies = [ - "bitflags 2.10.0", - "rustix 1.1.2", - "wayland-backend", - "wayland-scanner", + "futures", + "js-sys", + "parking_lot", + "pin-utils", + "slab", + "wasm-bindgen", ] [[package]] -name = "wayland-csd-frame" -version = "0.3.0" +name = "web-sys" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" +checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" dependencies = [ - "bitflags 2.10.0", - "cursor-icon", - "wayland-backend", + "js-sys", + "wasm-bindgen", ] [[package]] -name = "wayland-cursor" -version = "0.31.11" +name = "web-time" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447ccc440a881271b19e9989f75726d60faa09b95b0200a9b7eb5cc47c3eeb29" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" dependencies = [ - "rustix 1.1.2", - "wayland-client", - "xcursor", + "js-sys", + "wasm-bindgen", ] [[package]] -name = "wayland-protocols" -version = "0.32.9" +name = "wgpu" +version = "27.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efa790ed75fbfd71283bd2521a1cfdc022aabcc28bdcff00851f9e4ae88d9901" +checksum = "bfe68bac7cde125de7a731c3400723cadaaf1703795ad3f4805f187459cd7a77" dependencies = [ + "arrayvec", "bitflags 2.10.0", - "wayland-backend", - "wayland-client", - "wayland-scanner", + "cfg-if", + "cfg_aliases", + "document-features", + "hashbrown 0.16.1", + "js-sys", + "log", + "naga", + "parking_lot", + "portable-atomic", + "profiling", + "raw-window-handle", + "smallvec", + "static_assertions", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "wgpu-core", + "wgpu-hal", + "wgpu-types", ] [[package]] -name = "wayland-protocols-plasma" -version = "0.3.9" +name = "wgpu-core" +version = "27.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a07a14257c077ab3279987c4f8bb987851bf57081b93710381daea94f2c2c032" +checksum = "27a75de515543b1897b26119f93731b385a19aea165a1ec5f0e3acecc229cae7" dependencies = [ + "arrayvec", + "bit-set", + "bit-vec", "bitflags 2.10.0", - "wayland-backend", - "wayland-client", - "wayland-protocols", - "wayland-scanner", + "bytemuck", + "cfg_aliases", + "document-features", + "hashbrown 0.16.1", + "indexmap", + "log", + "naga", + "once_cell", + "parking_lot", + "portable-atomic", + "profiling", + "raw-window-handle", + "rustc-hash 1.1.0", + "smallvec", + "thiserror 2.0.17", + "wgpu-core-deps-apple", + "wgpu-core-deps-emscripten", + "wgpu-core-deps-windows-linux-android", + "wgpu-hal", + "wgpu-types", ] [[package]] -name = "wayland-protocols-wlr" -version = "0.3.9" +name = "wgpu-core-deps-apple" +version = "27.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd94963ed43cf9938a090ca4f7da58eb55325ec8200c3848963e98dc25b78ec" +checksum = "0772ae958e9be0c729561d5e3fd9a19679bcdfb945b8b1a1969d9bfe8056d233" dependencies = [ - "bitflags 2.10.0", - "wayland-backend", - "wayland-client", - "wayland-protocols", - "wayland-scanner", + "wgpu-hal", ] [[package]] -name = "wayland-scanner" -version = "0.31.7" +name = "wgpu-core-deps-emscripten" +version = "27.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54cb1e9dc49da91950bdfd8b848c49330536d9d1fb03d4bfec8cae50caa50ae3" +checksum = "b06ac3444a95b0813ecfd81ddb2774b66220b264b3e2031152a4a29fda4da6b5" dependencies = [ - "proc-macro2", - "quick-xml", - "quote", + "wgpu-hal", ] [[package]] -name = "wayland-sys" -version = "0.31.7" +name = "wgpu-core-deps-windows-linux-android" +version = "27.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34949b42822155826b41db8e5d0c1be3a2bd296c747577a43a3e6daefc296142" +checksum = "71197027d61a71748e4120f05a9242b2ad142e3c01f8c1b47707945a879a03c3" dependencies = [ - "dlib", - "log", - "once_cell", - "pkg-config", + "wgpu-hal", ] [[package]] -name = "web-sys" -version = "0.3.82" +name = "wgpu-hal" +version = "27.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" +checksum = "5b21cb61c57ee198bc4aff71aeadff4cbb80b927beb912506af9c780d64313ce" dependencies = [ + "android_system_properties", + "arrayvec", + "ash", + "bit-set", + "bitflags 2.10.0", + "block", + "bytemuck", + "cfg-if", + "cfg_aliases", + "core-graphics-types 0.2.0", + "glow", + "glutin_wgl_sys", + "gpu-alloc", + "gpu-allocator", + "gpu-descriptor", + "hashbrown 0.16.1", "js-sys", + "khronos-egl", + "libc", + "libloading", + "log", + "metal", + "naga", + "ndk-sys", + "objc", + "once_cell", + "ordered-float 5.1.0", + "parking_lot", + "portable-atomic", + "portable-atomic-util", + "profiling", + "range-alloc", + "raw-window-handle", + "renderdoc-sys", + "smallvec", + "thiserror 2.0.17", "wasm-bindgen", + "web-sys", + "wgpu-types", + "windows 0.58.0", + "windows-core 0.58.0", ] [[package]] -name = "web-time" -version = "1.1.0" +name = "wgpu-types" +version = "27.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +checksum = "afdcf84c395990db737f2dd91628706cb31e86d72e53482320d368e52b5da5eb" dependencies = [ + "bitflags 2.10.0", + "bytemuck", "js-sys", - "wasm-bindgen", + "log", + "thiserror 2.0.17", + "web-sys", ] [[package]] @@ -2180,6 +3148,28 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "window_clipboard" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5654226305eaf2dde8853fb482861d28e5dcecbbd40cb88e8393d94bb80d733" +dependencies = [ + "clipboard-win", + "clipboard_macos", + "raw-window-handle", + "thiserror 2.0.17", +] + +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core 0.58.0", + "windows-targets 0.52.6", +] + [[package]] name = "windows" version = "0.62.2" @@ -2187,7 +3177,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" dependencies = [ "windows-collections", - "windows-core", + "windows-core 0.62.2", "windows-future", "windows-numerics", ] @@ -2198,7 +3188,20 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" dependencies = [ - "windows-core", + "windows-core 0.62.2", +] + +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement 0.58.0", + "windows-interface 0.58.0", + "windows-result 0.2.0", + "windows-strings 0.1.0", + "windows-targets 0.52.6", ] [[package]] @@ -2207,11 +3210,11 @@ version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ - "windows-implement", - "windows-interface", + "windows-implement 0.60.2", + "windows-interface 0.59.3", "windows-link", - "windows-result", - "windows-strings", + "windows-result 0.4.1", + "windows-strings 0.5.1", ] [[package]] @@ -2220,11 +3223,22 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" dependencies = [ - "windows-core", + "windows-core 0.62.2", "windows-link", "windows-threading", ] +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "windows-implement" version = "0.60.2" @@ -2236,6 +3250,17 @@ dependencies = [ "syn", ] +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "windows-interface" version = "0.59.3" @@ -2259,10 +3284,19 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" dependencies = [ - "windows-core", + "windows-core 0.62.2", "windows-link", ] +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-result" version = "0.4.1" @@ -2272,6 +3306,16 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result 0.2.0", + "windows-targets 0.52.6", +] + [[package]] name = "windows-strings" version = "0.5.1" @@ -2308,6 +3352,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + [[package]] name = "windows-sys" version = "0.61.2" @@ -2341,13 +3394,30 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + [[package]] name = "windows-threading" version = "0.2.1" @@ -2369,6 +3439,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -2381,6 +3457,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -2393,12 +3475,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -2411,6 +3505,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -2423,6 +3523,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -2435,6 +3541,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -2448,17 +3560,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] -name = "winit" -version = "0.30.12" +name = "windows_x86_64_msvc" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c66d4b9ed69c4009f6321f762d6e61ad8a2389cd431b97cb1e146812e9e6c732" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "winit" +version = "0.30.8" +source = "git+https://github.com/iced-rs/winit.git?rev=05b8ff17a06562f0a10bb46e6eaacbe2a95cb5ed#05b8ff17a06562f0a10bb46e6eaacbe2a95cb5ed" dependencies = [ - "ahash", "android-activity", "atomic-waker", "bitflags 2.10.0", "block2", - "bytemuck", "calloop", "cfg_aliases", "concurrent-queue", @@ -2468,42 +3583,32 @@ dependencies = [ "dpi", "js-sys", "libc", - "memmap2", "ndk", - "objc2", + "objc2 0.5.2", "objc2-app-kit", - "objc2-foundation", + "objc2-foundation 0.2.2", "objc2-ui-kit", "orbclient", - "percent-encoding", "pin-project", "raw-window-handle", "redox_syscall 0.4.1", "rustix 0.38.44", - "sctk-adwaita", - "smithay-client-toolkit", "smol_str", "tracing", "unicode-segmentation", "wasm-bindgen", "wasm-bindgen-futures", - "wayland-backend", - "wayland-client", - "wayland-protocols", - "wayland-protocols-plasma", "web-sys", "web-time", "windows-sys 0.52.0", - "x11-dl", - "x11rb", "xkbcommon-dl", ] [[package]] name = "winnow" -version = "0.7.13" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" dependencies = [ "memchr", ] @@ -2513,23 +3618,18 @@ name = "winri" version = "0.2.1" dependencies = [ "anyhow", - "channel-protocol", "dirs", "easy-ext", + "iced", "itertools", "joy-error", "joy-vector", "keyboard-types", "log", "log4rs", - "oneshot", - "raw-window-handle", "rdev", - "softbuffer", - "tiny-skia", - "windows", - "windows-strings", - "winit", + "windows 0.62.2", + "windows-strings 0.5.1", ] [[package]] @@ -2548,44 +3648,6 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "x11-dl" -version = "2.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" -dependencies = [ - "libc", - "once_cell", - "pkg-config", -] - -[[package]] -name = "x11rb" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414" -dependencies = [ - "as-raw-xcb-connection", - "gethostname", - "libc", - "libloading", - "once_cell", - "rustix 1.1.2", - "x11rb-protocol", -] - -[[package]] -name = "x11rb-protocol" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" - -[[package]] -name = "xcursor" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b" - [[package]] name = "xkbcommon-dl" version = "0.4.2" @@ -2605,20 +3667,38 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" +[[package]] +name = "xml-rs" +version = "0.8.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" + +[[package]] +name = "yazi" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01738255b5a16e78bbb83e7fbba0a1e7dd506905cfc53f4622d89015a03fbb5" + +[[package]] +name = "zeno" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524" + [[package]] name = "zerocopy" -version = "0.8.27" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.27" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 7c1673f..ad3de66 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,17 +12,11 @@ log = "0" log4rs = "1.4.0" rdev = { version = "0", features = ["unstable_grab"] } easy-ext = "1" -winit = { version = "0" } -raw-window-handle = "0" -oneshot = { version = "0", default-features = false, features = ["std"] } -softbuffer = "0" -channel-protocol = "0.3" joy-vector = { git = "https://github.com/sub07/rust-utils", version = "0.2.1" } joy-error = { git = "https://github.com/sub07/rust-utils", version = "0.6.0", features = ["log-crate", "anyhow-crate"] } itertools = "0.14.0" keyboard-types = { version = "0.8.3", features = ["std", "serde"] } dirs = "6" -tiny-skia = { version = "0.11.4", features = ["simd", "std"], default-features = false } [dependencies.windows] version = "0.62" @@ -37,7 +31,16 @@ features = [ "Win32_Graphics_Gdi" ] -[package.metadata.cargo-machete] -ignored = [ - "oneshot" # Generated by channel-protocol proc macro so machete can't detect it +[dependencies.iced] +git = "https://github.com/sub07/iced.git" +branch = "custom-winri" +features = [ + "debug", + "time-travel", + "web-colors", + "wgpu", + "canvas", + "tokio", + "crisp" ] +default-features = false diff --git a/src/adapter/iced.rs b/src/adapter/iced.rs new file mode 100644 index 0000000..eaa88b1 --- /dev/null +++ b/src/adapter/iced.rs @@ -0,0 +1,31 @@ +use crate::utils; + +impl From for iced::Size { + fn from(value: utils::math::Size) -> Self { + Self { + width: value.width(), + height: value.height(), + } + } +} + +impl From for utils::math::Size { + fn from(value: iced::Size) -> Self { + Self([value.width, value.height]) + } +} + +impl From for iced::Point { + fn from(value: utils::math::Position) -> Self { + Self { + x: value.x(), + y: value.y(), + } + } +} + +impl From for utils::math::Position { + fn from(value: iced::Point) -> Self { + Self([value.x, value.y]) + } +} diff --git a/src/adapter/mod.rs b/src/adapter/mod.rs new file mode 100644 index 0000000..ebda348 --- /dev/null +++ b/src/adapter/mod.rs @@ -0,0 +1 @@ +pub mod iced; diff --git a/src/action.rs b/src/app/action.rs similarity index 83% rename from src/action.rs rename to src/app/action.rs index e30ef60..575f5bf 100644 --- a/src/action.rs +++ b/src/app/action.rs @@ -1,11 +1,11 @@ -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum Action { Tiler(TilerAction), Overview(OverviewAction), Exit, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum TilerAction { CloseCurrent, MoveFocusNext, @@ -19,7 +19,7 @@ pub enum TilerAction { OpenOverview, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum OverviewAction { CloseOverview, } diff --git a/src/app/mod.rs b/src/app/mod.rs new file mode 100644 index 0000000..0a8cec7 --- /dev/null +++ b/src/app/mod.rs @@ -0,0 +1,183 @@ +mod action; +pub mod model; +mod service; +mod subscription; +mod view; + +use anyhow::Context; +use iced::{Color, Task, theme::Palette, window::Settings}; + +use crate::{ + app::{ + service::{ + overview::{self}, + tiler::{self}, + }, + subscription::global::GlobalMessage, + }, + assert_log_fail, + scroll_tiler::ScrollTiler, + system, + utils::math::Size, + window::{self}, +}; + +pub struct State { + pub tiler: ScrollTiler, + pub mode: Mode, + pub configuration: model::Configuration, + overlay_window_id: iced::window::Id, +} + +pub enum Mode { + Tiler(tiler::State), + Overview(overview::State), + Exit, +} + +impl Default for Mode { + fn default() -> Self { + Self::Tiler(tiler::State::default()) + } +} + +#[derive(Debug, Clone)] +pub enum Message { + Action(action::Action), + + Overview(overview::Message), + + Global(subscription::global::GlobalMessage), + + CleanupAndExit, +} + +fn create_overlay_window(screen_size: Size) -> (iced::window::Id, Task) { + let (id, task) = iced::window::open(Settings { + decorations: false, + transparent: true, + resizable: false, + closeable: false, + level: iced::window::Level::AlwaysOnTop, + position: iced::window::Position::Specific(iced::Point::ORIGIN), + size: screen_size.into(), + ..Default::default() + }); + + (id, task.then(iced::window::enable_mouse_passthrough)) +} + +impl State { + pub fn new() -> (Self, Task) { + let screen_size = system::screen_size().expect("Screen size retrieval"); + let tiler = ScrollTiler::new(10.0, 20.0, screen_size); + let (overlay_window_id, overlay_window_creation_task) = create_overlay_window(screen_size); + ( + Self { + tiler, + mode: Mode::default(), + configuration: model::Configuration { + tiler_border_style: model::BorderStyle { + color: system::highlight_color().unwrap(), + thickness: 4.0, + radius: 8.0, + }, + }, + overlay_window_id, + }, + overlay_window_creation_task, + ) + } + + pub fn title(_: &Self, _window_id: iced::window::Id) -> String { + window::filter::WINRI_IGNORED_WINDOW_TITLE_SUBSTRING.into() + } + + pub fn handle_app_message(&mut self, message: Message) -> Task { + let mut task = Task::none(); + match message { + Message::Global(global_message) => { + task = task.chain(self.handle_global_message(global_message)); + } + Message::CleanupAndExit => { + system::restore_windows(); + return iced::exit(); + } + Message::Overview(message) => self + .handle_overview_message(message) + .handle_faillible_process() + .discard(), + Message::Action(action) => { + if let Ok(action_task) = self + .handle_action(action) + .context("action handling") + .handle_faillible_process() + { + task = task.chain(action_task); + } + } + } + if matches!(self.mode, Mode::Exit) { + task = task.chain(Task::done(Message::CleanupAndExit)); + } + task + } + + pub fn handle_global_message(&mut self, message: GlobalMessage) -> Task { + match message { + GlobalMessage::Key(modifiers, key) => { + if let Some(action) = self.resolve_action(modifiers, key) { + return Task::done(Message::Action(action)); + } + } + GlobalMessage::Window => { + self.update_tiler() + .context("global window event") + .handle_faillible_process() + .discard(); + } + } + Task::none() + } + + pub fn view(&self, window_id: iced::window::Id) -> iced::Element<'_, Message> { + if window_id == self.overlay_window_id { + view::overlay::view(self) + } else { + view::empty() + } + } + + pub fn theme(&self, window_id: iced::window::Id) -> iced::Theme { + if window_id == self.overlay_window_id { + iced::Theme::custom( + "Overlay transparent theme", + Palette { + background: Color::from_rgba(0.0, 0.0, 0.0, 0.0), + ..Palette::DARK + }, + ) + } else { + iced::Theme::Dark // TODO: Adapt to system theme + } + } + + pub fn subscription(_: &Self) -> iced::Subscription { + iced::Subscription::run(subscription::global::subscription) + } +} + +#[easy_ext::ext(HandleFaillibleProcessResultExt)] +impl Result { + fn handle_faillible_process(self) -> Self { + match &self { + Ok(_) => {} + Err(e) => { + assert_log_fail!("{:?}", e); + } + } + self + } + + fn discard(self) {} +} diff --git a/src/app/model/mod.rs b/src/app/model/mod.rs new file mode 100644 index 0000000..2353a10 --- /dev/null +++ b/src/app/model/mod.rs @@ -0,0 +1,10 @@ +#[derive(Clone, Copy)] +pub struct BorderStyle { + pub color: iced::Color, + pub thickness: f32, + pub radius: f32, +} + +pub struct Configuration { + pub tiler_border_style: BorderStyle, +} diff --git a/src/app/service/mod.rs b/src/app/service/mod.rs new file mode 100644 index 0000000..1cc966f --- /dev/null +++ b/src/app/service/mod.rs @@ -0,0 +1,114 @@ +use iced::Task; +use keyboard_types::Modifiers; +use rdev::Key; + +use crate::app::{ + self, Mode, + action::{Action, OverviewAction, TilerAction}, +}; + +pub mod overview; +pub mod tiler; + +impl app::State { + pub fn resolve_action(&self, modifiers: Modifiers, key: Key) -> Option { + match (&self.mode, modifiers, key) { + (Mode::Tiler { .. }, Modifiers::META, Key::LeftArrow) => { + Some(Action::Tiler(TilerAction::MoveFocusPrevious)) + } + (Mode::Tiler { .. }, Modifiers::META, Key::RightArrow) => { + Some(Action::Tiler(TilerAction::MoveFocusNext)) + } + (Mode::Tiler { .. }, _, Key::LeftArrow) + if modifiers == Modifiers::META.union(Modifiers::CONTROL) => + { + Some(Action::Tiler(TilerAction::SwapWithPrevious)) + } + (Mode::Tiler { .. }, _, Key::RightArrow) + if modifiers == Modifiers::META.union(Modifiers::CONTROL) => + { + Some(Action::Tiler(TilerAction::SwapWithNext)) + } + (Mode::Tiler { .. }, Modifiers::META, Key::KeyQ) => { + Some(Action::Tiler(TilerAction::CloseCurrent)) + } + (Mode::Tiler { .. }, Modifiers::META, Key::KeyF) => { + Some(Action::Tiler(TilerAction::ResizeToFullscreen)) + } + (Mode::Tiler { .. }, Modifiers::META, Key::KeyC) => { + Some(Action::Tiler(TilerAction::ResizeToHalfScreen)) + } + (Mode::Tiler { .. }, _, Key::LeftArrow) + if modifiers == Modifiers::META.union(Modifiers::SHIFT) => + { + Some(Action::Tiler(TilerAction::DecrementWidth)) + } + (Mode::Tiler { .. }, _, Key::RightArrow) + if modifiers == Modifiers::META.union(Modifiers::SHIFT) => + { + Some(Action::Tiler(TilerAction::IncrementWidth)) + } + (Mode::Tiler { .. }, Modifiers::META, Key::UpArrow) => { + Some(Action::Tiler(TilerAction::OpenOverview)) + } + (Mode::Overview { .. }, Modifiers::META, Key::DownArrow) + | (Mode::Overview { .. }, Modifiers::META, Key::Escape) => { + Some(Action::Overview(OverviewAction::CloseOverview)) + } + (_, Modifiers::META, Key::Escape) => Some(Action::Exit), + _ => None, + } + } + + pub fn handle_action(&mut self, action: Action) -> anyhow::Result> { + log::info!("Executing action: {action:?}"); + match action { + Action::Tiler(tiler_action) => match tiler_action { + TilerAction::CloseCurrent => { + if let Some(window) = self.tiler.focused_window() { + window.close()?; + self.update_tiler()?; + } + } + TilerAction::MoveFocusNext => { + self.tiler.focus_right(); + } + TilerAction::MoveFocusPrevious => { + self.tiler.focus_left(); + } + TilerAction::SwapWithNext => { + self.tiler.swap_current_right(); + self.update_tiler()?; + } + TilerAction::SwapWithPrevious => { + self.tiler.swap_current_left(); + self.update_tiler()?; + } + TilerAction::ResizeToFullscreen => { + self.tiler.set_current_window_fullscreen(); + self.update_tiler()?; + self.update_tiler()?; + } + TilerAction::ResizeToHalfScreen => { + self.tiler.set_current_window_halfscreen(); + self.update_tiler()?; + } + TilerAction::OpenOverview => return Ok(self.prepare_open_overview()), + TilerAction::IncrementWidth => { + self.tiler.increment_current_window_width(); + self.update_tiler()?; + } + TilerAction::DecrementWidth => { + self.tiler.decrement_current_window_width(); + self.update_tiler()?; + } + }, + Action::Overview(overview_action) => match overview_action { + OverviewAction::CloseOverview => return self.close_overview(), + }, + Action::Exit => self.mode = Mode::Exit, + } + + Ok(Task::none()) + } +} diff --git a/src/app/service/overview/mod.rs b/src/app/service/overview/mod.rs new file mode 100644 index 0000000..3a9bc7a --- /dev/null +++ b/src/app/service/overview/mod.rs @@ -0,0 +1,143 @@ +mod thumbnail; + +use anyhow::Context; +use iced::Task; +use itertools::Itertools; + +use crate::{ + app::{ + self, Mode, + service::overview::{self, thumbnail::ThumbnailId}, + }, + window::Window, +}; + +pub struct State { + opened_thumbnails: Vec<(ThumbnailId, iced::window::Id)>, +} + +#[derive(Debug, Clone)] +pub struct ThumbnailWindowCreated { + pub src: Window, + pub dest_id: iced::window::Id, + pub dest_raw_handle: u64, + pub size: crate::utils::math::Size, +} + +#[derive(Debug, Clone)] +pub enum Message { + ThumbnailWindowCreated(ThumbnailWindowCreated), +} + +impl app::State { + pub fn prepare_open_overview(&self) -> Task { + if matches!(self.mode, Mode::Overview(_)) { + log::warn!( + "Overview operation requested in {} while already in Overview mode", + crate::function!() + ); + return Task::none(); + } + + let windows = self.tiler.windows(); + + let windows_data = windows + .map(|item| thumbnail::WindowData { + inner: item.inner, + width: item.width, + }) + .collect_vec(); + + let thumbnails_data = thumbnail::compute_thumbnails_bounds_from_tiler_windows( + &windows_data, + self.tiler.screen_size(), + 10.0, + ); + + Task::batch(thumbnails_data.into_iter().zip(windows_data).map( + |(thumbnail_data, window)| { + thumbnail::thumbnail_window_creation_task( + window.inner, + thumbnail_data.pos, + thumbnail_data.size, + ) + .then(|overview_message| Task::done(app::Message::Overview(overview_message))) + }, + )) + } + + pub fn handle_overview_message(&mut self, message: overview::Message) -> anyhow::Result<()> { + match message { + overview::Message::ThumbnailWindowCreated(thumbnail_window_created) => { + self.finalize_open_overview(thumbnail_window_created)?; + } + } + + Ok(()) + } + + fn finalize_open_overview( + &mut self, + ThumbnailWindowCreated { + src, + dest_id, + dest_raw_handle, + size, + }: ThumbnailWindowCreated, + ) -> anyhow::Result<()> { + // Switch to overview mode if not already in it + if !matches!(self.mode, Mode::Overview(_)) { + log::info!("switching to Overview mode"); + self.mode = Mode::Overview(State { + opened_thumbnails: Vec::new(), + }); + } + + let Mode::Overview(State { + opened_thumbnails: thumbnails, + }) = &mut self.mode + else { + unreachable!("checked above that we are in Overview mode") + }; + + for tiled_window in self.tiler.windows() { + tiled_window.inner.move_offscreen()?; + } + + let dest_window = Window::from_safe_hwnd(dest_raw_handle) + .context(dest_raw_handle) + .context("invalid hwnd from thumbnail window")?; + + let thumbnail_id = + thumbnail::bind_thumbnail(src, dest_window, size).context("thumbnail binding")?; + + thumbnails.push((thumbnail_id, dest_id)); + + Ok(()) + } + + pub fn close_overview(&mut self) -> anyhow::Result> { + let Mode::Overview(State { + opened_thumbnails: thumbnails, + }) = &self.mode + else { + log::warn!( + "Close overview requested in {} while not in Overview mode", + crate::function!() + ); + return Ok(Task::none()); + }; + + let tasks = thumbnails + .iter() + .map(|(thumbnail_id, window_id)| { + thumbnail::unbind_thumbnail(*thumbnail_id).map(|()| window_id) + }) + .map(|window_id| window_id.map(|id| iced::window::close(*id))) + .collect::>>()?; + + self.switch_to_tiler_mode()?; + + Ok(Task::batch(tasks)) + } +} diff --git a/src/app/service/overview/thumbnail.rs b/src/app/service/overview/thumbnail.rs new file mode 100644 index 0000000..55ae9c7 --- /dev/null +++ b/src/app/service/overview/thumbnail.rs @@ -0,0 +1,145 @@ +pub type ThumbnailId = isize; + +use iced::{ + Task, + window::{Settings, settings::PlatformSpecific}, +}; +use log::debug; +use windows::Win32::{ + Foundation::RECT, + Graphics::Dwm::{ + DWM_THUMBNAIL_PROPERTIES, DWM_TNP_RECTDESTINATION, DWM_TNP_VISIBLE, DwmRegisterThumbnail, + DwmUnregisterThumbnail, DwmUpdateThumbnailProperties, + }, +}; + +use crate::{ + app::service::overview::{self, ThumbnailWindowCreated}, + utils::math::{Position, Size}, + wincall_result, + window::Window, +}; + +pub struct ThumbnailData { + pub pos: Position, + pub size: Size, +} + +pub struct WindowData { + pub inner: Window, + pub width: f32, +} + +pub fn compute_thumbnails_bounds_from_tiler_windows( + windows: &[WindowData], + screen_size: Size, + padding: f32, +) -> Vec { + // Width of packed windows + let total_tiler_width = windows.iter().map(|w| w.width + padding).sum::() - padding; + + let reduction_ratio = screen_size.width() / total_tiler_width; + + debug!("Thumbnail reduction ratio: {reduction_ratio}"); + + let reduction_ratio = if reduction_ratio > 1.0 { + reduction_ratio * 0.6 + } else if reduction_ratio < 1.0 { + reduction_ratio * 0.9 + } else { + reduction_ratio + }; + + let mut current_x = 0.0; + let mut thumbnails = Vec::new(); + + let thumbnail_height = reduction_ratio * screen_size.height(); + let thumbnail_y = (screen_size.height() - thumbnail_height).abs() / 2.0; + let thumbnail_x_center_offset = + (screen_size.width() - reduction_ratio * total_tiler_width).abs() / 2.0; + + for window in windows { + let width = reduction_ratio * window.width; + thumbnails.push(ThumbnailData { + pos: [current_x + thumbnail_x_center_offset, thumbnail_y].into(), + size: [width, thumbnail_height].into(), + }); + current_x += width + padding; + } + + thumbnails +} + +pub fn thumbnail_window_creation_task( + source_window: Window, + at: Position, + size: Size, +) -> Task { + let (_, window_creation) = iced::window::open(Settings { + decorations: false, + size: size.into(), + position: iced::window::Position::Specific(at.into()), + resizable: false, + visible: false, + platform_specific: PlatformSpecific { + skip_taskbar: true, + ..Default::default() + }, + ..Default::default() + }); + + window_creation + .then(|id| { + iced::window::raw_id::(id) + .then(move |raw_handle| Task::done((id, raw_handle))) + }) + .then(move |(id, raw_handle)| { + iced::window::enable_mouse_passthrough(id).chain(Task::done( + overview::Message::ThumbnailWindowCreated(ThumbnailWindowCreated { + src: source_window, + dest_id: id, + dest_raw_handle: raw_handle, + size, + }), + )) + }) +} + +pub fn bind_thumbnail(src: Window, dest: Window, size: Size) -> anyhow::Result { + dest.set_no_activate()?; + let thumbnail_id = wincall_result!(DwmRegisterThumbnail(dest.handle(), src.handle()))?; + + let thumbnail_props = DWM_THUMBNAIL_PROPERTIES { + dwFlags: DWM_TNP_RECTDESTINATION | DWM_TNP_VISIBLE, + rcDestination: RECT { + left: 0, + top: 0, + right: size.width() as i32, + bottom: size.height() as i32, + }, + rcSource: RECT { + left: 0, + top: 0, + right: 0, + bottom: 0, + }, + opacity: 0, + fVisible: true.into(), + fSourceClientAreaOnly: true.into(), + }; + + wincall_result!(DwmUpdateThumbnailProperties( + thumbnail_id, + &raw const thumbnail_props + ))?; + + dest.show()?; + dest.set_max_zindex()?; + + Ok(thumbnail_id) +} + +pub fn unbind_thumbnail(thumbnail_id: ThumbnailId) -> anyhow::Result<()> { + wincall_result!(DwmUnregisterThumbnail(thumbnail_id))?; + Ok(()) +} diff --git a/src/app/service/tiler.rs b/src/app/service/tiler.rs new file mode 100644 index 0000000..34a9b6a --- /dev/null +++ b/src/app/service/tiler.rs @@ -0,0 +1,105 @@ +use std::collections::HashSet; + +use anyhow::Context; + +use crate::{ + app::{self, Mode}, + utils::math::Bounds, + window::{Window, filter::opened_windows}, +}; + +#[derive(Default)] +pub struct State { + pub current_border_bounds: Option, +} + +macro_rules! bind_tiler_mode_result { + ($mode:expr => TilerState { $($bindings:tt),+ }) => { + let Mode::Tiler(State { $($bindings),+ ,.. }) = &mut $mode else { + log::warn!( + "Tiler operation requested in {} while not in Tiler mode", + crate::function!() + ); + return Ok(()); + }; + }; +} + +macro_rules! ensure_tiler_mode_result { + ($mode:expr) => { + match &$mode { + Mode::Tiler(_) => {} + _ => { + log::warn!( + "Tiler operation requested in {} while not in Tiler mode", + crate::function!() + ); + return Ok(()); + } + } + }; +} + +fn get_process_names(windows: &HashSet) -> Vec { + windows + .iter() + .map(|w| { + let is_focused = w.is_focused().unwrap_or(false); + format!( + "{}{}[class: {}][hwnd: {:?}][title: {}]", + if is_focused { "[FOCUSED] " } else { "" }, + w.process_name() + .ok() + .unwrap_or_else(|| "[ERROR] Could not get process name".to_string()), + w.class() + .unwrap_or_else(|_| "[ERROR] Could not get class name".to_string()), + w.handle(), + w.title() + .unwrap_or_else(|_| Some("[ERROR] Could not get window title".to_string())) + .unwrap_or_else(|| "[UNNAMED]".to_string()), + ) + }) + .collect::>() +} + +impl app::State { + pub fn update_tiler(&mut self) -> anyhow::Result<()> { + ensure_tiler_mode_result!(self.mode); + + let windows_snapshot = opened_windows().context("Window enumeration for tiler update")?; + + log::info!("Opened windows: {:?}", get_process_names(&windows_snapshot)); + + self.tiler.handle_window_snapshot(&windows_snapshot); + + self.update_tiler_border()?; + + Ok(()) + } + + pub fn update_tiler_border(&mut self) -> anyhow::Result<()> { + bind_tiler_mode_result!(self.mode => TilerState { current_border_bounds }); + if let Some(focused_window) = self.tiler.focused_window() { + let bounds = focused_window + .desktop_manager_bounds() + .context("Desktop manager bounds querying for tiler border update")?; + if current_border_bounds != &Some(bounds) { + *current_border_bounds = Some(bounds); + } + } else { + *current_border_bounds = None; + } + + Ok(()) + } + + pub fn switch_to_tiler_mode(&mut self) -> anyhow::Result<()> { + if !matches!(self.mode, Mode::Tiler(_)) { + log::info!("switching to Tiler mode"); + self.mode = Mode::Tiler(State::default()); + self.update_tiler() + .context("initial tiler update on mode switch")?; + } + Ok(()) + } +} diff --git a/src/app/subscription/global/input.rs b/src/app/subscription/global/input.rs new file mode 100644 index 0000000..8506b2a --- /dev/null +++ b/src/app/subscription/global/input.rs @@ -0,0 +1,86 @@ +use std::thread; + +use iced::futures::channel::mpsc::Sender; +use keyboard_types::Modifiers; + +use crate::app::subscription::global::GlobalMessage; + +fn grab_event_processing( + event: rdev::Event, + modifiers: &mut Modifiers, + mut tx: Sender, +) -> Option { + use rdev::{EventType, Key}; + + if matches!( + event.event_type, + EventType::MouseMove { .. } + | EventType::Wheel { .. } + | EventType::ButtonPress(_) + | EventType::ButtonRelease(_) + ) { + return Some(event); + } + + match event.event_type { + rdev::EventType::KeyPress(key) => { + match key { + Key::ShiftLeft | Key::ShiftRight => { + modifiers.set(Modifiers::SHIFT, true); + return Some(event); + } + Key::ControlLeft | Key::ControlRight => { + modifiers.set(Modifiers::CONTROL, true); + return Some(event); + } + Key::Alt => { + modifiers.set(Modifiers::ALT, true); + return Some(event); + } + // Got an unknown key code for the right Meta key for some reason + Key::MetaLeft | Key::MetaRight | Key::Unknown(92) => { + modifiers.set(Modifiers::META, true); + // Win key presses are swallowed to avoid opening the Start Menu, and triggering system shortcuts + // Winri is supposed to be a sort of "command center" for the system, so the native system shortcuts should not be needed + // I know this might be controversial, but it's the intended behavior for now, and I'm open to feedback on this matter + return None; + } + _ => { + tx.try_send(GlobalMessage::Key(*modifiers, key)).unwrap(); + return (!modifiers.contains(Modifiers::META)).then_some(event); + } + } + } + rdev::EventType::KeyRelease(key) => { + match key { + Key::ShiftLeft | Key::ShiftRight => { + modifiers.set(Modifiers::SHIFT, false); + } + Key::ControlLeft | Key::ControlRight => { + modifiers.set(Modifiers::CONTROL, false); + } + Key::Alt => { + modifiers.set(Modifiers::ALT, false); + } + // Got an unknown key code for the right Meta key for some reason + Key::MetaLeft | Key::MetaRight | Key::Unknown(92) => { + modifiers.set(Modifiers::META, false); + return None; + } + _ => {} + } + } + _ => {} + } + Some(event) +} + +pub fn launch(tx: Sender) { + let _ = thread::Builder::new() + .name("global-key-hook".into()) + .spawn(move || { + let mut modifiers = Modifiers::default(); // TODO: Check initial state of modifiers + rdev::_grab(move |event| grab_event_processing(event, &mut modifiers, tx.clone())) + .unwrap(); + }); +} diff --git a/src/app/subscription/global/mod.rs b/src/app/subscription/global/mod.rs new file mode 100644 index 0000000..7f63d0f --- /dev/null +++ b/src/app/subscription/global/mod.rs @@ -0,0 +1,32 @@ +mod input; +mod window; + +use iced::{ + futures::{SinkExt, Stream, StreamExt, channel::mpsc::channel}, + stream, +}; +use keyboard_types::Modifiers; + +use crate::app::{Message, subscription::STREAM_CHANNEL_BUFFER_SIZE}; + +#[derive(Debug, Clone)] +pub enum GlobalMessage { + Key(Modifiers, rdev::Key), + Window, +} + +pub fn subscription() -> impl Stream { + stream::channel(STREAM_CHANNEL_BUFFER_SIZE, async |mut output| { + let (intermediate_message_tx, mut intermediate_message_rx) = channel(100); + + let global_input_tx = intermediate_message_tx.clone(); + let window_event_tx = intermediate_message_tx; + + input::launch(global_input_tx); + window::launch(window_event_tx); + + while let Some(event) = intermediate_message_rx.next().await { + output.send(Message::Global(event)).await.unwrap(); + } + }) +} diff --git a/src/hook/window.rs b/src/app/subscription/global/window.rs similarity index 52% rename from src/hook/window.rs rename to src/app/subscription/global/window.rs index 35ecc0f..251e992 100644 --- a/src/hook/window.rs +++ b/src/app/subscription/global/window.rs @@ -1,36 +1,32 @@ use std::{ ptr::null_mut, - sync::{ - Mutex, - mpsc::{Receiver, Sender}, - }, + sync::Mutex, thread, time::{Duration, Instant}, }; -use anyhow::ensure; -use windows::Win32::{ - Foundation::HWND, - UI::{ - Accessibility::{HWINEVENTHOOK, SetWinEventHook, UnhookWinEvent}, - WindowsAndMessaging::{ - EVENT_OBJECT_CREATE, EVENT_OBJECT_FOCUS, GetMessageA, WINEVENT_OUTOFCONTEXT, - WINEVENT_SKIPOWNPROCESS, - }, +use iced::futures::channel::mpsc::Sender; +use windows::Win32::UI::{ + Accessibility::{SetWinEventHook, UnhookWinEvent}, + WindowsAndMessaging::{ + EVENT_OBJECT_CREATE, EVENT_OBJECT_FOCUS, GetMessageW, WINEVENT_OUTOFCONTEXT, + WINEVENT_SKIPOWNPROCESS, }, }; +use crate::app::subscription::global::GlobalMessage; + const WINDOW_HOOK_COOLDOWN: Duration = Duration::from_millis(200); -struct WindowHookContext { - notifier: Sender<()>, +struct WindowHookManager { + tx: Sender, last_time_notified: Instant, } -impl WindowHookContext { - fn new(notifier: Sender<()>) -> Self { +impl WindowHookManager { + fn new(notifier: Sender) -> Self { Self { - notifier, + tx: notifier, last_time_notified: Instant::now(), } } @@ -38,10 +34,11 @@ impl WindowHookContext { fn tick(&mut self) { let elapsed = self.last_time_notified.elapsed(); if elapsed > WINDOW_HOOK_COOLDOWN { - self.notifier.send(()).unwrap(); + self.tx.try_send(GlobalMessage::Window).unwrap(); self.last_time_notified = Instant::now(); } else { let original_last_time_notified = self.last_time_notified; + // TODO: Needs serious rework to avoid spawning threads like this. Use async timers instead. thread::Builder::new() .name("window-hook-cooldown-timer".to_string()) .spawn(move || { @@ -50,7 +47,7 @@ impl WindowHookContext { reason = "Underflow is prevented by condition above" )] thread::sleep(WINDOW_HOOK_COOLDOWN - elapsed); - if let Some(context) = WINDOW_HOOK_CHANNEL.lock().unwrap().as_mut() + if let Some(context) = WINDOW_HOOK_MANAGER.lock().unwrap().as_mut() && context.last_time_notified == original_last_time_notified { context.tick(); @@ -61,31 +58,32 @@ impl WindowHookContext { } } -static WINDOW_HOOK_CHANNEL: Mutex> = Mutex::new(None); +static WINDOW_HOOK_MANAGER: Mutex> = Mutex::new(None); unsafe extern "system" fn hook_callback( - _hwineventhook: HWINEVENTHOOK, + _hwineventhook: windows::Win32::UI::Accessibility::HWINEVENTHOOK, _event: u32, - _hwnd: HWND, + _hwnd: windows::Win32::Foundation::HWND, _idobject: i32, _idchild: i32, _ideventthread: u32, _dwmseventtime: u32, ) { - if let Some(context) = WINDOW_HOOK_CHANNEL.lock().unwrap().as_mut() { - context.tick(); + // TODO: try async here with a block on + if let Some(manager) = WINDOW_HOOK_MANAGER.lock().unwrap().as_mut() { + manager.tick(); } } -pub fn launch_hook() -> anyhow::Result> { - let mut window_hook_context = WINDOW_HOOK_CHANNEL.lock().unwrap(); - ensure!(window_hook_context.is_none(), "Hook already launched"); - let (sender, receiver) = std::sync::mpsc::channel(); - *window_hook_context = Some(WindowHookContext::new(sender)); - drop(window_hook_context); - thread::Builder::new() +pub fn launch(tx: Sender) { + { + let mut window_hook_context = WINDOW_HOOK_MANAGER.lock().unwrap(); + debug_assert!(window_hook_context.is_none(), "Hook already launched"); + *window_hook_context = Some(WindowHookManager::new(tx)); + } + if let Err(e) = thread::Builder::new() .name("win-event-hook-loop".to_string()) - .spawn(|| unsafe { + .spawn(move || unsafe { let hook = SetWinEventHook( EVENT_OBJECT_CREATE, EVENT_OBJECT_FOCUS, @@ -95,11 +93,12 @@ pub fn launch_hook() -> anyhow::Result> { 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS, ); - if !GetMessageA(null_mut(), None, 0, 0).as_bool() { + if !GetMessageW(null_mut(), None, 0, 0).as_bool() { let _ = UnhookWinEvent(hook); - WINDOW_HOOK_CHANNEL.lock().unwrap().take(); + WINDOW_HOOK_MANAGER.lock().unwrap().take(); } }) - .unwrap(); - Ok(receiver) + { + log::error!("Failed to launch window hook thread: {e:?}"); + } } diff --git a/src/app/subscription/mod.rs b/src/app/subscription/mod.rs new file mode 100644 index 0000000..7503066 --- /dev/null +++ b/src/app/subscription/mod.rs @@ -0,0 +1,3 @@ +pub mod global; + +pub const STREAM_CHANNEL_BUFFER_SIZE: usize = 100; diff --git a/src/app/view/mod.rs b/src/app/view/mod.rs new file mode 100644 index 0000000..270fb5f --- /dev/null +++ b/src/app/view/mod.rs @@ -0,0 +1,7 @@ +use crate::app; + +pub mod overlay; + +pub fn empty<'a>() -> iced::Element<'a, app::Message> { + iced::widget::Row::new().into() +} diff --git a/src/app/view/overlay.rs b/src/app/view/overlay.rs new file mode 100644 index 0000000..65dcee3 --- /dev/null +++ b/src/app/view/overlay.rs @@ -0,0 +1,68 @@ +use iced::{ + Renderer, Theme, mouse, + widget::{ + self, + canvas::{self, Path, Stroke}, + }, +}; + +use crate::{ + app::{self, model::BorderStyle, service::tiler::State, view}, + utils::math::Bounds, +}; + +pub fn view(app: &app::State) -> iced::Element<'_, app::Message> { + match &app.mode { + app::Mode::Tiler(tiler_state) => tiler_view(app, tiler_state), + _ => view::empty(), + } +} + +fn tiler_view<'a>(app: &'a app::State, tiler_state: &'a State) -> iced::Element<'a, app::Message> { + if let Some(border_bounds) = tiler_state.current_border_bounds { + widget::canvas(TilerBorder { + border_bounds, + border_style: app.configuration.tiler_border_style, + }) + .width(iced::Length::Fill) + .height(iced::Length::Fill) + .into() + } else { + view::empty() + } +} + +struct TilerBorder { + border_bounds: Bounds, + border_style: BorderStyle, +} + +impl canvas::Program for TilerBorder { + type State = (); + + fn draw( + &self, + _state: &Self::State, + renderer: &Renderer, + _theme: &Theme, + bounds: iced::Rectangle, + _cursor: mouse::Cursor, + ) -> Vec> { + let mut frame = canvas::Frame::new(renderer, bounds.size()); + + let path = Path::rounded_rectangle( + self.border_bounds.position().into(), + self.border_bounds.size().into(), + self.border_style.radius.into(), + ); + + frame.stroke( + &path, + Stroke::default() + .with_color(self.border_style.color) + .with_width(self.border_style.thickness), + ); + + vec![frame.into_geometry()] + } +} diff --git a/src/hook/key.rs b/src/hook/key.rs deleted file mode 100644 index 50dc4be..0000000 --- a/src/hook/key.rs +++ /dev/null @@ -1,82 +0,0 @@ -use std::sync::mpsc::Receiver; - -use keyboard_types::Modifiers; -use rdev::{EventType, Key}; - -#[derive(Debug)] -pub struct Event(pub Modifiers, pub Key); - -pub fn launch_hook() -> Receiver { - let (sender, receiver) = std::sync::mpsc::channel(); - std::thread::Builder::new() - .name("global-key-hook".into()) - .spawn(move || { - let mut modifiers = Modifiers::default(); - rdev::_grab(move |event| { - if matches!( - event.event_type, - EventType::MouseMove { .. } - | EventType::Wheel { .. } - | EventType::ButtonPress(_) - | EventType::ButtonRelease(_) - ) { - return Some(event); - } - - match event.event_type { - rdev::EventType::KeyPress(key) => { - match key { - Key::ShiftLeft | Key::ShiftRight => { - modifiers.set(Modifiers::SHIFT, true); - return Some(event); - } - Key::ControlLeft | Key::ControlRight => { - modifiers.set(Modifiers::CONTROL, true); - return Some(event); - } - Key::Alt => { - modifiers.set(Modifiers::ALT, true); - return Some(event); - } - // Got an unknown key code for the right Meta key for some reason - Key::MetaLeft | Key::MetaRight | Key::Unknown(92) => { - modifiers.set(Modifiers::META, true); - // Win key presses are swallowed to avoid opening the Start Menu, and triggering system shortcuts - // Winri is supposed to be a sort of "command center" for the system, so the native system shortcuts should not be needed - // I know this might be controversial, but it's the intended behavior for now, and I'm open to feedback on this matter - return None; - } - _ => { - sender.send(Event(modifiers, key)).unwrap(); - return (!modifiers.contains(Modifiers::META)).then_some(event); - } - } - } - rdev::EventType::KeyRelease(key) => { - match key { - Key::ShiftLeft | Key::ShiftRight => { - modifiers.set(Modifiers::SHIFT, false); - } - Key::ControlLeft | Key::ControlRight => { - modifiers.set(Modifiers::CONTROL, false); - } - Key::Alt => { - modifiers.set(Modifiers::ALT, false); - } - // Got an unknown key code for the right Meta key for some reason - Key::MetaLeft | Key::MetaRight | Key::Unknown(92) => { - modifiers.set(Modifiers::META, false); - return None; - } - _ => {} - } - } - _ => {} - } - Some(event) - }) - .unwrap(); - }) - .unwrap(); - receiver -} diff --git a/src/hook/mod.rs b/src/hook/mod.rs deleted file mode 100644 index 8c18a45..0000000 --- a/src/hook/mod.rs +++ /dev/null @@ -1,35 +0,0 @@ -use std::sync::mpsc::Sender; - -use crate::Event; - -pub mod key; -// pub mod thumbnail; -pub mod window; - -pub fn launch_hooks(event_tx: Sender) -> anyhow::Result<()> { - let window_event_receiver = window::launch_hook()?; - let key_event_receiver = key::launch_hook(); - - let window_event_tx = event_tx.clone(); - let key_event_tx = event_tx; - - std::thread::Builder::new() - .name("window-event-forwarder".into()) - .spawn(move || { - for () in window_event_receiver { - window_event_tx.send(Event::Window).unwrap(); - } - }) - .unwrap(); - - std::thread::Builder::new() - .name("key-event-forwarder".into()) - .spawn(move || { - for key_event in key_event_receiver { - key_event_tx.send(Event::Key(key_event)).unwrap(); - } - }) - .unwrap(); - - Ok(()) -} diff --git a/src/logger.rs b/src/logger.rs index 7bfbde8..6b52b04 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -12,18 +12,27 @@ use log4rs::{ }, }, }, - config::{Appender, Root}, + config::{Appender, Logger, Root}, encode::pattern::PatternEncoder, }; -use crate::{root_dir, utils::IS_DEBUG}; +const DISABLED_MODULES: &[&str] = &[ + "wgpu_core", + "wgpu_hal", + "naga", + "iced_wgpu", + "cosmic_text", + "iced_beacon", +]; + +use crate::{DEBUG_MODE, root_dir}; fn log_dir() -> anyhow::Result { Ok(root_dir()?.join("logs")) } pub fn setup() -> anyhow::Result<()> { - const LEVEL_FILTER: log::LevelFilter = if IS_DEBUG { + const LEVEL_FILTER: log::LevelFilter = if DEBUG_MODE { log::LevelFilter::Debug } else { log::LevelFilter::Info @@ -59,9 +68,14 @@ pub fn setup() -> anyhow::Result<()> { ), ); + let disabled_loggers = DISABLED_MODULES + .iter() + .map(|&module| Logger::builder().build(module, log::LevelFilter::Off)); + log4rs::init_config( Config::builder() .appenders([console_appender, file_appender]) + .loggers(disabled_loggers) .build( Root::builder() .appenders(["console", "file"]) diff --git a/src/main.rs b/src/main.rs index a9a4393..eebbf9d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,383 +1,51 @@ -#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] - -mod action; -mod hook; +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release builds +#![warn(clippy::pedantic, clippy::nursery, clippy::dbg_macro)] +#![allow( + clippy::missing_errors_doc, + clippy::cast_possible_truncation, + clippy::missing_const_for_fn, + clippy::needless_pass_by_value, + clippy::option_if_let_else, + clippy::default_trait_access +)] + +use std::{panic, path::PathBuf}; + +use anyhow::anyhow; + +mod adapter; +mod app; mod logger; +mod scroll_tiler; mod system; -mod thumbnail; -mod tiler; mod utils; +mod winapi; mod window; -use std::{ - collections::{HashMap, HashSet}, - panic, - path::PathBuf, - sync::mpsc::Sender, -}; - -use anyhow::{anyhow, bail}; -use itertools::Itertools; -use keyboard_types::Modifiers; -use log::{error, info, warn}; -use rdev::Key; - -use crate::{ - action::{Action, TilerAction}, - hook::{ - key::{self}, - launch_hooks, - }, - system::{restore_windows, screen_size}, - tiler::ScrollTiler, - utils::{Bounds, IS_DEBUG}, - window::{ - Window, - filter::opened_windows, - manager::{BorderStyle, HandleOutputProtocol, ThumbnailId}, - }, -}; +pub const DEBUG_MODE: bool = cfg!(debug_assertions); pub fn root_dir() -> anyhow::Result { - const PROJECT_DIR_NAME: &str = if IS_DEBUG { "winri-dev" } else { "winri" }; + const PROJECT_DIR_NAME: &str = if DEBUG_MODE { "winri-dev" } else { "winri" }; Ok(dirs::config_dir() .ok_or_else(|| anyhow!("Could not determine config directory"))? .join(PROJECT_DIR_NAME)) } -pub enum Event { - Key(key::Event), - WindowManager(window::manager::OutputProtocolMessage), - Window, -} - -enum Mode { - Tiler { - current_border_target: Option<(Window, Bounds)>, - }, - Overview { - thumbnails: HashMap, - }, - ExitingSuccessfully, - ExitingWithError(anyhow::Error), -} - -pub struct Winri { - mode: Mode, - window_manager_client: window::manager::InputProtocolClient, - tiler: ScrollTiler, - event_rx: std::sync::mpsc::Receiver, - event_tx: Sender, -} - -fn get_process_names(windows: &HashSet) -> Vec { - windows - .iter() - .map(|w| { - let is_focused = w.is_focused().unwrap_or(false); - format!( - "{}{}[class: {}][hwnd: {:?}][title: {}]", - if is_focused { "[FOCUSED] " } else { "" }, - w.process_name() - .ok() - .unwrap_or_else(|| "[ERROR] Could not get process name".to_string()), - w.class() - .unwrap_or_else(|_| "[ERROR] Could not get class name".to_string()), - w.handle(), - w.title() - .unwrap_or_else(|_| Some("[ERROR] Could not get window title".to_string())) - .unwrap_or_else(|| "[UNNAMED]".to_string()), - ) - }) - .collect::>() -} - -impl window::manager::HandleOutputProtocol for Winri { - fn cursor_entered_thumbnail(&mut self, id: ThumbnailId) { - if let Err(e) = self.window_manager_client.border_thumbnail(id) { - self.mode = Mode::ExitingWithError(e); - } - } - - fn cursor_exited_thumbnail(&mut self, _id: ThumbnailId) { - if let Err(e) = self.window_manager_client.unborder_all_thumbnails() { - self.mode = Mode::ExitingWithError(e); - } - } - - fn thumbnail_clicked(&mut self, id: ThumbnailId) { - let Mode::Overview { thumbnails } = std::mem::replace( - &mut self.mode, - Mode::Tiler { - current_border_target: None, - }, - ) else { - return; - }; - let Some(window) = thumbnails.get(&id) else { - return; - }; - self.window_manager_client.close_all_thumbnails().unwrap(); - window.focus().unwrap(); - self.update_tiler().unwrap(); - } - - fn unrecoverable_error(&mut self, err: anyhow::Error) { - self.mode = Mode::ExitingWithError(err); - } -} - -impl Winri { - fn update_tiler(&mut self) -> anyhow::Result<()> { - let windows_snapshot = opened_windows()?; - if windows_snapshot.is_empty() { - self.reset_tiler_border()?; - } - - let Mode::Tiler { - current_border_target, - } = &mut self.mode - else { - warn!("Tiler update requested while not in Tiler mode; ignoring."); - return Ok(()); - }; - - info!( - "Opened windows: {:#?}", - get_process_names(&windows_snapshot) - ); - - self.tiler.handle_window_snapshot(&windows_snapshot); - - if let Some(focused_window) = self.tiler.current_window() - && windows_snapshot.contains(&focused_window) - { - let bounds = focused_window.desktop_manager_bounds()?; - let window_cache_info = (focused_window, bounds); - if current_border_target != &Some(window_cache_info) { - info!("Bordering focused window"); - self.window_manager_client - .border_tiler_window(focused_window)?; - *current_border_target = Some(window_cache_info); - } - } else { - self.reset_tiler_border()?; - } - Ok(()) - } - - fn reset_tiler_border(&mut self) -> anyhow::Result<()> { - let Mode::Tiler { - current_border_target, - } = &mut self.mode - else { - warn!("Tiler border reset requested while not in Tiler mode; ignoring."); - return Ok(()); - }; - - if current_border_target.is_some() { - info!("Unbordering tiler window"); - self.window_manager_client.unborder_tiler_window()?; - *current_border_target = None; - } - - Ok(()) - } - - fn open_overview(&mut self) -> anyhow::Result<()> { - if matches!(self.mode, Mode::Overview { .. }) { - return Ok(()); - } - - let windows = self.tiler.windows(); - - let windows_data = windows - .map(|item| thumbnail::WindowData { - inner: item.inner, - width: item.width, - }) - .collect_vec(); - - let thumbnails = thumbnail::create_thumbnails_from_tiler_windows( - &windows_data, - self.tiler.screen_size(), - 10, +fn main() { + if let Err(e) = logger::setup() { + winapi::message_box( + "Log initialization error", + &format!("Could not initialize log system. No log will be written.\n\n{e}"), ); - - for window in &windows_data { - window.inner.move_offscreen()?; - } - - let thumbnails = thumbnails - .into_iter() - .zip(windows_data) - .map(|(thumbnail, window)| { - self.window_manager_client - .create_thumbnail(window.inner, thumbnail.pos, thumbnail.size) - .map(|id| (id, window.inner)) - }) - .collect::>>()?; - - self.mode = Mode::Overview { thumbnails }; - - Ok(()) - } - - fn handle_event(&mut self, event: Event) -> anyhow::Result<()> { - match event { - Event::Key(key_event) => { - if let Some(action) = self.resolve_action(key_event) { - info!("Executing action: {action:?}"); - match action { - Action::Tiler(tiler_action) => match tiler_action { - TilerAction::CloseCurrent => { - if let Some(window) = self.tiler.current_window() { - window.close()?; - self.update_tiler()?; - } - } - TilerAction::MoveFocusNext => { - self.tiler.focus_right(); - } - TilerAction::MoveFocusPrevious => { - self.tiler.focus_left(); - } - TilerAction::SwapWithNext => { - self.tiler.swap_current_right(); - self.update_tiler()?; - } - TilerAction::SwapWithPrevious => { - self.tiler.swap_current_left(); - self.update_tiler()?; - } - TilerAction::ResizeToFullscreen => { - self.tiler.set_current_window_fullscreen(); - self.update_tiler()?; - self.update_tiler()?; - } - TilerAction::ResizeToHalfScreen => { - self.tiler.set_current_window_halfscreen(); - self.update_tiler()?; - } - TilerAction::OpenOverview => { - self.reset_tiler_border()?; - self.open_overview()?; - } - TilerAction::IncrementWidth => { - self.tiler.increment_current_window_width(); - self.update_tiler()?; - } - TilerAction::DecrementWidth => { - self.tiler.decrement_current_window_width(); - self.update_tiler()?; - } - }, - Action::Overview(overview_action) => match overview_action { - action::OverviewAction::CloseOverview => { - if matches!(self.mode, Mode::Tiler { .. }) { - return Ok(()); - } - self.window_manager_client.close_all_thumbnails()?; - self.mode = Mode::Tiler { - current_border_target: None, - }; - self.event_tx.send(Event::Window)?; - } - }, - Action::Exit => self.mode = Mode::ExitingSuccessfully, - } - } - } - Event::Window => { - if matches!(self.mode, Mode::Tiler { .. }) { - self.update_tiler()?; - } - } - Event::WindowManager(msg) => self.dispatch(msg), - } - Ok(()) - } - - fn resolve_action(&self, key::Event(modifiers, key): key::Event) -> Option { - match (&self.mode, modifiers, key) { - (Mode::Tiler { .. }, Modifiers::META, Key::LeftArrow) => { - Some(Action::Tiler(TilerAction::MoveFocusPrevious)) - } - (Mode::Tiler { .. }, Modifiers::META, Key::RightArrow) => { - Some(Action::Tiler(TilerAction::MoveFocusNext)) - } - (Mode::Tiler { .. }, _, Key::LeftArrow) - if modifiers == Modifiers::META.union(Modifiers::CONTROL) => - { - Some(Action::Tiler(TilerAction::SwapWithPrevious)) - } - (Mode::Tiler { .. }, _, Key::RightArrow) - if modifiers == Modifiers::META.union(Modifiers::CONTROL) => - { - Some(Action::Tiler(TilerAction::SwapWithNext)) - } - (Mode::Tiler { .. }, Modifiers::META, Key::KeyQ) => { - Some(Action::Tiler(TilerAction::CloseCurrent)) - } - (Mode::Tiler { .. }, Modifiers::META, Key::KeyF) => { - Some(Action::Tiler(TilerAction::ResizeToFullscreen)) - } - (Mode::Tiler { .. }, Modifiers::META, Key::KeyC) => { - Some(Action::Tiler(TilerAction::ResizeToHalfScreen)) - } - (Mode::Tiler { .. }, _, Key::LeftArrow) - if modifiers == Modifiers::META.union(Modifiers::SHIFT) => - { - Some(Action::Tiler(TilerAction::DecrementWidth)) - } - (Mode::Tiler { .. }, _, Key::RightArrow) - if modifiers == Modifiers::META.union(Modifiers::SHIFT) => - { - Some(Action::Tiler(TilerAction::IncrementWidth)) - } - (Mode::Tiler { .. }, Modifiers::META, Key::UpArrow) => { - Some(Action::Tiler(TilerAction::OpenOverview)) - } - (Mode::Overview { .. }, Modifiers::META, Key::DownArrow) - | (Mode::Overview { .. }, Modifiers::META, Key::Escape) => { - Some(Action::Overview(action::OverviewAction::CloseOverview)) - } - (_, Modifiers::META, Key::Escape) => Some(Action::Exit), - _ => None, - } } - - fn run(mut self) -> anyhow::Result<()> { - self.update_tiler()?; - - while let Ok(event) = self.event_rx.recv() { - self.handle_event(event)?; - - match self.mode { - Mode::ExitingSuccessfully => { - info!("Exiting successfully."); - break; - } - Mode::ExitingWithError(ref err) => { - bail!("Exiting due to unrecoverable error: {err:#}"); - } - _ => {} - } - } - - Ok(()) - } -} - -pub fn launch_winri() -> anyhow::Result<()> { - logger::setup()?; + log::info!("Winri starting up"); let default_hook = panic::take_hook(); panic::set_hook(Box::new(move |info| { - error!("Winri panicked: {info}"); + log::error!("Winri panicked: {info}"); system::restore_windows(); if let Some(error_cause) = info.payload_as_str() { - utils::winapi::message_box( + winapi::message_box( "Fatal error", &format!("{error_cause}.\nThe application will now exit."), ); @@ -385,52 +53,18 @@ pub fn launch_winri() -> anyhow::Result<()> { default_hook(info); })); - let screen_size = screen_size()?; - let (event_tx, event_rx) = std::sync::mpsc::channel(); - - launch_hooks(event_tx.clone())?; - - let system_highlight_color = system::highlight_color()?; - - let window_manager_client = window::manager::launch( - event_tx.clone(), - BorderStyle { - color: system_highlight_color, - thickness: 4, - radius: 6, - }, - BorderStyle { - color: system_highlight_color, - thickness: 4, - radius: 12, - }, - )?; - - let app = Winri { - mode: Mode::Tiler { - current_border_target: None, - }, - window_manager_client, - tiler: ScrollTiler::new(10, 20, screen_size), - event_rx, - event_tx, - }; - - if let Err(e) = app.run() { - error!("Fatal error: {e:?}"); - restore_windows(); - utils::winapi::message_box( - "Fatal error", - &format!("{e:#}.\nThe application will now exit."), - ); + if let Err(e) = iced::daemon( + app::State::new, + app::State::handle_app_message, + app::State::view, + ) + .subscription(app::State::subscription) + .title(app::State::title) + .theme(app::State::theme) + .run() + { + log::error!("Winri exited with error: {e}"); } - restore_windows(); - Ok(()) -} - -fn main() -> anyhow::Result<()> { - launch_winri()?; - - Ok(()) + log::info!("Winri exited successfully"); } diff --git a/src/tiler.rs b/src/scroll_tiler.rs similarity index 72% rename from src/tiler.rs rename to src/scroll_tiler.rs index 8182ace..5cecc6d 100644 --- a/src/tiler.rs +++ b/src/scroll_tiler.rs @@ -4,20 +4,16 @@ use anyhow::Context; use joy_error::log::ResultLogExt; use log::{debug, info, warn}; -use crate::{ - cast, - utils::{Size, cast::FaillibleCastUtils}, - window::Window, -}; +use crate::{cast, utils::math::Size, window::Window}; -#[derive(PartialEq, Eq)] +#[derive(PartialEq)] pub struct WindowItem { pub inner: Window, - pub width: u32, + pub width: f32, } impl WindowItem { - pub const fn new(inner: Window, width: u32) -> Self { + pub const fn new(inner: Window, width: f32) -> Self { Self { inner, width } } } @@ -25,15 +21,15 @@ impl WindowItem { #[derive(Default)] pub struct ScrollTiler { windows: Vec, - padding: u32, - resize_increment: u32, - scroll_offset: i32, + padding: f32, + resize_increment: f32, + scroll_offset: f32, screen_size: Size, previously_focused_window_index: Option, } impl ScrollTiler { - pub fn new(padding: u32, resize_increment: u32, screen_size: Size) -> Self { + pub fn new(padding: f32, resize_increment: f32, screen_size: Size) -> Self { Self { padding, resize_increment, @@ -89,16 +85,21 @@ impl ScrollTiler { self.swap_current(1); } + #[allow( + clippy::cast_sign_loss, + reason = "return value is guaranteed to be positive by the clamp call" + )] + fn compute_index_for_direction(&self, focus_index: usize, direction: i32) -> usize { + cast! { + focus_index => i32, + self.windows.len() => i32 as windows_len, + } + (focus_index + direction).clamp(0, windows_len - 1) as usize + } + fn swap_current(&mut self, direction: i32) { if let Some(focus_index) = self.logged_focus_index() { - #[allow( - clippy::cast_possible_truncation, - clippy::cast_sign_loss, - clippy::cast_possible_wrap, - reason = "to add a potential negative number to a usize" - )] - let other_swap_index = - (focus_index as i32 + direction).clamp(0, self.windows.len() as i32 - 1) as usize; + let other_swap_index = self.compute_index_for_direction(focus_index, direction); self.windows.swap(focus_index, other_swap_index); } } @@ -113,14 +114,7 @@ impl ScrollTiler { fn focus(&self, direction: i32) { if let Some(focus_index) = self.focus_index_with_fallback_and_log() { - #[allow( - clippy::cast_possible_truncation, - clippy::cast_sign_loss, - clippy::cast_possible_wrap, - reason = "to add a potential negative number to a usize" - )] - let new_focus_index = - (focus_index as i32 + direction).clamp(0, self.windows.len() as i32 - 1) as usize; + let new_focus_index = self.compute_index_for_direction(focus_index, direction); let window = self.windows[new_focus_index].inner; let _ = window @@ -139,14 +133,14 @@ impl ScrollTiler { // TODO: find a better solution for this, the problem is that the scroll system // doesn't handle windows that are equals or bigger than the screen size well. // Fix for now: prevent windows width from being equal or bigger than screen size. - self.windows[focus_index].width = screen_width - self.padding * 2 - 1; + self.windows[focus_index].width = self.padding.mul_add(-2.0, screen_width) - 1.0; } } pub fn set_current_window_halfscreen(&mut self) { if let Some(focus_index) = self.focus_index() { let screen_width = self.screen_size.width(); - self.windows[focus_index].width = (screen_width / 2) - self.padding * 2; + self.windows[focus_index].width = self.padding.mul_add(-2.0, screen_width / 2.0); } } @@ -162,21 +156,22 @@ impl ScrollTiler { /// Direction should be 1 for increasing width and -1 for decreasing width. fn resize_current_window_width_by_resize_increment(&mut self, direction: i32) { if let Some(focus_index) = self.focus_index() { + // TODO: check explanation in `set_current_window_fullscreen` about -1 cast! { - self.windows[focus_index].width => i32 as current_width, - self.resize_increment => i32 as resize_increment, - self.screen_size.width() => i32 as screen_width, - self.padding => i32 as padding, + direction.signum() => f32 as direction, } - // TODO: check explanation in `set_current_window_fullscreen` about -1 - let new_width = (current_width + resize_increment * direction.signum()) - .max(0) - .min(screen_width - padding * 2 - 1); - self.windows[focus_index].width = new_width.cast(); + let new_width = self + .resize_increment + .mul_add(direction, self.windows[focus_index].width) + .clamp( + 0.0, + self.padding.mul_add(-2.0, self.screen_size().width()) - 1.0, + ); + self.windows[focus_index].width = new_width; } } - pub fn current_window(&self) -> Option { + pub fn focused_window(&self) -> Option { self.focus_index().map(|index| self.windows[index].inner) } @@ -195,7 +190,7 @@ impl ScrollTiler { let previous_scroll_offset = self.scroll_offset; self.ajust_scroll(&windows_positions); - if previous_scroll_offset != self.scroll_offset { + if (previous_scroll_offset - self.scroll_offset).abs() > 1.0 { debug!( "Adjusted scroll offset from {} to {}", previous_scroll_offset, self.scroll_offset @@ -240,7 +235,6 @@ impl ScrollTiler { .iter() .any(|window_item| window_item.inner == *window) { - log::info!("Appending at end"); self.windows .push(WindowItem::new(*window, self.default_size())); } @@ -248,14 +242,14 @@ impl ScrollTiler { } } - fn default_size(&self) -> u32 { - self.screen_size.width() / 2 - self.padding * 2 + fn default_size(&self) -> f32 { + self.padding.mul_add(-2.0, self.screen_size.width() / 2.0) } - fn layout_windows(&mut self, windows_positions: &[i32]) { + fn layout_windows(&mut self, windows_positions: &[f32]) { for (window, x) in self.windows.iter_mut().zip(windows_positions) { - let y = self.padding.cast(); - let height = self.screen_size.height() - self.padding * 2; + let y = self.padding; + let height = self.padding.mul_add(-2.0, self.screen_size.height()); if let Err(e) = window.inner.move_to( [x - self.scroll_offset, y].into(), [window.width, height].into(), @@ -281,39 +275,32 @@ impl ScrollTiler { let actual_size = window_rect.right - window_rect.left; - cast! { - actual_size => u32, - } - let expected_size = window.width; if expected_size < actual_size { - window.width = actual_size + self.padding * 2; + window.width = self.padding.mul_add(2.0, actual_size); } } } - fn ajust_scroll(&mut self, windows_positions: &[i32]) { + fn ajust_scroll(&mut self, windows_positions: &[f32]) { if let Some((index, focused_window)) = self .windows .iter() .enumerate() .find(|(_, window_item)| window_item.inner.is_focused().unwrap_or(false)) { - cast! { - self.padding => i32 as padding, - focused_window.width => i32 as focused_window_width, - self.screen_size.width() => i32 as screen_width, - } - - let focused_window_left = windows_positions[index] - padding - self.scroll_offset; - let focused_window_right = focused_window_left + focused_window_width + padding * 2; + let focused_window_left = windows_positions[index] - self.padding - self.scroll_offset; + let focused_window_right = self + .padding + .mul_add(2.0, focused_window_left + focused_window.width); - if focused_window_left >= 0 && focused_window_right <= screen_width { + if focused_window_left >= 0.0 && focused_window_right <= self.screen_size.width() { return; } let window_left_to_screen_left = focused_window_left.abs(); - let window_right_to_screen_right = focused_window_right.sub(screen_width).abs(); + let window_right_to_screen_right = + focused_window_right.sub(self.screen_size.width()).abs(); if window_left_to_screen_left < window_right_to_screen_right { self.scroll_offset -= window_left_to_screen_left; @@ -323,22 +310,14 @@ impl ScrollTiler { } } - pub fn windows_positions(&self) -> Vec { + pub fn windows_positions(&self) -> Vec { let mut positions = Vec::new(); - let mut current_position = 0; - - cast! { - self.padding => i32 as padding, - } + let mut current_position = 0.0; for window in &self.windows { - cast! { - window.width => i32 as window_width, - } - - current_position += padding; + current_position += self.padding; positions.push(current_position); - current_position += window_width + padding; + current_position += window.width + self.padding; } positions diff --git a/src/system.rs b/src/system.rs index a5357f5..0d2331e 100644 --- a/src/system.rs +++ b/src/system.rs @@ -5,23 +5,30 @@ use windows::Win32::{ }; use crate::{ - utils::{Position, Size, cast::FaillibleCastUtils, color::Color}, + utils::math::{Position, Size}, wincall_into_result, - window::{Window, filter::is_managed_window}, + window::{self, Window}, }; pub fn screen_size() -> anyhow::Result { - Ok([ - wincall_into_result!(GetSystemMetrics(SM_CXSCREEN).try_cast()?)?, - wincall_into_result!(GetSystemMetrics(SM_CYSCREEN).try_cast()?)?, - ] - .into()) + #[allow( + clippy::cast_precision_loss, + reason = "The values will stay within screen size orders of magnitude" + )] + Ok(Size([ + wincall_into_result!(GetSystemMetrics(SM_CXSCREEN))? as f32, + wincall_into_result!(GetSystemMetrics(SM_CYSCREEN))? as f32, + ])) } -pub fn highlight_color() -> anyhow::Result { - wincall_into_result!(GetSysColor(COLOR_HIGHLIGHT)) - .map(Color::from_abgr_packed) - .map(Color::without_alpha) +pub fn highlight_color() -> anyhow::Result { + // argb + let packed = wincall_into_result!(GetSysColor(COLOR_HIGHLIGHT))?; + + let r = (packed & 0x0000_00FF) as u8; + let g = ((packed & 0x0000_FF00) >> 8) as u8; + let b = ((packed & 0x00FF_0000) >> 16) as u8; + Ok(iced::Color::from_rgb8(r, g, b)) } pub fn restore_windows() { @@ -30,13 +37,13 @@ pub fn restore_windows() { vec![] }); - windows.retain(|w| is_managed_window(*w).unwrap_or(false)); + windows.retain(|w| window::filter::should_be_tiled(*w).unwrap_or(false)); - let mut pos = Position([100, 100]); + let mut pos = Position([100.0, 100.0]); for window in windows { - if let Err(err) = window.move_to(pos, [800, 600].into()) { + if let Err(err) = window.move_to(pos, [800.0, 600.0].into()) { warn!("Failed to move window {window:?}: {err}"); } - pos += 100; + pos += 100.0; } } diff --git a/src/thumbnail.rs b/src/thumbnail.rs deleted file mode 100644 index bab127f..0000000 --- a/src/thumbnail.rs +++ /dev/null @@ -1,69 +0,0 @@ -use log::debug; - -use crate::{ - cast, f, - utils::{ - Position, Size, - frac::{self, f}, - }, - window::Window, -}; - -pub struct ThumbnailData { - pub pos: Position, - pub size: Size, -} - -pub struct WindowData { - pub inner: Window, - pub width: u32, -} - -pub fn create_thumbnails_from_tiler_windows( - windows: &[WindowData], - screen_size: Size, - padding: u32, -) -> Vec { - // Width of packed windows - let total_tiler_width = windows.iter().map(|w| w.width + padding).sum::() - padding; - - let reduction_ratio = f(screen_size.width(), total_tiler_width); - - debug!("Thumbnail reduction ratio: {reduction_ratio}"); - - let reduction_ratio = match reduction_ratio.category() { - frac::Category::Upside => reduction_ratio * f!(6 / 10), - frac::Category::Downside => reduction_ratio * f!(9 / 10), - frac::Category::Equal => reduction_ratio, - }; - - let mut current_x = 0; - let mut thumbnails = Vec::new(); - - let thumbnail_height = reduction_ratio * screen_size.height(); - let thumbnail_y = screen_size.height().abs_diff(thumbnail_height) / 2; - let thumbnail_x_center_offset = screen_size - .width() - .abs_diff(reduction_ratio * total_tiler_width) - / 2; - - cast! { - thumbnail_y => i32, - thumbnail_x_center_offset => i32, - padding => i32, - } - - for window in windows { - let width = reduction_ratio * window.width; - thumbnails.push(ThumbnailData { - pos: [current_x + thumbnail_x_center_offset, thumbnail_y].into(), - size: [width, thumbnail_height].into(), - }); - cast! { - width => i32, - } - current_x += width + padding; - } - - thumbnails -} diff --git a/src/utils/cast.rs b/src/utils/cast.rs index 60fc3c2..74559bb 100644 --- a/src/utils/cast.rs +++ b/src/utils/cast.rs @@ -1,70 +1,16 @@ -use std::fmt::Display; - -#[easy_ext::ext(FaillibleCastUtils)] -pub impl T -where - T: TryInto + Display + Clone + Copy, -{ - fn cast(self) -> R { - self.try_cast().expect("Cast failed") - } - - fn try_cast(self) -> anyhow::Result { - self.try_into().map_err(|_| { - anyhow::anyhow!( - "Cast from {} with value {self} to {} failed", - std::any::type_name::(), - std::any::type_name::() - ) - }) - } -} - -#[easy_ext::ext(InfaillibleCastUtils)] -pub impl T -where - T: Into + Display + Clone + Copy, -{ - fn infaillible_cast(self) -> R { - self.into() - } -} - #[macro_export] macro_rules! cast { ($src:expr => $t:ty as $dest:ident, $($rem:tt)*) => { - let $dest: $t = $crate::utils::cast::FaillibleCastUtils::cast($src); + #[allow(clippy::cast_possible_wrap)] + #[allow(clippy::cast_precision_loss)] + let $dest: $t = $src as $t; cast!($($rem)*); }; ($i:ident => $t:ty, $($rem:tt)*) => { - let $i: $t = $crate::utils::cast::FaillibleCastUtils::cast($i); + #[allow(clippy::cast_possible_wrap)] + #[allow(clippy::cast_precision_loss)] + let $i: $t = $i as $t; cast!($($rem)*); }; () => {}; } - -#[macro_export] -macro_rules! try_cast { - ($src:expr => $t:ty as $dest:ident, $($rem:tt)*) => { - let $dest: $t = $crate::utils::cast::FaillibleCastUtils::try_cast($src)?; - try_cast!($($rem)*); - }; - ($i:ident => $t:ty, $($rem:tt)*) => { - let $i: $t = $crate::utils::cast::FaillibleCastUtils::try_cast($i)?; - try_cast!($($rem)*); - }; - () => {}; -} - -#[macro_export] -macro_rules! infaillible_cast { - ($src:expr => $t:ty as $dest:ident, $($rem:tt)*) => { - let $dest: $t = $crate::utils::cast::InfaillibleCastUtils::infaillible_cast($src); - infaillible_cast!($($rem)*); - }; - ($i:ident => $t:ty, $($rem:tt)*) => { - let $i: $t = $crate::utils::cast::InfaillibleCastUtils::infaillible_cast($i); - infaillible_cast!($($rem)*); - }; - () => {}; -} diff --git a/src/utils/color.rs b/src/utils/color.rs deleted file mode 100644 index f7208a2..0000000 --- a/src/utils/color.rs +++ /dev/null @@ -1,27 +0,0 @@ -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct Color { - pub r: u8, - pub g: u8, - pub b: u8, - pub a: u8, -} - -impl Color { - pub const fn from_abgr_packed(abgr: u32) -> Self { - Self { - a: ((abgr >> 24) & 0xFF) as u8, - b: ((abgr >> 16) & 0xFF) as u8, - g: ((abgr >> 8) & 0xFF) as u8, - r: (abgr & 0xFF) as u8, - } - } - - pub const fn without_alpha(self) -> Self { - Self { - r: self.r, - g: self.g, - b: self.b, - a: 255, - } - } -} diff --git a/src/utils/frac.rs b/src/utils/frac.rs deleted file mode 100644 index a49fde6..0000000 --- a/src/utils/frac.rs +++ /dev/null @@ -1,128 +0,0 @@ -use std::fmt::Display; - -use crate::infaillible_cast; - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct Frac { - pub numerator: u32, - pub denominator: u32, -} - -impl Display for Frac { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.as_f32()) - } -} - -pub enum Category { - /// val > 1 - Upside, - /// val < 1 - Downside, - /// val == 1 - Equal, -} - -pub const fn f(numerator: u32, denominator: u32) -> Frac { - Frac::new(numerator, denominator) -} - -impl Frac { - pub const fn new(numerator: u32, denominator: u32) -> Self { - Self { - numerator, - denominator, - } - } - - #[allow(clippy::cast_precision_loss)] - pub fn as_f32(self) -> f32 { - self.numerator as f32 / self.denominator as f32 - } - - pub fn category(self) -> Category { - match self.numerator.cmp(&self.denominator) { - std::cmp::Ordering::Less => Category::Downside, - std::cmp::Ordering::Equal => Category::Equal, - std::cmp::Ordering::Greater => Category::Upside, - } - } - - pub const fn is_unit(self) -> bool { - self.numerator == self.denominator - } -} - -impl std::ops::Mul for Frac { - type Output = u32; - - fn mul(self, rhs: u32) -> Self::Output { - if self.is_unit() { - return rhs; - } - infaillible_cast! { - rhs => u64, - (self.numerator) => u64 as numerator, - (self.denominator) => u64 as denominator, - } - (rhs * numerator / denominator) - .try_into() - .expect("Multiplication overflowed u32") - } -} - -impl std::ops::Mul for u32 { - type Output = Self; - - fn mul(self, rhs: Frac) -> Self::Output { - rhs * self - } -} - -impl std::ops::Mul for Frac { - type Output = Self; - - fn mul(self, rhs: Self) -> Self::Output { - if self.is_unit() { - return rhs; - } - if rhs.is_unit() { - return self; - } - Self::new( - self.numerator * rhs.numerator, - self.denominator * rhs.denominator, - ) - } -} - -#[macro_export] -macro_rules! f { - ($n:literal / $d:literal) => { - $crate::utils::frac::Frac::new($n, $d) - }; -} - -#[cfg(test)] -mod test { - #[test] - fn test_frac() { - let f = f!(1920 / 1080); - assert_eq!(f.numerator, 1920); - assert_eq!(f.denominator, 1080); - assert_eq!(f * 1080, 1920); - - let f = f!(1280 / 720); - assert_eq!(f.numerator, 1280); - assert_eq!(f.denominator, 720); - - let f = f!(1024 / 768); - assert_eq!(f.numerator, 1024); - assert_eq!(f.denominator, 768); - - let f = f!(500 / 1000); - assert_eq!(f.numerator, 500); - assert_eq!(f.denominator, 1000); - assert_eq!(20, f * 41); - } -} diff --git a/src/utils/math.rs b/src/utils/math.rs new file mode 100644 index 0000000..070915e --- /dev/null +++ b/src/utils/math.rs @@ -0,0 +1,22 @@ +use joy_vector::gen_vector; + +gen_vector!(Position with two_dim); +gen_vector!(Size with two_dim); + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Bounds { + pub left: f32, + pub top: f32, + pub right: f32, + pub bottom: f32, +} + +impl Bounds { + pub fn position(&self) -> Position { + Position([self.left, self.top]) + } + + pub fn size(&self) -> Size { + Size([self.right - self.left, self.bottom - self.top]) + } +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 7f43a70..60aebbd 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,13 +1,5 @@ -use joy_vector::gen_vector; - -use crate::utils::cast::FaillibleCastUtils; - pub mod cast; -pub mod color; -pub mod frac; -pub mod winapi; - -pub const IS_DEBUG: bool = cfg!(debug_assertions); +pub mod math; #[macro_export] macro_rules! function { @@ -21,27 +13,50 @@ macro_rules! function { }}; } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct Bounds { - pub left: i32, - pub top: i32, - pub right: i32, - pub bottom: i32, -} +// Code from near_o11y crate: https://github.com/near/nearcore and then adapted for my needs +pub mod invariants { + /// + /// If assert fails, panic on debug, and log error on release + /// + #[macro_export] + macro_rules! assert_log { + ($cond:expr) => { + $crate::assert_log!($cond, "assertion failed: {}", stringify!($cond)) + }; + + ($cond:expr, $fmt:literal $($arg:tt)*) => { + if cfg!(debug_assertions) { + assert!($cond, $fmt $($arg)*); + } else { + if !$cond { + log::error!($fmt $($arg)*); + } + } + }; + } -impl Bounds { - pub fn position(&self) -> Position { - [self.left, self.top].into() + #[macro_export] + macro_rules! assert_log_bail { + ($cond:expr) => { + $crate::assert_log_bail!($cond, "assertion failed: {}", stringify!($cond)) + }; + + ($cond:expr, $fmt:literal $($arg:tt)*) => { + if cfg!(debug_assertions) { + assert!($cond, $fmt $($arg)*); + } else { + if !$cond { + log::error!($fmt $($arg)*); + return; + } + } + }; } - pub fn size(&self) -> Size { - [ - (self.right - self.left).cast(), - (self.bottom - self.top).cast(), - ] - .into() + #[macro_export] + macro_rules! assert_log_fail { + ($fmt:literal $($arg:tt)*) => { + $crate::assert_log!(false, $fmt $($arg)*) + }; } } - -gen_vector!(Position with two_dim); -gen_vector!(Size with two_dim); diff --git a/src/utils/winapi.rs b/src/winapi.rs similarity index 89% rename from src/utils/winapi.rs rename to src/winapi.rs index 6a6a092..b371b92 100644 --- a/src/utils/winapi.rs +++ b/src/winapi.rs @@ -42,7 +42,7 @@ macro_rules! wincall { reason = "This macro should always call a winapi function and thus is always unsafe. The caller should know that a unsafe block is automatically applied" )] unsafe { - $crate::utils::winapi::clear_last_error(); + $crate::winapi::clear_last_error(); $fn } } @@ -54,7 +54,7 @@ macro_rules! wincall_result { ($fn:expr) => { anyhow::Context::context( anyhow::Context::context($crate::wincall!($fn), $crate::function!()), - $crate::utils::winapi::last_error().unwrap_or(anyhow::anyhow!("Unknown error")), + $crate::winapi::last_error().unwrap_or(anyhow::anyhow!("Unknown error")), ) }; } @@ -63,7 +63,7 @@ macro_rules! wincall_result { macro_rules! wincall_into_result { ($fn:expr) => {{ let res = $crate::wincall!($fn); - $crate::utils::winapi::last_error().map_or_else( + $crate::winapi::last_error().map_or_else( || Ok(res), |err| anyhow::Context::context(Err(err), $crate::function!()), ) diff --git a/src/window/filter.rs b/src/window/filter.rs index 199d4d4..a9de396 100644 --- a/src/window/filter.rs +++ b/src/window/filter.rs @@ -1,6 +1,9 @@ use std::collections::HashSet; -use crate::window::{Window, manager::utils::WINRI_WINDOW_MANAGER_CLASS_NAME}; +use crate::window::Window; + +pub const WINRI_IGNORED_CLASS_NAME: &str = "Winri_IgnoreWindowClass"; +pub const WINRI_IGNORED_WINDOW_TITLE_SUBSTRING: &str = "[Winri Ignore Window]"; const IGNORED_CLASSES: &[&str] = &[ "Progman", @@ -9,7 +12,7 @@ const IGNORED_CLASSES: &[&str] = &[ "Xaml_WindowedPopupClass", "Shell_TrayWnd", "FindMyMouse", - WINRI_WINDOW_MANAGER_CLASS_NAME, + WINRI_IGNORED_CLASS_NAME, ]; const IGNORED_PROCESS_NAMES: &[&str] = &[ @@ -29,12 +32,14 @@ macro_rules! filter_out_if { }; } -pub fn is_managed_window(window: Window) -> anyhow::Result { +pub fn should_be_tiled(window: Window) -> anyhow::Result { filter_out_if!(!window.is_visible()?); filter_out_if!(window.is_cloaked()?); filter_out_if!(!window.is_ancestor()?); filter_out_if!(window.is_dialog()?); - filter_out_if!(window.title()?.is_none()); + let title = window.title()?; + filter_out_if!(title.is_none()); + filter_out_if!(title.is_some_and(|title| title.contains(WINRI_IGNORED_WINDOW_TITLE_SUBSTRING))); filter_out_if!(IGNORED_CLASSES.contains(&window.class()?.as_str())); filter_out_if!(IGNORED_PROCESS_NAMES.contains(&window.process_name()?.as_str())); filter_out_if!(!window.is_valid()?); @@ -45,7 +50,7 @@ pub fn is_managed_window(window: Window) -> anyhow::Result { pub fn opened_windows() -> anyhow::Result> { let windows = Window::enumerate()? .into_iter() - .filter(|window| is_managed_window(*window).unwrap_or(false)) + .filter(|window| should_be_tiled(*window).unwrap_or(false)) .collect::>(); Ok(windows) diff --git a/src/window/manager.rs b/src/window/manager.rs deleted file mode 100644 index 3076f62..0000000 --- a/src/window/manager.rs +++ /dev/null @@ -1,632 +0,0 @@ -use std::{collections::HashMap, num::NonZero, sync::mpsc::Sender, thread}; - -use anyhow::{Context, anyhow}; -use channel_protocol::channel_protocol; -use log::{debug, info}; -use windows::Win32::{ - Foundation::RECT, - Graphics::Dwm::{ - DWM_THUMBNAIL_PROPERTIES, DWM_TNP_RECTDESTINATION, DWM_TNP_VISIBLE, DwmRegisterThumbnail, - DwmUnregisterThumbnail, DwmUpdateThumbnailProperties, - }, - UI::WindowsAndMessaging::{ - GWL_EXSTYLE, SW_HIDE, SWP_SHOWWINDOW, SetWindowLongPtrW, SetWindowPos, ShowWindow, - WS_EX_NOACTIVATE, - }, -}; -use winit::{ - application::ApplicationHandler, - dpi::{PhysicalPosition, PhysicalSize}, - event::WindowEvent, - event_loop::{ActiveEventLoop, EventLoop, OwnedDisplayHandle}, - platform::windows::EventLoopBuilderExtWindows, - window::WindowId, -}; - -use crate::{ - Event, try_cast, - utils::{Position, Size, cast::FaillibleCastUtils, color::Color}, - wincall_into_result, wincall_result, - window::{ - Window, - manager::utils::{WindowUtils, create_new_border_window}, - }, -}; - -#[derive(Debug)] -pub struct BorderStyle { - pub color: Color, - pub thickness: u8, - pub radius: u8, -} - -pub type ThumbnailId = isize; - -#[channel_protocol] -pub trait InputProtocol { - fn create_thumbnail(src: Window, at: Position, size: Size) -> anyhow::Result; - fn close_all_thumbnails() -> anyhow::Result<()>; - fn border_thumbnail(id: ThumbnailId) -> anyhow::Result<()>; - fn unborder_all_thumbnails() -> anyhow::Result<()>; - fn border_tiler_window(window: Window) -> anyhow::Result<()>; - fn unborder_tiler_window() -> anyhow::Result<()>; -} - -#[channel_protocol] -pub trait OutputProtocol { - fn cursor_entered_thumbnail(id: ThumbnailId); - fn cursor_exited_thumbnail(id: ThumbnailId); - fn thumbnail_clicked(id: ThumbnailId); - - fn unrecoverable_error(err: anyhow::Error); -} - -struct Thumbnail { - id: isize, - window: winit::window::Window, -} - -struct Border(winit::window::Window); - -impl Border { - pub const fn window(&self) -> &winit::window::Window { - &self.0 - } -} - -pub struct App { - thumbnails: HashMap, - thumbnail_border_style: BorderStyle, - thumbnail_border: Option, - - tiler_border_style: BorderStyle, - tiler_border: Option, - - context: softbuffer::Context, - output_client: OutputProtocolClient, -} - -impl HandleInputProtocolWithState<&ActiveEventLoop> for App { - fn create_thumbnail( - &mut self, - src: Window, - at: Position, - size: Size, - event_loop: &ActiveEventLoop, - ) -> anyhow::Result { - let window = utils::create_window( - event_loop, - winit::window::WindowAttributes::default() - .with_title("thumbnail") - .with_active(false) - .with_position(PhysicalPosition::new(at.x(), at.y())) - .with_inner_size(PhysicalSize::new(size.width(), size.height())) - .with_visible(false) - .with_decorations(false), - )?; - - let thumbnail_hwnd = window.hwnd()?; - - wincall_into_result!(SetWindowLongPtrW( - thumbnail_hwnd, - GWL_EXSTYLE, - WS_EX_NOACTIVATE.0.try_cast()? - ))?; - - let thumbnail_id = wincall_result!(DwmRegisterThumbnail(thumbnail_hwnd, src.handle()))?; - - let thumbnail_props = DWM_THUMBNAIL_PROPERTIES { - dwFlags: DWM_TNP_RECTDESTINATION | DWM_TNP_VISIBLE, - rcDestination: RECT { - left: 0, - top: 0, - right: size.width().try_cast()?, - bottom: size.height().try_cast()?, - }, - rcSource: RECT { - left: 0, - top: 0, - right: 0, - bottom: 0, - }, - opacity: 0, - fVisible: true.into(), - fSourceClientAreaOnly: true.into(), - }; - - wincall_result!(DwmUpdateThumbnailProperties( - thumbnail_id, - &raw const thumbnail_props - ))?; - - { - let win = window.to_crate_window()?; - win.show()?; - win.set_max_zindex()?; - } - window.set_visible(true); - - self.thumbnails.insert( - thumbnail_id, - Thumbnail { - window, - id: thumbnail_id, - }, - ); - - Ok(thumbnail_id) - } - - fn close_all_thumbnails(&mut self, event_loop: &ActiveEventLoop) -> anyhow::Result<()> { - self.unborder_all_thumbnails(event_loop)?; - - for Thumbnail { id, .. } in self.thumbnails.values() { - wincall_result!(DwmUnregisterThumbnail(*id))?; - } - - self.thumbnails.clear(); - Ok(()) - } - - fn border_thumbnail(&mut self, id: ThumbnailId, _: &ActiveEventLoop) -> anyhow::Result<()> { - let thumbnail = self.thumbnails.get(&id).context(id)?; - let border = self.get_initialized_thumbnail_border()?; - - self.border_window( - thumbnail.window.to_crate_window()?, - border, - &self.thumbnail_border_style, - )?; - - Ok(()) - } - - fn unborder_all_thumbnails(&mut self, _: &ActiveEventLoop) -> anyhow::Result<()> { - let window = self.get_initialized_thumbnail_border()?.window(); - - window.set_visible(false); // winit set_visible doesn't work. So we use ShowWindow directly. But to maintain state consistency inside winit we also call set_visible. - let _ = wincall_into_result!(ShowWindow(window.hwnd()?, SW_HIDE))?; - - Ok(()) - } - - fn border_tiler_window( - &mut self, - window: Window, - _state: &ActiveEventLoop, - ) -> anyhow::Result<()> { - let border_window = self.get_initialized_tiler_border()?; - - self.border_window(window, border_window, &self.tiler_border_style)?; - - Ok(()) - } - - fn unborder_tiler_window(&mut self, _state: &ActiveEventLoop) -> anyhow::Result<()> { - let border_window = self.get_initialized_tiler_border()?.window(); - border_window.set_visible(false); - let _ = wincall_into_result!(ShowWindow(border_window.hwnd()?, SW_HIDE))?; - Ok(()) - } -} - -impl App { - fn find_thumbnail_by_window_id(&self, window_id: WindowId) -> Option<&Thumbnail> { - self.thumbnails - .iter() - .find(|(_, thumbnail)| thumbnail.window.id() == window_id) - .map(|(_, thumbnail)| thumbnail) - } - - fn get_initialized_thumbnail_border(&self) -> anyhow::Result<&Border> { - let border_window = self - .thumbnail_border - .as_ref() - .context("Uninitialized thumbnail border")?; - Ok(border_window) - } - - fn get_initialized_tiler_border(&self) -> anyhow::Result<&Border> { - let border_window = self - .tiler_border - .as_ref() - .context("Uninitialized tiler border")?; - Ok(border_window) - } - - fn handle_window_event(&self, event: &WindowEvent, window_id: WindowId) { - let Some(thumbnail) = self.find_thumbnail_by_window_id(window_id) else { - return; - }; - - match event { - WindowEvent::CursorEntered { .. } => { - self.output_client.cursor_entered_thumbnail(thumbnail.id); - } - WindowEvent::CursorLeft { .. } => { - self.output_client.cursor_exited_thumbnail(thumbnail.id); - } - WindowEvent::MouseInput { state, button, .. } => { - if *state == winit::event::ElementState::Pressed - && *button == winit::event::MouseButton::Left - { - self.output_client.thumbnail_clicked(thumbnail.id); - } - } - _ => {} - } - } - - fn border_window( - &self, - dest: Window, - border: &Border, - border_style: &BorderStyle, - ) -> anyhow::Result<()> { - let dest_bounds = dest.desktop_manager_bounds()?; - let Position([dest_x, dest_y]) = dest_bounds.position(); - let Size([dest_width, dest_height]) = dest_bounds.size(); - - try_cast! { - border_style.thickness => i32 as thickness, - dest_width => i32, - dest_height => i32, - } - - let border_window_x = dest_x - thickness; - let border_window_y = dest_y - thickness; - let border_window_width = dest_width + thickness * 2; - let border_window_height = dest_height + thickness * 2; - - let border_window = border.window().to_crate_window()?; - - border_window.move_to( - [border_window_x, border_window_y].into(), - [ - border_window_width.try_cast()?, - border_window_height.try_cast()?, - ] - .into(), - )?; - - wincall_result!(SetWindowPos( - border_window.handle(), - Some(dest.handle()), - border_window_x, - border_window_y, - border_window_width, - border_window_height, - SWP_SHOWWINDOW, - ))?; - - border.window().set_visible(true); - - border_window.set_max_zindex()?; - dest.set_max_zindex()?; - - self.prepare_border_surface(border, border_style)?; - - Ok(()) - } - - fn prepare_border_surface( - &self, - border: &Border, - border_style: &BorderStyle, - ) -> anyhow::Result<()> { - let border_window = border.window(); - - let mut surface = softbuffer::Surface::new(&self.context, &border_window) - .map_err(|e| anyhow!("{e:?}")) - .context("Softbuffer surface creation for border window")?; - - surface - .resize( - NonZero::new(border_window.inner_size().width).context("border window width")?, - NonZero::new(border_window.inner_size().height).context("border window height")?, - ) - .map_err(|e| anyhow!("{e:?}")) - .context("Softbuffer surface resize to match border window")?; - - let mut buffer = surface - .buffer_mut() - .map_err(|e| anyhow!("{e:?}")) - .context("border window surface mutable buffer extraction")?; - - buffer.fill(0); - - let border_pixmap = utils::draw_border( - border - .window() - .to_crate_window()? - .desktop_manager_bounds()? - .size(), - border_style, - ); - - for (out, [r, g, b, a]) in buffer - .iter_mut() - .zip(border_pixmap.data().as_chunks().0.iter()) - { - let color = u32::from_be_bytes([*a, *r, *g, *b]); - *out = color; - } - - buffer - .present() - .map_err(|e| anyhow!("{e:?}")) - .context("Border window buffer presentation")?; - - Ok(()) - } - - fn initialize_thumbnail_border_window( - &mut self, - event_loop: &ActiveEventLoop, - ) -> anyhow::Result<()> { - let border_window = create_new_border_window(event_loop)?; - self.thumbnail_border = Some(Border(border_window)); - Ok(()) - } - - fn create_tiler_border_window(&mut self, event_loop: &ActiveEventLoop) -> anyhow::Result<()> { - let border_window = create_new_border_window(event_loop)?; - self.tiler_border = Some(Border(border_window)); - Ok(()) - } -} - -impl ApplicationHandler for App { - fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { - info!("Window manager started"); - if let Err(e) = self - .initialize_thumbnail_border_window(event_loop) - .and_then(|()| self.create_tiler_border_window(event_loop)) - { - self.output_client.unrecoverable_error(e); - } - } - - fn user_event( - &mut self, - event_loop: &winit::event_loop::ActiveEventLoop, - message: InputProtocolMessage, - ) { - debug!("Window manager recieved event {message:#?}"); - self.dispatch_with_state(message, event_loop); - } - - fn window_event( - &mut self, - _event_loop: &winit::event_loop::ActiveEventLoop, - window_id: WindowId, - event: winit::event::WindowEvent, - ) { - self.handle_window_event(&event, window_id); - } -} - -pub fn launch( - event_tx: Sender, - thumbnail_border_style: BorderStyle, - tiler_border_style: BorderStyle, -) -> anyhow::Result { - let (input_client, input_rx) = InputProtocolClient::new(); - let (output_client, output_rx) = OutputProtocolClient::new(); - - thread::Builder::new() - .name("Window manager".into()) - .spawn(move || { - let event_loop = EventLoop::with_user_event() - .with_any_thread(true) - .build() - .expect("Window manager event loop"); - - let event_sender = event_loop.create_proxy(); - - thread::spawn(move || { - for input_event in input_rx { - event_sender.send_event(input_event).unwrap(); - } - }); - - let context = softbuffer::Context::new(event_loop.owned_display_handle()) - .map_err(|e| anyhow!("{e:?}")) - .context("Softbuffer context creation for window manager"); - - let context = match context { - Ok(c) => c, - Err(err) => { - output_client.unrecoverable_error(err); - return; - } - }; - - event_loop - .run_app(&mut App { - thumbnails: HashMap::default(), - thumbnail_border_style, - thumbnail_border: None, - tiler_border_style, - tiler_border: None, - context, - output_client, - }) - .expect("Window manager execution"); - })?; - - thread::Builder::new() - .name("window manager output event mapper".into()) - .spawn(move || { - for output_event in output_rx { - event_tx.send(Event::WindowManager(output_event)).unwrap(); - } - })?; - - Ok(input_client) -} - -pub mod utils { - use anyhow::bail; - use raw_window_handle::HasWindowHandle; - use tiny_skia::{Pixmap, Stroke}; - use windows::Win32::{ - Foundation::HWND, - UI::WindowsAndMessaging::{ - GWL_EXSTYLE, GWL_STYLE, SetWindowLongPtrW, WS_EX_NOACTIVATE, WS_POPUP, - }, - }; - use winit::{ - event_loop::ActiveEventLoop, platform::windows::WindowAttributesExtWindows, - window::WindowAttributes, - }; - - use crate::{ - utils::{Size, cast::FaillibleCastUtils}, - wincall_into_result, - window::manager::BorderStyle, - }; - - pub const WINRI_WINDOW_MANAGER_CLASS_NAME: &str = "WinriWindowManagerWindow"; - - #[easy_ext::ext(WindowUtils)] - impl winit::window::Window { - pub fn hwnd(&self) -> anyhow::Result { - let handle = self.window_handle()?; - let handle = match handle.as_raw() { - raw_window_handle::RawWindowHandle::Win32(win32_window_handle) => { - win32_window_handle.hwnd - } - _ => bail!("Unsupported platform"), - }; - let handle = handle.get() as *mut std::ffi::c_void; - Ok(HWND(handle)) - } - - pub fn to_crate_window(&self) -> anyhow::Result { - crate::window::Window::from_hwnd(self.hwnd()?) - } - } - - pub fn create_window( - event_loop: &ActiveEventLoop, - attrib: WindowAttributes, - ) -> anyhow::Result { - let attrib = attrib.with_class_name(WINRI_WINDOW_MANAGER_CLASS_NAME); - let create_window = event_loop.create_window(attrib)?; - Ok(create_window) - } - - pub fn create_new_border_window( - event_loop: &ActiveEventLoop, - ) -> anyhow::Result { - let border_window = create_window( - event_loop, - WindowAttributes::default() - .with_visible(false) - .with_decorations(false) - .with_transparent(true), - )?; - - let hwnd = border_window.hwnd()?; - - wincall_into_result!(SetWindowLongPtrW(hwnd, GWL_STYLE, WS_POPUP.0.try_cast()?))?; - - wincall_into_result!(SetWindowLongPtrW( - hwnd, - GWL_EXSTYLE, - WS_EX_NOACTIVATE.0.try_cast()? - ))?; - - Ok(border_window) - } - - pub fn draw_border(border_window_size: Size, border_style: &BorderStyle) -> Pixmap { - let mut pixmap = Pixmap::new(border_window_size.width(), border_window_size.height()) - .expect("Failed to create border pixmap"); - - let border_color = tiny_skia::Color::from_rgba8( - border_style.color.r, - border_style.color.g, - border_style.color.b, - border_style.color.a, - ); - - let mut paint = tiny_skia::Paint::default(); - paint.set_color(border_color); - paint.anti_alias = true; - - let stroke = Stroke { - width: (border_style.thickness + 1).cast(), - ..Default::default() - }; - - #[allow(clippy::cast_precision_loss)] - let w = pixmap.width() as f32; - #[allow(clippy::cast_precision_loss)] - let h = pixmap.height() as f32; - - let border_radius = border_style.radius.cast(); - - let mut path = tiny_skia::PathBuilder::new(); - - let half_stroke_width = stroke.width / 2.0; - - // top edge - path.move_to(border_radius, half_stroke_width); - path.line_to(w - border_radius, half_stroke_width); - path.cubic_to( - w - half_stroke_width, - half_stroke_width, - w - half_stroke_width, - border_radius, - w - half_stroke_width, - border_radius, - ); - - // right edge - path.line_to(w - half_stroke_width, h - border_radius); - - path.cubic_to( - w - half_stroke_width, - h - half_stroke_width, - w - border_radius, - h - half_stroke_width, - w - border_radius, - h - half_stroke_width, - ); - - // bottom edge - path.line_to(border_radius, h - half_stroke_width); - path.cubic_to( - half_stroke_width, - h - half_stroke_width, - half_stroke_width, - h - border_radius, - half_stroke_width, - h - border_radius, - ); - - // left edge - path.line_to(half_stroke_width, border_radius); - path.cubic_to( - half_stroke_width, - half_stroke_width, - border_radius, - half_stroke_width, - border_radius, - half_stroke_width, - ); - let path = path.finish().expect("Failed to create border path"); - - pixmap.stroke_path( - &path, - &paint, - &stroke, - tiny_skia::Transform::identity(), - None, - ); - - pixmap - } -} diff --git a/src/window/mod.rs b/src/window/mod.rs index 0eb51fd..305c161 100644 --- a/src/window/mod.rs +++ b/src/window/mod.rs @@ -1,5 +1,4 @@ pub mod filter; -pub mod manager; use std::{ffi::c_void, hash::Hash, thread, time::Duration}; @@ -15,24 +14,23 @@ use windows::{ Threading::{OpenProcess, PROCESS_QUERY_INFORMATION, PROCESS_VM_READ}, }, UI::WindowsAndMessaging::{ - EnumWindows, GA_ROOT, GWL_STYLE, GetAncestor, GetClassNameW, GetClientRect, - GetWindowLongW, GetWindowRect, GetWindowTextLengthW, GetWindowTextW, + EnumWindows, GA_ROOT, GWL_EXSTYLE, GWL_STYLE, GetAncestor, GetClassNameW, + GetClientRect, GetWindowLongW, GetWindowRect, GetWindowTextLengthW, GetWindowTextW, GetWindowThreadProcessId, HWND_TOP, IsIconic, IsWindow, IsWindowVisible, MoveWindow, PostMessageW, SW_RESTORE, SW_SHOW, SWP_NOMOVE, SWP_NOSIZE, SetForegroundWindow, - SetWindowPos, ShowWindow, WINDOW_LONG_PTR_INDEX, WINDOW_STYLE, WM_CLOSE, WS_DLGFRAME, - WS_POPUP, + SetWindowLongPtrW, SetWindowPos, ShowWindow, WINDOW_LONG_PTR_INDEX, WINDOW_STYLE, + WM_CLOSE, WS_DLGFRAME, WS_EX_NOACTIVATE, WS_POPUP, }, }, core::BOOL, }; use crate::{ - try_cast, - utils::{Bounds, Position, Size, cast::FaillibleCastUtils}, + utils::math::{Bounds, Position, Size}, wincall_into_result, wincall_result, }; -pub type SafeHWND = isize; +pub type SafeHWND = u64; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Window { @@ -47,11 +45,15 @@ impl Hash for Window { impl From for Bounds { fn from(rect: RECT) -> Self { + #[allow( + clippy::cast_precision_loss, + reason = "The values will stay within screen size orders of magnitude" + )] Self { - left: rect.left, - top: rect.top, - right: rect.right, - bottom: rect.bottom, + left: rect.left as f32, + top: rect.top as f32, + right: rect.right as f32, + bottom: rect.bottom as f32, } } } @@ -71,10 +73,15 @@ impl Window { pub fn from_hwnd(hwnd: HWND) -> anyhow::Result { ensure!(!hwnd.is_invalid(), "Invalid window handle"); Ok(Self { - hwnd: hwnd.0 as isize, + hwnd: hwnd.0 as SafeHWND, }) } + pub fn from_safe_hwnd(safe_hwnd: SafeHWND) -> anyhow::Result { + let hwnd = HWND(safe_hwnd as *mut c_void); + Self::from_hwnd(hwnd) + } + pub fn focused() -> anyhow::Result { let hwnd = wincall_into_result!(windows::Win32::UI::WindowsAndMessaging::GetForegroundWindow())?; @@ -243,23 +250,34 @@ impl Window { ensure_valid!(self); let [left, top, right, bottom] = self.padding()?; - try_cast! { - left => i32 as left_i32, - top => i32 as top_i32, - } - - let x = pos.x() - left_i32; - let y = pos.y() - top_i32; + let x = pos.x() - left; + let y = pos.y() - top; let w = size.width() + right + left; let h = size.height() + bottom + top; - try_cast! { - w => i32, - h => i32, - } - let _ = wincall_into_result!(ShowWindow(self.handle(), SW_RESTORE))?; - wincall_result!(MoveWindow(self.handle(), x, y, w, h, true))?; + wincall_result!(MoveWindow( + self.handle(), + x as i32, + y as i32, + w as i32, + h as i32, + true + ))?; + Ok(()) + } + + pub fn set_no_activate(self) -> anyhow::Result<()> { + ensure_valid!(self); + #[allow( + clippy::cast_possible_wrap, + reason = "Will never run on 32-bit systems" + )] + wincall_into_result!(SetWindowLongPtrW( + self.handle(), + GWL_EXSTYLE, + WS_EX_NOACTIVATE.0 as isize, + ))?; Ok(()) } @@ -275,17 +293,17 @@ impl Window { } pub fn move_offscreen(self) -> anyhow::Result<()> { + const ADDITIONAL_OFFSCREEN_OFFSET: f32 = 100.0; + ensure_valid!(self); let width = self.desktop_manager_bounds()?.size().width(); - try_cast! { - width => i32, - } + let offscreen_offset = width + ADDITIONAL_OFFSCREEN_OFFSET; wincall_result!(SetWindowPos( self.handle(), None, - -width - 100, + -offscreen_offset as i32, 0, 0, 0, @@ -335,15 +353,15 @@ impl Window { Ok(rect.into()) } - pub fn padding(self) -> anyhow::Result<[u32; 4]> { + pub fn padding(self) -> anyhow::Result<[f32; 4]> { ensure_valid!(self); let dm_rect = self.desktop_manager_bounds()?; let rect = self.outer_bounds()?; Ok([ - (rect.left - dm_rect.left).abs().try_cast()?, - (rect.top - dm_rect.top).abs().try_cast()?, - (rect.right - dm_rect.right).abs().try_cast()?, - (rect.bottom - dm_rect.bottom).abs().try_cast()?, + (rect.left - dm_rect.left).abs(), + (rect.top - dm_rect.top).abs(), + (rect.right - dm_rect.right).abs(), + (rect.bottom - dm_rect.bottom).abs(), ]) } From e39daea8510378b5e32c004d2a322feeaf640019 Mon Sep 17 00:00:00 2001 From: Marius Pardo Date: Mon, 22 Dec 2025 15:22:09 +0100 Subject: [PATCH 04/16] Bug reporting in error message box (#53) --- Cargo.lock | 288 +++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/main.rs | 22 ++-- src/message_box.rs | 157 ++++++++++++++++++++++++ src/winapi.rs | 13 +- 5 files changed, 459 insertions(+), 22 deletions(-) create mode 100644 src/message_box.rs diff --git a/Cargo.lock b/Cargo.lock index 180a994..3ab5a83 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -517,6 +517,17 @@ dependencies = [ "objc2 0.6.3", ] +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "dlib" version = "0.5.2" @@ -730,6 +741,15 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + [[package]] name = "futures" version = "0.3.31" @@ -1229,6 +1249,108 @@ dependencies = [ "winit", ] +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + [[package]] name = "indexmap" version = "2.12.1" @@ -1434,6 +1556,12 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + [[package]] name = "litrs" version = "1.0.0" @@ -2042,6 +2170,12 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + [[package]] name = "pin-project" version = "1.1.10" @@ -2109,6 +2243,15 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -2533,6 +2676,12 @@ dependencies = [ "bitflags 2.10.0", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + [[package]] name = "static_assertions" version = "1.1.0" @@ -2573,6 +2722,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "sys-locale" version = "0.3.2" @@ -2666,6 +2826,16 @@ dependencies = [ "strict-num", ] +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinyvec" version = "1.10.0" @@ -2843,6 +3013,24 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" +[[package]] +name = "url" +version = "2.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "version_check" version = "0.9.5" @@ -2966,6 +3154,22 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webbrowser" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00f1243ef785213e3a32fa0396093424a3a6ea566f9948497e5a2309261a4c97" +dependencies = [ + "core-foundation 0.10.1", + "jni", + "log", + "ndk-context", + "objc2 0.6.3", + "objc2-foundation 0.3.2", + "url", + "web-sys", +] + [[package]] name = "wgpu" version = "27.0.1" @@ -3628,6 +3832,7 @@ dependencies = [ "log", "log4rs", "rdev", + "webbrowser", "windows 0.62.2", "windows-strings 0.5.1", ] @@ -3638,6 +3843,12 @@ version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + [[package]] name = "x11" version = "2.21.0" @@ -3679,6 +3890,29 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e01738255b5a16e78bbb83e7fbba0a1e7dd506905cfc53f4622d89015a03fbb5" +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "zeno" version = "0.3.3" @@ -3704,3 +3938,57 @@ dependencies = [ "quote", "syn", ] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml index ad3de66..9af9f5b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ joy-error = { git = "https://github.com/sub07/rust-utils", version = "0.6.0", fe itertools = "0.14.0" keyboard-types = { version = "0.8.3", features = ["std", "serde"] } dirs = "6" +webbrowser = "1" [dependencies.windows] version = "0.62" diff --git a/src/main.rs b/src/main.rs index eebbf9d..c25a43a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,11 +11,12 @@ use std::{panic, path::PathBuf}; -use anyhow::anyhow; +use anyhow::{Context, anyhow}; mod adapter; mod app; mod logger; +mod message_box; mod scroll_tiler; mod system; mod utils; @@ -23,6 +24,7 @@ mod winapi; mod window; pub const DEBUG_MODE: bool = cfg!(debug_assertions); +pub const WINRI_VERSION: &str = env!("CARGO_PKG_VERSION"); pub fn root_dir() -> anyhow::Result { const PROJECT_DIR_NAME: &str = if DEBUG_MODE { "winri-dev" } else { "winri" }; @@ -32,24 +34,19 @@ pub fn root_dir() -> anyhow::Result { } fn main() { - if let Err(e) = logger::setup() { - winapi::message_box( - "Log initialization error", - &format!("Could not initialize log system. No log will be written.\n\n{e}"), - ); + if let Err(e) = logger::setup() + .context("Could not initialize log system, no log will be written for this session") + { + message_box::message_box_info_bug_report(e); } + log::info!("Winri starting up"); let default_hook = panic::take_hook(); panic::set_hook(Box::new(move |info| { log::error!("Winri panicked: {info}"); + message_box::message_box_fatal_bug_report(info); system::restore_windows(); - if let Some(error_cause) = info.payload_as_str() { - winapi::message_box( - "Fatal error", - &format!("{error_cause}.\nThe application will now exit."), - ); - } default_hook(info); })); @@ -64,6 +61,7 @@ fn main() { .run() { log::error!("Winri exited with error: {e}"); + message_box::message_box_fatal_bug_report(anyhow!(e)); } log::info!("Winri exited successfully"); diff --git a/src/message_box.rs b/src/message_box.rs new file mode 100644 index 0000000..fb5b9af --- /dev/null +++ b/src/message_box.rs @@ -0,0 +1,157 @@ +use std::panic::PanicHookInfo; + +use windows::Win32::UI::WindowsAndMessaging::{IDYES, MB_OK, MB_YESNO}; + +use crate::{DEBUG_MODE, WINRI_VERSION, winapi}; + +#[allow(dead_code, reason = "Could be useful at some point")] +pub fn message_box_info(title: &str, message: &str) { + winapi::message_box(title, message, MB_OK); +} + +pub fn message_box_query(title: &str, message: &str) -> bool { + winapi::message_box(title, message, MB_YESNO) == IDYES +} + +pub trait IntoBugReportInfo { + fn title(&self) -> String; + fn short(&self) -> String; + fn long(&self) -> String; +} + +impl IntoBugReportInfo for anyhow::Error { + fn title(&self) -> String { + "Bug report: ".into() + } + + fn short(&self) -> String { + format!("{self}") + } + + fn long(&self) -> String { + format!("{self:#?}") + } +} + +impl IntoBugReportInfo for &PanicHookInfo<'_> { + fn title(&self) -> String { + "Panic report: ".into() + } + + fn short(&self) -> String { + if let Some(str) = self.payload_as_str() { + str.to_string() + } else { + "".into() + } + .trim() + .to_string() + } + + fn long(&self) -> String { + self.to_string() + } +} + +struct FatalBugReport(B); +struct NonFatalBugReport(B); + +impl IntoBugReportInfo for FatalBugReport { + fn title(&self) -> String { + format!("Fatal {}", self.0.title()) + } + + fn short(&self) -> String { + self.0.short() + } + + fn long(&self) -> String { + format!( + r" +An unrecoverable error occurred, Application will now exit: + +``` +{} +``` + +Before it closes, would you like to submit a pre-filled github issue about this bug ? + + +>Please note: +> 1. Your browser will open +> 2. You can edit the issue before submitting it +> 3. Error reports are automatically included and may contain personal information such as window titles, browser tab names, etc. +> 4. To help debugging, you can include your session logs. But note that it may contains personal information such as window titles, browser tab names, etc. + ", + if DEBUG_MODE { + self.0.long() + } else { + self.0.short() + } + ) + } +} + +impl IntoBugReportInfo for NonFatalBugReport { + fn title(&self) -> String { + self.0.title() + } + + fn short(&self) -> String { + self.0.short() + } + + fn long(&self) -> String { + format!( + r" +An error occurred, but winri will continue its execution. Some systems might not be working properly: + +``` +{} +``` + +Before resuming winri, +would you like to submit a pre-filled github issue about this bug ? + + +>Please note: +> 1. Your browser will open +> 2. You can edit the issue before submitting it +> 3. Error reports are automatically included and may contain personal information such as window titles, browser tab names, etc. +> 4. To help debugging, you can include your session logs. But note that it may contains personal information such as window titles, browser tab names, etc. + ", + if DEBUG_MODE { + self.0.long() + } else { + self.0.short() + } + ) + } +} + +pub fn message_box_bug_report(report: impl IntoBugReportInfo) { + let create_bug_report = message_box_query(report.title().as_str(), report.long().as_str()); + + if create_bug_report { + let create_issue_url = format!( + "https://github.com/sub07/winri/issues/new?labels=crash logs&title=[v{}] {}{}&body={}", + WINRI_VERSION, + report.title(), + report.short(), + report.long().replace('\n', "%0A") + ); + if let Err(err) = webbrowser::open(&create_issue_url) { + log::error!("Could not open web browser to create bug report: {err:?}"); + } else { + log::info!("Opened web browser to create bug report: {create_issue_url}"); + } + } +} + +pub fn message_box_fatal_bug_report(report: impl IntoBugReportInfo) { + message_box_bug_report(FatalBugReport(report)); +} + +pub fn message_box_info_bug_report(report: impl IntoBugReportInfo) { + message_box_bug_report(NonFatalBugReport(report)); +} diff --git a/src/winapi.rs b/src/winapi.rs index b371b92..8172351 100644 --- a/src/winapi.rs +++ b/src/winapi.rs @@ -1,8 +1,8 @@ use windows::Win32::Foundation::{GetLastError, SetLastError, WIN32_ERROR}; -use windows::Win32::UI::WindowsAndMessaging::{MB_OK, MessageBoxW}; +use windows::Win32::UI::WindowsAndMessaging::{MESSAGEBOX_RESULT, MESSAGEBOX_STYLE, MessageBoxW}; use windows_strings::PCWSTR; -pub fn message_box(title: &str, message: &str) { +pub fn message_box(title: &str, message: &str, kind: MESSAGEBOX_STYLE) -> MESSAGEBOX_RESULT { use std::os::windows::ffi::OsStrExt; let title_os = std::ffi::OsStr::new(&title); @@ -13,14 +13,7 @@ pub fn message_box(title: &str, message: &str) { let message = message_os.encode_wide().chain(std::iter::once(0)); let message = message.collect::>(); - unsafe { - MessageBoxW( - None, - PCWSTR(message.as_ptr()), - PCWSTR(title.as_ptr()), - MB_OK, - ); - } + unsafe { MessageBoxW(None, PCWSTR(message.as_ptr()), PCWSTR(title.as_ptr()), kind) } } pub fn clear_last_error() { From 5c5fe9dc9647a1a1049b2a18975ce33a3441deb8 Mon Sep 17 00:00:00 2001 From: Marius Pardo Date: Mon, 22 Dec 2025 15:32:06 +0100 Subject: [PATCH 05/16] add log path in bug report --- src/logger.rs | 2 +- src/message_box.rs | 22 +++++++++++++++++----- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/logger.rs b/src/logger.rs index 6b52b04..2860fa0 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -27,7 +27,7 @@ const DISABLED_MODULES: &[&str] = &[ use crate::{DEBUG_MODE, root_dir}; -fn log_dir() -> anyhow::Result { +pub fn log_dir() -> anyhow::Result { Ok(root_dir()?.join("logs")) } diff --git a/src/message_box.rs b/src/message_box.rs index fb5b9af..b8ac7ca 100644 --- a/src/message_box.rs +++ b/src/message_box.rs @@ -2,7 +2,7 @@ use std::panic::PanicHookInfo; use windows::Win32::UI::WindowsAndMessaging::{IDYES, MB_OK, MB_YESNO}; -use crate::{DEBUG_MODE, WINRI_VERSION, winapi}; +use crate::{DEBUG_MODE, WINRI_VERSION, logger, winapi}; #[allow(dead_code, reason = "Could be useful at some point")] pub fn message_box_info(title: &str, message: &str) { @@ -13,6 +13,16 @@ pub fn message_box_query(title: &str, message: &str) -> bool { winapi::message_box(title, message, MB_YESNO) == IDYES } +pub fn log_file_path() -> String { + logger::log_dir().map_or_else( + |_| "".into(), + |mut p| { + p.push("winri.log"); + p.display().to_string() + }, + ) +} + pub trait IntoBugReportInfo { fn title(&self) -> String; fn short(&self) -> String; @@ -81,13 +91,14 @@ Before it closes, would you like to submit a pre-filled github issue about this > 1. Your browser will open > 2. You can edit the issue before submitting it > 3. Error reports are automatically included and may contain personal information such as window titles, browser tab names, etc. -> 4. To help debugging, you can include your session logs. But note that it may contains personal information such as window titles, browser tab names, etc. +> 4. To help debugging, you can include your session logs ({}). But note that it may contains personal information such as window titles, browser tab names, etc. ", if DEBUG_MODE { self.0.long() } else { self.0.short() - } + }, + log_file_path() ) } } @@ -118,13 +129,14 @@ would you like to submit a pre-filled github issue about this bug ? > 1. Your browser will open > 2. You can edit the issue before submitting it > 3. Error reports are automatically included and may contain personal information such as window titles, browser tab names, etc. -> 4. To help debugging, you can include your session logs. But note that it may contains personal information such as window titles, browser tab names, etc. +> 4. To help debugging, you can include your session logs ({}). But note that it may contains personal information such as window titles, browser tab names, etc. ", if DEBUG_MODE { self.0.long() } else { self.0.short() - } + }, + log_file_path() ) } } From 0eb19ac44530dc904b1242c1dc5eb117f9263ddd Mon Sep 17 00:00:00 2001 From: Marius Pardo Date: Tue, 23 Dec 2025 14:37:26 +0100 Subject: [PATCH 06/16] Force refresh action (#43) --- README.md | 2 +- src/app/action.rs | 1 + src/app/service/mod.rs | 4 ++++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 95692a4..8e51a4d 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,7 @@ In Tiler mode: | Win + F | Resize focused window to fullscreen width | | Win + C | Resize focused window to half of screen width | | Win + Shift + Left / Right | Resize by increment (20 px by default) | +| Win + R | Force tiler refresh | | Win + Up | Enter Overview mode | | Win + Escape | Exit winri and restore windows | @@ -85,7 +86,6 @@ In Tiler mode: In Overview mode: | Input | Action | |-------------------|-----------------------------------------| -| Click thumbnail | Focus original window & return to Tiler | | Win + Down | Close overview | ## Configuration diff --git a/src/app/action.rs b/src/app/action.rs index 575f5bf..ff458bd 100644 --- a/src/app/action.rs +++ b/src/app/action.rs @@ -17,6 +17,7 @@ pub enum TilerAction { IncrementWidth, DecrementWidth, OpenOverview, + ForceRefresh, } #[derive(Debug, Clone)] diff --git a/src/app/service/mod.rs b/src/app/service/mod.rs index 1cc966f..309cbf4 100644 --- a/src/app/service/mod.rs +++ b/src/app/service/mod.rs @@ -38,6 +38,9 @@ impl app::State { (Mode::Tiler { .. }, Modifiers::META, Key::KeyC) => { Some(Action::Tiler(TilerAction::ResizeToHalfScreen)) } + (Mode::Tiler { .. }, Modifiers::META, Key::KeyR) => { + Some(Action::Tiler(TilerAction::ForceRefresh)) + } (Mode::Tiler { .. }, _, Key::LeftArrow) if modifiers == Modifiers::META.union(Modifiers::SHIFT) => { @@ -102,6 +105,7 @@ impl app::State { self.tiler.decrement_current_window_width(); self.update_tiler()?; } + TilerAction::ForceRefresh => self.update_tiler()?, }, Action::Overview(overview_action) => match overview_action { OverviewAction::CloseOverview => return self.close_overview(), From 0423a065572b4918f9c8a9bd79767108caaa3dee Mon Sep 17 00:00:00 2001 From: Marius Pardo Date: Tue, 23 Dec 2025 14:56:19 +0100 Subject: [PATCH 07/16] Fix overview reduction ratio going above one --- README.md | 6 +++--- src/app/service/overview/thumbnail.rs | 8 ++++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 8e51a4d..91c726b 100644 --- a/README.md +++ b/README.md @@ -84,9 +84,9 @@ In Tiler mode: In Overview mode: -| Input | Action | -|-------------------|-----------------------------------------| -| Win + Down | Close overview | +| Input | Action | +|--------------|----------------| +| Win + Down | Close overview | ## Configuration diff --git a/src/app/service/overview/thumbnail.rs b/src/app/service/overview/thumbnail.rs index 55ae9c7..870807b 100644 --- a/src/app/service/overview/thumbnail.rs +++ b/src/app/service/overview/thumbnail.rs @@ -40,7 +40,8 @@ pub fn compute_thumbnails_bounds_from_tiler_windows( let reduction_ratio = screen_size.width() / total_tiler_width; - debug!("Thumbnail reduction ratio: {reduction_ratio}"); + debug!("total tiler width including padding: {total_tiler_width}"); + debug!("Thumbnail reduction ratio for packing windows: {reduction_ratio}"); let reduction_ratio = if reduction_ratio > 1.0 { reduction_ratio * 0.6 @@ -48,7 +49,10 @@ pub fn compute_thumbnails_bounds_from_tiler_windows( reduction_ratio * 0.9 } else { reduction_ratio - }; + } + .clamp(0.0, 0.6); + + debug!("Thumbnail reduction ratio after size adaptation: {reduction_ratio}"); let mut current_x = 0.0; let mut thumbnails = Vec::new(); From f61f9a6d3660a4e8fc5a6c01578dc0e3f547847f Mon Sep 17 00:00:00 2001 From: Marius Pardo Date: Tue, 23 Dec 2025 15:30:58 +0100 Subject: [PATCH 08/16] fix overlay initial focus --- src/app/mod.rs | 26 +++++++++++++++++++++++--- src/app/service/overview/mod.rs | 5 +++++ src/app/service/overview/thumbnail.rs | 10 +++++----- src/logger.rs | 3 ++- src/main.rs | 16 ++++++++-------- src/message_box.rs | 6 +++--- src/system.rs | 6 +++++- src/window/mod.rs | 3 ++- 8 files changed, 53 insertions(+), 22 deletions(-) diff --git a/src/app/mod.rs b/src/app/mod.rs index 0a8cec7..f8c55cc 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -5,7 +5,11 @@ mod subscription; mod view; use anyhow::Context; -use iced::{Color, Task, theme::Palette, window::Settings}; +use iced::{ + Color, Task, + theme::Palette, + window::{Settings, settings::PlatformSpecific}, +}; use crate::{ app::{ @@ -19,7 +23,7 @@ use crate::{ scroll_tiler::ScrollTiler, system, utils::math::Size, - window::{self}, + window::{self, Window}, }; pub struct State { @@ -61,10 +65,26 @@ fn create_overlay_window(screen_size: Size) -> (iced::window::Id, Task) level: iced::window::Level::AlwaysOnTop, position: iced::window::Position::Specific(iced::Point::ORIGIN), size: screen_size.into(), + platform_specific: PlatformSpecific { + skip_taskbar: true, + ..Default::default() + }, ..Default::default() }); - (id, task.then(iced::window::enable_mouse_passthrough)) + ( + id, + task.then(iced::window::enable_mouse_passthrough) + .then(|_: Message| { + // HACK: By default the overlay window steals focus when created, but should not be able to be focused. + // It causes weird behavior like keystroke not recorded until another window is focused. + // So we refocus the desktop window after creation. + if let Err(err) = system::get_desktop_window().and_then(Window::focus) { + log::error!("Could not focus desktop window after overlay creation: {err}"); + } + Task::none() + }), + ) } impl State { diff --git a/src/app/service/overview/mod.rs b/src/app/service/overview/mod.rs index 3a9bc7a..be9b22b 100644 --- a/src/app/service/overview/mod.rs +++ b/src/app/service/overview/mod.rs @@ -108,9 +108,14 @@ impl app::State { .context(dest_raw_handle) .context("invalid hwnd from thumbnail window")?; + dest_window.set_no_activate()?; + let thumbnail_id = thumbnail::bind_thumbnail(src, dest_window, size).context("thumbnail binding")?; + dest_window.show()?; + dest_window.set_max_zindex()?; + thumbnails.push((thumbnail_id, dest_id)); Ok(()) diff --git a/src/app/service/overview/thumbnail.rs b/src/app/service/overview/thumbnail.rs index 870807b..fca808a 100644 --- a/src/app/service/overview/thumbnail.rs +++ b/src/app/service/overview/thumbnail.rs @@ -2,7 +2,10 @@ pub type ThumbnailId = isize; use iced::{ Task, - window::{Settings, settings::PlatformSpecific}, + window::{ + Settings, + settings::{PlatformSpecific, platform::CornerPreference}, + }, }; use log::debug; use windows::Win32::{ @@ -87,6 +90,7 @@ pub fn thumbnail_window_creation_task( visible: false, platform_specific: PlatformSpecific { skip_taskbar: true, + corner_preference: CornerPreference::Round, ..Default::default() }, ..Default::default() @@ -110,7 +114,6 @@ pub fn thumbnail_window_creation_task( } pub fn bind_thumbnail(src: Window, dest: Window, size: Size) -> anyhow::Result { - dest.set_no_activate()?; let thumbnail_id = wincall_result!(DwmRegisterThumbnail(dest.handle(), src.handle()))?; let thumbnail_props = DWM_THUMBNAIL_PROPERTIES { @@ -137,9 +140,6 @@ pub fn bind_thumbnail(src: Window, dest: Window, size: Size) -> anyhow::Result anyhow::Result { } fn main() { - if let Err(e) = logger::setup() - .context("Could not initialize log system, no log will be written for this session") - { - message_box::message_box_info_bug_report(e); - } - - log::info!("Winri starting up"); - let default_hook = panic::take_hook(); panic::set_hook(Box::new(move |info| { log::error!("Winri panicked: {info}"); @@ -50,6 +42,14 @@ fn main() { default_hook(info); })); + if let Err(e) = logger::setup() + .context("Could not initialize log system, no log will be written for this session") + { + message_box::message_box_info_bug_report(e); + } + + log::info!("Winri starting up"); + if let Err(e) = iced::daemon( app::State::new, app::State::handle_app_message, diff --git a/src/message_box.rs b/src/message_box.rs index b8ac7ca..0bc9ce8 100644 --- a/src/message_box.rs +++ b/src/message_box.rs @@ -31,7 +31,7 @@ pub trait IntoBugReportInfo { impl IntoBugReportInfo for anyhow::Error { fn title(&self) -> String { - "Bug report: ".into() + "Bug report".into() } fn short(&self) -> String { @@ -45,7 +45,7 @@ impl IntoBugReportInfo for anyhow::Error { impl IntoBugReportInfo for &PanicHookInfo<'_> { fn title(&self) -> String { - "Panic report: ".into() + "Panic report".into() } fn short(&self) -> String { @@ -146,7 +146,7 @@ pub fn message_box_bug_report(report: impl IntoBugReportInfo) { if create_bug_report { let create_issue_url = format!( - "https://github.com/sub07/winri/issues/new?labels=crash logs&title=[v{}] {}{}&body={}", + "https://github.com/sub07/winri/issues/new?labels=crash logs&title=[v{}] {}: {}&body={}", WINRI_VERSION, report.title(), report.short(), diff --git a/src/system.rs b/src/system.rs index 0d2331e..16696f4 100644 --- a/src/system.rs +++ b/src/system.rs @@ -1,7 +1,7 @@ use log::warn; use windows::Win32::{ Graphics::Gdi::{COLOR_HIGHLIGHT, GetSysColor}, - UI::WindowsAndMessaging::{GetSystemMetrics, SM_CXSCREEN, SM_CYSCREEN}, + UI::WindowsAndMessaging::{GetDesktopWindow, GetSystemMetrics, SM_CXSCREEN, SM_CYSCREEN}, }; use crate::{ @@ -47,3 +47,7 @@ pub fn restore_windows() { pos += 100.0; } } + +pub fn get_desktop_window() -> anyhow::Result { + Window::from_hwnd(wincall_into_result!(GetDesktopWindow())?) +} diff --git a/src/window/mod.rs b/src/window/mod.rs index 305c161..5618e88 100644 --- a/src/window/mod.rs +++ b/src/window/mod.rs @@ -382,7 +382,8 @@ impl Window { thread::sleep(Duration::from_millis(500)); } - // Simulate an alt key release to bypass focus stealing restrictions : https://stackoverflow.com/questions/10740346/setforegroundwindow-only-working-while-visual-studio-is-open + // HACK: Simulate an alt key release to bypass focus stealing restrictions: + // https://stackoverflow.com/questions/10740346/setforegroundwindow-only-working-while-visual-studio-is-open rdev::simulate(&rdev::EventType::KeyRelease(rdev::Key::Alt))?; let _ = wincall_into_result!(SetForegroundWindow(self.handle()))?; Ok(()) From 88ca7169683c68fa3a1f5f6b4c53606c5b9fe949 Mon Sep 17 00:00:00 2001 From: Marius Pardo Date: Tue, 23 Dec 2025 16:06:31 +0100 Subject: [PATCH 09/16] bump version and update readme --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- README.md | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3ab5a83..c5d5ca7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3819,7 +3819,7 @@ dependencies = [ [[package]] name = "winri" -version = "0.2.1" +version = "0.3.0" dependencies = [ "anyhow", "dirs", @@ -3834,7 +3834,7 @@ dependencies = [ "rdev", "webbrowser", "windows 0.62.2", - "windows-strings 0.5.1", + "windows-strings 0.1.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 9af9f5b..336f020 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "winri" description = "A window scrolling tiler for Windows 11" -version = "0.2.1" +version = "0.3.0" edition = "2024" license = "MIT" diff --git a/README.md b/README.md index 91c726b..d2f142f 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ Configuration is not implemented yet. It will include at least the following opt - Keybindings - Padding between windows -- Border color and thickness for thumbnails and focused window (border on focused window not yet implemented) +- Border color and thickness for thumbnails and focused window - Per-process modifiers (e.g. exclude certain apps from tiling) You can check the [config issue](https://github.com/sub07/winri/issues/3) for more fields to come. @@ -112,7 +112,7 @@ Before tackling an issue or submitting a PR, please check the issue tracker for For your PR to be accepted, please ensure the following: - Format code with `cargo fmt --all -- --check` -- Run this clippy command `cargo clippy --all-targets --all-features -- -D warnings -W clippy::pedantic -W clippy::nursery -W clippy::dbg_macro -A clippy::missing_errors_doc` +- Run this clippy command `cargo clippy -- -D warnings` - Run tests with `cargo test --all-features` - Run cargo-machete to ensure dependency hygiene: `cargo machete --with-metadata` (install with `cargo install cargo-machete`) @@ -128,7 +128,7 @@ Winri aims to be built with stable Rust. - Dependencies: - `windows` crate for Win32 API - `rdev` for global input capture - - `winit` + `softbuffer` (For custom windows) + - `iced` for overlay and other custom windows Releases are automated via GitHub Actions on pushes to `main`. @@ -136,7 +136,7 @@ Tagging is derived from GitHub releases automatically; manual tagging is not req ## Security -Winri must run with administrative privileges to manipulate windows of elevated processes (like task manager). One can choose to run winri without admin rights, but then windows of elevated processes will be ignored. +For Winri to manipulate windows of elevated processes (like task manager), it must run with administrative privileges. One can choose to run winri without admin rights, but then windows of elevated processes will be ignored. Winri will never collect or transmit any user data. Bugs will be reported by users voluntarily. From 13c19f2db50da13e60dfcc3571e22120df4f68a2 Mon Sep 17 00:00:00 2001 From: Marius Pardo Date: Tue, 23 Dec 2025 16:21:16 +0100 Subject: [PATCH 10/16] build release on release branch instead of main & update deps --- .github/workflows/main.yml | 4 +-- Cargo.lock | 71 ++++++++++++++++++++------------------ Cargo.toml | 6 ++-- 3 files changed, 42 insertions(+), 39 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2a1c873..7805d2e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,9 +6,9 @@ jobs: build: needs: check uses: ./.github/workflows/build.yml - if: github.ref == 'refs/heads/main' + if: startsWith(github.head_ref, 'release-') deploy: needs: build uses: ./.github/workflows/deploy.yml secrets: inherit - if: github.ref == 'refs/heads/main' + if: startsWith(github.head_ref, 'release-') diff --git a/Cargo.lock b/Cargo.lock index c5d5ca7..bb90b88 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -46,9 +46,12 @@ checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "arc-swap" -version = "1.7.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" +checksum = "51d03449bb8ca2cc2ef70869af31463d1ae5ccc8fa3e334b307203fbf815207e" +dependencies = [ + "rustversion", +] [[package]] name = "arrayref" @@ -185,9 +188,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.49" +version = "1.2.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" +checksum = "9f50d563227a1c37cc0a263f64eca3334388c01c5e4c4861a9def205c614383c" dependencies = [ "find-msvc-tools", "jobserver", @@ -454,18 +457,18 @@ checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" [[package]] name = "derive_more" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10b768e943bed7bf2cab53df09f4bc34bfd217cdb57d971e769874c9a6710618" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" dependencies = [ "derive_more-impl", ] [[package]] name = "derive_more-impl" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d286bfdaf75e988b4a78e013ecd79c581e06399ab53fbacd2d916c2f904f30b" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ "proc-macro2", "quote", @@ -1049,7 +1052,7 @@ dependencies = [ [[package]] name = "iced" version = "0.15.0-dev" -source = "git+https://github.com/sub07/iced.git?branch=custom-winri#17d55805dec08bea6af98b2613250a6f1d1c836b" +source = "git+https://github.com/sub07/iced.git?branch=custom-winri#2e7e5c59013e643ab4a257a41d615c97174047f7" dependencies = [ "iced_core", "iced_debug", @@ -1065,7 +1068,7 @@ dependencies = [ [[package]] name = "iced_beacon" version = "0.15.0-dev" -source = "git+https://github.com/sub07/iced.git?branch=custom-winri#17d55805dec08bea6af98b2613250a6f1d1c836b" +source = "git+https://github.com/sub07/iced.git?branch=custom-winri#2e7e5c59013e643ab4a257a41d615c97174047f7" dependencies = [ "bincode", "futures", @@ -1080,7 +1083,7 @@ dependencies = [ [[package]] name = "iced_core" version = "0.15.0-dev" -source = "git+https://github.com/sub07/iced.git?branch=custom-winri#17d55805dec08bea6af98b2613250a6f1d1c836b" +source = "git+https://github.com/sub07/iced.git?branch=custom-winri#2e7e5c59013e643ab4a257a41d615c97174047f7" dependencies = [ "bitflags 2.10.0", "bytes", @@ -1098,7 +1101,7 @@ dependencies = [ [[package]] name = "iced_debug" version = "0.15.0-dev" -source = "git+https://github.com/sub07/iced.git?branch=custom-winri#17d55805dec08bea6af98b2613250a6f1d1c836b" +source = "git+https://github.com/sub07/iced.git?branch=custom-winri#2e7e5c59013e643ab4a257a41d615c97174047f7" dependencies = [ "iced_beacon", "iced_core", @@ -1109,7 +1112,7 @@ dependencies = [ [[package]] name = "iced_devtools" version = "0.15.0-dev" -source = "git+https://github.com/sub07/iced.git?branch=custom-winri#17d55805dec08bea6af98b2613250a6f1d1c836b" +source = "git+https://github.com/sub07/iced.git?branch=custom-winri#2e7e5c59013e643ab4a257a41d615c97174047f7" dependencies = [ "iced_debug", "iced_program", @@ -1120,7 +1123,7 @@ dependencies = [ [[package]] name = "iced_futures" version = "0.15.0-dev" -source = "git+https://github.com/sub07/iced.git?branch=custom-winri#17d55805dec08bea6af98b2613250a6f1d1c836b" +source = "git+https://github.com/sub07/iced.git?branch=custom-winri#2e7e5c59013e643ab4a257a41d615c97174047f7" dependencies = [ "futures", "iced_core", @@ -1134,7 +1137,7 @@ dependencies = [ [[package]] name = "iced_graphics" version = "0.15.0-dev" -source = "git+https://github.com/sub07/iced.git?branch=custom-winri#17d55805dec08bea6af98b2613250a6f1d1c836b" +source = "git+https://github.com/sub07/iced.git?branch=custom-winri#2e7e5c59013e643ab4a257a41d615c97174047f7" dependencies = [ "bitflags 2.10.0", "bytemuck", @@ -1153,7 +1156,7 @@ dependencies = [ [[package]] name = "iced_program" version = "0.15.0-dev" -source = "git+https://github.com/sub07/iced.git?branch=custom-winri#17d55805dec08bea6af98b2613250a6f1d1c836b" +source = "git+https://github.com/sub07/iced.git?branch=custom-winri#2e7e5c59013e643ab4a257a41d615c97174047f7" dependencies = [ "iced_graphics", "iced_runtime", @@ -1162,7 +1165,7 @@ dependencies = [ [[package]] name = "iced_renderer" version = "0.15.0-dev" -source = "git+https://github.com/sub07/iced.git?branch=custom-winri#17d55805dec08bea6af98b2613250a6f1d1c836b" +source = "git+https://github.com/sub07/iced.git?branch=custom-winri#2e7e5c59013e643ab4a257a41d615c97174047f7" dependencies = [ "iced_graphics", "iced_tiny_skia", @@ -1174,7 +1177,7 @@ dependencies = [ [[package]] name = "iced_runtime" version = "0.15.0-dev" -source = "git+https://github.com/sub07/iced.git?branch=custom-winri#17d55805dec08bea6af98b2613250a6f1d1c836b" +source = "git+https://github.com/sub07/iced.git?branch=custom-winri#2e7e5c59013e643ab4a257a41d615c97174047f7" dependencies = [ "bytes", "iced_core", @@ -1186,7 +1189,7 @@ dependencies = [ [[package]] name = "iced_tiny_skia" version = "0.15.0-dev" -source = "git+https://github.com/sub07/iced.git?branch=custom-winri#17d55805dec08bea6af98b2613250a6f1d1c836b" +source = "git+https://github.com/sub07/iced.git?branch=custom-winri#2e7e5c59013e643ab4a257a41d615c97174047f7" dependencies = [ "bytemuck", "cosmic-text", @@ -1202,7 +1205,7 @@ dependencies = [ [[package]] name = "iced_wgpu" version = "0.15.0-dev" -source = "git+https://github.com/sub07/iced.git?branch=custom-winri#17d55805dec08bea6af98b2613250a6f1d1c836b" +source = "git+https://github.com/sub07/iced.git?branch=custom-winri#2e7e5c59013e643ab4a257a41d615c97174047f7" dependencies = [ "bitflags 2.10.0", "bytemuck", @@ -1222,7 +1225,7 @@ dependencies = [ [[package]] name = "iced_widget" version = "0.15.0-dev" -source = "git+https://github.com/sub07/iced.git?branch=custom-winri#17d55805dec08bea6af98b2613250a6f1d1c836b" +source = "git+https://github.com/sub07/iced.git?branch=custom-winri#2e7e5c59013e643ab4a257a41d615c97174047f7" dependencies = [ "iced_renderer", "log", @@ -1235,7 +1238,7 @@ dependencies = [ [[package]] name = "iced_winit" version = "0.15.0-dev" -source = "git+https://github.com/sub07/iced.git?branch=custom-winri#17d55805dec08bea6af98b2613250a6f1d1c836b" +source = "git+https://github.com/sub07/iced.git?branch=custom-winri#2e7e5c59013e643ab4a257a41d615c97174047f7" dependencies = [ "iced_debug", "iced_program", @@ -1392,9 +1395,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "7ee5b5339afb4c41626dde77b7a611bd4f2c202b897852b4bcf5d03eddc61010" [[package]] name = "jni" @@ -2224,15 +2227,15 @@ dependencies = [ "concurrent-queue", "hermit-abi", "pin-project-lite", - "rustix 1.1.2", + "rustix 1.1.3", "windows-sys 0.61.2", ] [[package]] name = "portable-atomic" -version = "1.11.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" +checksum = "f59e70c4aef1e55797c2e8fd94a4f2a973fc972cfde0e0b05f683667b0cd39dd" [[package]] name = "portable-atomic-util" @@ -2469,9 +2472,9 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" dependencies = [ "bitflags 2.10.0", "errno", @@ -2488,9 +2491,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +checksum = "62049b2877bf12821e8f9ad256ee38fdc31db7387ec2d3b3f403024de2034aea" [[package]] name = "same-file" @@ -2565,9 +2568,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.145" +version = "1.0.146" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +checksum = "217ca874ae0207aac254aa02c957ded05585a90892cc8d87f9e5fa49669dadd8" dependencies = [ "itoa", "memchr", @@ -3834,7 +3837,7 @@ dependencies = [ "rdev", "webbrowser", "windows 0.62.2", - "windows-strings 0.1.0", + "windows-strings 0.5.1", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 336f020..56db8a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,13 +9,13 @@ license = "MIT" anyhow = "1" windows-strings = "0" log = "0" -log4rs = "1.4.0" +log4rs = "1.4" rdev = { version = "0", features = ["unstable_grab"] } easy-ext = "1" joy-vector = { git = "https://github.com/sub07/rust-utils", version = "0.2.1" } joy-error = { git = "https://github.com/sub07/rust-utils", version = "0.6.0", features = ["log-crate", "anyhow-crate"] } -itertools = "0.14.0" -keyboard-types = { version = "0.8.3", features = ["std", "serde"] } +itertools = "0" +keyboard-types = { version = "0.8", features = ["std", "serde"] } dirs = "6" webbrowser = "1" From dbb098d0588f0272d5c0f5d0331d410b82413659 Mon Sep 17 00:00:00 2001 From: Marius Pardo Date: Tue, 23 Dec 2025 16:26:00 +0100 Subject: [PATCH 11/16] Update main.yml --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7805d2e..b0bb644 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,9 +6,9 @@ jobs: build: needs: check uses: ./.github/workflows/build.yml - if: startsWith(github.head_ref, 'release-') + if: ${{ startsWith(github.head_ref, 'release-') }} deploy: needs: build uses: ./.github/workflows/deploy.yml secrets: inherit - if: startsWith(github.head_ref, 'release-') + if: ${{ startsWith(github.head_ref, 'release-') }} From 470678b0c4f40981af153e72b2c69ef303a6cb03 Mon Sep 17 00:00:00 2001 From: Marius Pardo Date: Tue, 23 Dec 2025 16:29:10 +0100 Subject: [PATCH 12/16] Update main.yml --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b0bb644..2a1c873 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,9 +6,9 @@ jobs: build: needs: check uses: ./.github/workflows/build.yml - if: ${{ startsWith(github.head_ref, 'release-') }} + if: github.ref == 'refs/heads/main' deploy: needs: build uses: ./.github/workflows/deploy.yml secrets: inherit - if: ${{ startsWith(github.head_ref, 'release-') }} + if: github.ref == 'refs/heads/main' From c78d1883e740ef3560e1a6ff0b89d4683fde47b5 Mon Sep 17 00:00:00 2001 From: Marius Pardo Date: Tue, 23 Dec 2025 16:32:36 +0100 Subject: [PATCH 13/16] Update main.yml --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2a1c873..8dd57d5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,9 +6,9 @@ jobs: build: needs: check uses: ./.github/workflows/build.yml - if: github.ref == 'refs/heads/main' + if: contains(github.head_ref, 'release-') deploy: needs: build uses: ./.github/workflows/deploy.yml secrets: inherit - if: github.ref == 'refs/heads/main' + if: contains(github.head_ref, 'release-') From f7dbe4e95fede6c206a28d4fb621178925476c5d Mon Sep 17 00:00:00 2001 From: Marius Pardo Date: Tue, 23 Dec 2025 16:33:44 +0100 Subject: [PATCH 14/16] Update main.yml --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8dd57d5..9c38369 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,9 +6,9 @@ jobs: build: needs: check uses: ./.github/workflows/build.yml - if: contains(github.head_ref, 'release-') + if: startsWith(github.head_ref, 'refs/heads/release-') deploy: needs: build uses: ./.github/workflows/deploy.yml secrets: inherit - if: contains(github.head_ref, 'release-') + if: startsWith(github.head_ref, 'refs/heads/release-') From e42da935df9f80c0f356febfa778c6d5f9575a28 Mon Sep 17 00:00:00 2001 From: Marius Pardo Date: Tue, 23 Dec 2025 16:38:12 +0100 Subject: [PATCH 15/16] Update main.yml --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9c38369..2f6ba7c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,9 +6,9 @@ jobs: build: needs: check uses: ./.github/workflows/build.yml - if: startsWith(github.head_ref, 'refs/heads/release-') + if: ${{ startsWith(github.head_ref, 'refs/heads/release-') }} deploy: needs: build uses: ./.github/workflows/deploy.yml secrets: inherit - if: startsWith(github.head_ref, 'refs/heads/release-') + if: ${{ startsWith(github.head_ref, 'refs/heads/release-') }} From 1c84ab90032edc69664b32e01f4eb51f655219b4 Mon Sep 17 00:00:00 2001 From: Marius Pardo Date: Tue, 23 Dec 2025 17:05:27 +0100 Subject: [PATCH 16/16] Update main.yml --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2f6ba7c..2a1c873 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,9 +6,9 @@ jobs: build: needs: check uses: ./.github/workflows/build.yml - if: ${{ startsWith(github.head_ref, 'refs/heads/release-') }} + if: github.ref == 'refs/heads/main' deploy: needs: build uses: ./.github/workflows/deploy.yml secrets: inherit - if: ${{ startsWith(github.head_ref, 'refs/heads/release-') }} + if: github.ref == 'refs/heads/main'