From da392d58226c266d5f5e3e1bb03b9ce18d446b03 Mon Sep 17 00:00:00 2001 From: Will Davies Date: Wed, 7 Feb 2024 17:41:46 -0600 Subject: [PATCH] Add following window during move to window. --- README.md | 12 ++- WinJump/Core/Config.cs | 5 +- .../Windows11_22000.cs | 20 ++--- .../Windows11_22621_2215.cs | 75 ++++++++++++++++++ .../Windows11_22631_3085.cs | 76 +++++++++++++++++++ WinJump/Core/WinJumpManager.cs | 4 + WinJump/WinJump.csproj | 6 +- 7 files changed, 180 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 48fa97f..cab29fa 100644 --- a/README.md +++ b/README.md @@ -65,19 +65,23 @@ Below is an example configuration file that changes the shortcut to `alt+N` to j "move-window-to": [ { "shortcut": "alt+shift+d1", - "desktop": 1 + "desktop": 1, + "follow": false }, { "shortcut": "alt+shift+d2", - "desktop": 2 + "desktop": 2, + "follow": false }, { "shortcut": "alt+shift+d3", - "desktop": 3 + "desktop": 3, + "follow": false }, { "shortcut": "alt+shift+d4", - "desktop": 4 + "desktop": 4, + "follow": false } ], "change-desktops-with-scroll": false, diff --git a/WinJump/Core/Config.cs b/WinJump/Core/Config.cs index 418cc8e..4d33c18 100644 --- a/WinJump/Core/Config.cs +++ b/WinJump/Core/Config.cs @@ -122,7 +122,8 @@ private static Config Default() { Keys = x.Shortcut.Keys, ModifierKeys = x.Shortcut.ModifierKeys | ModifierKeys.Shift }, - Desktop = x.Desktop + Desktop = x.Desktop, + Follow = false }).ToList(), JumpTo = jumpTo, ToggleGroups = [], @@ -137,6 +138,8 @@ public sealed class JumpWindowToDesktop { public required Shortcut Shortcut { get; set; } public required uint Desktop { get; set; } + + public required bool Follow { get; set; } } public sealed class ToggleGroup { diff --git a/WinJump/Core/VirtualDesktopDefinitions/Windows11_22000.cs b/WinJump/Core/VirtualDesktopDefinitions/Windows11_22000.cs index f33dada..4b2541d 100644 --- a/WinJump/Core/VirtualDesktopDefinitions/Windows11_22000.cs +++ b/WinJump/Core/VirtualDesktopDefinitions/Windows11_22000.cs @@ -22,7 +22,7 @@ public void JumpToDesktop(int index) { } public void MoveFocusedWindowToDesktop(int index) { - throw new NotImplementedException(); + DesktopManager.MoveCurrentlyFocusedToDesktop(index); } public void Dispose() { @@ -42,12 +42,12 @@ internal static class DesktopManager { [DllImport("user32.dll")] private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId); - + private static IVirtualDesktopManagerInternal VirtualDesktopManagerInternal; internal static IVirtualDesktopNotificationService VirtualDesktopNotificationService; internal static IApplicationViewCollection ApplicationViewCollection; internal static IVirtualDesktopManager VirtualDesktopManager; - + static DesktopManager() { if(Activator.CreateInstance(Type.GetTypeFromCLSID(Guids.CLSID_ImmersiveShell) ?? throw new Exception("Failed to get shell")) is not IServiceProvider10 @@ -109,7 +109,7 @@ internal static int GetCurrentDesktopNum() { return GetIndex(vd); } - + internal static void MoveCurrentlyFocusedToDesktop(int index) { int processId; IntPtr hWnd = GetForegroundWindow(); @@ -117,7 +117,7 @@ internal static void MoveCurrentlyFocusedToDesktop(int index) { GetWindowThreadProcessId(hWnd, out processId); var desktop = GetDesktop(index); - + if(Environment.ProcessId == processId) { // window of process try // the easy way (if we are owner) @@ -192,8 +192,8 @@ internal static class Guids { public static readonly Guid CLSID_VirtualDesktopManagerInternal = new("C5E0CDCA-7B6E-41B2-9FC4-D93975CC467B"); - public static readonly Guid CLSID_VirtualDesktopManager = new("AA509086-5CA9-4C25-8F95-589D3C07B48A"); - + public static readonly Guid CLSID_VirtualDesktopManager = new("AA509086-5CA9-4C25-8F95-589D3C07B48A"); + public static readonly Guid CLSID_VirtualDesktopNotificationService = new("A501FDEC-4A09-464C-AE4E-1B9C21B84918"); } @@ -286,7 +286,7 @@ internal interface IApplicationView { int Unknown11(int unknown); int Unknown12(out Size size1); } - + [ComImport] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] [Guid("1841C6D7-4F9D-42C0-AF41-8747538F10E5")] @@ -303,7 +303,7 @@ internal interface IApplicationViewCollection { int RegisterForApplicationViewChanges(object listener, out int cookie); int UnregisterForApplicationViewChanges(int cookie); } - + [ComImport] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] [Guid("536D3495-B208-4CC9-AE26-DE8111275BF8")] @@ -357,7 +357,7 @@ internal interface IVirtualDesktopManager { Guid GetWindowDesktopId(IntPtr topLevelWindow); void MoveWindowToDesktop(IntPtr topLevelWindow, ref Guid desktopId); } - + [ComImport] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] [Guid("0CD45E71-D927-4F15-8B0A-8FEF525337BF")] diff --git a/WinJump/Core/VirtualDesktopDefinitions/Windows11_22621_2215.cs b/WinJump/Core/VirtualDesktopDefinitions/Windows11_22621_2215.cs index 85476a1..0dc91d9 100644 --- a/WinJump/Core/VirtualDesktopDefinitions/Windows11_22621_2215.cs +++ b/WinJump/Core/VirtualDesktopDefinitions/Windows11_22621_2215.cs @@ -22,6 +22,7 @@ public void JumpToDesktop(int index) { } public void MoveFocusedWindowToDesktop(int index) { + DesktopManager.MoveCurrentlyFocusedToDesktop(index); } public void Dispose() { @@ -35,8 +36,17 @@ public void Dispose() { */ internal static class DesktopManager { + // get handle of active window + [DllImport("user32.dll")] + private static extern IntPtr GetForegroundWindow(); + + [DllImport("user32.dll")] + private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId); + private static IVirtualDesktopManagerInternal VirtualDesktopManagerInternal; internal static IVirtualDesktopNotificationService VirtualDesktopNotificationService; + internal static IApplicationViewCollection ApplicationViewCollection; + internal static IVirtualDesktopManager VirtualDesktopManager; static DesktopManager() { if(Activator.CreateInstance(Type.GetTypeFromCLSID(Guids.CLSID_ImmersiveShell) ?? @@ -49,6 +59,11 @@ static DesktopManager() { Guids.CLSID_VirtualDesktopManagerInternal, typeof(IVirtualDesktopManagerInternal).GUID); VirtualDesktopNotificationService = (IVirtualDesktopNotificationService) shell.QueryService( Guids.CLSID_VirtualDesktopNotificationService, typeof(IVirtualDesktopNotificationService).GUID); + VirtualDesktopManager = + (IVirtualDesktopManager) Activator.CreateInstance( + Type.GetTypeFromCLSID(Guids.CLSID_VirtualDesktopManager)); + ApplicationViewCollection = (IApplicationViewCollection) shell.QueryService( + typeof(IApplicationViewCollection).GUID, typeof(IApplicationViewCollection).GUID); } // Helpers @@ -94,6 +109,39 @@ internal static int GetCurrentDesktopNum() { return GetIndex(vd); } + + internal static void MoveCurrentlyFocusedToDesktop(int index) { + int processId; + IntPtr hWnd = GetForegroundWindow(); + if(hWnd == IntPtr.Zero) throw new ArgumentNullException(); + GetWindowThreadProcessId(hWnd, out processId); + + var desktop = GetDesktop(index); + + if(Environment.ProcessId == processId) { + // window of process + try // the easy way (if we are owner) + { + VirtualDesktopManager.MoveWindowToDesktop(hWnd, desktop.GetId()); + } catch // window of process, but we are not the owner + { + IApplicationView view; + ApplicationViewCollection.GetViewForHwnd(hWnd, out view); + VirtualDesktopManagerInternal.MoveViewToDesktop(view, desktop); + } + } else { + // window of other process + ApplicationViewCollection.GetViewForHwnd(hWnd, out var view); + try { + VirtualDesktopManagerInternal.MoveViewToDesktop(view, desktop); + } catch { + // could not move active window, try main window (or whatever windows thinks is the main window) + ApplicationViewCollection.GetViewForHwnd( + System.Diagnostics.Process.GetProcessById(processId).MainWindowHandle, out view); + VirtualDesktopManagerInternal.MoveViewToDesktop(view, desktop); + } + } + } } internal class VirtualDesktopNotification : IVirtualDesktopNotification { @@ -144,6 +192,8 @@ internal static class Guids { public static readonly Guid CLSID_VirtualDesktopManagerInternal = new("C5E0CDCA-7B6E-41B2-9FC4-D93975CC467B"); + public static readonly Guid CLSID_VirtualDesktopManager = new("AA509086-5CA9-4C25-8F95-589D3C07B48A"); + public static readonly Guid CLSID_VirtualDesktopNotificationService = new("A501FDEC-4A09-464C-AE4E-1B9C21B84918"); } @@ -328,6 +378,31 @@ void VirtualDesktopWallpaperChanged(IVirtualDesktop pDesktop, void RemoteVirtualDesktopConnected(IVirtualDesktop pDesktop); } + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("A5CD92FF-29BE-454C-8D04-D82879FB3F1B")] + internal interface IVirtualDesktopManager { + bool IsWindowOnCurrentVirtualDesktop(IntPtr topLevelWindow); + Guid GetWindowDesktopId(IntPtr topLevelWindow); + void MoveWindowToDesktop(IntPtr topLevelWindow, ref Guid desktopId); + } + + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("1841C6D7-4F9D-42C0-AF41-8747538F10E5")] + internal interface IApplicationViewCollection { + int GetViews(out IObjectArray array); + int GetViewsByZOrder(out IObjectArray array); + int GetViewsByAppUserModelId(string id, out IObjectArray array); + int GetViewForHwnd(IntPtr hwnd, out IApplicationView view); + int GetViewForApplication(object application, out IApplicationView view); + int GetViewForAppUserModelId(string id, out IApplicationView view); + int GetViewInFocus(out IntPtr view); + int Unknown1(out IntPtr view); + void RefreshCollection(); + int RegisterForApplicationViewChanges(object listener, out int cookie); + int UnregisterForApplicationViewChanges(int cookie); + } [ComImport] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] [Guid("92CA9DCD-5622-4BBA-A805-5E9F541BD8C9")] diff --git a/WinJump/Core/VirtualDesktopDefinitions/Windows11_22631_3085.cs b/WinJump/Core/VirtualDesktopDefinitions/Windows11_22631_3085.cs index 57d36ec..5df5eaa 100644 --- a/WinJump/Core/VirtualDesktopDefinitions/Windows11_22631_3085.cs +++ b/WinJump/Core/VirtualDesktopDefinitions/Windows11_22631_3085.cs @@ -22,6 +22,7 @@ public void JumpToDesktop(int index) { } public void MoveFocusedWindowToDesktop(int index) { + DesktopManager.MoveCurrentlyFocusedToDesktop(index); } public void Dispose() { @@ -35,8 +36,17 @@ public void Dispose() { */ internal static class DesktopManager { + // get handle of active window + [DllImport("user32.dll")] + private static extern IntPtr GetForegroundWindow(); + + [DllImport("user32.dll")] + private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId); + private static IVirtualDesktopManagerInternal VirtualDesktopManagerInternal; internal static IVirtualDesktopNotificationService VirtualDesktopNotificationService; + internal static IApplicationViewCollection ApplicationViewCollection; + internal static IVirtualDesktopManager VirtualDesktopManager; static DesktopManager() { if(Activator.CreateInstance(Type.GetTypeFromCLSID(Guids.CLSID_ImmersiveShell) ?? @@ -49,6 +59,11 @@ static DesktopManager() { Guids.CLSID_VirtualDesktopManagerInternal, typeof(IVirtualDesktopManagerInternal).GUID); VirtualDesktopNotificationService = (IVirtualDesktopNotificationService) shell.QueryService( Guids.CLSID_VirtualDesktopNotificationService, typeof(IVirtualDesktopNotificationService).GUID); + VirtualDesktopManager = + (IVirtualDesktopManager) Activator.CreateInstance( + Type.GetTypeFromCLSID(Guids.CLSID_VirtualDesktopManager)); + ApplicationViewCollection = (IApplicationViewCollection) shell.QueryService( + typeof(IApplicationViewCollection).GUID, typeof(IApplicationViewCollection).GUID); } // Helpers @@ -94,6 +109,39 @@ internal static int GetCurrentDesktopNum() { return GetIndex(vd); } + + internal static void MoveCurrentlyFocusedToDesktop(int index) { + int processId; + IntPtr hWnd = GetForegroundWindow(); + if(hWnd == IntPtr.Zero) throw new ArgumentNullException(); + GetWindowThreadProcessId(hWnd, out processId); + + var desktop = GetDesktop(index); + + if(Environment.ProcessId == processId) { + // window of process + try // the easy way (if we are owner) + { + VirtualDesktopManager.MoveWindowToDesktop(hWnd, desktop.GetId()); + } catch // window of process, but we are not the owner + { + IApplicationView view; + ApplicationViewCollection.GetViewForHwnd(hWnd, out view); + VirtualDesktopManagerInternal.MoveViewToDesktop(view, desktop); + } + } else { + // window of other process + ApplicationViewCollection.GetViewForHwnd(hWnd, out var view); + try { + VirtualDesktopManagerInternal.MoveViewToDesktop(view, desktop); + } catch { + // could not move active window, try main window (or whatever windows thinks is the main window) + ApplicationViewCollection.GetViewForHwnd( + System.Diagnostics.Process.GetProcessById(processId).MainWindowHandle, out view); + VirtualDesktopManagerInternal.MoveViewToDesktop(view, desktop); + } + } + } } internal class VirtualDesktopNotification : IVirtualDesktopNotification { @@ -141,6 +189,8 @@ public void RemoteVirtualDesktopConnected(IVirtualDesktop pDesktop) { internal static class Guids { public static readonly Guid CLSID_ImmersiveShell = new("C2F03A33-21F5-47FA-B4BB-156362A2F239"); + public static readonly Guid CLSID_VirtualDesktopManager = new("AA509086-5CA9-4C25-8F95-589D3C07B48A"); + public static readonly Guid CLSID_VirtualDesktopManagerInternal = new("C5E0CDCA-7B6E-41B2-9FC4-D93975CC467B"); @@ -288,6 +338,32 @@ void GetDesktopSwitchIncludeExcludeViews(IVirtualDesktop desktop, out IObjectArr void WaitForAnimationToComplete(); } + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("1841C6D7-4F9D-42C0-AF41-8747538F10E5")] + internal interface IApplicationViewCollection { + int GetViews(out IObjectArray array); + int GetViewsByZOrder(out IObjectArray array); + int GetViewsByAppUserModelId(string id, out IObjectArray array); + int GetViewForHwnd(IntPtr hwnd, out IApplicationView view); + int GetViewForApplication(object application, out IApplicationView view); + int GetViewForAppUserModelId(string id, out IApplicationView view); + int GetViewInFocus(out IntPtr view); + int Unknown1(out IntPtr view); + void RefreshCollection(); + int RegisterForApplicationViewChanges(object listener, out int cookie); + int UnregisterForApplicationViewChanges(int cookie); + } + + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("A5CD92FF-29BE-454C-8D04-D82879FB3F1B")] + internal interface IVirtualDesktopManager { + bool IsWindowOnCurrentVirtualDesktop(IntPtr topLevelWindow); + Guid GetWindowDesktopId(IntPtr topLevelWindow); + void MoveWindowToDesktop(IntPtr topLevelWindow, ref Guid desktopId); + } + [ComImport] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] [Guid("0CD45E71-D927-4F15-8B0A-8FEF525337BF")] diff --git a/WinJump/Core/WinJumpManager.cs b/WinJump/Core/WinJumpManager.cs index 38ea390..b7e8260 100644 --- a/WinJump/Core/WinJumpManager.cs +++ b/WinJump/Core/WinJumpManager.cs @@ -105,6 +105,10 @@ public WinJumpManager(DesktopChanged desktopChanged) { if(moveTo != null) { // Move the current window to the specified desktop _thread?.MoveWindowToDesktop(moveTo.Desktop - 1); + + if(moveTo.Follow) { + _thread?.JumpTo(moveTo.Desktop - 1); + } } // Finally, look for a toggle group diff --git a/WinJump/WinJump.csproj b/WinJump/WinJump.csproj index 1577bc2..6b62c88 100644 --- a/WinJump/WinJump.csproj +++ b/WinJump/WinJump.csproj @@ -5,9 +5,9 @@ net8.0-windows enable true - 2.0.12 - 2.0.12 - 2.0.12 + 2.0.13 + 2.0.13 + 2.0.13