Skip to content

Commit

Permalink
feat(wm): add "cloak" window-hiding-behaviour
Browse files Browse the repository at this point in the history
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
LGUG2Z committed Feb 11, 2023
1 parent 89d1924 commit 80c9859
Show file tree
Hide file tree
Showing 10 changed files with 431 additions and 17 deletions.
26 changes: 26 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions komorebi-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ pub enum MoveBehaviour {
pub enum HidingBehaviour {
Hide,
Minimize,
Cloak,
}

#[derive(
Expand Down
6 changes: 5 additions & 1 deletion komorebi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,14 @@ uds_windows = "1"
which = "4"
winput = "0.2"
winreg = "0.10"

windows-interface = { version = "0.44" }
windows-implement = { version = "0.44" }
[dependencies.windows]
version = "0.44"
features = [
"implement",
"Win32_System_Com",
"Win32_UI_Shell_Common", # for IObjectArray
"Win32_Foundation",
"Win32_Graphics_Dwm",
"Win32_Graphics_Gdi",
Expand Down
246 changes: 246 additions & 0 deletions komorebi/src/com/interfaces.rs
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;
}
Loading

0 comments on commit 80c9859

Please sign in to comment.