diff --git a/Cargo.lock b/Cargo.lock index bb90b88..b727c50 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -86,15 +86,6 @@ 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" @@ -188,9 +179,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.50" +version = "1.2.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f50d563227a1c37cc0a263f64eca3334388c01c5e4c4861a9def205c614383c" +checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203" dependencies = [ "find-msvc-tools", "jobserver", @@ -642,9 +633,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" +checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff" [[package]] name = "float_next_after" @@ -1056,7 +1047,6 @@ source = "git+https://github.com/sub07/iced.git?branch=custom-winri#2e7e5c59013e dependencies = [ "iced_core", "iced_debug", - "iced_devtools", "iced_futures", "iced_renderer", "iced_runtime", @@ -1065,21 +1055,6 @@ dependencies = [ "thiserror 2.0.17", ] -[[package]] -name = "iced_beacon" -version = "0.15.0-dev" -source = "git+https://github.com/sub07/iced.git?branch=custom-winri#2e7e5c59013e643ab4a257a41d615c97174047f7" -dependencies = [ - "bincode", - "futures", - "iced_core", - "log", - "semver", - "serde", - "thiserror 2.0.17", - "tokio", -] - [[package]] name = "iced_core" version = "0.15.0-dev" @@ -1092,7 +1067,6 @@ dependencies = [ "log", "num-traits", "rustc-hash 2.1.1", - "serde", "smol_str", "thiserror 2.0.17", "web-time", @@ -1103,23 +1077,11 @@ name = "iced_debug" version = "0.15.0-dev" source = "git+https://github.com/sub07/iced.git?branch=custom-winri#2e7e5c59013e643ab4a257a41d615c97174047f7" 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#2e7e5c59013e643ab4a257a41d615c97174047f7" -dependencies = [ - "iced_debug", - "iced_program", - "iced_widget", - "log", -] - [[package]] name = "iced_futures" version = "0.15.0-dev" @@ -1395,9 +1357,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee5b5339afb4c41626dde77b7a611bd4f2c202b897852b4bcf5d03eddc61010" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "jni" @@ -1433,8 +1395,8 @@ dependencies = [ [[package]] name = "joy-error" -version = "0.6.0" -source = "git+https://github.com/sub07/rust-utils#0511d29b84705811e51d249a3327494053499ecc" +version = "0.8.0" +source = "git+https://github.com/sub07/rust-utils?rev=a7103d4dacf67412b9e85678581b1387649a2897#a7103d4dacf67412b9e85678581b1387649a2897" dependencies = [ "anyhow", "easy-ext", @@ -1444,7 +1406,7 @@ dependencies = [ [[package]] name = "joy-vector" version = "0.2.1" -source = "git+https://github.com/sub07/rust-utils#0511d29b84705811e51d249a3327494053499ecc" +source = "git+https://github.com/sub07/rust-utils?rev=a7103d4dacf67412b9e85678581b1387649a2897#a7103d4dacf67412b9e85678581b1387649a2897" [[package]] name = "js-sys" @@ -1523,13 +1485,13 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libredox" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df15f6eac291ed1cf25865b1ee60399f57e7c227e7f51bdbd4c5270396a9ed50" +checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" dependencies = [ "bitflags 2.10.0", "libc", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", ] [[package]] @@ -1721,17 +1683,6 @@ dependencies = [ "paste", ] -[[package]] -name = "mio" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" -dependencies = [ - "libc", - "wasi", - "windows-sys 0.61.2", -] - [[package]] name = "mock_instant" version = "0.6.0" @@ -2233,9 +2184,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f59e70c4aef1e55797c2e8fd94a4f2a973fc972cfde0e0b05f683667b0cd39dd" +checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" [[package]] name = "portable-atomic-util" @@ -2281,9 +2232,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.103" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +checksum = "9695f8df41bb4f3d222c95a67532365f569318332d03d5f3f67f37b20e6ebdf0" dependencies = [ "unicode-ident", ] @@ -2406,9 +2357,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec96166dafa0886eb81fe1c0a388bece180fbef2135f97c1e2cf8302e74b43b5" +checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27" dependencies = [ "bitflags 2.10.0", ] @@ -2491,9 +2442,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62049b2877bf12821e8f9ad256ee38fdc31db7387ec2d3b3f403024de2034aea" +checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" [[package]] name = "same-file" @@ -2521,10 +2472,6 @@ name = "semver" version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" -dependencies = [ - "serde", - "serde_core", -] [[package]] name = "serde" @@ -2568,15 +2515,15 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.146" +version = "1.0.148" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "217ca874ae0207aac254aa02c957ded05585a90892cc8d87f9e5fa49669dadd8" +checksum = "3084b546a1dd6289475996f182a22aba973866ea8e8b02c51d9f46b1336a22da" dependencies = [ "itoa", "memchr", - "ryu", "serde", "serde_core", + "zmij", ] [[package]] @@ -2638,16 +2585,6 @@ dependencies = [ "serde", ] -[[package]] -name = "socket2" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" -dependencies = [ - "libc", - "windows-sys 0.60.2", -] - [[package]] name = "softbuffer" version = "0.4.8" @@ -2860,24 +2797,7 @@ 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]] @@ -3559,15 +3479,6 @@ 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" @@ -3601,30 +3512,13 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", + "windows_i686_gnullvm", "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" @@ -3646,12 +3540,6 @@ 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" @@ -3664,12 +3552,6 @@ 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" @@ -3682,24 +3564,12 @@ 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" @@ -3712,12 +3582,6 @@ 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" @@ -3730,12 +3594,6 @@ 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" @@ -3748,12 +3606,6 @@ 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" @@ -3766,12 +3618,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - [[package]] name = "winit" version = "0.30.8" @@ -3822,7 +3668,7 @@ dependencies = [ [[package]] name = "winri" -version = "0.3.0" +version = "0.3.1" dependencies = [ "anyhow", "dirs", @@ -3995,3 +3841,9 @@ dependencies = [ "quote", "syn", ] + +[[package]] +name = "zmij" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5858cd3a46fff31e77adea2935e357e3a2538d870741617bfb7c943e218fee6" diff --git a/Cargo.toml b/Cargo.toml index 56db8a5..ed587b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "winri" description = "A window scrolling tiler for Windows 11" -version = "0.3.0" +version = "0.3.1" edition = "2024" license = "MIT" @@ -12,8 +12,8 @@ log = "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"] } +joy-vector = { git = "https://github.com/sub07/rust-utils", rev = "a7103d4dacf67412b9e85678581b1387649a2897" } +joy-error = { git = "https://github.com/sub07/rust-utils", rev = "a7103d4dacf67412b9e85678581b1387649a2897", features = ["log-crate", "anyhow-crate"] } itertools = "0" keyboard-types = { version = "0.8", features = ["std", "serde"] } dirs = "6" @@ -28,7 +28,6 @@ features = [ "Win32_System_Threading", "Win32_System_ProcessStatus", "Win32_Graphics_Dwm", - "Win32_UI_Controls", "Win32_Graphics_Gdi" ] @@ -36,9 +35,6 @@ features = [ git = "https://github.com/sub07/iced.git" branch = "custom-winri" features = [ - "debug", - "time-travel", - "web-colors", "wgpu", "canvas", "tokio", diff --git a/src/app/mod.rs b/src/app/mod.rs index f8c55cc..46374e2 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -2,6 +2,7 @@ mod action; pub mod model; mod service; mod subscription; +pub mod task; mod view; use anyhow::Context; @@ -10,6 +11,7 @@ use iced::{ theme::Palette, window::{Settings, settings::PlatformSpecific}, }; +use joy_error::ResultUtilityExt; use crate::{ app::{ @@ -23,7 +25,7 @@ use crate::{ scroll_tiler::ScrollTiler, system, utils::math::Size, - window::{self, Window}, + window::{self}, }; pub struct State { @@ -72,19 +74,7 @@ fn create_overlay_window(screen_size: Size) -> (iced::window::Id, Task) ..Default::default() }); - ( - 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() - }), - ) + (id, task.then(iced::window::enable_mouse_passthrough)) } impl State { @@ -115,6 +105,12 @@ impl State { pub fn handle_app_message(&mut self, message: Message) -> Task { let mut task = Task::none(); + + // 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. + task = task.chain(task::ensure_overlay_not_focused(self.overlay_window_id)); + match message { Message::Global(global_message) => { task = task.chain(self.handle_global_message(global_message)); @@ -198,6 +194,4 @@ impl Result { } self } - - fn discard(self) {} } diff --git a/src/app/subscription/global/input.rs b/src/app/subscription/global/input.rs index 8506b2a..694b025 100644 --- a/src/app/subscription/global/input.rs +++ b/src/app/subscription/global/input.rs @@ -1,9 +1,11 @@ -use std::thread; +use std::{thread, time::Duration}; use iced::futures::channel::mpsc::Sender; +use joy_error::ResultUtilityExt; use keyboard_types::Modifiers; +use rdev::simulate; -use crate::app::subscription::global::GlobalMessage; +use crate::{app::subscription::global::GlobalMessage, system}; fn grab_event_processing( event: rdev::Event, @@ -22,6 +24,8 @@ fn grab_event_processing( return Some(event); } + log::debug!("{:?}", event.event_type); + match event.event_type { rdev::EventType::KeyPress(key) => { match key { @@ -47,6 +51,16 @@ fn grab_event_processing( } _ => { tx.try_send(GlobalMessage::Key(*modifiers, key)).unwrap(); + if (*modifiers, key) == (Modifiers::META, Key::KeyL) { + // Win + L is a system shortcut to lock the screen + // We cannot override or block it + // It causes an unwanted behavior when triggering it causing the win key down to be registered but not the win up + // So when on the lock screen the win key is considered down despite the user not pressing it + // FIX: Simulate a win up event after the lock screen appeared by wait after a delay + thread::sleep(Duration::from_millis(300)); + simulate(&EventType::KeyRelease(Key::MetaLeft)).discard(); + simulate(&EventType::KeyRelease(Key::MetaRight)).discard(); + } return (!modifiers.contains(Modifiers::META)).then_some(event); } } @@ -79,7 +93,7 @@ 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 + let mut modifiers = system::current_modifiers(); rdev::_grab(move |event| grab_event_processing(event, &mut modifiers, tx.clone())) .unwrap(); }); diff --git a/src/app/subscription/global/window.rs b/src/app/subscription/global/window.rs index 251e992..ce3c12a 100644 --- a/src/app/subscription/global/window.rs +++ b/src/app/subscription/global/window.rs @@ -9,7 +9,7 @@ use iced::futures::channel::mpsc::Sender; use windows::Win32::UI::{ Accessibility::{SetWinEventHook, UnhookWinEvent}, WindowsAndMessaging::{ - EVENT_OBJECT_CREATE, EVENT_OBJECT_FOCUS, GetMessageW, WINEVENT_OUTOFCONTEXT, + EVENT_OBJECT_CREATE, EVENT_OBJECT_LOCATIONCHANGE, GetMessageW, WINEVENT_OUTOFCONTEXT, WINEVENT_SKIPOWNPROCESS, }, }; @@ -60,7 +60,7 @@ impl WindowHookManager { static WINDOW_HOOK_MANAGER: Mutex> = Mutex::new(None); -unsafe extern "system" fn hook_callback( +unsafe extern "system" fn win_event_hook_callback( _hwineventhook: windows::Win32::UI::Accessibility::HWINEVENTHOOK, _event: u32, _hwnd: windows::Win32::Foundation::HWND, @@ -86,9 +86,9 @@ pub fn launch(tx: Sender) { .spawn(move || unsafe { let hook = SetWinEventHook( EVENT_OBJECT_CREATE, - EVENT_OBJECT_FOCUS, + EVENT_OBJECT_LOCATIONCHANGE, None, - Some(hook_callback), + Some(win_event_hook_callback), 0, 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS, diff --git a/src/app/task/mod.rs b/src/app/task/mod.rs new file mode 100644 index 0000000..dadd700 --- /dev/null +++ b/src/app/task/mod.rs @@ -0,0 +1,36 @@ +/// This module contains asynchronous `task`s for the iced runtime. +use anyhow::Context; +use iced::Task; +use joy_error::{ResultUtilityExt, log::ResultLogExt}; + +use crate::{ + app::{self}, + system, + window::Window, +}; + +pub fn ensure_overlay_not_focused(overlay_window_id: iced::window::Id) -> Task { + iced::window::raw_id::(overlay_window_id).then(|raw_id| { + unfocus_window(raw_id) + .context("unfocusing overlay window") + .error() + .log_err() + .discard(); + Task::none() + }) +} + +fn unfocus_window(raw_id: u64) -> anyhow::Result<()> { + let focused_window = Window::focused().context("getting focused window")?; + + let overlay_window = Window::from_safe_hwnd(raw_id).context(format!( + "given raw id ({raw_id}) is invalid: expected overlay raw id (aka. HWND)" + ))?; + + let desktop_window = system::get_desktop_window().context("getting desktop window")?; + if focused_window == overlay_window { + desktop_window.focus()?; + } + + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index a6f76e6..2ee52c9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -40,6 +40,7 @@ fn main() { message_box::message_box_fatal_bug_report(info); system::restore_windows(); default_hook(info); + std::process::exit(1); })); if let Err(e) = logger::setup() diff --git a/src/scroll_tiler.rs b/src/scroll_tiler.rs index 5cecc6d..9399a8c 100644 --- a/src/scroll_tiler.rs +++ b/src/scroll_tiler.rs @@ -9,12 +9,25 @@ use crate::{cast, utils::math::Size, window::Window}; #[derive(PartialEq)] pub struct WindowItem { pub inner: Window, + pub requested_width: Option, pub width: f32, } impl WindowItem { pub const fn new(inner: Window, width: f32) -> Self { - Self { inner, width } + Self { + inner, + requested_width: Some(width), + width, + } + } + + fn request_width(&mut self, width: f32) { + self.requested_width = Some(width); + } + + fn requested_width(&mut self) -> Option { + self.requested_width.take() } } @@ -128,12 +141,12 @@ impl ScrollTiler { pub fn set_current_window_fullscreen(&mut self) { if let Some(focus_index) = self.focus_index() { - let screen_width = self.screen_size.width(); + let width = self.max_screen_width(); // -1 to avoid occupying the whole screen and causing scroll issues // 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 = self.padding.mul_add(-2.0, screen_width) - 1.0; + self.windows[focus_index].request_width(width); } } @@ -152,8 +165,12 @@ impl ScrollTiler { self.resize_current_window_width_by_resize_increment(-1); } + pub fn max_screen_width(&self) -> f32 { + self.padding.mul_add(-2.0, self.screen_size.width()) - 1.0 + } + /// Resize the current window width by the resize increment in the given direction. - /// Direction should be 1 for increasing width and -1 for decreasing width. + /// Direction should be 1 for increasing width and -1 for decreasing width.ze 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 @@ -167,7 +184,7 @@ impl ScrollTiler { 0.0, self.padding.mul_add(-2.0, self.screen_size().width()) - 1.0, ); - self.windows[focus_index].width = new_width; + self.windows[focus_index].request_width(new_width); } } @@ -184,6 +201,8 @@ impl ScrollTiler { self.windows .retain(|item| windows_snapshot.contains(&item.inner)); + self.update_widths(); + self.append_new_windows(windows_snapshot); let windows_positions = self.windows_positions(); @@ -197,7 +216,6 @@ impl ScrollTiler { ); } self.layout_windows(&windows_positions); - self.reconciliate_window_widths(); if let Some(new_focused_window_index) = self .focus_index() @@ -261,23 +279,20 @@ impl ScrollTiler { } } - fn reconciliate_window_widths(&mut self) { + /// Width + fn update_widths(&mut self) { + let max_screen_width = self.max_screen_width(); for window in &mut self.windows { - let window_rect = match window.inner.desktop_manager_bounds() { - Ok(rect) => rect, - Err(e) => { - warn!( - "Error while reconciliating window, skipping to next one (window might have been closed just after enumeration): {e}" - ); - continue; - } - }; - - let actual_size = window_rect.right - window_rect.left; - - let expected_size = window.width; - if expected_size < actual_size { - window.width = self.padding.mul_add(2.0, actual_size); + if let Some(requested_width) = window.requested_width() { + window.width = requested_width; + } else if let Ok(bounds) = window + .inner + .desktop_manager_bounds() + .context("Updating widths") + .error() + .log_err() + { + window.width = bounds.size().width().min(max_screen_width); } } } diff --git a/src/system.rs b/src/system.rs index 16696f4..807eca1 100644 --- a/src/system.rs +++ b/src/system.rs @@ -1,7 +1,13 @@ use log::warn; use windows::Win32::{ Graphics::Gdi::{COLOR_HIGHLIGHT, GetSysColor}, - UI::WindowsAndMessaging::{GetDesktopWindow, GetSystemMetrics, SM_CXSCREEN, SM_CYSCREEN}, + UI::{ + Input::KeyboardAndMouse::{ + GetKeyState, VIRTUAL_KEY, VK_CONTROL, VK_LCONTROL, VK_LSHIFT, VK_LWIN, VK_MENU, + VK_RWIN, VK_SHIFT, + }, + WindowsAndMessaging::{GetDesktopWindow, GetSystemMetrics, SM_CXSCREEN, SM_CYSCREEN}, + }, }; use crate::{ @@ -51,3 +57,40 @@ pub fn restore_windows() { pub fn get_desktop_window() -> anyhow::Result { Window::from_hwnd(wincall_into_result!(GetDesktopWindow())?) } + +#[must_use] +pub fn current_modifiers() -> keyboard_types::Modifiers { + fn is_vkey_down(key: VIRTUAL_KEY) -> bool { + let key_state = match wincall_into_result!(GetKeyState(key.0.into())) { + Ok(res) => u16::from_ne_bytes(res.to_ne_bytes()), + Err(e) => { + log::error!( + "Error while checking if {key:?} modifier is pressed, defaulting to not pressed: {e}" + ); + 0 + } + }; + + key_state & 0xFF80 == 0xFF80 + } + + let mut modifiers = keyboard_types::Modifiers::empty(); + + if is_vkey_down(VK_SHIFT) || is_vkey_down(VK_LSHIFT) { + modifiers.insert(keyboard_types::Modifiers::SHIFT); + } + + if is_vkey_down(VK_CONTROL) || is_vkey_down(VK_LCONTROL) { + modifiers.insert(keyboard_types::Modifiers::CONTROL); + } + + if is_vkey_down(VK_LWIN) || is_vkey_down(VK_RWIN) { + modifiers.insert(keyboard_types::Modifiers::META); + } + + if is_vkey_down(VK_MENU) { + modifiers.insert(keyboard_types::Modifiers::ALT); + } + + modifiers +} diff --git a/src/winapi.rs b/src/winapi.rs index 8172351..7f078ed 100644 --- a/src/winapi.rs +++ b/src/winapi.rs @@ -2,18 +2,26 @@ use windows::Win32::Foundation::{GetLastError, SetLastError, WIN32_ERROR}; use windows::Win32::UI::WindowsAndMessaging::{MESSAGEBOX_RESULT, MESSAGEBOX_STYLE, MessageBoxW}; use windows_strings::PCWSTR; -pub fn message_box(title: &str, message: &str, kind: MESSAGEBOX_STYLE) -> MESSAGEBOX_RESULT { +pub fn str_to_wide(s: &str) -> Vec { use std::os::windows::ffi::OsStrExt; - let title_os = std::ffi::OsStr::new(&title); - let title = title_os.encode_wide().chain(std::iter::once(0)); - let title = title.collect::>(); + let os_str = std::ffi::OsStr::new(&s); + let wide: Vec = os_str.encode_wide().chain(std::iter::once(0)).collect(); + wide +} - let message_os = std::ffi::OsStr::new(&message); - let message = message_os.encode_wide().chain(std::iter::once(0)); - let message = message.collect::>(); +#[easy_ext::ext(WindowsStringsExt)] +pub impl Vec { + fn as_pcwstr(&self) -> PCWSTR { + PCWSTR::from_raw(self.as_ptr()) + } +} + +pub fn message_box(title: &str, message: &str, kind: MESSAGEBOX_STYLE) -> MESSAGEBOX_RESULT { + let title = str_to_wide(title); + let message = str_to_wide(message); - unsafe { MessageBoxW(None, PCWSTR(message.as_ptr()), PCWSTR(title.as_ptr()), kind) } + unsafe { MessageBoxW(None, message.as_pcwstr(), title.as_pcwstr(), kind) } } pub fn clear_last_error() {