From b657f44bb823c91ea0bc081381daa718075f8728 Mon Sep 17 00:00:00 2001 From: DARKGuy Date: Sat, 16 Oct 2021 23:01:18 -0600 Subject: [PATCH] Initial v1.0 upload, yay! --- .gitignore | 2 + prod/Switchie.exe | Bin 0 -> 241152 bytes src/.vscode/launch.json | 24 +++ src/.vscode/tasks.json | 42 ++++ src/Core/API/VirtualDesktopAPI.cs | 309 ++++++++++++++++++++++++++++++ src/Core/API/WinAPI.cs | 67 +++++++ src/Core/Helpers.cs | 48 +++++ src/Core/Types.cs | 25 +++ src/Core/WindowManager.cs | 71 +++++++ src/GUI/MainForm.cs | 138 +++++++++++++ src/GUI/VirtualDesktop.cs | 127 ++++++++++++ src/GUI/VirtualDesktopScreen.cs | 75 ++++++++ src/Program.cs | 19 ++ src/Publish.cmd | 7 + src/Resources/icon.ico | Bin 0 -> 102870 bytes src/Resources/icon.png | Bin 0 -> 5515 bytes src/Resources/icon.svg | 13 ++ src/Switchie.csproj | 38 ++++ 18 files changed, 1005 insertions(+) create mode 100644 .gitignore create mode 100644 prod/Switchie.exe create mode 100644 src/.vscode/launch.json create mode 100644 src/.vscode/tasks.json create mode 100644 src/Core/API/VirtualDesktopAPI.cs create mode 100644 src/Core/API/WinAPI.cs create mode 100644 src/Core/Helpers.cs create mode 100644 src/Core/Types.cs create mode 100644 src/Core/WindowManager.cs create mode 100644 src/GUI/MainForm.cs create mode 100644 src/GUI/VirtualDesktop.cs create mode 100644 src/GUI/VirtualDesktopScreen.cs create mode 100644 src/Program.cs create mode 100644 src/Publish.cmd create mode 100644 src/Resources/icon.ico create mode 100644 src/Resources/icon.png create mode 100644 src/Resources/icon.svg create mode 100644 src/Switchie.csproj diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cd42ee3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +bin/ +obj/ diff --git a/prod/Switchie.exe b/prod/Switchie.exe new file mode 100644 index 0000000000000000000000000000000000000000..6eabf3d381c6948ee000aa84b96de21540cfb23a GIT binary patch literal 241152 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 literal 0 HcmV?d00001 diff --git a/src/.vscode/launch.json b/src/.vscode/launch.json new file mode 100644 index 0000000..2bcae30 --- /dev/null +++ b/src/.vscode/launch.json @@ -0,0 +1,24 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": ".NET Core Launch (console)", + "type": "clr", + "request": "launch", + "preLaunchTask": "build", + "program": "${workspaceFolder}/bin/Debug/net4.8/Switchie.exe", + "args": [], + "cwd": "${workspaceFolder}", + "console": "internalConsole", + "stopAtEntry": false, + "logging": { + "moduleLoad": false + } + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach" + } + ] +} \ No newline at end of file diff --git a/src/.vscode/tasks.json b/src/.vscode/tasks.json new file mode 100644 index 0000000..f2b77c6 --- /dev/null +++ b/src/.vscode/tasks.json @@ -0,0 +1,42 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/Switchie.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "publish", + "command": "dotnet", + "type": "process", + "args": [ + "publish", + "${workspaceFolder}/Switchie.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "watch", + "command": "dotnet", + "type": "process", + "args": [ + "watch", + "run", + "${workspaceFolder}/Switchie.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/src/Core/API/VirtualDesktopAPI.cs b/src/Core/API/VirtualDesktopAPI.cs new file mode 100644 index 0000000..6eae713 --- /dev/null +++ b/src/Core/API/VirtualDesktopAPI.cs @@ -0,0 +1,309 @@ +// Adapted from https://github.com/MScholtes/VirtualDesktop with a few modifications +// TODO: Needs to be adapted to the latest versions on his repo targeting Win10/Win11 + +using System; +using System.Runtime.InteropServices; + +namespace Switchie +{ + + public static class WindowsVirtualDesktopAPI + { + public static readonly Guid CLSID_IMMERSIVESHELL = new Guid("C2F03A33-21F5-47FA-B4BB-156362A2F239"); + public static readonly Guid CLSID_VIRTUALDESKTOPMANAGERINTERNAL = new Guid("C5E0CDCA-7B6E-41B2-9FC4-D93975CC467B"); + public static readonly Guid CLSID_VIRTUALDESKTOPMANAGER = new Guid("AA509086-5CA9-4C25-8F95-589D3C07B48A"); + public static readonly Guid CLSID_VIRTUALDESKTOPPINNEDAPPS = new Guid("B5A399E7-1C87-46B8-88E9-FC5747B171BD"); + + [StructLayout(LayoutKind.Sequential)] + public struct SIZE + { + public int X; + public int Y; + } + + [StructLayout(LayoutKind.Sequential)] + public struct RECT + { + public int Left; + public int Top; + public int Right; + public int Bottom; + } + + public enum APPLICATION_VIEW_CLOAK_TYPE : int + { + AVCT_NONE = 0, + AVCT_DEFAULT = 1, + AVCT_VIRTUAL_DESKTOP = 2 + } + + public enum APPLICATION_VIEW_COMPATIBILITY_POLICY : int + { + AVCP_NONE = 0, + AVCP_SMALL_SCREEN = 1, + AVCP_TABLET_SMALL_SCREEN = 2, + AVCP_VERY_SMALL_SCREEN = 3, + AVCP_HIGH_SCALE_FACTOR = 4 + } + + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("92CA9DCD-5622-4BBA-A805-5E9F541BD8C9")] + public interface IObjectArray + { + void GetCount(out int count); + void GetAt(int index, ref Guid iid, [MarshalAs(UnmanagedType.Interface)] out object obj); + } + + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("4CE81583-1E4C-4632-A621-07A53543148F")] + public interface IVirtualDesktopPinnedApps + { + bool IsAppIdPinned(string appId); + void PinAppID(string appId); + void UnpinAppID(string appId); + bool IsViewPinned(IApplicationView applicationView); + void PinView(IApplicationView applicationView); + void UnpinView(IApplicationView applicationView); + } + + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("6D5140C1-7436-11CE-8034-00AA006009FA")] + public interface IServiceProvider10 + { + [return: MarshalAs(UnmanagedType.IUnknown)] + object QueryService(ref Guid service, ref Guid riid); + } + + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIInspectable)] + [Guid("372E1D3B-38D3-42E4-A15B-8AB2B178F513")] + public interface IApplicationView + { + int SetFocus(); + int SwitchTo(); + int TryInvokeBack(IntPtr /* IAsyncCallback* */ callback); + int GetThumbnailWindow(out IntPtr hwnd); + int GetMonitor(out IntPtr /* IImmersiveMonitor */ immersiveMonitor); + int GetVisibility(out int visibility); + int SetCloak(APPLICATION_VIEW_CLOAK_TYPE cloakType, int unknown); + int GetPosition(ref Guid guid /* GUID for IApplicationViewPosition */, out IntPtr /* IApplicationViewPosition** */ position); + int SetPosition(ref IntPtr /* IApplicationViewPosition* */ position); + int InsertAfterWindow(IntPtr hwnd); + int GetExtendedFramePosition(out RECT rect); + int GetAppUserModelId([MarshalAs(UnmanagedType.LPWStr)] out string id); + int SetAppUserModelId(string id); + int IsEqualByAppUserModelId(string id, out int result); + int GetViewState(out uint state); + int SetViewState(uint state); + int GetNeediness(out int neediness); + int GetLastActivationTimestamp(out ulong timestamp); + int SetLastActivationTimestamp(ulong timestamp); + int GetVirtualDesktopId(out Guid guid); + int SetVirtualDesktopId(ref Guid guid); + int GetShowInSwitchers(out int flag); + int SetShowInSwitchers(int flag); + int GetScaleFactor(out int factor); + int CanReceiveInput(out bool canReceiveInput); + int GetCompatibilityPolicyType(out APPLICATION_VIEW_COMPATIBILITY_POLICY flags); + int SetCompatibilityPolicyType(APPLICATION_VIEW_COMPATIBILITY_POLICY flags); + int GetSizeConstraints(IntPtr /* IImmersiveMonitor* */ monitor, out SIZE size1, out SIZE size2); + int GetSizeConstraintsForDpi(uint uint1, out SIZE size1, out SIZE size2); + int SetSizeConstraintsForDpi(ref uint uint1, ref SIZE size1, ref SIZE size2); + int OnMinSizePreferencesUpdated(IntPtr hwnd); + int ApplyOperation(IntPtr /* IApplicationViewOperation* */ operation); + int IsTray(out bool isTray); + int IsInHighZOrderBand(out bool isInHighZOrderBand); + int IsSplashScreenPresented(out bool isSplashScreenPresented); + int Flash(); + int GetRootSwitchableOwner(out IApplicationView rootSwitchableOwner); + int EnumerateOwnershipTree(out IObjectArray ownershipTree); + int GetEnterpriseId([MarshalAs(UnmanagedType.LPWStr)] out string enterpriseId); + int IsMirrored(out bool isMirrored); + int Unknown1(out int unknown); + int Unknown2(out int unknown); + int Unknown3(out int unknown); + int Unknown4(out int unknown); + int Unknown5(out int unknown); + int Unknown6(int unknown); + int Unknown7(); + int Unknown8(out int unknown); + int Unknown9(int unknown); + int Unknown10(int unknownX, int unknownY); + int Unknown11(int unknown); + int Unknown12(out SIZE size1); + } + + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("1841C6D7-4F9D-42C0-AF41-8747538F10E5")] + public 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("F31574D6-B682-4CDC-BD56-1827860ABEC6")] + public interface IVirtualDesktopManagerInternal + { + int GetCount(); + void MoveViewToDesktop(IApplicationView view, IVirtualDesktop desktop); + bool CanViewMoveDesktops(IApplicationView view); + IVirtualDesktop GetCurrentDesktop(); + void GetDesktops(out IObjectArray desktops); + [PreserveSig] + int GetAdjacentDesktop(IVirtualDesktop from, int direction, out IVirtualDesktop desktop); + void SwitchDesktop(IVirtualDesktop desktop); + IVirtualDesktop CreateDesktop(); + void RemoveDesktop(IVirtualDesktop desktop, IVirtualDesktop fallback); + IVirtualDesktop FindDesktop(ref Guid desktopid); + } + + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("A5CD92FF-29BE-454C-8D04-D82879FB3F1B")] + public interface IVirtualDesktopManager + { + bool IsWindowOnCurrentVirtualDesktop(IntPtr topLevelWindow); + Guid GetWindowDesktopId(IntPtr topLevelWindow); + void MoveWindowToDesktop(IntPtr topLevelWindow, ref Guid desktopId); + } + + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("FF72FFDD-BE7E-43FC-9C03-AD81681E88E4")] + public interface IVirtualDesktop + { + bool IsViewVisible(IApplicationView view); + Guid GetId(); + } + } + + public class WindowsVirtualDesktop + { + [DllImport("user32.dll")] private static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId); + + public WindowsVirtualDesktopAPI.IVirtualDesktop ivd; + public WindowsVirtualDesktop(WindowsVirtualDesktopAPI.IVirtualDesktop desktop) { this.ivd = desktop; } + public void MakeVisible() => WindowsVirtualDesktopManager.VirtualDesktopManagerInternal.SwitchDesktop(ivd); + public static WindowsVirtualDesktop FromIndex(int index) => new WindowsVirtualDesktop(WindowsVirtualDesktopManager.GetDesktop(index)); + + public static int Count + { + get => WindowsVirtualDesktopManager.VirtualDesktopManagerInternal.GetCount(); + } + + public static WindowsVirtualDesktop Current + { + get => new WindowsVirtualDesktop(WindowsVirtualDesktopManager.VirtualDesktopManagerInternal.GetCurrentDesktop()); + } + + public void MoveWindow(IntPtr hWnd) + { + int processId; + if (hWnd == IntPtr.Zero) throw new ArgumentNullException(); + GetWindowThreadProcessId(hWnd, out processId); + WindowsVirtualDesktopAPI.IApplicationView view; + WindowsVirtualDesktopManager.ApplicationViewCollection.GetViewForHwnd(hWnd, out view); + try + { + WindowsVirtualDesktopManager.VirtualDesktopManagerInternal.MoveViewToDesktop(view, ivd); + } + catch + { + WindowsVirtualDesktopManager.ApplicationViewCollection.GetViewForHwnd(System.Diagnostics.Process.GetProcessById(processId).MainWindowHandle, out view); + WindowsVirtualDesktopManager.VirtualDesktopManagerInternal.MoveViewToDesktop(view, ivd); + } + } + } + + public static class WindowsVirtualDesktopManager + { + public static WindowsVirtualDesktopAPI.IVirtualDesktopPinnedApps VirtualDesktopPinnedApps; + public static WindowsVirtualDesktopAPI.IVirtualDesktopManager VirtualDesktopManagerPrivate; + public static WindowsVirtualDesktopAPI.IApplicationViewCollection ApplicationViewCollection; + public static WindowsVirtualDesktopAPI.IVirtualDesktopManagerInternal VirtualDesktopManagerInternal; + public static int FromDesktop(WindowsVirtualDesktop desktop) => desktop != null ? GetDesktopIndex(desktop.ivd) : -1; + public static bool IsWindowPinned(IntPtr hWnd) => VirtualDesktopPinnedApps.IsViewPinned(hWnd.GetApplicationView()); + public static bool IsApplicationPinned(IntPtr hWnd) => VirtualDesktopPinnedApps.IsAppIdPinned(GetAppId(hWnd)); + + private static WindowsVirtualDesktopAPI.IApplicationView GetApplicationView(this IntPtr hWnd) + { + ApplicationViewCollection.GetViewForHwnd(hWnd, out WindowsVirtualDesktopAPI.IApplicationView view); + return view; + } + + private static string GetAppId(IntPtr hWnd) + { + hWnd.GetApplicationView().GetAppUserModelId(out string appId); + return appId; + } + + static WindowsVirtualDesktopManager() + { + var shell = (WindowsVirtualDesktopAPI.IServiceProvider10)Activator.CreateInstance(Type.GetTypeFromCLSID(WindowsVirtualDesktopAPI.CLSID_IMMERSIVESHELL)); + VirtualDesktopManagerPrivate = (WindowsVirtualDesktopAPI.IVirtualDesktopManager)Activator.CreateInstance(Type.GetTypeFromCLSID(WindowsVirtualDesktopAPI.CLSID_VIRTUALDESKTOPMANAGER)); + ApplicationViewCollection = (WindowsVirtualDesktopAPI.IApplicationViewCollection)shell.QueryService(typeof(WindowsVirtualDesktopAPI.IApplicationViewCollection).GUID, typeof(WindowsVirtualDesktopAPI.IApplicationViewCollection).GUID); + VirtualDesktopManagerInternal = (WindowsVirtualDesktopAPI.IVirtualDesktopManagerInternal)shell.QueryService(WindowsVirtualDesktopAPI.CLSID_VIRTUALDESKTOPMANAGERINTERNAL, typeof(WindowsVirtualDesktopAPI.IVirtualDesktopManagerInternal).GUID); + VirtualDesktopPinnedApps = (WindowsVirtualDesktopAPI.IVirtualDesktopPinnedApps)shell.QueryService(WindowsVirtualDesktopAPI.CLSID_VIRTUALDESKTOPPINNEDAPPS, typeof(WindowsVirtualDesktopAPI.IVirtualDesktopPinnedApps).GUID); + } + + public static WindowsVirtualDesktopAPI.IVirtualDesktop GetDesktop(int index) + { + int count = VirtualDesktopManagerInternal.GetCount(); + if (index < 0 || index >= count) throw new ArgumentOutOfRangeException("index"); + WindowsVirtualDesktopAPI.IObjectArray desktops; + VirtualDesktopManagerInternal.GetDesktops(out desktops); + object objdesktop; + desktops.GetAt(index, typeof(WindowsVirtualDesktopAPI.IVirtualDesktop).GUID, out objdesktop); + Marshal.ReleaseComObject(desktops); + return (WindowsVirtualDesktopAPI.IVirtualDesktop)objdesktop; + } + + public static int GetDesktopIndex(WindowsVirtualDesktopAPI.IVirtualDesktop desktop) + { + int index = -1; + Guid IdSearch = desktop.GetId(); + VirtualDesktopManagerInternal.GetDesktops(out WindowsVirtualDesktopAPI.IObjectArray desktops); + for (int i = 0; i < VirtualDesktopManagerInternal.GetCount(); i++) + { + desktops.GetAt(i, typeof(WindowsVirtualDesktopAPI.IVirtualDesktop).GUID, out object objdesktop); + if (IdSearch.CompareTo(((WindowsVirtualDesktopAPI.IVirtualDesktop)objdesktop).GetId()) == 0) + { + index = i; + break; + } + } + Marshal.ReleaseComObject(desktops); + return index; + } + + public static WindowsVirtualDesktop FromWindow(IntPtr hWnd) + { + Guid id = VirtualDesktopManagerPrivate.GetWindowDesktopId(hWnd); + if (id != Guid.Empty) return new WindowsVirtualDesktop(VirtualDesktopManagerInternal.FindDesktop(ref id)); + return null; + } + + public static void PinApplication(IntPtr hWnd) + { + string appId = GetAppId(hWnd); + if (!VirtualDesktopPinnedApps.IsAppIdPinned(appId)) + VirtualDesktopPinnedApps.PinAppID(appId); + } + } + +} \ No newline at end of file diff --git a/src/Core/API/WinAPI.cs b/src/Core/API/WinAPI.cs new file mode 100644 index 0000000..eab628a --- /dev/null +++ b/src/Core/API/WinAPI.cs @@ -0,0 +1,67 @@ +using System; +using System.Runtime.InteropServices; +using System.Text; + +namespace Switchie +{ + public class WinAPI + { + [StructLayout(LayoutKind.Sequential)] + public struct RECT + { + public int Left; + public int Top; + public int Right; + public int Bottom; + } + + public delegate bool EnumWindowsProc(IntPtr hWnd, int lParam); + + public const uint SWP_NOSIZE = 0x0001; + public const uint SWP_NOMOVE = 0x0002; + public const uint SWP_SHOWWINDOW = 0x0040; + public static readonly IntPtr HWND_TOPMOST = new IntPtr(-1); + public static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2); + + public const int WM_KEYDOWN = 0x100; + public const int WM_KEYUP = 0x101; + public const int WM_COMMAND = 0x111; + public const int WM_LBUTTONDOWN = 0x201; + public const int WM_LBUTTONUP = 0x202; + public const int WM_LBUTTONDBLCLK = 0x203; + public const int WM_RBUTTONDOWN = 0x204; + public const int WM_RBUTTONUP = 0x205; + public const int WM_RBUTTONDBLCLK = 0x206; + public const int WM_NCLBUTTONDOWN = 0xA1; + public static int WM_GETICON = 0x007f; + + public const int HT_CAPTION = 0x2; + + public static int GCL_HICON = -14; + public static int ICON_SMALL2 = 2; + public static uint GW_HWNDNEXT = 2; + public static int IDI_APPLICATION = 0x7F00; + + [DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow(); + [DllImport("user32.dll")] public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); + [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool SetForegroundWindow(IntPtr hWnd); + [DllImport("user32.dll", SetLastError = true)] public static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect); + [DllImport("user32.dll")] public static extern bool EnumWindows(EnumWindowsProc enumFunc, int lParam); + [DllImport("user32.dll")] public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); + [DllImport("user32.dll")] public static extern int GetWindowTextLength(IntPtr hWnd); + [DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr hWnd); + [DllImport("user32.dll")] public static extern IntPtr GetShellWindow(); + [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd); + [DllImport("user32.dll", SetLastError = true)] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId); + [DllImport("user32.dll")] public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam); + [DllImport("user32.dll")] public static extern bool ReleaseCapture(); + [DllImport("user32.dll")] public static extern int PostMessage(IntPtr hWnd, int Msg, int wParam, int lParam); + [DllImport("user32.dll")] public static extern int LoadIcon(IntPtr hInstance, IntPtr lpIconName); + [DllImport("user32.dll", EntryPoint = "GetClassLong")] public static extern uint GetClassLong32(IntPtr hWnd, int nIndex); + [DllImport("user32.dll", EntryPoint = "GetClassLongPtr")] public static extern int GetClassLong64(IntPtr hWnd, int nIndex); + + // 64 bit version maybe loses significant 64-bit specific information + public static int GetClassLongPtr(IntPtr hWnd, int nIndex) => IntPtr.Size == 4 ? (int)GetClassLong32(hWnd, nIndex) : GetClassLong64(hWnd, nIndex); + public static int MakeLParam(int x, int y) => (y << 16) | (x & 0xFFFF); + } +} diff --git a/src/Core/Helpers.cs b/src/Core/Helpers.cs new file mode 100644 index 0000000..921950b --- /dev/null +++ b/src/Core/Helpers.cs @@ -0,0 +1,48 @@ +using System; +using System.Drawing; +using System.IO; +using System.Reflection; +using System.Windows.Forms; + +namespace Switchie +{ + + public class Helpers + { + public static byte[] GetResourceFromAssembly(Type type, string name) + { + MemoryStream ms = new MemoryStream(); + Assembly.GetAssembly(type).GetManifestResourceStream(name).CopyTo(ms); + return ms.ToArray(); + } + + public static void AddMenuItem(Form main, ContextMenuStrip menu, ToolStripMenuItem m, Action onClick = null) + { + m.Click += (s, e) => onClick?.Invoke(); + menu.Items.Add(m); + } + + public static Size AspectRatioResize(Size sz, int finalWidth, int finalHeight) + { + int iWidth; + int iHeight; + if ((finalHeight == 0) && (finalWidth != 0)) + { + iWidth = finalWidth; + iHeight = (sz.Height * iWidth / sz.Width); + } + else if ((finalHeight != 0) && (finalWidth == 0)) + { + iHeight = finalHeight; + iWidth = (sz.Width * iHeight / sz.Height); + } + else + { + iWidth = finalWidth; + iHeight = finalHeight; + } + return new Size(iWidth, iHeight); + } + } + +} \ No newline at end of file diff --git a/src/Core/Types.cs b/src/Core/Types.cs new file mode 100644 index 0000000..86599ba --- /dev/null +++ b/src/Core/Types.cs @@ -0,0 +1,25 @@ +using System; +using System.Drawing; + +namespace Switchie +{ + + public class Window + { + public bool IsActive { get; set; } + public int VirtualDesktopIndex { get; set; } + public int ZOrder { get; set; } + public IntPtr Handle { get; set; } + public string Title { get; set; } + public uint ProcessID { get; set; } + public Rectangle Dimensions { get; set; } + public Bitmap Icon { get; set; } + } + + public class DragDropData + { + public int OriginDesktopIndex { get; set; } + public Window DraggedWindow { get; set; } + } + +} \ No newline at end of file diff --git a/src/Core/WindowManager.cs b/src/Core/WindowManager.cs new file mode 100644 index 0000000..f9dd999 --- /dev/null +++ b/src/Core/WindowManager.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; + +namespace Switchie +{ + + public class WindowManager + { + static int GetWindowZOrder(IntPtr hWnd) + { + var zOrder = -1; + while ((hWnd = WinAPI.GetWindow(hWnd, WinAPI.GW_HWNDNEXT)) != IntPtr.Zero) zOrder++; + return zOrder; + } + + public static List GetOpenWindows() + { + List rv = new List(); + IntPtr shellWindow = WinAPI.GetShellWindow(); + + WinAPI.EnumWindows((IntPtr hWnd, int lParam) => + { + if (hWnd == shellWindow) return true; + if (!WinAPI.IsWindowVisible(hWnd)) return true; + + int length = WinAPI.GetWindowTextLength(hWnd); + if (length == 0) return true; + + StringBuilder builder = new StringBuilder(length); + WinAPI.GetWindowText(hWnd, builder, length + 1); + + WinAPI.RECT rct = new WinAPI.RECT(); + WinAPI.GetWindowRect(hWnd, ref rct); + + WinAPI.GetWindowThreadProcessId(hWnd, out uint pid); + int index = WindowsVirtualDesktopManager.FromDesktop(WindowsVirtualDesktopManager.FromWindow((IntPtr)hWnd)); + + int hIcon = WinAPI.SendMessage(hWnd, WinAPI.WM_GETICON, WinAPI.ICON_SMALL2, 0); + if (hIcon == 0) { hIcon = WinAPI.GetClassLongPtr(hWnd, WinAPI.GCL_HICON); } + if (hIcon == 0) { hIcon = WinAPI.LoadIcon(IntPtr.Zero, (IntPtr)WinAPI.IDI_APPLICATION); } + + rv.Add(new Window() + { + Handle = hWnd, + Title = builder.ToString(), + ProcessID = pid, + ZOrder = GetWindowZOrder(hWnd), + Icon = hIcon != 0 ? new Bitmap(Icon.FromHandle((IntPtr)hIcon).ToBitmap(), 16, 16) : null, + IsActive = hWnd == WinAPI.GetForegroundWindow(), + Dimensions = new Rectangle(rct.Left, rct.Top, rct.Right - rct.Left, rct.Bottom - rct.Top), + VirtualDesktopIndex = index + }); + + return true; + }, 0); + + return rv; + } + + public static Window GetActiveWindow() + { + var hwnd = WinAPI.GetForegroundWindow(); + return GetOpenWindows().SingleOrDefault(x => x.Handle == hwnd); + } + + public static void SetAlwaysOnTop(IntPtr handle, bool value) => WinAPI.SetWindowPos(handle, value ? WinAPI.HWND_TOPMOST : WinAPI.HWND_NOTOPMOST, 0, 0, 0, 0, WinAPI.SWP_NOMOVE | WinAPI.SWP_NOSIZE | WinAPI.SWP_SHOWWINDOW); + } +} diff --git a/src/GUI/MainForm.cs b/src/GUI/MainForm.cs new file mode 100644 index 0000000..b92eebf --- /dev/null +++ b/src/GUI/MainForm.cs @@ -0,0 +1,138 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Switchie +{ + public class MainForm : Form + { + private Point dragOffset; + private bool _forceAlwaysOnTop = false; + private string _windowsHash = string.Empty; + private List _virtualDesktops = new List(); + + public int BorderSize { get; set; } = 1; + public int PagerHeight { get; set; } = 40; + public bool IsDraggingWindow { get; set; } + public int VirtualDesktopSpacing { get; set; } = 4; + public Color DesktopColor { get; set; } = Color.FromArgb(64, 64, 64); + public Color WindowColor { get; set; } = Color.Gray; + public Color WindowBorderColor { get; set; } = Color.Silver; + public Color ActiveWindowColor { get; set; } = Color.Silver; + public Color ActiveWindowBorderColor { get; set; } = Color.White; + public Color ActiveDesktopBorderColor { get; set; } = Color.White; + public ConcurrentBag Windows = new ConcurrentBag(); + + public MainForm() + { + SuspendLayout(); + DoubleBuffered = true; + AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64))))); + ClientSize = new System.Drawing.Size(1, 1); + ControlBox = false; + AllowDrop = true; + MinimumSize = new System.Drawing.Size(1, 1); + StartPosition = FormStartPosition.Manual; + FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; + MaximizeBox = false; + MinimizeBox = false; + Name = "frmMain"; + TopMost = true; + Icon = new System.Drawing.Icon(new MemoryStream(Helpers.GetResourceFromAssembly(typeof(Program), "Switchie.Resources.icon.ico"))); + Enumerable.Range(0, WindowsVirtualDesktop.Count).ToList().ForEach(x => + { + VirtualDesktop desktop = new VirtualDesktop(x, this, new Point(_virtualDesktops.Sum(y => y.Size.Width), 0)); + MouseUp += desktop.OnMouseUp; + MouseDown += desktop.OnMouseDown; + MouseMove += desktop.OnMouseMove; + DragOver += desktop.OnDragOver; + DragDrop += desktop.OnDragDrop; + _virtualDesktops.Add(desktop); + }); + Size = new Size(_virtualDesktops.Sum(x => x.Size.Width), PagerHeight); + MinimumSize = Size; + MaximumSize = Size; + ClientSize = Size; + Location = new System.Drawing.Point((Screen.PrimaryScreen.Bounds.Width / 2) - (Size.Width / 2), Screen.PrimaryScreen.WorkingArea.Bottom - Size.Height); + ResumeLayout(false); + Shown += OnShown; + MouseUp += OnMouseUp; + MouseDown += OnMouseDown; + MouseMove += OnMouseMove; + } + + private void OnShown(object sender, EventArgs e) + { + WindowsVirtualDesktopManager.PinApplication(Handle); + new TaskFactory().StartNew(async () => + { + while (!Program.ApplicationClosing.IsCancellationRequested) + { + Invoke(new Action(() => + { + try + { + if (_forceAlwaysOnTop) + WindowManager.SetAlwaysOnTop(Handle, _forceAlwaysOnTop); + Windows = new ConcurrentBag(WindowManager.GetOpenWindows()); + var hash = $"{Windows.Sum(x => Math.Abs(x.Dimensions.X))}{Windows.Sum(x => Math.Abs(x.Dimensions.Y))}{Windows.Sum(x => x.Dimensions.Width)}{Windows.Sum(x => x.Dimensions.Height)}{Windows.Sum(x => x.IsActive ? 1 : 0)}{Windows.Sum(x => x.VirtualDesktopIndex)}"; + if (hash != _windowsHash) + { + _windowsHash = hash; + Invalidate(); + } + } + catch { } + })); + await Task.Delay(1); + } + }); + } + + private void OnMouseUp(object sender, MouseEventArgs e) + { + IsDraggingWindow = false; + Cursor = Cursors.Default; + if ((e.Button & MouseButtons.Right) == MouseButtons.Right) + { + _forceAlwaysOnTop = false; + ContextMenuStrip menu = new ContextMenuStrip(); + Helpers.AddMenuItem(this, menu, new ToolStripMenuItem() { Text = "About..." }, () => { MessageBox.Show("Made by darkguy2008", "About"); _forceAlwaysOnTop = true; }); + Helpers.AddMenuItem(this, menu, new ToolStripMenuItem() { Text = "Exit" }, () => { Environment.Exit(1); }); + menu.Opened += (ss, ee) => _forceAlwaysOnTop = false; + menu.Show(this, PointToClient(Cursor.Position)); + } + } + + private void OnMouseDown(object sender, MouseEventArgs e) + { + if ((e.Button & MouseButtons.Middle) == MouseButtons.Middle) + { + IsDraggingWindow = true; + dragOffset = e.Location; + } + } + + private void OnMouseMove(object sender, MouseEventArgs e) + { + if (IsDraggingWindow) + { + Cursor = Cursors.SizeAll; + Location = new Point(e.X + Location.X - dragOffset.X, e.Y + Location.Y - dragOffset.Y); + } + } + + protected override void OnPaint(PaintEventArgs e) + { + base.OnPaint(e); + _virtualDesktops.ForEach(x => x.OnPaint(e)); + } + } +} diff --git a/src/GUI/VirtualDesktop.cs b/src/GUI/VirtualDesktop.cs new file mode 100644 index 0000000..2e4fcbe --- /dev/null +++ b/src/GUI/VirtualDesktop.cs @@ -0,0 +1,127 @@ +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Windows.Forms; + +namespace Switchie +{ + + public class VirtualDesktop + { + public MainForm Form { get; set; } + public Size Size { get; set; } + public Point Location { get; set; } + public int VirtualDesktopIndex { get; set; } + public bool IsCurrentActiveDesktop { get => WindowsVirtualDesktopManager.FromDesktop(WindowsVirtualDesktop.Current) == VirtualDesktopIndex; } + + private DragDropData _dragDropData; + private Rectangle dragBoxFromMouseDown; + private List _screens = new List(); + private bool IsInsideBounds(Point p) => IsInsideBounds(p.X, p.Y); + private bool IsInsideBounds(int x, int y) => x >= Location.X && x < (Location.X + Size.Width) && y >= Location.Y && y < (Location.Y + Size.Height); + + public VirtualDesktop(int virtualDesktopIndex, MainForm form, Point location) + { + Form = form; + Location = location; + VirtualDesktopIndex = virtualDesktopIndex; + foreach (var screen in Screen.AllScreens.OrderBy(x => x.Bounds.Left).ThenBy(x => x.Bounds.Top)) + _screens.Add(new VirtualDesktopScreen() + { + Form = Form, + VirtualDesktop = this, + AttachedScreen = screen, + Location = new Point(location.X + _screens.Sum(x => x.Size.Width), location.Y), + Size = Helpers.AspectRatioResize(new Size(screen.Bounds.Width, screen.Bounds.Height), 0, Form.PagerHeight) + }); + Size = new Size(_screens.Sum(x => x.Size.Width), Form.PagerHeight); + } + + public Window GetWindowUnderCursor(Point mousePosition) + { + var coord = Form.PointToClient(mousePosition); + var windows = Form.Windows.Where(x => x.VirtualDesktopIndex == VirtualDesktopIndex).OrderByDescending(x => x.ZOrder); + foreach (var w in windows) + if (_screens.Any(x => x.WindowAreas.ContainsKey(w.Handle) && x.WindowAreas[w.Handle].Contains(mousePosition))) + if (_screens.Select(x => x.AttachedScreen.DeviceName).Contains(Screen.FromHandle(w.Handle).DeviceName)) + return w; + return null; + } + + public void OnPaint(PaintEventArgs e) + { + _screens.ForEach(x => x.OnPaint(e)); + + Graphics g = e.Graphics; + Color desktopBorderColor = IsCurrentActiveDesktop ? Form.ActiveDesktopBorderColor : Form.DesktopColor; + g.DrawRectangle( + new Pen(new SolidBrush(desktopBorderColor), Form.BorderSize), + new Rectangle(Location.X, Location.Y, Size.Width - (Form.BorderSize), Size.Height - (Form.BorderSize))); + } + + public void OnMouseUp(object sender, MouseEventArgs e) + { + if (!IsInsideBounds(e.X, e.Y)) return; + if ((e.Button & MouseButtons.Left) == MouseButtons.Left) + { + WindowsVirtualDesktop.FromIndex(VirtualDesktopIndex).MakeVisible(); + Form.Invalidate(); + } + } + + public void OnMouseDown(object sender, MouseEventArgs e) + { + if (!IsInsideBounds(e.X, e.Y)) return; + if ((e.Button & MouseButtons.Left) == MouseButtons.Left) + { + var w = GetWindowUnderCursor(e.Location); + if (w != null) + { + _dragDropData = new DragDropData() + { + OriginDesktopIndex = VirtualDesktopIndex, + DraggedWindow = w + }; + Size dragSize = SystemInformation.DragSize; + dragBoxFromMouseDown = new Rectangle(new Point(e.X - (dragSize.Width / 2), e.Y - (dragSize.Height / 2)), dragSize); + } + else + dragBoxFromMouseDown = Rectangle.Empty; + } + } + + public void OnMouseMove(object sender, MouseEventArgs e) + { + if (!IsInsideBounds(e.X, e.Y)) return; + if ((e.Button & MouseButtons.Left) == MouseButtons.Left) + if (dragBoxFromMouseDown != Rectangle.Empty && !dragBoxFromMouseDown.Contains(e.X, e.Y)) + Form.DoDragDrop(_dragDropData, DragDropEffects.Move); + } + + public void OnDragDrop(object sender, DragEventArgs e) + { + if (!IsInsideBounds(Form.PointToClient(new Point(e.X, e.Y)))) return; + if (e.Data.GetDataPresent(typeof(DragDropData))) + { + var ddd = (DragDropData)e.Data.GetData(typeof(DragDropData)); + if (e.Effect == DragDropEffects.Move) + { + WindowsVirtualDesktop.FromIndex(VirtualDesktopIndex).MoveWindow(ddd.DraggedWindow.Handle); + Form.Invalidate(); + } + } + } + + public void OnDragOver(object sender, DragEventArgs e) + { + if (!e.Data.GetDataPresent(typeof(DragDropData))) + { + e.Effect = DragDropEffects.None; + return; + } + if ((e.AllowedEffect & DragDropEffects.Move) == DragDropEffects.Move) + e.Effect = DragDropEffects.Move; + } + } + +} \ No newline at end of file diff --git a/src/GUI/VirtualDesktopScreen.cs b/src/GUI/VirtualDesktopScreen.cs new file mode 100644 index 0000000..d54b185 --- /dev/null +++ b/src/GUI/VirtualDesktopScreen.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Windows.Forms; + +namespace Switchie +{ + + public class VirtualDesktopScreen + { + public Size Size { get; set; } + public MainForm Form { get; set; } + public Point Location { get; set; } + public Screen AttachedScreen { get; set; } + public VirtualDesktop VirtualDesktop { get; set; } + public Dictionary WindowAreas { get; set; } = new Dictionary(); + + private Point MousePosition { get => Control.MousePosition; } + private Window ActiveWindow { get => Form.Windows.SingleOrDefault(x => x.IsActive); } + + public void OnPaint(PaintEventArgs e) + { + Graphics g = e.Graphics; + var windows = Form.Windows.Where(x => x.VirtualDesktopIndex == VirtualDesktop.VirtualDesktopIndex).OrderBy(x => x.ZOrder).ToArray(); + + foreach (var w in windows) + { + if (Screen.FromHandle(w.Handle).DeviceName == AttachedScreen.DeviceName) + { + Color fillColor = w.IsActive ? Form.ActiveWindowColor : Form.WindowColor; + Color borderColor = w.IsActive ? Form.ActiveWindowBorderColor : Form.WindowBorderColor; + + var x = w.Dimensions.X; + var y = w.Dimensions.Y; + x -= AttachedScreen.Bounds.Left; + y -= AttachedScreen.Bounds.Top; + var area = new Rectangle(x, y, w.Dimensions.Width - Form.BorderSize, w.Dimensions.Height - Form.BorderSize); + + // Scale rectangles down to the thumbnail's desired size + var ar = Helpers.AspectRatioResize(new Size(AttachedScreen.Bounds.Width, AttachedScreen.Bounds.Height), 0, Form.PagerHeight); + float percentageWidth = (float)ar.Width * 100 / AttachedScreen.Bounds.Width; + float percentageHeight = (float)ar.Height * 100 / AttachedScreen.Bounds.Height; + + area.X = (int)(area.X * (percentageWidth / 100)); + area.Y = (int)(area.Y * (percentageHeight / 100)); + area.Width = (int)(area.Width * (percentageWidth / 100)); + area.Height = (int)(area.Height * (percentageWidth / 100)); + + area.X += Location.X; + area.Y += Location.Y; + WindowAreas[w.Handle] = area; + + // Window rectangle + g.FillRectangle(new SolidBrush(fillColor), new Rectangle(area.X, area.Y, area.Width - (Form.BorderSize), area.Height - (Form.BorderSize))); + + // Window icon + var oldBounds = e.Graphics.ClipBounds; + e.Graphics.Clip = new Region(area); + g.DrawImage(w.Icon, new Point( + (area.X + area.Width / 2) - w.Icon.Width / 2, + (area.Y + area.Height / 2) - w.Icon.Height / 2 + )); + e.Graphics.Clip = new Region(oldBounds); + + // Window border + g.DrawRectangle(new Pen(new SolidBrush(borderColor), Form.BorderSize), new Rectangle(area.X, area.Y, area.Width - (Form.BorderSize), area.Height - (Form.BorderSize))); + } + else + continue; + } + } + } + +} \ No newline at end of file diff --git a/src/Program.cs b/src/Program.cs new file mode 100644 index 0000000..5446709 --- /dev/null +++ b/src/Program.cs @@ -0,0 +1,19 @@ +using System; +using System.Threading; +using System.Windows.Forms; + +namespace Switchie +{ + static class Program + { + public static CancellationToken ApplicationClosing = new CancellationToken(); + + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new MainForm()); + } + } +} diff --git a/src/Publish.cmd b/src/Publish.cmd new file mode 100644 index 0000000..1702736 --- /dev/null +++ b/src/Publish.cmd @@ -0,0 +1,7 @@ +@echo off +dotnet restore +dotnet publish -c Release -o ../prod +cd .. +cd prod +del /q *.pdb +del /q *.config diff --git a/src/Resources/icon.ico b/src/Resources/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..a7ac068f4917d87b2beed0a06cc868635fdd4459 GIT binary patch literal 102870 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 YPoPdpduY1433^I&ANLgYM*o8TAC^{IjQ{`u literal 0 HcmV?d00001 diff --git a/src/Resources/icon.png b/src/Resources/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..fe0e9b1382bf1abc3f30a8a6cfa8f235a5ef3aa7 GIT binary patch literal 5515 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 literal 0 HcmV?d00001 diff --git a/src/Resources/icon.svg b/src/Resources/icon.svg new file mode 100644 index 0000000..5db07c4 --- /dev/null +++ b/src/Resources/icon.svg @@ -0,0 +1,13 @@ + + + + + + + diff --git a/src/Switchie.csproj b/src/Switchie.csproj new file mode 100644 index 0000000..c802424 --- /dev/null +++ b/src/Switchie.csproj @@ -0,0 +1,38 @@ + + + + WinExe + net4.8 + true + x64 + win-x64 + Resources/icon.ico + false + + + + + + + + + + + + + + \ No newline at end of file