-
-
Notifications
You must be signed in to change notification settings - Fork 207
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(wm): add "cloak" window-hiding-behaviour
This commit adds cloaking as a window-hiding-behaviour option. https://devblogs.microsoft.com/oldnewthing/20200302-00/?p=103507 for more information on cloaking. Cloaking is the same mechanism used by the native Virtual Desktops feature by the Windows team, however it is deliberately hidden from the public Windows API. GitHub user Ciantic's VirtualDesktopAccessor crate documents the private IApplicationView COM interface which contains the hidden and undocumented SetCloak method, which can be used to cloak and uncloak application windows by their HWNDs. With some help from Ciantic and manual exploration to determine the correct flags values to use, komorebi is now able to use the cloaking mechanism when switching workspaces, which results in significantly higher reliability and significantly less jank on workspace transitions. komorebi's use of this cloaking mechanism also retains the flexibility of per-monitor workspaces that users have come to know and enjoy. This has only been tested on Windows 11, it is not yet known if calling the SetCloak function in IApplicationWindow will cause crashes on Windows 10.
- Loading branch information
Showing
10 changed files
with
431 additions
and
17 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -211,6 +211,7 @@ pub enum MoveBehaviour { | |
pub enum HidingBehaviour { | ||
Hide, | ||
Minimize, | ||
Cloak, | ||
} | ||
|
||
#[derive( | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,246 @@ | ||
// This code is largely taken verbatim from this repository: https://github.com/Ciantic/AltTabAccessor | ||
// which the author Jari Pennanen (Ciantic) has kindly made available with the MIT license, available | ||
// in full here: https://github.com/Ciantic/AltTabAccessor/blob/main/LICENSE.txt | ||
|
||
#![allow(clippy::use_self)] | ||
|
||
use std::ffi::c_void; | ||
use std::ops::Deref; | ||
use windows::core::IUnknown; | ||
use windows::core::IUnknown_Vtbl; | ||
use windows::core::GUID; | ||
use windows::core::HRESULT; | ||
use windows::core::HSTRING; | ||
use windows::core::PCWSTR; | ||
use windows::core::PWSTR; | ||
use windows::Win32::Foundation::BOOL; | ||
use windows::Win32::Foundation::HWND; | ||
use windows::Win32::Foundation::RECT; | ||
use windows::Win32::Foundation::SIZE; | ||
use windows::Win32::UI::Shell::Common::IObjectArray; | ||
|
||
type DesktopID = GUID; | ||
|
||
// Idea here is that the cloned ComIn instance lifetime is within the original ComIn instance lifetime | ||
#[repr(transparent)] | ||
pub struct ComIn<'a, T> { | ||
data: T, | ||
_phantom: std::marker::PhantomData<&'a T>, | ||
} | ||
|
||
impl<'a, T: Clone> ComIn<'a, T> { | ||
pub fn new(t: &'a T) -> Self { | ||
Self { | ||
data: t.clone(), | ||
_phantom: std::marker::PhantomData, | ||
} | ||
} | ||
|
||
pub const unsafe fn unsafe_new_no_clone(t: T) -> Self { | ||
Self { | ||
data: t, | ||
_phantom: std::marker::PhantomData, | ||
} | ||
} | ||
} | ||
|
||
impl<'a, T> Deref for ComIn<'a, T> { | ||
type Target = T; | ||
fn deref(&self) -> &Self::Target { | ||
&self.data | ||
} | ||
} | ||
|
||
#[allow(non_upper_case_globals)] | ||
pub const CLSID_ImmersiveShell: GUID = GUID { | ||
data1: 0xC2F0_3A33, | ||
data2: 0x21F5, | ||
data3: 0x47FA, | ||
data4: [0xB4, 0xBB, 0x15, 0x63, 0x62, 0xA2, 0xF2, 0x39], | ||
}; | ||
|
||
#[allow(clippy::upper_case_acronyms)] | ||
type DWORD = u32; | ||
#[allow(clippy::upper_case_acronyms)] | ||
type INT = i32; | ||
#[allow(clippy::upper_case_acronyms)] | ||
type LPVOID = *mut c_void; | ||
#[allow(clippy::upper_case_acronyms)] | ||
type UINT = u32; | ||
#[allow(clippy::upper_case_acronyms)] | ||
type ULONG = u32; | ||
#[allow(clippy::upper_case_acronyms)] | ||
type ULONGLONG = u64; | ||
|
||
type IAsyncCallback = UINT; | ||
type IImmersiveMonitor = UINT; | ||
type IApplicationViewOperation = UINT; | ||
type IApplicationViewPosition = UINT; | ||
type IImmersiveApplication = UINT; | ||
type IApplicationViewChangeListener = UINT; | ||
|
||
#[allow(non_camel_case_types)] | ||
type APPLICATION_VIEW_COMPATIBILITY_POLICY = UINT; | ||
#[allow(non_camel_case_types)] | ||
type APPLICATION_VIEW_CLOAK_TYPE = UINT; | ||
|
||
#[windows_interface::interface("6D5140C1-7436-11CE-8034-00AA006009FA")] | ||
pub unsafe trait IServiceProvider: IUnknown { | ||
pub unsafe fn query_service( | ||
&self, | ||
guid_service: *const GUID, | ||
riid: *const GUID, | ||
ppv_object: *mut *mut c_void, | ||
) -> HRESULT; | ||
} | ||
|
||
#[windows_interface::interface("372E1D3B-38D3-42E4-A15B-8AB2B178F513")] | ||
pub unsafe trait IApplicationView: IUnknown { | ||
/* IInspecateble */ | ||
pub unsafe fn get_iids( | ||
&self, | ||
out_iid_count: *mut ULONG, | ||
out_opt_iid_array_ptr: *mut *mut GUID, | ||
) -> HRESULT; | ||
pub unsafe fn get_runtime_class_name(&self, out_opt_class_name: *mut HSTRING) -> HRESULT; | ||
pub unsafe fn get_trust_level(&self, ptr_trust_level: LPVOID) -> HRESULT; | ||
|
||
/* IApplicationView methods */ | ||
pub unsafe fn set_focus(&self) -> HRESULT; | ||
pub unsafe fn switch_to(&self) -> HRESULT; | ||
|
||
pub unsafe fn try_invoke_back(&self, ptr_async_callback: IAsyncCallback) -> HRESULT; | ||
pub unsafe fn get_thumbnail_window(&self, out_hwnd: *mut HWND) -> HRESULT; | ||
pub unsafe fn get_monitor(&self, out_monitors: *mut *mut IImmersiveMonitor) -> HRESULT; | ||
pub unsafe fn get_visibility(&self, out_int: LPVOID) -> HRESULT; | ||
pub unsafe fn set_cloak( | ||
&self, | ||
application_view_cloak_type: APPLICATION_VIEW_CLOAK_TYPE, | ||
unknown: INT, | ||
) -> HRESULT; | ||
pub unsafe fn get_position( | ||
&self, | ||
unknowniid: *const GUID, | ||
unknown_array_ptr: LPVOID, | ||
) -> HRESULT; | ||
pub unsafe fn set_position(&self, view_position: *mut IApplicationViewPosition) -> HRESULT; | ||
pub unsafe fn insert_after_window(&self, window: HWND) -> HRESULT; | ||
pub unsafe fn get_extended_frame_position(&self, rect: *mut RECT) -> HRESULT; | ||
pub unsafe fn get_app_user_model_id(&self, id: *mut PWSTR) -> HRESULT; // Proc17 | ||
pub unsafe fn set_app_user_model_id(&self, id: PCWSTR) -> HRESULT; | ||
pub unsafe fn is_equal_by_app_user_model_id(&self, id: PCWSTR, out_result: *mut INT) | ||
-> HRESULT; | ||
|
||
/*** IApplicationView methods ***/ | ||
pub unsafe fn get_view_state(&self, out_state: *mut UINT) -> HRESULT; // Proc20 | ||
pub unsafe fn set_view_state(&self, state: UINT) -> HRESULT; // Proc21 | ||
pub unsafe fn get_neediness(&self, out_neediness: *mut INT) -> HRESULT; // Proc22 | ||
pub unsafe fn get_last_activation_timestamp(&self, out_timestamp: *mut ULONGLONG) -> HRESULT; | ||
pub unsafe fn set_last_activation_timestamp(&self, timestamp: ULONGLONG) -> HRESULT; | ||
pub unsafe fn get_virtual_desktop_id(&self, out_desktop_guid: *mut DesktopID) -> HRESULT; | ||
pub unsafe fn set_virtual_desktop_id(&self, desktop_guid: *const DesktopID) -> HRESULT; | ||
pub unsafe fn get_show_in_switchers(&self, out_show: *mut INT) -> HRESULT; | ||
pub unsafe fn set_show_in_switchers(&self, show: INT) -> HRESULT; | ||
pub unsafe fn get_scale_factor(&self, out_scale_factor: *mut INT) -> HRESULT; | ||
pub unsafe fn can_receive_input(&self, out_can: *mut BOOL) -> HRESULT; | ||
pub unsafe fn get_compatibility_policy_type( | ||
&self, | ||
out_policy_type: *mut APPLICATION_VIEW_COMPATIBILITY_POLICY, | ||
) -> HRESULT; | ||
pub unsafe fn set_compatibility_policy_type( | ||
&self, | ||
policy_type: APPLICATION_VIEW_COMPATIBILITY_POLICY, | ||
) -> HRESULT; | ||
|
||
pub unsafe fn get_size_constraints( | ||
&self, | ||
monitor: *mut IImmersiveMonitor, | ||
out_size1: *mut SIZE, | ||
out_size2: *mut SIZE, | ||
) -> HRESULT; | ||
pub unsafe fn get_size_constraints_for_dpi( | ||
&self, | ||
dpi: UINT, | ||
out_size1: *mut SIZE, | ||
out_size2: *mut SIZE, | ||
) -> HRESULT; | ||
pub unsafe fn set_size_constraints_for_dpi( | ||
&self, | ||
dpi: *const UINT, | ||
size1: *const SIZE, | ||
size2: *const SIZE, | ||
) -> HRESULT; | ||
|
||
pub unsafe fn on_min_size_preferences_updated(&self, window: HWND) -> HRESULT; | ||
pub unsafe fn apply_operation(&self, operation: *mut IApplicationViewOperation) -> HRESULT; | ||
pub unsafe fn is_tray(&self, out_is: *mut BOOL) -> HRESULT; | ||
pub unsafe fn is_in_high_zorder_band(&self, out_is: *mut BOOL) -> HRESULT; | ||
pub unsafe fn is_splash_screen_presented(&self, out_is: *mut BOOL) -> HRESULT; | ||
pub unsafe fn flash(&self) -> HRESULT; | ||
pub unsafe fn get_root_switchable_owner(&self, app_view: *mut IApplicationView) -> HRESULT; // proc45 | ||
pub unsafe fn enumerate_ownership_tree(&self, objects: *mut IObjectArray) -> HRESULT; // proc46 | ||
|
||
pub unsafe fn get_enterprise_id(&self, out_id: *mut PWSTR) -> HRESULT; // proc47 | ||
pub unsafe fn is_mirrored(&self, out_is: *mut BOOL) -> HRESULT; // | ||
|
||
pub unsafe fn unknown1(&self, arg: *mut INT) -> HRESULT; | ||
pub unsafe fn unknown2(&self, arg: *mut INT) -> HRESULT; | ||
pub unsafe fn unknown3(&self, arg: *mut INT) -> HRESULT; | ||
pub unsafe fn unknown4(&self, arg: INT) -> HRESULT; | ||
pub unsafe fn unknown5(&self, arg: *mut INT) -> HRESULT; | ||
pub unsafe fn unknown6(&self, arg: INT) -> HRESULT; | ||
pub unsafe fn unknown7(&self) -> HRESULT; | ||
pub unsafe fn unknown8(&self, arg: *mut INT) -> HRESULT; | ||
pub unsafe fn unknown9(&self, arg: INT) -> HRESULT; | ||
pub unsafe fn unknown10(&self, arg: INT, arg2: INT) -> HRESULT; | ||
pub unsafe fn unknown11(&self, arg: INT) -> HRESULT; | ||
pub unsafe fn unknown12(&self, arg: *mut SIZE) -> HRESULT; | ||
} | ||
|
||
#[windows_interface::interface("1841c6d7-4f9d-42c0-af41-8747538f10e5")] | ||
pub unsafe trait IApplicationViewCollection: IUnknown { | ||
pub unsafe fn get_views(&self, out_views: *mut IObjectArray) -> HRESULT; | ||
|
||
pub unsafe fn get_views_by_zorder(&self, out_views: *mut IObjectArray) -> HRESULT; | ||
|
||
pub unsafe fn get_views_by_app_user_model_id( | ||
&self, | ||
id: PCWSTR, | ||
out_views: *mut IObjectArray, | ||
) -> HRESULT; | ||
|
||
pub unsafe fn get_view_for_hwnd( | ||
&self, | ||
window: HWND, | ||
out_view: *mut Option<IApplicationView>, | ||
) -> HRESULT; | ||
|
||
pub unsafe fn get_view_for_application( | ||
&self, | ||
app: ComIn<IImmersiveApplication>, | ||
out_view: *mut IApplicationView, | ||
) -> HRESULT; | ||
|
||
pub unsafe fn get_view_for_app_user_model_id( | ||
&self, | ||
id: PCWSTR, | ||
out_view: *mut IApplicationView, | ||
) -> HRESULT; | ||
|
||
pub unsafe fn get_view_in_focus(&self, out_view: *mut IApplicationView) -> HRESULT; | ||
|
||
pub unsafe fn try_get_last_active_visible_view( | ||
&self, | ||
out_view: *mut IApplicationView, | ||
) -> HRESULT; | ||
|
||
pub unsafe fn refresh_collection(&self) -> HRESULT; | ||
|
||
pub unsafe fn register_for_application_view_changes( | ||
&self, | ||
listener: ComIn<IApplicationViewChangeListener>, | ||
out_id: *mut DWORD, | ||
) -> HRESULT; | ||
|
||
pub unsafe fn unregister_for_application_view_changes(&self, id: DWORD) -> HRESULT; | ||
} |
Oops, something went wrong.