From a350b88420e9877b034504e22dfca1cae4682aad Mon Sep 17 00:00:00 2001 From: rgdevment Date: Sat, 17 Oct 2026 00:16:01 -0300 Subject: [PATCH 1/2] fix(picker): the shadow and LinkBear stay see-through after Windows restarts its compositor --- crates/linkunbound-shell/src/look.rs | 11 +++++- crates/linkunbound-shell/src/mascot.rs | 1 + crates/linkunbound-win/Cargo.toml | 1 - crates/linkunbound-win/src/lib.rs | 2 +- crates/linkunbound-win/src/native.rs | 47 ++++++++++++++++---------- 5 files changed, 41 insertions(+), 21 deletions(-) diff --git a/crates/linkunbound-shell/src/look.rs b/crates/linkunbound-shell/src/look.rs index dc90941..26a567d 100644 --- a/crates/linkunbound-shell/src/look.rs +++ b/crates/linkunbound-shell/src/look.rs @@ -14,7 +14,7 @@ pub fn style(picker: &Picker, chosen: PickerStyle) { settle(picker); } -/// A remote session or energy saver can start between two links. +/// The system can change between two links, and a refused material must be asked for again. pub fn settle(picker: &Picker) { dress_for(picker, !blends()); } @@ -95,6 +95,7 @@ pub fn dress_the_window(picker: &Picker) { let Some(handle) = crate::native_handle(picker.window()) else { return; }; + keep_see_through(handle); crate::host::keep_off_the_taskbar(handle, corner_of(picker)); if wear_backdrop(picker, handle) { beside_the_pointer(picker); @@ -196,6 +197,14 @@ fn blends() -> bool { true } +#[cfg(windows)] +fn keep_see_through(window: isize) { + linkunbound_win::keep_see_through(window); +} + +#[cfg(not(windows))] +fn keep_see_through(_window: isize) {} + #[cfg(windows)] fn dwm_rounds() -> bool { linkunbound_win::dwm_rounds_corners() diff --git a/crates/linkunbound-shell/src/mascot.rs b/crates/linkunbound-shell/src/mascot.rs index 6f958b4..0294828 100644 --- a/crates/linkunbound-shell/src/mascot.rs +++ b/crates/linkunbound-shell/src/mascot.rs @@ -194,6 +194,7 @@ fn stack(head: &BearHead, paws: &BearPaws, picker: &Picker) { return; }; for window in [head, paws] { + linkunbound_win::keep_see_through(window); linkunbound_win::keep_off_the_taskbar(window); linkunbound_win::never_activates(window); } diff --git a/crates/linkunbound-win/Cargo.toml b/crates/linkunbound-win/Cargo.toml index 1e21925..153d90d 100644 --- a/crates/linkunbound-win/Cargo.toml +++ b/crates/linkunbound-win/Cargo.toml @@ -29,7 +29,6 @@ windows = { version = "0.62", features = [ "Win32_Security_Authorization", "Win32_System_IO", "Win32_System_Pipes", - "Win32_System_Power", "Win32_System_RemoteDesktop", "Win32_System_Threading", "Win32_UI_Shell", diff --git a/crates/linkunbound-win/src/lib.rs b/crates/linkunbound-win/src/lib.rs index 659c618..c5f0ebe 100644 --- a/crates/linkunbound-win/src/lib.rs +++ b/crates/linkunbound-win/src/lib.rs @@ -16,7 +16,7 @@ pub use icons::cached_or_extract as icon_for; #[cfg(windows)] pub use native::{ attach_parent_console, blends_windows, copy_text, current_session, current_user_sid, cursor, - digit_behind, front_is_ours, front_window, is_in_front, keep_off_the_taskbar, + digit_behind, front_is_ours, front_window, is_in_front, keep_off_the_taskbar, keep_see_through, let_whoever_opens_next_come_forward, never_activates, notify_associations_changed, shift_is_down, source_app, strip_title_icon, take_the_keyboard, tuck_under, wear_acrylic, wear_solid, work_area_at, write_line_to_pipe, diff --git a/crates/linkunbound-win/src/native.rs b/crates/linkunbound-win/src/native.rs index cdbe4a4..348b652 100644 --- a/crates/linkunbound-win/src/native.rs +++ b/crates/linkunbound-win/src/native.rs @@ -11,14 +11,15 @@ use windows::Win32::Foundation::{ LocalFree, POINT, WPARAM, }; use windows::Win32::Graphics::Dwm::{ - DWM_SYSTEMBACKDROP_TYPE, DWM_WINDOW_CORNER_PREFERENCE, DWMSBT_NONE, DWMSBT_TRANSIENTWINDOW, - DWMWA_SYSTEMBACKDROP_TYPE, DWMWA_USE_IMMERSIVE_DARK_MODE, DWMWA_WINDOW_CORNER_PREFERENCE, - DWMWCP_ROUND, DwmExtendFrameIntoClientArea, DwmSetWindowAttribute, + DWM_BB_BLURREGION, DWM_BB_ENABLE, DWM_BLURBEHIND, DWM_SYSTEMBACKDROP_TYPE, + DWM_WINDOW_CORNER_PREFERENCE, DWMSBT_NONE, DWMSBT_TRANSIENTWINDOW, DWMWA_SYSTEMBACKDROP_TYPE, + DWMWA_USE_IMMERSIVE_DARK_MODE, DWMWA_WINDOW_CORNER_PREFERENCE, DWMWCP_ROUND, + DwmEnableBlurBehindWindow, DwmExtendFrameIntoClientArea, DwmSetWindowAttribute, }; use windows::Win32::Graphics::Gdi::{ - BI_RGB, BITMAP, BITMAPINFO, BITMAPINFOHEADER, DIB_RGB_COLORS, DeleteObject, GetDC, GetDIBits, - GetMonitorInfoW, GetObjectW, HBITMAP, HMONITOR, MONITOR_DEFAULTTONEAREST, MONITORINFO, - MonitorFromPoint, ReleaseDC, + BI_RGB, BITMAP, BITMAPINFO, BITMAPINFOHEADER, CreateRectRgn, DIB_RGB_COLORS, DeleteObject, + GetDC, GetDIBits, GetMonitorInfoW, GetObjectW, HBITMAP, HMONITOR, MONITOR_DEFAULTTONEAREST, + MONITORINFO, MonitorFromPoint, ReleaseDC, }; use windows::Win32::Security::Authorization::{ ConvertSidToStringSidW, GetSecurityInfo, SE_KERNEL_OBJECT, @@ -38,7 +39,6 @@ use windows::Win32::System::DataExchange::{ use windows::Win32::System::Memory::{GMEM_MOVEABLE, GlobalAlloc, GlobalLock, GlobalUnlock}; use windows::Win32::System::Ole::CF_UNICODETEXT; use windows::Win32::System::Pipes::{GetNamedPipeServerProcessId, WaitNamedPipeW}; -use windows::Win32::System::Power::{GetSystemPowerStatus, SYSTEM_POWER_STATUS}; use windows::Win32::System::RemoteDesktop::ProcessIdToSessionId; use windows::Win32::System::Threading::{ AttachThreadInput, GetCurrentProcess, GetCurrentProcessId, GetCurrentThreadId, OpenProcess, @@ -52,11 +52,11 @@ use windows::Win32::UI::Shell::{SHCNE_ASSOCCHANGED, SHCNF_IDLIST, SHChangeNotify use windows::Win32::UI::Shell::{SHFILEINFOW, SHGFI_ICON, SHGFI_LARGEICON, SHGetFileInfoW}; use windows::Win32::UI::WindowsAndMessaging::{ ASFW_ANY, AllowSetForegroundWindow, GWL_EXSTYLE, GWL_STYLE, GetCursorPos, GetForegroundWindow, - GetSystemMetrics, GetWindowLongPtrW, GetWindowThreadProcessId, HWND_TOPMOST, ICON_BIG, - ICON_SMALL, IsHungAppWindow, SM_REMOTESESSION, SPI_GETHIGHCONTRAST, SWP_FRAMECHANGED, - SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, SWP_NOZORDER, SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS, - SendMessageW, SetForegroundWindow, SetWindowLongPtrW, SetWindowPos, SystemParametersInfoW, - WM_SETICON, WS_EX_DLGMODALFRAME, WS_EX_NOACTIVATE, WS_EX_TOOLWINDOW, WS_SYSMENU, + GetWindowLongPtrW, GetWindowThreadProcessId, HWND_TOPMOST, ICON_BIG, ICON_SMALL, + IsHungAppWindow, SPI_GETHIGHCONTRAST, SWP_FRAMECHANGED, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, + SWP_NOZORDER, SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS, SendMessageW, SetForegroundWindow, + SetWindowLongPtrW, SetWindowPos, SystemParametersInfoW, WM_SETICON, WS_EX_DLGMODALFRAME, + WS_EX_NOACTIVATE, WS_EX_TOOLWINDOW, WS_SYSMENU, }; use windows::Win32::UI::WindowsAndMessaging::{DestroyIcon, GetIconInfo, HICON, ICONINFO}; @@ -196,13 +196,9 @@ fn framed_by_dwm( } } -/// Remote Desktop, energy saver and high contrast can leave see-through pixels black. +/// High contrast draws no material and no see-through pixels. #[must_use] pub fn blends_windows() -> bool { - let remote = unsafe { GetSystemMetrics(SM_REMOTESESSION) } != 0; - let mut power = SYSTEM_POWER_STATUS::default(); - let saving = - unsafe { GetSystemPowerStatus(&raw mut power) }.is_ok() && power.SystemStatusFlag == 1; let mut contrast = HIGHCONTRASTW { cbSize: size_of::() as u32, ..Default::default() @@ -217,7 +213,22 @@ pub fn blends_windows() -> bool { } .is_ok() && contrast.dwFlags.contains(HCF_HIGHCONTRASTON); - !(remote || saving || high) + !high +} + +/// winit asks for see-through pixels only when the window is made, and a restarted DWM forgets. +pub fn keep_see_through(window: isize) { + let region = unsafe { CreateRectRgn(0, 0, -1, -1) }; + let blur = DWM_BLURBEHIND { + dwFlags: DWM_BB_ENABLE | DWM_BB_BLURREGION, + fEnable: true.into(), + hRgnBlur: region, + fTransitionOnMaximized: false.into(), + }; + unsafe { + let _ = DwmEnableBlurBehindWindow(HWND(window as *mut std::ffi::c_void), &blur); + let _ = DeleteObject(region.into()); + } } /// Slides `window` just below `above` in the topmost band, so what `above` draws covers it. From 2be1853cb64a824527bbef3edb4156c79d2282e5 Mon Sep 17 00:00:00 2001 From: rgdevment Date: Sat, 17 Oct 2026 00:22:24 -0300 Subject: [PATCH 2/2] fix(picker): ask for see-through pixels where the backdrop is worn --- crates/linkunbound-shell/src/look.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/linkunbound-shell/src/look.rs b/crates/linkunbound-shell/src/look.rs index 26a567d..d67054e 100644 --- a/crates/linkunbound-shell/src/look.rs +++ b/crates/linkunbound-shell/src/look.rs @@ -71,6 +71,7 @@ pub fn corner_of(picker: &Picker) -> f64 { /// Refused, the material would leave the clear sheet showing the desktop through it, so the /// painted sheet and its shadow come back. Returns whether the window changed size. pub fn wear_backdrop(picker: &Picker, handle: isize) -> bool { + keep_see_through(handle); let palette = picker.global::(); if palette.get_solid() { wear_solid(handle, !palette.get_light()); @@ -95,7 +96,6 @@ pub fn dress_the_window(picker: &Picker) { let Some(handle) = crate::native_handle(picker.window()) else { return; }; - keep_see_through(handle); crate::host::keep_off_the_taskbar(handle, corner_of(picker)); if wear_backdrop(picker, handle) { beside_the_pointer(picker);