From fd4c3fe4411d0368c8901511f9d7b532bc86ccb5 Mon Sep 17 00:00:00 2001 From: Will Davies Date: Mon, 17 Apr 2023 16:26:05 -0500 Subject: [PATCH 1/3] Complete revamp of project: - Switch to .NET core - Added new config file option for "jump to last" if trying to jump to desktop you're on - Brought virtual desktop API in-house - Much better lifecycle management & response to Windows explorer - Icon displaying which desktop you are currently on --- .gitmodules | 3 - .idea/.idea.WinJump/.idea/.gitignore | 2 +- .idea/.idea.WinJump/.idea/encodings.xml | 12 +- .idea/.idea.WinJump/.idea/vcs.xml | 2 +- README.md | 163 +++++---- VirtualDesktop/Properties/AssemblyInfo.cs | 35 -- VirtualDesktop/VirtualDesktop | 1 - VirtualDesktop/VirtualDesktop.csproj | 69 ---- WinJump.sln | 16 +- WinJump/App.config | 6 - WinJump/AssemblyInfo.cs | 10 + WinJump/Config.cs | 137 ------- WinJump/Core/Config.cs | 192 ++++++++++ WinJump/Core/ExplorerMonitor.cs | 122 +++++++ WinJump/Core/KeyboardHook.cs | 121 +++++++ .../IVirtualDesktopAPI.cs | 43 +++ .../Core/VirtualDesktopDefinitions/README.md | 177 +++++++++ .../Windows10_17763.cs | 321 +++++++++++++++++ .../Windows11_22000.cs | 339 +++++++++++++++++ .../Windows11_22621.cs | 340 ++++++++++++++++++ WinJump/Core/WinJumpManager.cs | 237 ++++++++++++ WinJump/KeyboardHook.cs | 128 ------- WinJump/Program.cs | 144 -------- WinJump/Properties/AssemblyInfo.cs | 36 -- WinJump/Properties/Resources.Designer.cs | 62 ---- WinJump/Properties/Resources.resx | 117 ------ WinJump/Properties/Settings.Designer.cs | 23 -- WinJump/Properties/Settings.settings | 7 - WinJump/UI/App.xaml | 12 + WinJump/UI/App.xaml.cs | 70 ++++ WinJump/UI/Icons/Dark/1.ico | Bin 0 -> 67646 bytes WinJump/UI/Icons/Dark/10.ico | Bin 0 -> 67646 bytes WinJump/UI/Icons/Dark/11.ico | Bin 0 -> 67646 bytes WinJump/UI/Icons/Dark/12.ico | Bin 0 -> 67646 bytes WinJump/UI/Icons/Dark/13.ico | Bin 0 -> 67646 bytes WinJump/UI/Icons/Dark/14.ico | Bin 0 -> 67646 bytes WinJump/UI/Icons/Dark/15+.ico | Bin 0 -> 67646 bytes WinJump/UI/Icons/Dark/15.ico | Bin 0 -> 67646 bytes WinJump/UI/Icons/Dark/2.ico | Bin 0 -> 67646 bytes WinJump/UI/Icons/Dark/3.ico | Bin 0 -> 67646 bytes WinJump/UI/Icons/Dark/4.ico | Bin 0 -> 67646 bytes WinJump/UI/Icons/Dark/5.ico | Bin 0 -> 67646 bytes WinJump/UI/Icons/Dark/6.ico | Bin 0 -> 67646 bytes WinJump/UI/Icons/Dark/7.ico | Bin 0 -> 67646 bytes WinJump/UI/Icons/Dark/8.ico | Bin 0 -> 67646 bytes WinJump/UI/Icons/Dark/9.ico | Bin 0 -> 67646 bytes WinJump/UI/Icons/Light/1.ico | Bin 0 -> 67646 bytes WinJump/UI/Icons/Light/10.ico | Bin 0 -> 67646 bytes WinJump/UI/Icons/Light/11.ico | Bin 0 -> 67646 bytes WinJump/UI/Icons/Light/12.ico | Bin 0 -> 67646 bytes WinJump/UI/Icons/Light/13.ico | Bin 0 -> 67646 bytes WinJump/UI/Icons/Light/14.ico | Bin 0 -> 67646 bytes WinJump/UI/Icons/Light/15+.ico | Bin 0 -> 67646 bytes WinJump/UI/Icons/Light/15.ico | Bin 0 -> 67646 bytes WinJump/UI/Icons/Light/2.ico | Bin 0 -> 67646 bytes WinJump/UI/Icons/Light/3.ico | Bin 0 -> 67646 bytes WinJump/UI/Icons/Light/4.ico | Bin 0 -> 67646 bytes WinJump/UI/Icons/Light/5.ico | Bin 0 -> 67646 bytes WinJump/UI/Icons/Light/6.ico | Bin 0 -> 67646 bytes WinJump/UI/Icons/Light/7.ico | Bin 0 -> 67646 bytes WinJump/UI/Icons/Light/8.ico | Bin 0 -> 67646 bytes WinJump/UI/Icons/Light/9.ico | Bin 0 -> 67646 bytes WinJump/UI/TrayModel.cs | 88 +++++ WinJump/UI/TrayResources.xaml | 22 ++ WinJump/WinJump.csproj | 244 ++++++------- 65 files changed, 2325 insertions(+), 976 deletions(-) delete mode 100644 .gitmodules delete mode 100644 VirtualDesktop/Properties/AssemblyInfo.cs delete mode 160000 VirtualDesktop/VirtualDesktop delete mode 100644 VirtualDesktop/VirtualDesktop.csproj delete mode 100644 WinJump/App.config create mode 100644 WinJump/AssemblyInfo.cs delete mode 100644 WinJump/Config.cs create mode 100644 WinJump/Core/Config.cs create mode 100644 WinJump/Core/ExplorerMonitor.cs create mode 100644 WinJump/Core/KeyboardHook.cs create mode 100644 WinJump/Core/VirtualDesktopDefinitions/IVirtualDesktopAPI.cs create mode 100644 WinJump/Core/VirtualDesktopDefinitions/README.md create mode 100644 WinJump/Core/VirtualDesktopDefinitions/Windows10_17763.cs create mode 100644 WinJump/Core/VirtualDesktopDefinitions/Windows11_22000.cs create mode 100644 WinJump/Core/VirtualDesktopDefinitions/Windows11_22621.cs create mode 100644 WinJump/Core/WinJumpManager.cs delete mode 100644 WinJump/KeyboardHook.cs delete mode 100644 WinJump/Program.cs delete mode 100644 WinJump/Properties/AssemblyInfo.cs delete mode 100644 WinJump/Properties/Resources.Designer.cs delete mode 100644 WinJump/Properties/Resources.resx delete mode 100644 WinJump/Properties/Settings.Designer.cs delete mode 100644 WinJump/Properties/Settings.settings create mode 100644 WinJump/UI/App.xaml create mode 100644 WinJump/UI/App.xaml.cs create mode 100644 WinJump/UI/Icons/Dark/1.ico create mode 100644 WinJump/UI/Icons/Dark/10.ico create mode 100644 WinJump/UI/Icons/Dark/11.ico create mode 100644 WinJump/UI/Icons/Dark/12.ico create mode 100644 WinJump/UI/Icons/Dark/13.ico create mode 100644 WinJump/UI/Icons/Dark/14.ico create mode 100644 WinJump/UI/Icons/Dark/15+.ico create mode 100644 WinJump/UI/Icons/Dark/15.ico create mode 100644 WinJump/UI/Icons/Dark/2.ico create mode 100644 WinJump/UI/Icons/Dark/3.ico create mode 100644 WinJump/UI/Icons/Dark/4.ico create mode 100644 WinJump/UI/Icons/Dark/5.ico create mode 100644 WinJump/UI/Icons/Dark/6.ico create mode 100644 WinJump/UI/Icons/Dark/7.ico create mode 100644 WinJump/UI/Icons/Dark/8.ico create mode 100644 WinJump/UI/Icons/Dark/9.ico create mode 100644 WinJump/UI/Icons/Light/1.ico create mode 100644 WinJump/UI/Icons/Light/10.ico create mode 100644 WinJump/UI/Icons/Light/11.ico create mode 100644 WinJump/UI/Icons/Light/12.ico create mode 100644 WinJump/UI/Icons/Light/13.ico create mode 100644 WinJump/UI/Icons/Light/14.ico create mode 100644 WinJump/UI/Icons/Light/15+.ico create mode 100644 WinJump/UI/Icons/Light/15.ico create mode 100644 WinJump/UI/Icons/Light/2.ico create mode 100644 WinJump/UI/Icons/Light/3.ico create mode 100644 WinJump/UI/Icons/Light/4.ico create mode 100644 WinJump/UI/Icons/Light/5.ico create mode 100644 WinJump/UI/Icons/Light/6.ico create mode 100644 WinJump/UI/Icons/Light/7.ico create mode 100644 WinJump/UI/Icons/Light/8.ico create mode 100644 WinJump/UI/Icons/Light/9.ico create mode 100644 WinJump/UI/TrayModel.cs create mode 100644 WinJump/UI/TrayResources.xaml diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 09fcc83..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "VirtualDesktop/VirtualDesktop"] - path = VirtualDesktop/VirtualDesktop - url = git@github.com:widavies/VirtualDesktop.git diff --git a/.idea/.idea.WinJump/.idea/.gitignore b/.idea/.idea.WinJump/.idea/.gitignore index b40851e..ba71b90 100644 --- a/.idea/.idea.WinJump/.idea/.gitignore +++ b/.idea/.idea.WinJump/.idea/.gitignore @@ -3,9 +3,9 @@ /workspace.xml # Rider ignored files /modules.xml -/.idea.WinJump.iml /contentModel.xml /projectSettingsUpdater.xml +/.idea.WinJump.iml # Editor-based HTTP Client requests /httpRequests/ # Datasource local storage ignored files diff --git a/.idea/.idea.WinJump/.idea/encodings.xml b/.idea/.idea.WinJump/.idea/encodings.xml index ef271a4..37d6b97 100644 --- a/.idea/.idea.WinJump/.idea/encodings.xml +++ b/.idea/.idea.WinJump/.idea/encodings.xml @@ -1,11 +1,11 @@ - - - - - - + + + + + + \ No newline at end of file diff --git a/.idea/.idea.WinJump/.idea/vcs.xml b/.idea/.idea.WinJump/.idea/vcs.xml index 35eb1dd..94a25f7 100644 --- a/.idea/.idea.WinJump/.idea/vcs.xml +++ b/.idea/.idea.WinJump/.idea/vcs.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/README.md b/README.md index 4cc25c6..60c8831 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,47 @@ -# Overview -Ever wanted a way to jump to the `Nth` desktop on Windows 10 or 11 with a keyboard shortcut? WinJump allows you to create custom shortcuts to jump to a specific desktop or cycle between a predefined group of desktops. +# WinJump -WinJump uses the excellent [VirtualDesktop](https://github.com/MScholtes/VirtualDesktop) library. Most other solutions use an [AutoHotKey](https://www.autohotkey.com/) based solution which automates pressing the Windows built-in `WINDOWS_KEY + CTRL + [LEFT ARROW OR RIGHT ARROW]` shortcut a number of times in a row. -This often results in glitchly visuals and lagging while jumping to the desktop you want. VirtualDesktop instead immediately jumps to the desired desktop. +Ever wanted to jump directly to your `Nth` desktop on Windows 10 or 11 with a keyboard shortcut of your choice? WinJump enables you to create custom shortcuts to jump to any desktop and cycle between groups of desktops. + +Most other solutions use an [AutoHotKey](https://www.autohotkey.com/) based solution which automates pressing the Windows default shortuct Win + Ctrl + Left Arrow or Right Arrow multiple times. +This often results in glitchly visuals and lagging while jumping to the desktop you want. +WinJump uses the excellent [VirtualDesktop](https://github.com/MScholtes/VirtualDesktop) library which jumps directly to the desired desktop. + +## Features + +### Jump To + +Jump directly to a desktop with Win + [ 0 - 9 ] *(default)*. + +### Toggle Groups + +Cycle through a group of desktops with a single shortcut *(there are no groups by default)*. + +### Back and Forth + +Pressing the shortcut for the desktop you are currently on will jump back to the last desktop you were on. + +## Installation + +### Supported versions -# Installation -## Supported versions Currently, the following versions of Windows are supported: | Windows Edition | Version | | ----------- | ----------- | | Windows 10 | 1607-1709, 1803, 1809 - 21H2 | | Windows 11 | 21H2, 22H2 | -## How to install -1) [Download](https://github.com/widavies/WinJump/releases/download/1.4.0/Release_1_4_0.zip) -2) Extract and run `setup.exe` -3) You're done! WinJump will start automatically and will register itself to start when your computer boots. -## Config file -You can optionally include a configuration file named `.winjump` in your home directory to change default behavior. +### How to install + +1. [Download](https://github.com/widavies/WinJump/releases/download/1.4.0/Release_1_4_0.zip) +2. Extract and run *setup.exe* +3. You're done! WinJump will start automatically and will register itself to start when your computer boots. + +### Config file + +You can optionally include a configuration file named *.winjump* in your home directory to change the default behavior. + +#### Syntax -### Syntax There are two blocks: - `toggle-groups` let you group desktops together and cycle through them with a keyboard shortcut @@ -29,71 +51,88 @@ Both blocks contain a list of items, each item has a `shortcut` property. This s `win`, `alt`, `shift`, and `ctrl`, it must be terminated by a key listed [here](https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.keys?view=windowsdesktop-7.0), and each token must be separated by `+`. -Each toggle group has the `desktops` property, which should be a list of positive integers, with `1` representing the first desktop. +Each `toggle-groups` item has the `desktops` property, which should be a list of positive integers, with `1` representing the first desktop. -Each jump to item has the `desktop` property, which should be a single positive integer, with `1` representing the first desktop. +Each `jump-to` item has the `desktop` property, which should be a single positive integer, with `1` representing the first desktop. -> Warning: If no `.winjump` config file is found or a syntax error exists within it, WinJump will revert to the default key mappings. +> ⚠️ If no *.winjump* config file is found or a syntax error exists within it, WinJump will use default key mappings. -WinJump does not auto-reload your configuration file. You must either launch task manager, kill `WinJump`, then launch it again from the start menu or reboot. +> ⚠️ WinJump does not auto-reload your configuration file. To apply changes, restart WinJump via one of the following methods: +> +> - launch task manager, kill WinJump, launch it again from the start menu +> - log out and back in +> - reboot -### Example -Below is an example configuration file that changes the shortcut to `alt+N` to jump to a desktop and adds a toggle group that is triggered by `alt+w` that will cycle between desktops `1`, `5`, and `6`: +#### Example -``` -C:\Users\\.winjump +Below is an example configuration file that changes the shortcut to `alt+N` to jump to a desktop and adds a toggle group that is triggered by `alt+w` that will cycle between desktops `1`, `5`, and `6`: +```jsonc +// C:\Users\\.winjump { "toggle-groups": [ { "shortcut": "alt+w", - "desktops": [1, 5, 6] + "desktops": [ 1, 5, 6 ] } ], "jump-to": [ - { - shortcut: "alt+d1", - desktop: 1 - }, - { - shortcut: "alt+d2", - desktop: 2 - }, - { - shortcut: "alt+d3", - desktop: 3 - }, - { - shortcut: "alt+d4", - desktop: 4 - }, - { - shortcut: "alt+d5", - desktop: 5 - }, - { - shortcut: "alt+d6", - desktop: 6 - }, - { - shortcut: "alt+d7", - desktop: 7 - }, - { - shortcut: "alt+d8", - desktop: 8 - }, - { - shortcut: "alt+d9", - desktop: 9 - }, - { - shortcut: "alt+d0", - desktop: 10 + { + "shortcut": "alt+d1", + "desktop": 1 + }, + { + "shortcut": "alt+d2", + "desktop": 2 + }, + { + "shortcut": "alt+d3", + "desktop": 3 + }, + { + "shortcut": "alt+d4", + "desktop": 4 + }, + { + "shortcut": "alt+d5", + "desktop": 5 + }, + { + "shortcut": "alt+d6", + "desktop": 6 + }, + { + "shortcut": "alt+d7", + "desktop": 7 + }, + { + "shortcut": "alt+d8", + "desktop": 8 + }, + { + "shortcut": "alt+d9", + "desktop": 9 + }, + { + "shortcut": "alt+d0", + "desktop": 10 } ] } ``` -# Known issues +## Uninstallation + +WinJump can be uninstalled via the windows application manager + +1. Press the start button +2. Search for "Add or remove programs" +3. Find WinJump +4. Uninstall it + +## Known issues + - Launching WinJump while it is already running will hang Windows explorer. To fix this you have to use `ctrl+shift+esc` to open task manager, kill all WinJump instances, use `Run new task` and type `explorer`, then start WinJump again + +## Attributions +[Icon created by Freepik](https://www.flaticon.com/free-icons/monitor) diff --git a/VirtualDesktop/Properties/AssemblyInfo.cs b/VirtualDesktop/Properties/AssemblyInfo.cs deleted file mode 100644 index 0c91f1b..0000000 --- a/VirtualDesktop/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly:AssemblyTitle("Command line tool to manage virtual desktops")] -[assembly:AssemblyDescription("Command line tool to manage virtual desktops")] -[assembly:AssemblyConfiguration("")] -[assembly:AssemblyCompany("MS")] -[assembly:AssemblyProduct("VirtualDesktop")] -[assembly:AssemblyCopyright("© Markus Scholtes 2022")] -[assembly:AssemblyTrademark("")] -[assembly:AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("153091D1-8AF0-4609-AA38-DB01F11D0C4A")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly:AssemblyVersion("1.11.0.0")] -[assembly:AssemblyFileVersion("1.11.0.0")] \ No newline at end of file diff --git a/VirtualDesktop/VirtualDesktop b/VirtualDesktop/VirtualDesktop deleted file mode 160000 index e8d2d0c..0000000 --- a/VirtualDesktop/VirtualDesktop +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e8d2d0cd8cf426364f99e3e0c80a2e744fd9a714 diff --git a/VirtualDesktop/VirtualDesktop.csproj b/VirtualDesktop/VirtualDesktop.csproj deleted file mode 100644 index 2d4098f..0000000 --- a/VirtualDesktop/VirtualDesktop.csproj +++ /dev/null @@ -1,69 +0,0 @@ - - - - - Debug - AnyCPU - {153091D1-8AF0-4609-AA38-DB01F11D0C4A} - Library - Properties - VirtualDesktop - VirtualDesktop - v4.8.1 - 512 - 7.3 - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/WinJump.sln b/WinJump.sln index fefdfe7..e51cb5d 100644 --- a/WinJump.sln +++ b/WinJump.sln @@ -1,8 +1,6 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinJump", "WinJump\WinJump.csproj", "{22774C1D-A813-493F-AB33-AE3A4049FADC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VirtualDesktop", "VirtualDesktop\VirtualDesktop.csproj", "{153091D1-8AF0-4609-AA38-DB01F11D0C4A}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinJump", "WinJump\WinJump.csproj", "{5D1F2C40-1F60-4394-93C9-337A738F3BC0}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -10,13 +8,9 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {22774C1D-A813-493F-AB33-AE3A4049FADC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {22774C1D-A813-493F-AB33-AE3A4049FADC}.Release|Any CPU.Build.0 = Release|Any CPU - {22774C1D-A813-493F-AB33-AE3A4049FADC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {22774C1D-A813-493F-AB33-AE3A4049FADC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {153091D1-8AF0-4609-AA38-DB01F11D0C4A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {153091D1-8AF0-4609-AA38-DB01F11D0C4A}.Release|Any CPU.Build.0 = Release|Any CPU - {153091D1-8AF0-4609-AA38-DB01F11D0C4A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {153091D1-8AF0-4609-AA38-DB01F11D0C4A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5D1F2C40-1F60-4394-93C9-337A738F3BC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5D1F2C40-1F60-4394-93C9-337A738F3BC0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5D1F2C40-1F60-4394-93C9-337A738F3BC0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5D1F2C40-1F60-4394-93C9-337A738F3BC0}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/WinJump/App.config b/WinJump/App.config deleted file mode 100644 index aee9adf..0000000 --- a/WinJump/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/WinJump/AssemblyInfo.cs b/WinJump/AssemblyInfo.cs new file mode 100644 index 0000000..4a05c7d --- /dev/null +++ b/WinJump/AssemblyInfo.cs @@ -0,0 +1,10 @@ +using System.Windows; + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] \ No newline at end of file diff --git a/WinJump/Config.cs b/WinJump/Config.cs deleted file mode 100644 index 189e002..0000000 --- a/WinJump/Config.cs +++ /dev/null @@ -1,137 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Windows.Forms; -using Newtonsoft.Json; - -namespace WinJump { - internal sealed class Config { - [JsonProperty("toggle-groups")] - public List ToggleGroups { get; set; } - - [JsonProperty("jump-to")] - public List JumpTo { get; set; } - - public static Config FromFile(string path) { - try { - string content = File.ReadAllText(path); - - var config = JsonConvert.DeserializeObject(content); - - // Check for jump tos with duplicate shortcuts - for (int i = 0; i < config.JumpTo.Count; i++) { - var shortcut = config.JumpTo[i].Shortcut; - for (int j = i + 1; j < config.JumpTo.Count; j++) { - if (config.JumpTo[j].Shortcut.IsEqual(shortcut)) { - throw new Exception("Duplicate jump to shortcut"); - } - } - } - - // Check for toggle groups with duplicate shortcuts - for (int i = 0; i < config.ToggleGroups.Count; i++) { - var shortcut = config.ToggleGroups[i].Shortcut; - for (int j = i + 1; j < config.ToggleGroups.Count; j++) { - if (config.ToggleGroups[j].Shortcut.IsEqual(shortcut)) { - throw new Exception("Duplicate toggle group shortcut"); - } - } - } - - return config; - } catch (Exception) { - return Default(); - } - } - - private static Config Default() { - var jumpTo = new List(); - - for (var k = Keys.D0; k <= Keys.D9; k++) { - jumpTo.Add(new JumpTo() { - Shortcut = new Shortcut() { - ModifierKeys = ModifierKeys.Win, - Keys = k - } - }); - } - - return new Config { - JumpTo = jumpTo, - ToggleGroups = new List() - }; - } - } - - public sealed class ToggleGroup { - [JsonConverter(typeof(ShortcutConverter))] - public Shortcut Shortcut { get; set; } - - public List Desktops { get; set; } - - public bool IsEqual(ToggleGroup other) { - return Shortcut.IsEqual(other.Shortcut); - } - } - - public sealed class JumpTo { - [JsonConverter(typeof(ShortcutConverter))] - public Shortcut Shortcut { get; set; } - - public int Desktop { get; set; } - - public bool IsEqual(JumpTo other) { - return Shortcut.IsEqual(other.Shortcut); - } - } - - public sealed class Shortcut { - public ModifierKeys ModifierKeys { get; set; } - public Keys Keys { get; set; } - - public bool IsEqual(Shortcut other) { - return ModifierKeys == other.ModifierKeys && Keys == other.Keys; - } - } - - public sealed class ShortcutConverter : JsonConverter { - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { - throw new NotImplementedException(); - } - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, - JsonSerializer serializer) { - string expression = reader.Value?.ToString() ?? throw new Exception("Invalid shortcut"); - - var stack = new Queue(expression.Split('+')); - - ModifierKeys modifiers = 0; - - var lookup = new Dictionary { - {"ctrl", ModifierKeys.Control}, - {"alt", ModifierKeys.Alt}, - {"shift", ModifierKeys.Shift}, - {"win", ModifierKeys.Win} - }; - - while (stack.Count > 0) { - string token = stack.Dequeue(); - - if (lookup.ContainsKey(token)) { - modifiers |= lookup[token]; - } else { - return new Shortcut { - ModifierKeys = modifiers, - Keys = (Keys) Enum.Parse(typeof(Keys), token, true) - }; - } - } - - throw new Exception($"Invalid shortcut: {expression}"); - } - - public override bool CanConvert(Type objectType) { - return objectType == typeof(string); - } - } -} \ No newline at end of file diff --git a/WinJump/Core/Config.cs b/WinJump/Core/Config.cs new file mode 100644 index 0000000..af1343f --- /dev/null +++ b/WinJump/Core/Config.cs @@ -0,0 +1,192 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Windows.Forms; +using Newtonsoft.Json; + +namespace WinJump.Core; + +internal sealed class Config { + public static readonly string LOCATION = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), + ".winjump"); + + [JsonProperty("toggle-groups")] + public required List ToggleGroups { get; set; } + + [JsonProperty("jump-to")] + public required List JumpTo { get; set; } + + [JsonProperty("jump-current-goes-to-last")] + public required bool JumpCurrentGoesToLast { get; set; } + + public static Config Load() { + try { + EnsureCreated(); + + string content = File.ReadAllText(LOCATION); + + var config = JsonConvert.DeserializeObject(content); + + if (config == null) { + throw new Exception("Failed to deserialize config file"); + } + + // Check for jump tos with duplicate shortcuts + for (int i = 0; i < config.JumpTo.Count; i++) { + var shortcut = config.JumpTo[i].Shortcut; + for (int j = i + 1; j < config.JumpTo.Count; j++) { + if (config.JumpTo[j].Shortcut.IsEqual(shortcut)) { + throw new Exception("Duplicate jump to shortcut"); + } + + if (config.JumpTo[i].Desktop <= 0) { + throw new Exception("Invalid desktop number"); + } + } + } + + // Check for toggle groups with duplicate shortcuts + for (int i = 0; i < config.ToggleGroups.Count; i++) { + var shortcut = config.ToggleGroups[i].Shortcut; + for (int j = i + 1; j < config.ToggleGroups.Count; j++) { + if (config.ToggleGroups[j].Shortcut.IsEqual(shortcut)) { + throw new Exception("Duplicate toggle group shortcut"); + } + + if (config.ToggleGroups[i].Desktops.Any((d) => d <= 0)) { + throw new Exception("Invalid desktop number"); + } + } + } + + return config; + } catch (Exception) { + return Default(); + } + } + + public static void EnsureCreated() { + if (File.Exists(LOCATION)) return; + + var config = Default(); + string content = JsonConvert.SerializeObject(config, Formatting.Indented); + File.WriteAllText(LOCATION, content); + } + + /// + /// Creates the default config file + /// + /// Default configuration + private static Config Default() { + var jumpTo = new List(); + + uint desktop = 1; + for (var k = Keys.D0; k <= Keys.D9; k++) { + jumpTo.Add(new JumpTo { + Shortcut = new Shortcut { + ModifierKeys = ModifierKeys.Alt, + Keys = k, + }, + Desktop = k == Keys.D0 ? 10 : desktop++ + }); + } + + return new Config { + JumpTo = jumpTo, + ToggleGroups = new List(), + JumpCurrentGoesToLast = true + }; + } +} + +public sealed class ToggleGroup { + [JsonConverter(typeof(ShortcutConverter))] + public required Shortcut Shortcut { get; set; } + + public required List Desktops { get; set; } + + public bool IsEqual(ToggleGroup other) { + return Shortcut.IsEqual(other.Shortcut); + } +} + +public sealed class JumpTo { + [JsonConverter(typeof(ShortcutConverter))] + public required Shortcut Shortcut { get; set; } + + [JsonProperty("desktop")] + public required uint Desktop { get; set; } + + public bool IsEqual(JumpTo other) { + return Shortcut.IsEqual(other.Shortcut); + } +} + +public sealed class Shortcut { + private static readonly Dictionary LOOKUP = new() { + {"ctrl", ModifierKeys.Control}, + {"alt", ModifierKeys.Alt}, + {"shift", ModifierKeys.Shift}, + {"win", ModifierKeys.Win} + }; + + public ModifierKeys ModifierKeys { get; set; } + public Keys Keys { get; set; } + + public bool IsEqual(Shortcut other) { + return ModifierKeys == other.ModifierKeys && Keys == other.Keys; + } + + // Parsing stuff + + // Parses the shortcut from a string + public static Shortcut FromString(string expression) { + var stack = new Queue(expression.Split('+')); + + ModifierKeys modifiers = 0; + + while (stack.Count > 0) { + string token = stack.Dequeue(); + + if (LOOKUP.TryGetValue(token, out var value)) { + modifiers |= value; + } else { + return new Shortcut { + ModifierKeys = modifiers, + Keys = (Keys) Enum.Parse(typeof(Keys), token, true) + }; + } + } + + throw new Exception("Invalid shortcut"); + } + + public override string ToString() { + string modifiers = string.Join("+", Enum.GetValues().Where(key => ModifierKeys.HasFlag(key)) + .Select(key => LOOKUP.First(lookup => lookup.Value == key).Key)); + + return modifiers + "+" + Keys.ToString().ToLower(); + } +} + +public sealed class ShortcutConverter : JsonConverter { + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) { + if (value is not Shortcut shortcut) { + throw new Exception("Can't serialize null shortcut"); + } + + serializer.Serialize(writer, shortcut.ToString()); + } + + public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, + JsonSerializer serializer) { + string expression = reader.Value?.ToString() ?? throw new Exception("Invalid shortcut"); + return Shortcut.FromString(expression); + } + + public override bool CanConvert(Type objectType) { + return objectType == typeof(string) || objectType == typeof(Shortcut); + } +} \ No newline at end of file diff --git a/WinJump/Core/ExplorerMonitor.cs b/WinJump/Core/ExplorerMonitor.cs new file mode 100644 index 0000000..4d256bc --- /dev/null +++ b/WinJump/Core/ExplorerMonitor.cs @@ -0,0 +1,122 @@ +using System; +using System.Diagnostics; +using System.Linq; +using System.Runtime.InteropServices; +using System.Threading; +using System.Windows.Interop; +using System.Windows.Threading; +using Microsoft.Win32; + +namespace WinJump.Core; + +public delegate void ColorSchemeChanged(bool lightMode); + +public delegate void ExplorerRestarted(); + +/// +/// ExplorerMonitor is responsible for: +/// - Starting/stopping explorer.exe (if necessary) +/// - Listening to system color scheme changes +/// - Listening for taskbar restart events (several notification registrations require explorer to be running) +/// +/// Takes some inspiration from https://github.com/mntone/VirtualDesktop/tree/develop +/// +public class ExplorerMonitor : IDisposable { + public event ColorSchemeChanged OnColorSchemeChanged { + add { + _onColorSchemeChanged += value; + // Immediately notify the caller of the current light/dark mode + value.Invoke(IsLightMode()); + } + remove => _onColorSchemeChanged -= value; + } + + public event ExplorerRestarted? OnExplorerRestarted; + + /* + * Internal fields + */ + + [DllImport("user32.dll", CharSet = CharSet.Unicode)] + private static extern uint RegisterWindowMessage(string lpProcName); + + [DllImport("user32.dll")] + private static extern bool CloseWindow(IntPtr hWnd); + + private event ColorSchemeChanged? _onColorSchemeChanged; + private HwndSource _source { get; } + private readonly uint _explorerRestartedMessage; + + private readonly AutoResetEvent taskbarCreatedEvent = new(false); + + + public ExplorerMonitor() { + _source = new HwndSource(new HwndSourceParameters("ExplorerMonitorWinJump") { + Width = 1, + Height = 1, + WindowStyle = 0x800000 + }); + _source.AddHook(WndProc); + _explorerRestartedMessage = RegisterWindowMessage("TaskbarCreated"); + } + + private static bool IsLightMode() { + RegistryKey? startupApp = Registry.CurrentUser.OpenSubKey( + @"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize", false); + + object? val = startupApp?.GetValue("AppsUseLightTheme"); + + bool lightMode = (int) (val ?? 0) == 1; + return lightMode; + } + + private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { + if(msg == _explorerRestartedMessage) { + taskbarCreatedEvent.Set(); + + OnExplorerRestarted?.Invoke(); + + return IntPtr.Zero; + } + + if(msg == 0x001A) { + // https://stackoverflow.com/questions/51334674/how-to-detect-windows-10-light-dark-mode-in-win32-application + _onColorSchemeChanged?.Invoke(IsLightMode()); + + return IntPtr.Zero; + } + + return IntPtr.Zero; + } + + public void Kill() { + var killExplorer = Process.Start("cmd.exe", "/c taskkill /f /im explorer.exe"); + + killExplorer.WaitForExit(); + } + + public void EnsureExplorerIsAlive() { + var processes = Process.GetProcessesByName("explorer"); + + if(processes.Length > 0 && !processes.Any(x => x.HasExited)) { + return; + } + + taskbarCreatedEvent.Reset(); + + // If it's not, start it + Process.Start(Environment.SystemDirectory + "\\..\\explorer.exe"); + + taskbarCreatedEvent.WaitOne(); + } + + public void Dispose() { + taskbarCreatedEvent.Dispose(); + _source.RemoveHook(WndProc); + // Source could have been created on a different thread, which means we + // have to Dispose of it on the UI thread or it will crash. + _source.Dispatcher?.BeginInvoke(DispatcherPriority.Send, (Action) (() => _source.Dispose())); + CloseWindow(_source.Handle); + GC.SuppressFinalize(this); + } +} \ No newline at end of file diff --git a/WinJump/Core/KeyboardHook.cs b/WinJump/Core/KeyboardHook.cs new file mode 100644 index 0000000..deeb48b --- /dev/null +++ b/WinJump/Core/KeyboardHook.cs @@ -0,0 +1,121 @@ +using System; +using System.Runtime.InteropServices; +using System.Windows.Forms; + +namespace WinJump.Core; + +// Credit: https://stackoverflow.com/a/27309185/4779937 +public sealed class KeyboardHook : IDisposable { + // Registers a hot key with Windows. + [DllImport("user32.dll")] + private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk); + + // Unregisters the hot key with Windows. + [DllImport("user32.dll")] + private static extern bool UnregisterHotKey(IntPtr hWnd, int id); + + /// + /// Represents the window that is used internally to get the messages. + /// + private sealed class Window : NativeWindow, IDisposable { + private const int WM_HOTKEY = 0x0312; + public event EventHandler? KeyPressed; + + public Window() { + // create the handle for the window. + CreateHandle(new CreateParams()); + } + + /// + /// Overridden to get the notifications. + /// + /// + protected override void WndProc(ref Message m) { + base.WndProc(ref m); + + // check if we got a hot key pressed. + if (m.Msg == WM_HOTKEY) { + // get the keys. + Keys key = (Keys) (((int) m.LParam >> 16) & 0xFFFF); + ModifierKeys modifier = (ModifierKeys) ((int) m.LParam & 0xFFFF); + + // invoke the event to notify the parent. + KeyPressed?.Invoke(this, new KeyPressedEventArgs(modifier, key)); + } + } + + + #region IDisposable Members + + public void Dispose() { + DestroyHandle(); + } + + #endregion + } + + private readonly Window _window = new(); + private int _currentId; + /// + /// A hot key has been pressed. + /// + public event EventHandler? KeyPressed; + + public KeyboardHook() { + // register the event of the inner native window. + _window.KeyPressed += delegate(object? _, KeyPressedEventArgs args) { KeyPressed?.Invoke(this, args); }; + } + + /// + /// Registers a hot key in the system. + /// + /// The modifiers that are associated with the hot key. + /// The key itself that is associated with the hot key. + public bool RegisterHotKey(ModifierKeys modifier, Keys key) { + // increment the counter. + _currentId++; + + // register the hot key. + return RegisterHotKey(_window.Handle, _currentId, (uint) (modifier | ModifierKeys.NoRepeat), (uint) key); + } + + #region IDisposable Members + + public void Dispose() { + // unregister all the registered hot keys. + for (int i = _currentId; i > 0; i--) { + UnregisterHotKey(_window.Handle, i); + } + + // dispose the inner native window. + _window.Dispose(); + } + + #endregion +} + +/// +/// Event Args for the event that is fired after the hot key has been pressed. +/// +public class KeyPressedEventArgs : EventArgs { + internal KeyPressedEventArgs(ModifierKeys modifier, Keys key) { + Modifier = modifier; + Key = key; + } + + public ModifierKeys Modifier { get; } + + public Keys Key { get; } +} + +/// +/// The enumeration of possible modifiers. +/// +[Flags] +public enum ModifierKeys : uint { + Alt = 1, + Control = 2, + Shift = 4, + Win = 8, + NoRepeat = 0x4000 +} \ No newline at end of file diff --git a/WinJump/Core/VirtualDesktopDefinitions/IVirtualDesktopAPI.cs b/WinJump/Core/VirtualDesktopDefinitions/IVirtualDesktopAPI.cs new file mode 100644 index 0000000..7617260 --- /dev/null +++ b/WinJump/Core/VirtualDesktopDefinitions/IVirtualDesktopAPI.cs @@ -0,0 +1,43 @@ +using System; +using Microsoft.Win32; + +namespace WinJump.Core.VirtualDesktopDefinitions; + +public interface IVirtualDesktopAPI : IDisposable { + + /// + /// An event that notifies subscribers when the virtual desktop changes. + /// + event OnDesktopChanged OnDesktopChanged; + + /// + /// Returns the current virtual desktop that the user is on. + /// + /// 0-indexed, where '0' is the first desktop + int GetCurrentDesktop(); + + /// + /// Jumps to the virtual desktop. + /// + /// 0-indexed desktop number. If it is invalid it will be ignored. + void JumpToDesktop(int index); + + public static IVirtualDesktopAPI Create() { + string? releaseId = Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion", + "CurrentBuildNumber", "")?.ToString(); + + if(!int.TryParse(releaseId, out int buildNumber)) { + throw new Exception("Unrecognized Windows version"); + } + + return buildNumber switch { + // Work out the proper desktop wrapper + >= 22621 => new Windows11_22621.VirtualDesktopApi(), + >= 22000 => new Windows11_22000.VirtualDesktopApi(), + >= 17763 => new Windows10_17763.VirtualDesktopApi(), + _ => throw new Exception("Unsupported Windows version") + }; + } +} + +public delegate void OnDesktopChanged(int desktop); \ No newline at end of file diff --git a/WinJump/Core/VirtualDesktopDefinitions/README.md b/WinJump/Core/VirtualDesktopDefinitions/README.md new file mode 100644 index 0000000..a77f973 --- /dev/null +++ b/WinJump/Core/VirtualDesktopDefinitions/README.md @@ -0,0 +1,177 @@ +# Reverse engineering Windows virtual desktops + +## Introduction + +Unfortunately Windows does not publish an API for virtual desktops. This means two things: +1. Windows must be reverse engineered to figure out the internal API for virtual desktops. +2. Windows may change the API on a whim for any given release. Every time this happens, step #1 needs to be repeated. + +Much of the reverse engineering has been [done already](https://github.com/MScholtes/VirtualDesktop). +Huge thanks to [MScholtes](https://github.com/MScholtes) and [NyaMisty](https://github.com/NyaMisty). + +MScholtes and NyaMisty regularly release reverse-engineered virtual desktop APIs for the latest Windows builds, +so most of the necessary work can be copied from them. However, WinJump does require a few more APIs that they do not +reverse engineer (most notably `IVirtualDesktopNotificationService`). For this reason, WinJump maintains its own +reverse-engineered API definitions. + +Because Windows internal API changes are a regular occurence (and I don't have a lot of extra time), +I've decided to document the process here to make it easier for you to get WinJump working on your specific version of Windows. + +`WinJump/Core/VirtualDesktopDefinitions/` contains a list of _virtual desktop API definitions_. They have the format +like: +``` +Windows10_17763.cs +Windows11_22000.cs +Windows11_22621.cs +WindowsXX_.cs +... +``` + +Each of these provides an implementation for `IVirtualDesktopAPI` for a specific Windows build. Every time the virtual +desktop API changes, a new `WindowsXX_.cs` file needs to be added. Please abide by the naming scheme: the build +number in each file name indicates the first Windows build number for which the implementation applies. This +implementation will apply to all subsequent Windows build numbers until the API changes, at which point a new file +is created. If you take a look at `Create()` in the `IVirtualDesktopAPI` file, the Windows build version +is checked and the appropriate implementation is used. + +## Down to business +To begin, download a copy of the following [Python script](https://github.com/widavies/GetVirtualDesktopAPI_DIA). + +Make sure Visual Studio is installed. You may have to update [this line](https://github.com/widavies/GetVirtualDesktopAPI_DIA/blob/fcb6a6ed4ecbd5cfac6518853e79581596125ba6/DiaGetVDInfo.py#L12) +in the script to point to a `msdiaXXX.dll` file within your Visual Studio installation folder. It doesn't seem like the +particular number after this file matters. Run the script and it will produce an output like so: +``` +IID_IVirtualDesktopManager: A5CD92FF-29BE-454C-8D04-D82879FB3F1B +IID_IVirtualDesktopAccessibility: 9975B71D-0A84-4909-BDDE-B455BBFA55C6 +IID_IVirtualDesktopManagerInternal: B2F925B9-5A0F-4D2E-9F4D-2B1507593C10 +IID_IVirtualDesktopHotkeyHandler: 71DB071A-44AE-4271-B9F6-01CFB6A12DEE +IID_IVirtualDesktopSwitcherInvoker: 7A25165A-86F1-4B4A-B1D2-E89650CD9589 +IID_IVirtualDesktopNotificationService: 0CD45E71-D927-4F15-8B0A-8FEF525337BF +IID_IVirtualDesktopPinnedApps: 4CE81583-1E4C-4632-A621-07A53543148F +IID_IVirtualDesktopAnimationSyncNotification: 6CAFD3F1-05D1-4D26-A32A-9907A72C920B +IID_IVirtualDesktop: 536D3495-B208-4CC9-AE26-DE8111275BF8 +IID_IVirtualDesktopTabletModePolicyService: 56B32065-0BB3-42E2-975D-A559DE1316E8 +IID_IVirtualDesktopNotification: CD403E52-DEED-4C13-B437-B98380F2B1E8 +IID_IVirtualDesktopAnimationSyncNotificationService: 0DDAF2D8-C38F-4638-95FC-FB9C6DDAE52F +IID_IVirtualDesktopSwitcherHost: 1BE71764-E771-4442-B78F-EDA2C7F067F3 + + +Dumping vftable: const CVirtualDesktopComponent::`vftable'{for `Microsoft::WRL::Details::Selector,0,struct Microsoft::WRL::Details::ImplementsMarker,class Microsoft::WRL::FtmBase> >'} + Method 0: public: virtual long __cdecl Microsoft::WRL::Details::RuntimeClassImpl,1,0,0,class CImmersiveShellComponentWithGITSite,class Microsoft::WRL::FtmBase>::QueryInterface(struct _GUID const & __ptr64,void * __ptr64 * __ptr64) __ptr64 (?QueryInterface@?$RuntimeClassImpl@U?$RuntimeClassFlags@$01@WRL@Microsoft@@$00$0A@$0A@VCImmersiveShellComponentWithGITSite@@VFtmBase@23@@Details@WRL@Microsoft@@UEAAJAEBU_GUID@@PEAPEAX@Z) + Method 1: public: virtual unsigned long __cdecl Microsoft::WRL::Details::RuntimeClassImpl,1,0,0,class CImmersiveShellComponentWithSite,struct IShellPositionerManager,struct IApplicationViewChangeListener,struct ITabletModeChangeListener,struct IShellPositionerActivationHandler,struct IShellPositionerFrameworkViewTypeChangedHandler,struct IShellPositionerPresentationRequestedHandler>::AddRef(void) __ptr64 (?AddRef@?$RuntimeClassImpl@U?$RuntimeClassFlags@$01@WRL@Microsoft@@$00$0A@$0A@VCImmersiveShellComponentWithSite@@UIShellPositionerManager@@UIApplicationViewChangeListener@@UITabletModeChangeListener@@UIShellPositionerActivationHandler@@UIShellPositionerFrameworkViewTypeChangedHandler@@UIShellPositionerPresentationRequestedHandler@@@Details@WRL@Microsoft@@UEAAKXZ) + Method 2: public: virtual unsigned long __cdecl Microsoft::WRL::Details::RuntimeClassImpl,1,0,0,class CImmersiveShellComponentWithGITSite,class Microsoft::WRL::FtmBase>::Release(void) __ptr64 (?Release@?$RuntimeClassImpl@U?$RuntimeClassFlags@$01@WRL@Microsoft@@$00$0A@$0A@VCImmersiveShellComponentWithGITSite@@VFtmBase@23@@Details@WRL@Microsoft@@UEAAKXZ) + Method 3: public: virtual long __cdecl CWRLObjectWithGITSite::SetSite(struct IUnknown * __ptr64) __ptr64 (?SetSite@CWRLObjectWithGITSite@@UEAAJPEAUIUnknown@@@Z) + Method 4: public: virtual long __cdecl CWRLObjectWithGITSite::GetSite(struct _GUID const & __ptr64,void * __ptr64 * __ptr64) __ptr64 (?GetSite@CWRLObjectWithGITSite@@UEAAJAEBU_GUID@@PEAPEAX@Z) + + +Dumping vftable: const CVirtualDesktopHolographicViewTransitionNotification::`vftable'{for `IHolographicViewTransitionNotification'} + Method 0: public: virtual long __cdecl Microsoft::WRL::Details::RuntimeClassImpl,1,0,0,struct IHolographicViewTransitionNotification,class Microsoft::WRL::FtmBase>::QueryInterface(struct _GUID const & __ptr64,void * __ptr64 * __ptr64) __ptr64 (?QueryInterface@?$RuntimeClassImpl@U?$RuntimeClassFlags@$01@WRL@Microsoft@@$00$0A@$0A@UIHolographicViewTransitionNotification@@VFtmBase@23@@Details@WRL@Microsoft@@UEAAJAEBU_GUID@@PEAPEAX@Z) + Method 1: public: virtual unsigned long __cdecl Microsoft::WRL::Details::RuntimeClassImpl,1,0,0,struct IImmersiveShellHookNotification,class Microsoft::WRL::FtmBase>::AddRef(void) __ptr64 (?AddRef@?$RuntimeClassImpl@U?$RuntimeClassFlags@$01@WRL@Microsoft@@$00$0A@$0A@UIImmersiveShellHookNotification@@VFtmBase@23@@Details@WRL@Microsoft@@UEAAKXZ) + Method 2: public: virtual unsigned long __cdecl Microsoft::WRL::Details::RuntimeClassImpl,1,0,0,class CWRLObjectWithSite,struct IVisibilityOverride,struct IImmersiveSessionIdleNotification>::Release(void) __ptr64 (?Release@?$RuntimeClassImpl@U?$RuntimeClassFlags@$01@WRL@Microsoft@@$00$0A@$0A@VCWRLObjectWithSite@@UIVisibilityOverride@@UIImmersiveSessionIdleNotification@@@Details@WRL@Microsoft@@UEAAKXZ) + Method 3: public: virtual long __cdecl CVirtualDesktopHolographicViewTransitionNotification::ViewTransitionedToHolographic(struct IApplicationView * __ptr64) __ptr64 (?ViewTransitionedToHolographic@CVirtualDesktopHolographicViewTransitionNotification@@UEAAJPEAUIApplicationView@@@Z) + Method 4: public: virtual long __cdecl CVirtualDesktopHolographicViewTransitionNotification::ViewTransitionedFromHolographic(struct IApplicationView * __ptr64) __ptr64 (?ViewTransitionedFromHolographic@CVirtualDesktopHolographicViewTransitionNotification@@UEAAJPEAUIApplicationView@@@Z) + Method 5: public: virtual void * __ptr64 __cdecl CVirtualDesktopHolographicViewTransitionNotification::`vector deleting destructor'(unsigned int) __ptr64 (??_ECVirtualDesktopHolographicViewTransitionNotification@@UEAAPEAXI@Z) + +... +``` + +To get WinJump working, you have to provide definitions for the three functions in `IVirtualDesktopAPI`: +```c# +/// +/// An event that notifies subscribers when the virtual desktop changes. +/// +event OnDesktopChanged OnDesktopChanged; + +/// +/// Returns the current virtual desktop that the user is on. +/// +/// 0-indexed, where '0' is the first desktop +int GetCurrentDesktop(); + +/// +/// Jumps to the virtual desktop. +/// +/// 0-indexed desktop number. If it is invalid it will be ignored. +void JumpToDesktop(int index); +``` + +Under the hood, this usually requires reverse engineering and accessing the `IVirtualDesktopManagerInternal` and +`IVirtualDesktopNotificationService` internal services. For these services, you MUST figure out three items: +1. The `GUID` of the service +2. The signature of the service: the functions it contains with their signatures. +3. The `ComImport` attribute `GUID` + +### Item 1 +The `GUID` class contains the COM service `GUID`s: +```c# +public static readonly Guid CLSID_ImmersiveShell = new("C2F03A33-21F5-47FA-B4BB-156362A2F239"); + +public static readonly Guid CLSID_VirtualDesktopManagerInternal = new("C5E0CDCA-7B6E-41B2-9FC4-D93975CC467B"); + +public static readonly Guid CLSID_VirtualDesktopNotificationService = + new("A501FDEC-4A09-464C-AE4E-1B9C21B84918"); +``` + +These `GUID`s are fixed. It is unlikely that they will change in any future Windows release. They can be a bit hard +to track down, but using [magnumdb.com](https://www.magnumdb.com/search?q=CLSID_ImmersiveShell) and querying +by the name (e.g. "CLSID_ImmersiveShell") seems to work well. + +### Item 2 +Starting with the `IVirtualDesktopNotificationService` and `IVirtualDesktopManagerInternal`, all internal services +must be translated into C# interfaces. For example, the Python script will dump: + +``` +Dumping vftable: const CVirtualDesktopManager::`vftable'{for `IVirtualDesktopManagerInternal'} + Method 0: [thunk]:public: virtual long __cdecl Microsoft::WRL::Details::RuntimeClassImpl,1,1,0,struct Microsoft::WRL::ChainInterfaces,struct IVirtualDesktopManagerInternal,struct ISuspendableVirtualDesktopManager,struct IImmersiveWindowMessageNotification,class Microsoft::WRL::FtmBase>::QueryInterface`adjustor{24}' (struct _GUID const & __ptr64,void * __ptr64 * __ptr64) __ptr64 (?QueryInterface@?$RuntimeClassImpl@U?$RuntimeClassFlags@$02@WRL@Microsoft@@$00$00$0A@U?$ChainInterfaces@UIVirtualDesktopManagerPrivate@@UIVirtualDesktopManagerInternal@@VNil@Details@WRL@Microsoft@@V3456@V3456@V3456@V3456@V3456@V3456@V3456@@23@UIVirtualDesktopManagerInternal@@UISuspendableVirtualDesktopManager@@UIImmersiveWindowMessageNotification@@VFtmBase@23@@Details@WRL@Microsoft@@WBI@EAAJAEBU_GUID@@PEAPEAX@Z) + Method 1: [thunk]:public: virtual unsigned long __cdecl Microsoft::WRL::Details::RuntimeClassImpl,1,1,0,struct Microsoft::WRL::ChainInterfaces,struct IVirtualDesktopManagerInternal,struct ISuspendableVirtualDesktopManager,struct IImmersiveWindowMessageNotification,class Microsoft::WRL::FtmBase>::AddRef`adjustor{24}' (void) __ptr64 (?AddRef@?$RuntimeClassImpl@U?$RuntimeClassFlags@$02@WRL@Microsoft@@$00$00$0A@U?$ChainInterfaces@UIVirtualDesktopManagerPrivate@@UIVirtualDesktopManagerInternal@@VNil@Details@WRL@Microsoft@@V3456@V3456@V3456@V3456@V3456@V3456@V3456@@23@UIVirtualDesktopManagerInternal@@UISuspendableVirtualDesktopManager@@UIImmersiveWindowMessageNotification@@VFtmBase@23@@Details@WRL@Microsoft@@WBI@EAAKXZ) + Method 2: [thunk]:public: virtual unsigned long __cdecl Microsoft::WRL::Details::RuntimeClassImpl,1,1,0,struct Microsoft::WRL::ChainInterfaces,struct IVirtualDesktopManagerInternal,struct ISuspendableVirtualDesktopManager,struct IImmersiveWindowMessageNotification,class Microsoft::WRL::FtmBase>::Release`adjustor{24}' (void) __ptr64 (?Release@?$RuntimeClassImpl@U?$RuntimeClassFlags@$02@WRL@Microsoft@@$00$00$0A@U?$ChainInterfaces@UIVirtualDesktopManagerPrivate@@UIVirtualDesktopManagerInternal@@VNil@Details@WRL@Microsoft@@V3456@V3456@V3456@V3456@V3456@V3456@V3456@@23@UIVirtualDesktopManagerInternal@@UISuspendableVirtualDesktopManager@@UIImmersiveWindowMessageNotification@@VFtmBase@23@@Details@WRL@Microsoft@@WBI@EAAKXZ) + Method 3: [thunk]:public: virtual long __cdecl CVirtualDesktopManager::GetCount`adjustor{16}' (struct HMONITOR__ * __ptr64,unsigned int * __ptr64) __ptr64 (?GetCount@CVirtualDesktopManager@@WBA@EAAJPEAUHMONITOR__@@PEAI@Z) + Method 4: [thunk]:public: virtual long __cdecl CVirtualDesktopManager::MoveViewToDesktop`adjustor{16}' (struct IApplicationView * __ptr64,struct IVirtualDesktop * __ptr64) __ptr64 (?MoveViewToDesktop@CVirtualDesktopManager@@WBA@EAAJPEAUIApplicationView@@PEAUIVirtualDesktop@@@Z) + Method 5: [thunk]:public: virtual long __cdecl CVirtualDesktopManager::CanViewMoveDesktops`adjustor{16}' (struct IApplicationView * __ptr64,int * __ptr64) __ptr64 (?CanViewMoveDesktops@CVirtualDesktopManager@@WBA@EAAJPEAUIApplicationView@@PEAH@Z) + Method 6: [thunk]:public: virtual long __cdecl CVirtualDesktopManager::GetCurrentDesktop`adjustor{16}' (struct HMONITOR__ * __ptr64,struct IVirtualDesktop * __ptr64 * __ptr64) __ptr64 (?GetCurrentDesktop@CVirtualDesktopManager@@WBA@EAAJPEAUHMONITOR__@@PEAPEAUIVirtualDesktop@@@Z) + Method 7: [thunk]:public: virtual long __cdecl CVirtualDesktopManager::GetAllCurrentDesktops`adjustor{16}' (struct IObjectArray * __ptr64 * __ptr64) __ptr64 (?GetAllCurrentDesktops@CVirtualDesktopManager@@WBA@EAAJPEAPEAUIObjectArray@@@Z) + Method 8: [thunk]:public: virtual long __cdecl CVirtualDesktopManager::GetDesktops`adjustor{16}' (struct HMONITOR__ * __ptr64,struct IObjectArray * __ptr64 * __ptr64) __ptr64 (?GetDesktops@CVirtualDesktopManager@@WBA@EAAJPEAUHMONITOR__@@PEAPEAUIObjectArray@@@Z) + Method 9: [thunk]:public: virtual long __cdecl CVirtualDesktopManager::GetAdjacentDesktop`adjustor{16}' (struct IVirtualDesktop * __ptr64,unsigned int,struct IVirtualDesktop * __ptr64 * __ptr64) __ptr64 (?GetAdjacentDesktop@CVirtualDesktopManager@@WBA@EAAJPEAUIVirtualDesktop@@IPEAPEAU2@@Z) + Method 10: [thunk]:public: virtual long __cdecl CVirtualDesktopManager::SwitchDesktop`adjustor{16}' (struct HMONITOR__ * __ptr64,struct IVirtualDesktop * __ptr64) __ptr64 (?SwitchDesktop@CVirtualDesktopManager@@WBA@EAAJPEAUHMONITOR__@@PEAUIVirtualDesktop@@@Z) + Method 11: [thunk]:public: virtual long __cdecl CVirtualDesktopManager::CreateDesktopW`adjustor{16}' (struct HMONITOR__ * __ptr64,struct IVirtualDesktop * __ptr64 * __ptr64) __ptr64 (?CreateDesktopW@CVirtualDesktopManager@@WBA@EAAJPEAUHMONITOR__@@PEAPEAUIVirtualDesktop@@@Z) + Method 12: [thunk]:public: virtual long __cdecl CVirtualDesktopManager::MoveDesktop`adjustor{16}' (struct IVirtualDesktop * __ptr64,struct HMONITOR__ * __ptr64,unsigned int) __ptr64 (?MoveDesktop@CVirtualDesktopManager@@WBA@EAAJPEAUIVirtualDesktop@@PEAUHMONITOR__@@I@Z) + Method 13: [thunk]:public: virtual long __cdecl CVirtualDesktopManager::RemoveDesktop`adjustor{16}' (struct IVirtualDesktop * __ptr64,struct IVirtualDesktop * __ptr64) __ptr64 (?RemoveDesktop@CVirtualDesktopManager@@WBA@EAAJPEAUIVirtualDesktop@@0@Z) + Method 14: [thunk]:public: virtual long __cdecl CVirtualDesktopManager::FindDesktop`adjustor{16}' (struct _GUID const & __ptr64,struct IVirtualDesktop * __ptr64 * __ptr64) __ptr64 (?FindDesktop@CVirtualDesktopManager@@WBA@EAAJAEBU_GUID@@PEAPEAUIVirtualDesktop@@@Z) + Method 15: [thunk]:public: virtual long __cdecl CVirtualDesktopManager::GetDesktopSwitchIncludeExcludeViews`adjustor{16}' (struct IVirtualDesktop * __ptr64,struct IObjectArray * __ptr64 * __ptr64,struct IObjectArray * __ptr64 * __ptr64) __ptr64 (?GetDesktopSwitchIncludeExcludeViews@CVirtualDesktopManager@@WBA@EAAJPEAUIVirtualDesktop@@PEAPEAUIObjectArray@@1@Z) + Method 16: [thunk]:public: virtual long __cdecl CVirtualDesktopManager::SetDesktopName`adjustor{16}' (struct IVirtualDesktop * __ptr64,struct HSTRING__ * __ptr64) __ptr64 (?SetDesktopName@CVirtualDesktopManager@@WBA@EAAJPEAUIVirtualDesktop@@PEAUHSTRING__@@@Z) + Method 17: [thunk]:public: virtual long __cdecl CVirtualDesktopManager::SetDesktopWallpaper`adjustor{16}' (struct IVirtualDesktop * __ptr64,struct HSTRING__ * __ptr64) __ptr64 (?SetDesktopWallpaper@CVirtualDesktopManager@@WBA@EAAJPEAUIVirtualDesktop@@PEAUHSTRING__@@@Z) + Method 18: [thunk]:public: virtual long __cdecl CVirtualDesktopManager::UpdateWallpaperPathForAllDesktops`adjustor{16}' (struct HSTRING__ * __ptr64) __ptr64 (?UpdateWallpaperPathForAllDesktops@CVirtualDesktopManager@@WBA@EAAJPEAUHSTRING__@@@Z) + Method 19: [thunk]:public: virtual long __cdecl CVirtualDesktopManager::CopyDesktopState`adjustor{16}' (struct IApplicationView * __ptr64,struct IApplicationView * __ptr64) __ptr64 (?CopyDesktopState@CVirtualDesktopManager@@WBA@EAAJPEAUIApplicationView@@0@Z) + Method 20: [thunk]:public: virtual long __cdecl CVirtualDesktopManager::GetDesktopIsPerMonitor`adjustor{16}' (int * __ptr64) __ptr64 (?GetDesktopIsPerMonitor@CVirtualDesktopManager@@WBA@EAAJPEAH@Z) +``` + +Create a matching `IVirtualDesktopManagerInternal` C# interface that matches the _exact_ signature of the function listing above. +In other words, you must translate Method 3 through Method 20 into a C# interface functions. You will need to create +an additional C# interface for each type that is referenced in the method listing above. For example, `MoveViewToDesktop` +references an `IApplicationView` type which also needs to be translated to a C# interface. Each of these interfaces +must be labeled with a `[GUID]` attribute. You can determine the `GUID` to place in this attribute by following [Item 3](#item-3) +below. + +### Item 3 +The `ComImport` can be found at the beginning of the Python script dump: +``` +IID_IVirtualDesktopNotification: CD403E52-DEED-4C13-B437-B98380F2B1E8 +IID_IVirtualDesktopNotificationService: 0CD45E71-D927-4F15-8B0A-8FEF525337BF +IID_IVirtualDesktopManagerInternal: B2F925B9-5A0F-4D2E-9F4D-2B1507593C10 +... +``` + +These `GUID`s should be copied above the corresponding C# interface: +```c# +[ComImport] +[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] +[Guid("0CD45E71-D927-4F15-8B0A-8FEF525337BF")] // copy to here +internal interface IVirtualDesktopNotificationService { +``` + +You'll need to make sure to copy over all `[Guid]` attribute tags to match the Python dump. + +While WinJump only needs the `IVirtualDesktopNotificationService` and `IVirtualDesktopManagerInternal` services, +their function signatures reference other interface types. This means that you'll have to consult the GUID +for every referenced type as explained in [Item #2](#Item-3). + diff --git a/WinJump/Core/VirtualDesktopDefinitions/Windows10_17763.cs b/WinJump/Core/VirtualDesktopDefinitions/Windows10_17763.cs new file mode 100644 index 0000000..d08fe63 --- /dev/null +++ b/WinJump/Core/VirtualDesktopDefinitions/Windows10_17763.cs @@ -0,0 +1,321 @@ +using System; +using System.Runtime.InteropServices; + +namespace WinJump.Core.VirtualDesktopDefinitions { + namespace Windows10_17763 { + public class VirtualDesktopApi : IVirtualDesktopAPI { + public event OnDesktopChanged? OnDesktopChanged; + private readonly uint cookie; + + public VirtualDesktopApi() { + cookie = DesktopManager.VirtualDesktopNotificationService.Register(new VirtualDesktopNotification { + OnDesktopChanged = desktop => { OnDesktopChanged?.Invoke(desktop); } + }); + } + + public int GetCurrentDesktop() { + return DesktopManager.GetCurrentDesktopNum(); + } + + public void JumpToDesktop(int index) { + DesktopManager.SwitchDesktop(index); + } + + public void Dispose() { + DesktopManager.VirtualDesktopNotificationService.Unregister(cookie); + GC.SuppressFinalize(this); + } + } + + /* + * Implementation + */ + + internal static class DesktopManager { + private static IVirtualDesktopManagerInternal VirtualDesktopManagerInternal; + internal static IVirtualDesktopNotificationService VirtualDesktopNotificationService; + + static DesktopManager() { + if(Activator.CreateInstance(Type.GetTypeFromCLSID(Guids.CLSID_ImmersiveShell) ?? + throw new Exception("Failed to get shell")) is not IServiceProvider10 + shell) { + throw new Exception("Failed to get shell"); + } + + VirtualDesktopManagerInternal = (IVirtualDesktopManagerInternal) shell.QueryService( + Guids.CLSID_VirtualDesktopManagerInternal, typeof(IVirtualDesktopManagerInternal).GUID); + VirtualDesktopNotificationService = (IVirtualDesktopNotificationService) shell.QueryService( + Guids.CLSID_VirtualDesktopNotificationService, typeof(IVirtualDesktopNotificationService).GUID); + } + + // Helpers + private static IVirtualDesktop GetDesktop(int index) { + int count = VirtualDesktopManagerInternal.GetCount(); + if(index < 0 || index >= count) throw new ArgumentOutOfRangeException("index"); + IObjectArray desktops; + VirtualDesktopManagerInternal.GetDesktops(out desktops); + object objdesktop; + desktops.GetAt(index, typeof(IVirtualDesktop).GUID, out objdesktop); + Marshal.ReleaseComObject(desktops); + return (IVirtualDesktop) objdesktop; + } + + internal static int GetIndex(IVirtualDesktop ivd) { + IObjectArray desktops; + VirtualDesktopManagerInternal.GetDesktops(out desktops); + + int count; + desktops.GetCount(out count); + + for(int i = 0; i < count; i++) { + object objdesktop; + desktops.GetAt(i, typeof(IVirtualDesktop).GUID, out objdesktop); + if(ReferenceEquals(ivd, objdesktop)) { + return i; + } + } + + Marshal.ReleaseComObject(desktops); + return -1; + } + + internal static void SwitchDesktop(int index) { + IVirtualDesktop desktop = GetDesktop(index); + VirtualDesktopManagerInternal.SwitchDesktop(desktop); + Marshal.ReleaseComObject(desktop); + } + + internal static int GetCurrentDesktopNum() { + var vd = VirtualDesktopManagerInternal.GetCurrentDesktop(); + + return GetIndex(vd); + } + } + + internal class VirtualDesktopNotification : IVirtualDesktopNotification { + public required Action OnDesktopChanged; + + public void VirtualDesktopCreated(IObjectArray array, IVirtualDesktop pDesktop) { + } + + public void VirtualDesktopDestroyBegin(IObjectArray array, IVirtualDesktop pDesktopDestroyed, + IVirtualDesktop pDesktopFallback) { + } + + public void VirtualDesktopDestroyFailed(IObjectArray array, IVirtualDesktop pDesktopDestroyed, + IVirtualDesktop pDesktopFallback) { + } + + public void VirtualDesktopDestroyed(IObjectArray array, IVirtualDesktop pDesktopDestroyed, + IVirtualDesktop pDesktopFallback) { + } + + public void VirtualDesktopIsPerMonitorChanged(int value) { + } + + public void VirtualDesktopMoved(IObjectArray array, IVirtualDesktop pDesktop, int nIndexFrom, + int nIndexTo) { + } + + public void VirtualDesktopNameChanged(IVirtualDesktop pDesktop, string path) { + } + + public void ViewVirtualDesktopChanged(IApplicationView pView) { + } + + public void CurrentVirtualDesktopChanged(IObjectArray array, IVirtualDesktop pDesktopOld, + IVirtualDesktop pDesktopNew) { + OnDesktopChanged.Invoke(DesktopManager.GetIndex(pDesktopNew)); + } + + public void VirtualDesktopWallpaperChanged(IObjectArray array, IVirtualDesktop pDesktop, string path) { + } + } + + #region COM Interfaces + + internal static class Guids { + public static readonly Guid CLSID_ImmersiveShell = new("C2F03A33-21F5-47FA-B4BB-156362A2F239"); + + public static readonly Guid CLSID_VirtualDesktopManagerInternal = + new("C5E0CDCA-7B6E-41B2-9FC4-D93975CC467B"); + + public static readonly Guid CLSID_VirtualDesktopNotificationService = + new("A501FDEC-4A09-464C-AE4E-1B9C21B84918"); + } + + [StructLayout(LayoutKind.Sequential)] + internal struct Size { + public int X; + public int Y; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct Rect { + public int Left; + public int Top; + public int Right; + public int Bottom; + } + + internal enum APPLICATION_VIEW_CLOAK_TYPE { + AVCT_NONE = 0, + AVCT_DEFAULT = 1, + AVCT_VIRTUAL_DESKTOP = 2 + } + + internal enum APPLICATION_VIEW_COMPATIBILITY_POLICY { + 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.InterfaceIsIInspectable)] + [Guid("372E1D3B-38D3-42E4-A15B-8AB2B178F513")] + internal 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("FF72FFDD-BE7E-43FC-9C03-AD81681E88E4")] + internal interface IVirtualDesktop { + bool IsViewVisible(IApplicationView view); + Guid GetId(); + } + + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("F31574D6-B682-4CDC-BD56-1827860ABEC6")] + internal 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("0CD45E71-D927-4F15-8B0A-8FEF525337BF")] + internal interface IVirtualDesktopNotificationService { + uint Register(IVirtualDesktopNotification notification); + + void Unregister(uint cookie); + } + + [ComImport] + [Guid("CD403E52-DEED-4C13-B437-B98380F2B1E8")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IVirtualDesktopNotification { + void VirtualDesktopCreated(IObjectArray array, IVirtualDesktop pDesktop); + + void VirtualDesktopDestroyBegin(IObjectArray array, IVirtualDesktop pDesktopDestroyed, + IVirtualDesktop pDesktopFallback); + + void VirtualDesktopDestroyFailed(IObjectArray array, IVirtualDesktop pDesktopDestroyed, + IVirtualDesktop pDesktopFallback); + + void VirtualDesktopDestroyed(IObjectArray array, IVirtualDesktop pDesktopDestroyed, + IVirtualDesktop pDesktopFallback); + + void VirtualDesktopIsPerMonitorChanged(int value); + + void VirtualDesktopMoved(IObjectArray array, IVirtualDesktop pDesktop, int nIndexFrom, int nIndexTo); + + void VirtualDesktopNameChanged(IVirtualDesktop pDesktop, [MarshalAs(UnmanagedType.HString)] string path); + + void ViewVirtualDesktopChanged(IApplicationView pView); + + void CurrentVirtualDesktopChanged(IObjectArray array, IVirtualDesktop pDesktopOld, + IVirtualDesktop pDesktopNew); + + void VirtualDesktopWallpaperChanged(IObjectArray array, IVirtualDesktop pDesktop, + [MarshalAs(UnmanagedType.HString)] + string path); + } + + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("92CA9DCD-5622-4BBA-A805-5E9F541BD8C9")] + internal 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("6D5140C1-7436-11CE-8034-00AA006009FA")] + internal interface IServiceProvider10 { + [return: MarshalAs(UnmanagedType.IUnknown)] + object QueryService(ref Guid service, ref Guid riid); + } + + #endregion + } +} \ No newline at end of file diff --git a/WinJump/Core/VirtualDesktopDefinitions/Windows11_22000.cs b/WinJump/Core/VirtualDesktopDefinitions/Windows11_22000.cs new file mode 100644 index 0000000..7ee9abc --- /dev/null +++ b/WinJump/Core/VirtualDesktopDefinitions/Windows11_22000.cs @@ -0,0 +1,339 @@ +using System; +using System.Runtime.InteropServices; + +namespace WinJump.Core.VirtualDesktopDefinitions { + namespace Windows11_22000 { + public class VirtualDesktopApi : IVirtualDesktopAPI { + public event OnDesktopChanged? OnDesktopChanged; + private readonly uint cookie; + + public VirtualDesktopApi() { + cookie = DesktopManager.VirtualDesktopNotificationService.Register(new VirtualDesktopNotification { + OnDesktopChanged = desktop => { OnDesktopChanged?.Invoke(desktop); } + }); + } + + public int GetCurrentDesktop() { + return DesktopManager.GetCurrentDesktopNum(); + } + + public void JumpToDesktop(int index) { + DesktopManager.SwitchDesktop(index); + } + + public void Dispose() { + DesktopManager.VirtualDesktopNotificationService.Unregister(cookie); + GC.SuppressFinalize(this); + } + } + + /* + * Implementation + */ + + internal static class DesktopManager { + private static IVirtualDesktopManagerInternal VirtualDesktopManagerInternal; + internal static IVirtualDesktopNotificationService VirtualDesktopNotificationService; + + static DesktopManager() { + if(Activator.CreateInstance(Type.GetTypeFromCLSID(Guids.CLSID_ImmersiveShell) ?? + throw new Exception("Failed to get shell")) is not IServiceProvider10 + shell) { + throw new Exception("Failed to get shell"); + } + + VirtualDesktopManagerInternal = (IVirtualDesktopManagerInternal) shell.QueryService( + Guids.CLSID_VirtualDesktopManagerInternal, typeof(IVirtualDesktopManagerInternal).GUID); + VirtualDesktopNotificationService = (IVirtualDesktopNotificationService) shell.QueryService( + Guids.CLSID_VirtualDesktopNotificationService, typeof(IVirtualDesktopNotificationService).GUID); + } + + // Helpers + private static IVirtualDesktop GetDesktop(int index) { + int count = VirtualDesktopManagerInternal.GetCount(IntPtr.Zero); + if(index < 0 || index >= count) throw new ArgumentOutOfRangeException("index"); + IObjectArray desktops; + VirtualDesktopManagerInternal.GetDesktops(IntPtr.Zero, out desktops); + object objdesktop; + desktops.GetAt(index, typeof(IVirtualDesktop).GUID, out objdesktop); + Marshal.ReleaseComObject(desktops); + return (IVirtualDesktop) objdesktop; + } + + internal static int GetIndex(IVirtualDesktop ivd) { + IObjectArray desktops; + VirtualDesktopManagerInternal.GetDesktops(IntPtr.Zero, out desktops); + + int count; + desktops.GetCount(out count); + + for(int i = 0; i < count; i++) { + object objdesktop; + desktops.GetAt(i, typeof(IVirtualDesktop).GUID, out objdesktop); + if(ReferenceEquals(ivd, objdesktop)) { + return i; + } + } + + Marshal.ReleaseComObject(desktops); + return -1; + } + + internal static void SwitchDesktop(int index) { + IVirtualDesktop desktop = GetDesktop(index); + VirtualDesktopManagerInternal.SwitchDesktop(IntPtr.Zero, desktop); + Marshal.ReleaseComObject(desktop); + } + + internal static int GetCurrentDesktopNum() { + var vd = VirtualDesktopManagerInternal.GetCurrentDesktop(IntPtr.Zero); + + return GetIndex(vd); + } + } + + internal class VirtualDesktopNotification : IVirtualDesktopNotification { + public required Action OnDesktopChanged; + + public void VirtualDesktopCreated(IObjectArray array, IVirtualDesktop pDesktop) { + } + + public void VirtualDesktopDestroyBegin(IObjectArray array, IVirtualDesktop pDesktopDestroyed, + IVirtualDesktop pDesktopFallback) { + } + + public void VirtualDesktopDestroyFailed(IObjectArray array, IVirtualDesktop pDesktopDestroyed, + IVirtualDesktop pDesktopFallback) { + } + + public void VirtualDesktopDestroyed(IObjectArray array, IVirtualDesktop pDesktopDestroyed, + IVirtualDesktop pDesktopFallback) { + } + + public void VirtualDesktopIsPerMonitorChanged(int value) { + } + + public void VirtualDesktopMoved(IObjectArray array, IVirtualDesktop pDesktop, int nIndexFrom, + int nIndexTo) { + } + + public void VirtualDesktopNameChanged(IVirtualDesktop pDesktop, string path) { + } + + public void ViewVirtualDesktopChanged(IApplicationView pView) { + } + + public void CurrentVirtualDesktopChanged(IObjectArray array, IVirtualDesktop pDesktopOld, + IVirtualDesktop pDesktopNew) { + OnDesktopChanged.Invoke(DesktopManager.GetIndex(pDesktopNew)); + } + + public void VirtualDesktopWallpaperChanged(IObjectArray array, IVirtualDesktop pDesktop, string path) { + } + } + + #region COM Interfaces + + internal static class Guids { + public static readonly Guid CLSID_ImmersiveShell = new("C2F03A33-21F5-47FA-B4BB-156362A2F239"); + + public static readonly Guid CLSID_VirtualDesktopManagerInternal = + new("C5E0CDCA-7B6E-41B2-9FC4-D93975CC467B"); + + public static readonly Guid CLSID_VirtualDesktopNotificationService = + new("A501FDEC-4A09-464C-AE4E-1B9C21B84918"); + } + + [StructLayout(LayoutKind.Sequential)] + internal struct Size { + public int X; + public int Y; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct Rect { + public int Left; + public int Top; + public int Right; + public int Bottom; + } + + internal enum APPLICATION_VIEW_CLOAK_TYPE { + AVCT_NONE = 0, + AVCT_DEFAULT = 1, + AVCT_VIRTUAL_DESKTOP = 2 + } + + internal enum APPLICATION_VIEW_COMPATIBILITY_POLICY { + 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.InterfaceIsIInspectable)] + [Guid("372E1D3B-38D3-42E4-A15B-8AB2B178F513")] + internal 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("536D3495-B208-4CC9-AE26-DE8111275BF8")] + internal interface IVirtualDesktop { + bool IsViewVisible(IApplicationView view); + Guid GetId(); + IntPtr Unknown1(); + + [return: MarshalAs(UnmanagedType.HString)] + string GetName(); + + [return: MarshalAs(UnmanagedType.HString)] + string GetWallpaperPath(); + } + + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("B2F925B9-5A0F-4D2E-9F4D-2B1507593C10")] + internal interface IVirtualDesktopManagerInternal { + int GetCount(IntPtr hWndOrMon); + void MoveViewToDesktop(IApplicationView view, IVirtualDesktop desktop); + bool CanViewMoveDesktops(IApplicationView view); + IVirtualDesktop GetCurrentDesktop(IntPtr hWndOrMon); + void GetDesktops(IntPtr hWndOrMon, out IObjectArray desktops); + + [PreserveSig] + int GetAdjacentDesktop(IVirtualDesktop from, int direction, out IVirtualDesktop desktop); + + void SwitchDesktop(IntPtr hWndOrMon, IVirtualDesktop desktop); + IVirtualDesktop CreateDesktop(IntPtr hWndOrMon); + void MoveDesktop(IVirtualDesktop desktop, IntPtr hWndOrMon, int nIndex); + void RemoveDesktop(IVirtualDesktop desktop, IVirtualDesktop fallback); + IVirtualDesktop FindDesktop(ref Guid desktopid); + + void GetDesktopSwitchIncludeExcludeViews(IVirtualDesktop desktop, out IObjectArray unknown1, + out IObjectArray unknown2); + + void SetDesktopName(IVirtualDesktop desktop, [MarshalAs(UnmanagedType.HString)] string name); + void SetDesktopWallpaper(IVirtualDesktop desktop, [MarshalAs(UnmanagedType.HString)] string path); + void UpdateWallpaperPathForAllDesktops([MarshalAs(UnmanagedType.HString)] string path); + void CopyDesktopState(IApplicationView pView0, IApplicationView pView1); + int GetDesktopIsPerMonitor(); + void SetDesktopIsPerMonitor(bool state); + } + + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("0CD45E71-D927-4F15-8B0A-8FEF525337BF")] + internal interface IVirtualDesktopNotificationService { + uint Register(IVirtualDesktopNotification notification); + + void Unregister(uint cookie); + } + + [ComImport] + [Guid("CD403E52-DEED-4C13-B437-B98380F2B1E8")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IVirtualDesktopNotification { + void VirtualDesktopCreated(IObjectArray array, IVirtualDesktop pDesktop); + + void VirtualDesktopDestroyBegin(IObjectArray array, IVirtualDesktop pDesktopDestroyed, + IVirtualDesktop pDesktopFallback); + + void VirtualDesktopDestroyFailed(IObjectArray array, IVirtualDesktop pDesktopDestroyed, + IVirtualDesktop pDesktopFallback); + + void VirtualDesktopDestroyed(IObjectArray array, IVirtualDesktop pDesktopDestroyed, + IVirtualDesktop pDesktopFallback); + + void VirtualDesktopIsPerMonitorChanged(int value); + + void VirtualDesktopMoved(IObjectArray array, IVirtualDesktop pDesktop, int nIndexFrom, int nIndexTo); + + void VirtualDesktopNameChanged(IVirtualDesktop pDesktop, [MarshalAs(UnmanagedType.HString)] string path); + + void ViewVirtualDesktopChanged(IApplicationView pView); + + void CurrentVirtualDesktopChanged(IObjectArray array, IVirtualDesktop pDesktopOld, + IVirtualDesktop pDesktopNew); + + void VirtualDesktopWallpaperChanged(IObjectArray array, IVirtualDesktop pDesktop, + [MarshalAs(UnmanagedType.HString)] + string path); + } + + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("92CA9DCD-5622-4BBA-A805-5E9F541BD8C9")] + internal 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("6D5140C1-7436-11CE-8034-00AA006009FA")] + internal interface IServiceProvider10 { + [return: MarshalAs(UnmanagedType.IUnknown)] + object QueryService(ref Guid service, ref Guid riid); + } + + #endregion + } +} diff --git a/WinJump/Core/VirtualDesktopDefinitions/Windows11_22621.cs b/WinJump/Core/VirtualDesktopDefinitions/Windows11_22621.cs new file mode 100644 index 0000000..de9505d --- /dev/null +++ b/WinJump/Core/VirtualDesktopDefinitions/Windows11_22621.cs @@ -0,0 +1,340 @@ +using System; +using System.Runtime.InteropServices; + +namespace WinJump.Core.VirtualDesktopDefinitions { + namespace Windows11_22621 { + public class VirtualDesktopApi : IVirtualDesktopAPI { + public event OnDesktopChanged? OnDesktopChanged; + private readonly uint cookie; + + public VirtualDesktopApi() { + cookie = DesktopManager.VirtualDesktopNotificationService.Register(new VirtualDesktopNotification { + OnDesktopChanged = desktop => { OnDesktopChanged?.Invoke(desktop); } + }); + } + + public int GetCurrentDesktop() { + return DesktopManager.GetCurrentDesktopNum(); + } + + public void JumpToDesktop(int index) { + DesktopManager.SwitchDesktop(index); + } + + public void Dispose() { + DesktopManager.VirtualDesktopNotificationService.Unregister(cookie); + GC.SuppressFinalize(this); + } + } + + /* + * Implementation + */ + + internal static class DesktopManager { + private static IVirtualDesktopManagerInternal VirtualDesktopManagerInternal; + internal static IVirtualDesktopNotificationService VirtualDesktopNotificationService; + + static DesktopManager() { + if(Activator.CreateInstance(Type.GetTypeFromCLSID(Guids.CLSID_ImmersiveShell) ?? + throw new Exception("Failed to get shell")) is not IServiceProvider10 + shell) { + throw new Exception("Failed to get shell"); + } + + VirtualDesktopManagerInternal = (IVirtualDesktopManagerInternal) shell.QueryService( + Guids.CLSID_VirtualDesktopManagerInternal, typeof(IVirtualDesktopManagerInternal).GUID); + VirtualDesktopNotificationService = (IVirtualDesktopNotificationService) shell.QueryService( + Guids.CLSID_VirtualDesktopNotificationService, typeof(IVirtualDesktopNotificationService).GUID); + } + + // Helpers + private static IVirtualDesktop GetDesktop(int index) { + int count = VirtualDesktopManagerInternal.GetCount(IntPtr.Zero); + if(index < 0 || index >= count) throw new ArgumentOutOfRangeException("index"); + IObjectArray desktops; + VirtualDesktopManagerInternal.GetDesktops(IntPtr.Zero, out desktops); + object objdesktop; + desktops.GetAt(index, typeof(IVirtualDesktop).GUID, out objdesktop); + Marshal.ReleaseComObject(desktops); + return (IVirtualDesktop) objdesktop; + } + + internal static int GetIndex(IVirtualDesktop ivd) { + IObjectArray desktops; + VirtualDesktopManagerInternal.GetDesktops(IntPtr.Zero, out desktops); + + int count; + desktops.GetCount(out count); + + for(int i = 0; i < count; i++) { + object objdesktop; + desktops.GetAt(i, typeof(IVirtualDesktop).GUID, out objdesktop); + if(ReferenceEquals(ivd, objdesktop)) { + return i; + } + } + + Marshal.ReleaseComObject(desktops); + return -1; + } + + internal static void SwitchDesktop(int index) { + IVirtualDesktop desktop = GetDesktop(index); + VirtualDesktopManagerInternal.SwitchDesktop(IntPtr.Zero, desktop); + Marshal.ReleaseComObject(desktop); + } + + internal static int GetCurrentDesktopNum() { + var vd = VirtualDesktopManagerInternal.GetCurrentDesktop(IntPtr.Zero); + + return GetIndex(vd); + } + } + + internal class VirtualDesktopNotification : IVirtualDesktopNotification { + public required Action OnDesktopChanged; + + public void VirtualDesktopCreated(IObjectArray array, IVirtualDesktop pDesktop) { + } + + public void VirtualDesktopDestroyBegin(IObjectArray array, IVirtualDesktop pDesktopDestroyed, + IVirtualDesktop pDesktopFallback) { + } + + public void VirtualDesktopDestroyFailed(IObjectArray array, IVirtualDesktop pDesktopDestroyed, + IVirtualDesktop pDesktopFallback) { + } + + public void VirtualDesktopDestroyed(IObjectArray array, IVirtualDesktop pDesktopDestroyed, + IVirtualDesktop pDesktopFallback) { + } + + public void VirtualDesktopIsPerMonitorChanged(int value) { + } + + public void VirtualDesktopMoved(IObjectArray array, IVirtualDesktop pDesktop, int nIndexFrom, + int nIndexTo) { + } + + public void VirtualDesktopNameChanged(IVirtualDesktop pDesktop, string path) { + } + + public void ViewVirtualDesktopChanged(IApplicationView pView) { + } + + public void CurrentVirtualDesktopChanged(IObjectArray array, IVirtualDesktop pDesktopOld, + IVirtualDesktop pDesktopNew) { + OnDesktopChanged.Invoke(DesktopManager.GetIndex(pDesktopNew)); + } + + public void VirtualDesktopWallpaperChanged(IObjectArray array, IVirtualDesktop pDesktop, string path) { + } + } + + #region COM Interfaces + + internal static class Guids { + public static readonly Guid CLSID_ImmersiveShell = new("C2F03A33-21F5-47FA-B4BB-156362A2F239"); + + public static readonly Guid CLSID_VirtualDesktopManagerInternal = + new("C5E0CDCA-7B6E-41B2-9FC4-D93975CC467B"); + + public static readonly Guid CLSID_VirtualDesktopNotificationService = + new("A501FDEC-4A09-464C-AE4E-1B9C21B84918"); + } + + [StructLayout(LayoutKind.Sequential)] + internal struct Size { + public int X; + public int Y; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct Rect { + public int Left; + public int Top; + public int Right; + public int Bottom; + } + + internal enum APPLICATION_VIEW_CLOAK_TYPE { + AVCT_NONE = 0, + AVCT_DEFAULT = 1, + AVCT_VIRTUAL_DESKTOP = 2 + } + + internal enum APPLICATION_VIEW_COMPATIBILITY_POLICY { + 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.InterfaceIsIInspectable)] + [Guid("372E1D3B-38D3-42E4-A15B-8AB2B178F513")] + internal 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("536D3495-B208-4CC9-AE26-DE8111275BF8")] + internal interface IVirtualDesktop { + bool IsViewVisible(IApplicationView view); + Guid GetId(); + IntPtr Unknown1(); + + [return: MarshalAs(UnmanagedType.HString)] + string GetName(); + + [return: MarshalAs(UnmanagedType.HString)] + string GetWallpaperPath(); + } + + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("B2F925B9-5A0F-4D2E-9F4D-2B1507593C10")] + internal interface IVirtualDesktopManagerInternal { + int GetCount(IntPtr hWndOrMon); + void MoveViewToDesktop(IApplicationView view, IVirtualDesktop desktop); + bool CanViewMoveDesktops(IApplicationView view); + IVirtualDesktop GetCurrentDesktop(IntPtr hWndOrMon); + IObjectArray GetAllCurrentDesktops(); + void GetDesktops(IntPtr hWndOrMon, out IObjectArray desktops); + + [PreserveSig] + int GetAdjacentDesktop(IVirtualDesktop from, int direction, out IVirtualDesktop desktop); + + void SwitchDesktop(IntPtr hWndOrMon, IVirtualDesktop desktop); + IVirtualDesktop CreateDesktop(IntPtr hWndOrMon); + void MoveDesktop(IVirtualDesktop desktop, IntPtr hWndOrMon, int nIndex); + void RemoveDesktop(IVirtualDesktop desktop, IVirtualDesktop fallback); + IVirtualDesktop FindDesktop(ref Guid desktopid); + + void GetDesktopSwitchIncludeExcludeViews(IVirtualDesktop desktop, out IObjectArray unknown1, + out IObjectArray unknown2); + + void SetDesktopName(IVirtualDesktop desktop, [MarshalAs(UnmanagedType.HString)] string name); + void SetDesktopWallpaper(IVirtualDesktop desktop, [MarshalAs(UnmanagedType.HString)] string path); + void UpdateWallpaperPathForAllDesktops([MarshalAs(UnmanagedType.HString)] string path); + void CopyDesktopState(IApplicationView pView0, IApplicationView pView1); + int GetDesktopIsPerMonitor(); + void SetDesktopIsPerMonitor(bool state); + } + + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("0CD45E71-D927-4F15-8B0A-8FEF525337BF")] + internal interface IVirtualDesktopNotificationService { + uint Register(IVirtualDesktopNotification notification); + + void Unregister(uint cookie); + } + + [ComImport] + [Guid("CD403E52-DEED-4C13-B437-B98380F2B1E8")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IVirtualDesktopNotification { + void VirtualDesktopCreated(IObjectArray array, IVirtualDesktop pDesktop); + + void VirtualDesktopDestroyBegin(IObjectArray array, IVirtualDesktop pDesktopDestroyed, + IVirtualDesktop pDesktopFallback); + + void VirtualDesktopDestroyFailed(IObjectArray array, IVirtualDesktop pDesktopDestroyed, + IVirtualDesktop pDesktopFallback); + + void VirtualDesktopDestroyed(IObjectArray array, IVirtualDesktop pDesktopDestroyed, + IVirtualDesktop pDesktopFallback); + + void VirtualDesktopIsPerMonitorChanged(int value); + + void VirtualDesktopMoved(IObjectArray array, IVirtualDesktop pDesktop, int nIndexFrom, int nIndexTo); + + void VirtualDesktopNameChanged(IVirtualDesktop pDesktop, [MarshalAs(UnmanagedType.HString)] string path); + + void ViewVirtualDesktopChanged(IApplicationView pView); + + void CurrentVirtualDesktopChanged(IObjectArray array, IVirtualDesktop pDesktopOld, + IVirtualDesktop pDesktopNew); + + void VirtualDesktopWallpaperChanged(IObjectArray array, IVirtualDesktop pDesktop, + [MarshalAs(UnmanagedType.HString)] + string path); + } + + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("92CA9DCD-5622-4BBA-A805-5E9F541BD8C9")] + internal 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("6D5140C1-7436-11CE-8034-00AA006009FA")] + internal interface IServiceProvider10 { + [return: MarshalAs(UnmanagedType.IUnknown)] + object QueryService(ref Guid service, ref Guid riid); + } + + #endregion + } +} \ No newline at end of file diff --git a/WinJump/Core/WinJumpManager.cs b/WinJump/Core/WinJumpManager.cs new file mode 100644 index 0000000..1dd0728 --- /dev/null +++ b/WinJump/Core/WinJumpManager.cs @@ -0,0 +1,237 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.InteropServices; +using System.Threading; +using System.Windows; +using WinJump.Core.VirtualDesktopDefinitions; +using WinJump.UI; + +namespace WinJump.Core; + +public delegate void DesktopChanged(bool lightMode, uint desktopNum); + +// Main controller for WinJump core functionality + +/// +/// Ties everything together. +/// +/// - Loads config file & registers listeners for the shortcuts +/// - Calls VirtualDesktop API to jump to desktops/get notified when they change +/// - Manages ExplorerManager to make sure that explorer.exe's lifecycle is properly controlled +/// and that color scheme / taskbar created events are handled. +/// +public class WinJumpManager : IDisposable { + /* + * Internal fields + */ + + private readonly STAThread? _thread; // tied exactly to the lifecycle of explorer.exe + private readonly ExplorerMonitor _explorerMonitor = new(); + private readonly KeyboardHook _keyboardHook = new(); + private bool _lightMode { get; set; } + private uint _currentDesktop { get; set; } + private uint? _lastDesktop { get; set; } + + public WinJumpManager(DesktopChanged desktopChanged) { + // Load config file + var config = Config.Load(); + + // Attempt to register the shortcuts. + + var registerShortcuts = new List> { + () => { + return config.JumpTo.Select(t => t.Shortcut).All(shortcut => + _keyboardHook.RegisterHotKey(shortcut.ModifierKeys, shortcut.Keys)); + }, + () => { + // Check if the shortcuts are already registered + return config.ToggleGroups.Select(t => t.Shortcut).All(shortcut => + _keyboardHook.RegisterHotKey(shortcut.ModifierKeys, shortcut.Keys)); + } + }; + + if(!registerShortcuts.All(x => x.Invoke())) { + // If we failed to register the shortcuts, we need to kill explorer and try again + // and restart it after we've registered the shortcuts + _explorerMonitor.Kill(); + if(!registerShortcuts.All(x => x.Invoke())) { + // Still didn't work, exit out + return; + } + } + + _keyboardHook.KeyPressed += (_, args) => { + Shortcut pressed = new Shortcut { + ModifierKeys = args.Modifier, + Keys = args.Key + }; + + // First, scan for jump to shortcuts + JumpTo? jumpTo = config.JumpTo.FirstOrDefault(x => x.Shortcut.IsEqual(pressed)); + + if(jumpTo != null) { + if(config.JumpCurrentGoesToLast && _lastDesktop != null) { + _thread?.JumpTo(jumpTo.Desktop - 1, _lastDesktop.Value); + } else { + _thread?.JumpTo(jumpTo.Desktop - 1); + } + + return; + } + + ToggleGroup? toggleGroup = config.ToggleGroups.FirstOrDefault(x => x.Shortcut.IsEqual(pressed)); + + if(toggleGroup != null) { + _thread?.JumpToNext(toggleGroup.Desktops.Select(x => x - 1).ToArray()); + } + }; + + // We MUST wait for explorer to be online in order to register desktop change notification service + _explorerMonitor.EnsureExplorerIsAlive(); + + _thread = new STAThread(desktop => { + _lastDesktop = _currentDesktop; + _currentDesktop = desktop; + desktopChanged(_lightMode, desktop); + }); + + _explorerMonitor.OnColorSchemeChanged += lightMode => { + _lightMode = lightMode; + desktopChanged.Invoke(lightMode, (uint) _thread.GetCurrentDesktop()); + }; + + _explorerMonitor.OnExplorerRestarted += () => { + try { + App.SINGLE_INSTANCE_MUTEX.ReleaseMutex(); + } catch(Exception) { + // ignored + } + + string? currentExecutablePath = Process.GetCurrentProcess().MainModule?.FileName; + + if(currentExecutablePath == null) return; + + Process.Start(currentExecutablePath); + Application.Current.Shutdown(); + }; + + // Set the desktop right away + desktopChanged.Invoke(_lightMode, (uint) _thread.GetCurrentDesktop()); + } + + public void Dispose() { + _thread?.Dispose(); + _explorerMonitor.Dispose(); + _keyboardHook.Dispose(); + GC.SuppressFinalize(this); + } +} + +// Credit https://stackoverflow.com/a/21684059/4779937 +/// +/// This is a small wrapper utility that calls the IVirtualDesktop API. +/// Any calls to this API must be from an STAThread for it to work. +/// +internal sealed class STAThread : IDisposable { + private SynchronizationContext? ctx; + private readonly ManualResetEvent mre; + private readonly IVirtualDesktopAPI api = IVirtualDesktopAPI.Create(); + + public STAThread(Action DesktopChanged) { + using(mre = new ManualResetEvent(false)) { + var thread = new Thread(() => { + System.Windows.Forms.Application.Idle += Initialize; + System.Windows.Forms.Application.Run(); + }) { + IsBackground = true + }; + thread.SetApartmentState(ApartmentState.STA); + thread.Start(); + mre.WaitOne(); + + api.OnDesktopChanged += desktop => { DesktopChanged((uint) desktop); }; + } + } + + // public void BeginInvoke(Delegate dlg, params Object[] args) { + // if (ctx == null) throw new ObjectDisposedException("STAThread"); + // ctx.Post(_ => dlg.DynamicInvoke(args), null); + // } + + public int GetCurrentDesktop() { + if(ctx == null) throw new ObjectDisposedException("STAThread"); + + var blocks = new BlockingCollection(); + + return WrapCall(() => { blocks.Add(api.GetCurrentDesktop()); }) ? blocks.Take() : 0; + } + + // index must be 0-indexed + public void JumpTo(uint index, uint fallback) { + WrapCall(() => { + if(api.GetCurrentDesktop() == index) { + // Go to last desktop instead + index = fallback; + } + + api.JumpToDesktop((int) index); + }); + } + + public void JumpTo(uint index) { + WrapCall(() => api.JumpToDesktop((int) index)); + } + + // desktops must be 0-indexed + public void JumpToNext(int[] desktops) { + if(ctx == null) throw new ObjectDisposedException("STAThread"); + + WrapCall(() => { + int index = Array.FindIndex(desktops, x => x == api.GetCurrentDesktop()); + if(index < 0) index = 0; + int next = desktops[(index + 1) % desktops.Length]; + + api.JumpToDesktop(next); + }); + } + + private void Initialize(object? sender, EventArgs e) { + ctx = SynchronizationContext.Current; + mre.Set(); + System.Windows.Forms.Application.Idle -= Initialize; + } + + private bool WrapCall(Action action) { + if(ctx == null) throw new ObjectDisposedException("STAThread"); + + var result = new BlockingCollection(); + + ctx.Send(_ => { + try { + action.Invoke(); + result.Add(true); + } catch(COMException) { + // Specifically ignored. If explorer.exe dies these calls will start failing. + // This application can't work when explorer.exe is dead. It will just sit around in a zombie + // state until it detects that explorer.exe is back up and running at which point it will + // restart itself. + result.Add(false); + } + }, null); + + return result.TryTake(out bool ret, TimeSpan.FromSeconds(5)) && ret; + } + + public void Dispose() { + if(ctx == null) return; + + WrapCall(() => { + api.Dispose(); + System.Windows.Forms.Application.ExitThread(); + }); + ctx = null; + } +} \ No newline at end of file diff --git a/WinJump/KeyboardHook.cs b/WinJump/KeyboardHook.cs deleted file mode 100644 index e92c723..0000000 --- a/WinJump/KeyboardHook.cs +++ /dev/null @@ -1,128 +0,0 @@ -using System; -using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Windows.Forms; - -namespace WinJump { - // Credit: https://stackoverflow.com/a/27309185/4779937 - public sealed class KeyboardHook : IDisposable { - // Registers a hot key with Windows. - [DllImport("user32.dll")] - private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk); - - // Unregisters the hot key with Windows. - [DllImport("user32.dll")] - private static extern bool UnregisterHotKey(IntPtr hWnd, int id); - - /// - /// Represents the window that is used internally to get the messages. - /// - private sealed class Window : NativeWindow, IDisposable { - private const int WM_HOTKEY = 0x0312; - - public Window() { - // create the handle for the window. - CreateHandle(new CreateParams()); - } - - /// - /// Overridden to get the notifications. - /// - /// - protected override void WndProc(ref Message m) { - base.WndProc(ref m); - - // check if we got a hot key pressed. - if (m.Msg == WM_HOTKEY) { - // get the keys. - Keys key = (Keys) (((int) m.LParam >> 16) & 0xFFFF); - ModifierKeys modifier = (ModifierKeys) ((int) m.LParam & 0xFFFF); - - // invoke the event to notify the parent. - KeyPressed?.Invoke(this, new KeyPressedEventArgs(modifier, key)); - } - } - - public event EventHandler KeyPressed; - - #region IDisposable Members - - public void Dispose() { - DestroyHandle(); - } - - #endregion - } - - private readonly Window _window = new Window(); - private int _currentId; - - public KeyboardHook() { - // register the event of the inner native window. - _window.KeyPressed += delegate(object sender, KeyPressedEventArgs args) { - KeyPressed?.Invoke(this, args); - }; - } - - /// - /// Registers a hot key in the system. - /// - /// The modifiers that are associated with the hot key. - /// The key itself that is associated with the hot key. - public void RegisterHotKey(ModifierKeys modifier, Keys key) { - // increment the counter. - _currentId++; - - // register the hot key. - if (!RegisterHotKey(_window.Handle, _currentId, (uint)(modifier | ModifierKeys.NoRepeat), (uint) key)) - throw new InvalidOperationException("Could not register the hot key."); - } - - /// - /// A hot key has been pressed. - /// - public event EventHandler KeyPressed; - - #region IDisposable Members - - public void Dispose() { - Debug.WriteLine("Disposing!"); - - // unregister all the registered hot keys. - for (int i = _currentId; i > 0; i--) { - UnregisterHotKey(_window.Handle, i); - } - - // dispose the inner native window. - _window.Dispose(); - } - - #endregion - } - - /// - /// Event Args for the event that is fired after the hot key has been pressed. - /// - public class KeyPressedEventArgs : EventArgs { - internal KeyPressedEventArgs(ModifierKeys modifier, Keys key) { - Modifier = modifier; - Key = key; - } - - public ModifierKeys Modifier { get; } - - public Keys Key { get; } - } - - /// - /// The enumeration of possible modifiers. - /// - [Flags] - public enum ModifierKeys : uint { - Alt = 1, - Control = 2, - Shift = 4, - Win = 8, - NoRepeat = 0x4000 - } -} \ No newline at end of file diff --git a/WinJump/Program.cs b/WinJump/Program.cs deleted file mode 100644 index 92eefeb..0000000 --- a/WinJump/Program.cs +++ /dev/null @@ -1,144 +0,0 @@ -using System; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Threading; -using System.Windows.Forms; -using Microsoft.Win32; -using VirtualDesktop.VirtualDesktop; - -namespace WinJump { - internal static class Program { - [STAThread] - public static void Main() { - // Register win jump to launch at startup - // The path to the key where Windows looks for startup applications - RegistryKey startupApp = Registry.CurrentUser.OpenSubKey( - @"SOFTWARE\Microsoft\Windows\CurrentVersion\Run", true); - - //Path to launch shortcut - string startPath = Environment.GetFolderPath(Environment.SpecialFolder.Programs) - + @"\WinJump\WinJump.appref-ms"; - - startupApp?.SetValue("WinJump", startPath); - - // Load config file - var config = Config.FromFile(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), - ".winjump")); - - // Because windows_key + number is already a bulit in Windows shortcut, we need to kill explorer - // (explorer is the process that registers the shortcut) so that it releases the shortcut. - // Then, we can register it and restart explorer. - var killExplorer = Process.Start("cmd.exe", "/c taskkill /f /im explorer.exe"); - - killExplorer?.WaitForExit(); - - var thread = new STAThread(); - - // Start a thread that can handle UI requests - KeyboardHook hook = new KeyboardHook(); - - hook.KeyPressed += (sender, args) => { - Shortcut pressed = new Shortcut { - ModifierKeys = args.Modifier, - Keys = args.Key - }; - - // First, scan for jump to shortcuts - JumpTo jumpTo = config.JumpTo.FirstOrDefault(x => x.Shortcut.IsEqual(pressed)); - - if (jumpTo != null) { - thread.JumpTo(jumpTo.Desktop - 1); - return; - } - - ToggleGroup toggleGroup = config.ToggleGroups.FirstOrDefault(x => x.Shortcut.IsEqual(pressed)); - - if (toggleGroup != null) { - thread.JumpToNext(toggleGroup.Desktops.Select(x => x - 1).ToArray()); - } - }; - - // Register the shortcuts - foreach (var shortcut in config.JumpTo.Select(t => t.Shortcut)) { - hook.RegisterHotKey(shortcut.ModifierKeys, shortcut.Keys); - } - - // Register the toggle groups - foreach (var shortcut in config.ToggleGroups.Select(t => t.Shortcut)) { - hook.RegisterHotKey(shortcut.ModifierKeys, shortcut.Keys); - } - - Process.Start(Environment.SystemDirectory + "\\..\\explorer.exe"); - - AppDomain.CurrentDomain.ProcessExit += (s, e) => { hook.Dispose(); }; - - Application.Run(); - } - - // Credit https://stackoverflow.com/a/21684059/4779937 - private sealed class STAThread : IDisposable { - private readonly VirtualDesktopWrapper vdw = VirtualDesktopManager.Create(); - - public STAThread() { - using (mre = new ManualResetEvent(false)) { - var thread = new Thread(() => { - Application.Idle += Initialize; - Application.Run(); - }) { - IsBackground = true - }; - thread.SetApartmentState(ApartmentState.STA); - thread.Start(); - mre.WaitOne(); - } - } - - public void BeginInvoke(Delegate dlg, params Object[] args) { - if (ctx == null) throw new ObjectDisposedException("STAThread"); - ctx.Post(_ => dlg.DynamicInvoke(args), null); - } - - // index must be 0-indexed - public void JumpTo(int index) { - if (ctx == null) throw new ObjectDisposedException("STAThread"); - - ctx.Send((_) => { - vdw.JumpTo(index); - }, null); - } - - // desktops must be 0-indexed - public void JumpToNext(int[] desktops) { - - if (ctx == null) throw new ObjectDisposedException("STAThread"); - - ctx.Send(_ => { - int index = Array.FindIndex(desktops, x => x == vdw.GetDesktop()); - if (index < 0) index = 0; - int next = desktops[(index + 1) % desktops.Length]; - - vdw.JumpTo(next); - - }, null); - - } - - private void Initialize(object sender, EventArgs e) { - ctx = SynchronizationContext.Current; - mre.Set(); - Application.Idle -= Initialize; - } - - public void Dispose() { - if (ctx == null) return; - - ctx.Send(_ => Application.ExitThread(), null); - ctx = null; - } - - private SynchronizationContext ctx; - private readonly ManualResetEvent mre; - } - } -} \ No newline at end of file diff --git a/WinJump/Properties/AssemblyInfo.cs b/WinJump/Properties/AssemblyInfo.cs deleted file mode 100644 index 7af7a78..0000000 --- a/WinJump/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("WinJump")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("WinJump")] -[assembly: AssemblyCopyright("Copyright © 2022")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("22774C1D-A813-493F-AB33-AE3A4049FADC")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.4.0")] -[assembly: AssemblyFileVersion("1.0.4.0")] \ No newline at end of file diff --git a/WinJump/Properties/Resources.Designer.cs b/WinJump/Properties/Resources.Designer.cs deleted file mode 100644 index 7ab473e..0000000 --- a/WinJump/Properties/Resources.Designer.cs +++ /dev/null @@ -1,62 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace WinJump.Properties { - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", - "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", - "CA1811:AvoidUncalledPrivateCode")] - internal Resources() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState - .Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if ((resourceMan == null)) { - global::System.Resources.ResourceManager temp = - new global::System.Resources.ResourceManager("WinJump.Properties.Resources", - typeof(Resources).Assembly); - resourceMan = temp; - } - - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState - .Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { return resourceCulture; } - set { resourceCulture = value; } - } - } -} \ No newline at end of file diff --git a/WinJump/Properties/Resources.resx b/WinJump/Properties/Resources.resx deleted file mode 100644 index af7dbeb..0000000 --- a/WinJump/Properties/Resources.resx +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file diff --git a/WinJump/Properties/Settings.Designer.cs b/WinJump/Properties/Settings.Designer.cs deleted file mode 100644 index 256e68a..0000000 --- a/WinJump/Properties/Settings.Designer.cs +++ /dev/null @@ -1,23 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace WinJump.Properties { - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute( - "Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { - private static Settings defaultInstance = - ((Settings) (global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default { - get { return defaultInstance; } - } - } -} \ No newline at end of file diff --git a/WinJump/Properties/Settings.settings b/WinJump/Properties/Settings.settings deleted file mode 100644 index 3964565..0000000 --- a/WinJump/Properties/Settings.settings +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/WinJump/UI/App.xaml b/WinJump/UI/App.xaml new file mode 100644 index 0000000..e02750e --- /dev/null +++ b/WinJump/UI/App.xaml @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/WinJump/UI/App.xaml.cs b/WinJump/UI/App.xaml.cs new file mode 100644 index 0000000..79ca527 --- /dev/null +++ b/WinJump/UI/App.xaml.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Reflection; +using System.Threading; +using System.Windows; +using Hardcodet.Wpf.TaskbarNotification; +using Microsoft.Extensions.FileProviders; +using WinJump.Core; + +namespace WinJump.UI; + +public partial class App { + private TaskbarIcon? notifyIcon; + private WinJumpManager? manager; + + public static readonly Mutex SINGLE_INSTANCE_MUTEX = new(true, "WinJump"); + + protected override void OnStartup(StartupEventArgs e) { + base.OnStartup(e); + + if(!SINGLE_INSTANCE_MUTEX.WaitOne(TimeSpan.Zero, true)) { + MessageBox.Show("WinJump is already running"); + Shutdown(); + return; + } + + if(FindResource("NotifyIcon") is TaskbarIcon icon) { + notifyIcon = icon; + } else { + throw new Exception("Could not find NotifyIcon"); + } + + var embeddedProvider = new EmbeddedFileProvider(Assembly.GetExecutingAssembly()); + + // Preload icons + var lightIcons = new Dictionary(); + var darkIcons = new Dictionary(); + + for(uint i = 1; i <= 16; i++) { + string fileName = i > 15 ? "15+.ico" : $"{i}.ico"; + + var lightFileInfo = embeddedProvider.GetFileInfo("UI/Icons/Light/" + fileName); + var darkFileInfo = embeddedProvider.GetFileInfo("UI/Icons/Dark/" + fileName); + + using var lightStream = lightFileInfo.CreateReadStream(); + using var darkStream = darkFileInfo.CreateReadStream(); + + lightIcons.Add(i, new Icon(lightStream)); + darkIcons.Add(i, new Icon(darkStream)); + } + + manager = new WinJumpManager( + (lightMode, desktopIcon) => { + notifyIcon.Icon = lightMode ? darkIcons[desktopIcon + 1] : lightIcons[desktopIcon + 1]; + }); + } + + protected override void OnExit(ExitEventArgs e) { + try { + notifyIcon?.Dispose(); + manager?.Dispose(); + SINGLE_INSTANCE_MUTEX.ReleaseMutex(); + } catch(Exception) { + // ignored + } + + base.OnExit(e); + } +} \ No newline at end of file diff --git a/WinJump/UI/Icons/Dark/1.ico b/WinJump/UI/Icons/Dark/1.ico new file mode 100644 index 0000000000000000000000000000000000000000..ff56b77cea10d14de2cbd7b0c6e96791c3774f4e GIT binary patch literal 67646 zcmeI4%Zpq^6vpp3#u*eGjT&4K8d2Q1b}NX8 zN>mVphAdnvYKU(-e}E>wAoxi8o!ED*u5|a@d+Su4s_w7Cm)qy|tvaW_U-hl&+mEVR zz`u=+3jg0+tu8NAJF2Q$g|JI@UfeG?<<`lotBd+CPWt~&WIF>5sMa2Q2+y`bB{%Z# zKKIGLyLYp2z9E0|=lOqr=K!Do^UGe=FY+gUp8w}}4)FOuzwCMbpKtc$PyRgr&-WZ4 zfAXJ&zyJGqEi3EUg$yKhbXCgwXJ!4r3VuHf9f0;5>Ivk20}_T^`M-(2`=At$<4NSX z54sFu({Vt!h44S5QwB#dazM^Omj2XONow zGk{=N0{I)7pFwK!&j5m93FL2Reg>(@KLZGcC6K?N`5B}p{|q1)mLTt`@%q0Z0OQo; zZw$aVJ^330FiubY#sG}dlfN+lE0F2Xmj2XONowGk{=N0{I)7pFwK!&j5m93FL2Reg>(@ zKLZGcC6K?N`5B}p{|q1)mO%c7=4X(a{4;=HSi(W%?)uwL(vQFYV+c*Yjw?&M!Mz`*j!KhVI!yOV$600YY>|3Cu^?@s=O z0}L#m`~wXvygT_94luBM@((ny@b2VaIKaU2$v@D*!n>1y;Q#~6C;vbL3-3<;g#!#M zpZo(2EWA7U7Y;D6eDV)8u<-8WUpT-FF?5* zEBUuHOyTet{Ok4L8`Sp*w5?&C> zpn5$Zf0&V3lkW$-0{sSQo&)N!WZah_c|ZAJV2(rR&!+Fd6&Uhy=zFNpjS7Js|%vP!jLjj(kTT`JO-x__^1BkT=K%R<27g$N{C>b5 z=u0T(93cOp;hAn$QP*40pHPf>@JyzUn`Qm)fUF0{pwE=`ImVOsC}*M#27}(_1Gx#& z?8ovL&sU4G*=&sI$L3_f4bBJ7hgkFBtT(?t_2QFW-1Fo0(fQe1^WqgRUh-m~b?0}l z^5b>y{xvUN@!};fUi9M1kJm@@S8HCp;>AmD+{e#^xv_kf!;R&d@UzbOT|Vb=uM-b8 zs%qJddpN+2`(7*w-1{Yg8%qK=mINbl&x`wBED7A_nxPVB~lt H7{>nsP=EH~ literal 0 HcmV?d00001 diff --git a/WinJump/UI/Icons/Dark/10.ico b/WinJump/UI/Icons/Dark/10.ico new file mode 100644 index 0000000000000000000000000000000000000000..b59f826731b2f1c6da1de3dad6e3b2fc32342ba5 GIT binary patch literal 67646 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 d>7iPh3MpO{RP$4TiW|0KFx_#fQzP_zI5 literal 0 HcmV?d00001 diff --git a/WinJump/UI/Icons/Dark/11.ico b/WinJump/UI/Icons/Dark/11.ico new file mode 100644 index 0000000000000000000000000000000000000000..b13f300ebfc8593cc5e08ffc01cf5cc62ffcd19e GIT binary patch literal 67646 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 XSnGan*ifK~o3Lsve8RUq4+j1Z>*F{^ literal 0 HcmV?d00001 diff --git a/WinJump/UI/Icons/Dark/13.ico b/WinJump/UI/Icons/Dark/13.ico new file mode 100644 index 0000000000000000000000000000000000000000..3eb7b101a8658ad6b08557065b823428f6d67def GIT binary patch literal 67646 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 literal 0 HcmV?d00001 diff --git a/WinJump/UI/Icons/Dark/14.ico b/WinJump/UI/Icons/Dark/14.ico new file mode 100644 index 0000000000000000000000000000000000000000..bcceac2afddd37adda1e00ef92d50dc86211f603 GIT binary patch literal 67646 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 literal 0 HcmV?d00001 diff --git a/WinJump/UI/Icons/Dark/15.ico b/WinJump/UI/Icons/Dark/15.ico new file mode 100644 index 0000000000000000000000000000000000000000..e50444c083a13c2a05ff0ed5f275b16a6e91366a GIT binary patch literal 67646 zcmeI5eT*Gd8OHCjw%bynh_t1x(lu4A6+c2iEvR=DECr03ra%<5oA?n!OQ~Id&%Jkc&pqtiJ2Us(xig*j zZJwR;&YU^tJX8(fH7bU7z4(DF<=ZB1IBX8(fH7bU7z4(DF<=ZB1IBJmx0aTP2g={TWh(M=f`Z@N1wLT#d7)AETTcv!BHTz>A08nSz|2Perk)_l!xj3UI*8Le}H~+ zp+0pZI2Sw_gfi1V8e4C1Qvr{%=Ayr-a@jz75p9aQ`@9v|Ccy)&2coyYNmfO=5!5yF?bXt)hCa9|0A9& z!BasNGyS`>rg2us{l7pSnx?Hoec*DC<+{-Hhqx}bk?p;n^QF-q@K>OFy4%5BU>A4@ z=w2_a?rwhF_gn&WeUipBU$PGd`GC)Y ztzZLq7FZ25PqY$@f)|3e7QerJKl@ApwMFx}X^u&q!|OnEK3V1zZeqXXpa}MIf=F_` zB>lG*Az&{C9j|H6`^vu^^m0wEFctvKW2Qa#=p6bkPy|b(|9BAsc48PNt#gupYRe$PwNxm8qcmv5z7Jc zUf8Et(iB@}{dErSq_?|GW8Bs>#AL|3o_%y&yY+TTf0w*VGSm}tm$K@AI3(NvI`Qjn zyMnxnLJ}8KmadOA|L@ivm)5<&i81BmwYseOp8!F6W*^6`yI$9OC+9@y5!7>i!nhRU zww&UMtESF|q}TuBafJ+E{TAxfyBe;}ap^0`*U$Ge{O0SPQumi}@wn?hPri`XqJAvh z{5l!RsQ(9u+vPDXEgjD5Mnd52Yba0ihjB5v>u(|7(!lknDXvcDvgxn6nyY}zV_bSS z`Hly9n$9)Xg1DI6^?xGYnmmK^>Z;?HO@Cec?F255ap{}Mr!hHidmk0X#pJHn{NYJ? z2j|pRCtWuEk0EBw*}FW(rLQKR&KG&x`BbQRzPPxe^?DYvG4J4<`s$?1rvEBpPCCAR z!f9)s$xT%D7>IO^Tk0D2HF*Z+)m29QSJQ^uz{5cI&~f-D+ujcbVM%$_Jm9&52(VSB z;@k`3`e(HMAbG|?5o{qrJO{j>3-ruZ&-Mmkr&8&^LBwC&Qe(vVg9xxyXPsr)^dBHy ziK{+^oTh(bx(>OFWzl~O=I!_o-~CMg@SK*ZMAN@aYK4#Wr4*^}N#m~jqV>A|(=)?C z*%%eu`9D1Sde0=udzHHX+Z?|7#3hcA)AUbFSK^G)^l=a!=W)yZUwz^dRk!J%n6AVbKSfSg=eYC( z?)rPl_w=yUrz&lX+@^mj%DQIWKn^|Eb#;zQ59;}J)QB+>rhmjnnB|8lCQfgE{pZ3| znYol#u&3#tnZ76NxSlnC1isF3zOEAoX{LWT_D3jc2Z+CLQ7@-9u(5A+V(Al^-LgE#$MHnOFM*zFvU^m~Lz|957qF6dsSe{Xai&hGC3_k&2+ zB+F^?KN}3%q<@m{m5TKK&!9O|{rdL%#o!u{WS-ye6PK~yAl^%*+BP%&+vtdOq}39z z7Hk1u1Zj`?s1MxDzC}3p38Pka{a4V2?O+m2^Z}Pr=EuR8z!ca8lDyX&Y3(odzV|jz z6f3*_dIwbZQ9jQ__0^B``!Pi^(?7qy^8-X*QxxG_ahU%3=>IeL*14)^_DTXO^Ywoj z&i_+z{Jf$G--^fd@89vS=PG)>b6i2SS6HThc>VQG=P$q;!D3KkGyTKqulvGZf_H<} zMHbzO&GgT%zve`Bj=uw30nP)X|NTJkG-!_T zDzFxWu&U^<`S_zl5H^4^T>aGt1hwZ3(Cc_m75(-6{VAY~HlP0L0~!yugClxqR8{o9 z6^=A_S4NA|UwuI1!k57_Gd8R$`riUS`YyYS)=B?pJkWDT{pM`i@t`XD{}66^IZqC< z-z3pr{oq0Ho|NsZivBmjOUU~sip$NC>K}~3!E*iN(5{lQm& z-eH)}s-pi5G=5}00!pw>@0!%XkAUXk)94@N{T2J21%`oZRnh<3GW}OV zJDI=V1Ns)(*D{N5`iJ3B+~=V`)imCB|0v5O{|`~OK4JQoPk;4+h2R2k59p?Gr|qVH zZ~VF3=5_od`K;-8roRKSjt8cHx%F2^SOz`>^exQqTDYC1pzot1{GN1#J4p-jH-nX8(fH7bU7z4(DF<=ZB1IBX8( zfH7bU7z4(DF<=ZB1IBo*f~{xpRtfSH-;A^yxv*VfETIlj_+ zSQl-dBKUo-&t3-zINCmmf6(=5yzkG@pxOLNI({SR`;&9|8=G8TRj2UD-Il))exp%M z{u}W;!Jp$-+g(28yZq1RyZUMSw4T7MUd;U_-AMyNQ&#(FZM2qiM z`L(*|FZKKp&mZ>uTAR;lV!TmubFI!wqpq(HKbyYfM*k_#pZ0vtBW?Mqpv|X(HlI$` a?oWCCwCC^h`~#kU(DNI8!2aaK!2ba|m9|g- literal 0 HcmV?d00001 diff --git a/WinJump/UI/Icons/Dark/2.ico b/WinJump/UI/Icons/Dark/2.ico new file mode 100644 index 0000000000000000000000000000000000000000..29a0bd003f9831195f94f18c5c2cfb4183d72494 GIT binary patch literal 67646 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 L5Uc)Mt=|71Xn{PB literal 0 HcmV?d00001 diff --git a/WinJump/UI/Icons/Dark/3.ico b/WinJump/UI/Icons/Dark/3.ico new file mode 100644 index 0000000000000000000000000000000000000000..2ed39a9031f9a80f6d86518dbfe0128d2b62ea38 GIT binary patch literal 67646 zcmeI53#=Sf8OOI4X?@X#R|`V71{z8XXz&3V?X@Z@Xe1_9K`j(DMvMeec_`@JfSM?Z z1O&whwFGk)YnX@}xd-rzF%;WGF(d>6hC%itRL27DRLgVW({@Nzf^rh`dw zJ)m|x3l_pTa6SAQHo#`s0pne;lVi8SKVc25f=fX4NI$L51CzmeKz;TB@J_f29t6d@ zn>crS>~_lk2dsp%;b_%^DKJb2FHL&cOKZ6!!LzFUEAeZm;Q_$ zx;|82W-_@4W^;(f?;-W~lhv2UvFB8CBVb}a$x2XMk} zMC0eBP{!E1IvlahE(DX72Nv`3Dd<(qbuB&yYvCUF zDclUp;YPRx?ttHc?ipx4r&qeD+3=C_CAUZli@FZld1An57!=Or{ zvHqQq)pxpHf7O5-_gv2VQ5b`)c6@q3HVo>tZ{@_BGx}{U`Cm6E1CsM<+;A>ry-)Eg z%Fyp&6-52?m5`;)sNX%MAkNYl?pqn{(R{-@3Sv*M)i{I`{1LLm|1$DxY*rMhqsV=b z#U{0heqO^2%6F>gS^CR0fYtC0odrP;^m%xkuSgId(uAV+mG}8 z7JTBbdzdHoS>Jx0ZYn2Q4nBTSp0oO;XvXvC-n05DA3OEicxuM;7d5-?m#hRIzi2J^ zqM{nsrkAb(uLU1FmFLX1^2(Cb*#9o@@r(LJ-7^YBE=5rvJC*1BP>k$SPtA|H^&aZy z_LQ9*y9M3|p-7fF&I>~^vP(VH|DOP-fqvt(9Y!aRaw7x^*Fx_i&@$}g~M1Fe~A14GTd!h3= zumyZ#@ABMF{+EGCcplLF+vgz5GkK~9zl8Z<5}pU9qw*I)V>#EReYSUye=eAW=YbcX z^4DPp_{84j(LA5Vf+pd4U?wUr1x`jM?FtZyMtPtsl7-vaBCb-WylLiL+`m1dDEdJH53l@KizxDsstpnEns~dY=%UJv^{?`9jw+>kQuWszE|F3rJ zE&djN>;J1=2Q2;;|EgX8FT!B0iM*-e$OrO8)kM$P=)SjM&y82D?ia@1@!iXgp;zX6l?Au zi4~nJz15YM96wo@u!PVHU9XpR^v~y m)!KsDXlooJ{Z}9r4wM{eQ>a89p=HD}6bGYf>7)wAoBsiAqk@?L literal 0 HcmV?d00001 diff --git a/WinJump/UI/Icons/Dark/4.ico b/WinJump/UI/Icons/Dark/4.ico new file mode 100644 index 0000000000000000000000000000000000000000..0de1131c90372495b767140f578fa2b45acebc93 GIT binary patch literal 67646 zcmeHQ3y2kE6dpBmwbEW@4`aikEW${7r*2k8R#;G26i5#!EXXo3u*a^2MMO_adLU6z zAU&uo3JDV}D~qC%vZotnY7bMhvX|R;?w#$p-;3|6h$KeERuz}!|-7`S_-Afo$0QC=|&wViUcQ0X30n|T; zKKH@Y-@SxE1yKJW`rHRofAo4qR)LW^>;5}Pyy6Gh(7nh z)Ze{?K?P9%Ao|<~Q-Ajo1{Fa4gXnV~O#R(U7*qiD52DX~F!gsYVNe0oKZri}!PMWq zgh2&R{~-F@2UGua2?xSuNH4{ssH@!fKe96ZU*2d)i9`)^LYV=a&W;`@14j{p04{?GLJpZfFo zulBW-hwbtFU;Mc6pj{<$r2f>ucYMR|z6$>TUwl93@n4*m=RHLK zYty^0sR#8>r$0phYtp-~sR#8>r$0phY3be9)Pwq`(;uS$J?Oq~`X*}TLH*OI57A%7 zSB^>FM9n;?e>(Lc`pf;47Xi8cug6CH(^a_*E|&XZ+V@YyUy;64$`^tUg>ABvj|3Ur3 zIX@I#d=6+`E%w)l9~GX8xd-ZRrmSedm00>0V9{LrK92Z?qP_RrLh4@`Zsqp@Uk0?M z7JDn=Cs!t&l6#^4B~z8^I1U~g0E_0@SAJ)3c_;Z_Y>v-(pNPa z^{*_;`=h(}0DW9rY&nOFt!!Fd_7BDa%+Ni{{$*JH#L3uXJV{GcW~H8S|%gwb;)fUdA4>`C~xF z`Yr%0dTU?Ve=Y{td@^t}(q02-T`l&pDumEQ)^x?1cf5iif1+59mu7%9I1SoGGuzajntf2A|y zn1K^9T?c4gE%y6}mvLuo{uq#FIx_xBp7B^T*S@zSzAwP$kAZ!W@;iXm)MEdD_$Gg) zGvk5ih@6&E}JVnMgbIJ<_@8 zgJVX0>c1z_UJU5t+G78L_=!HtXVzfgbYLT3(Omn!f%u^Sn?DBR+9AzsFuu=!&^#&|spSoGGuGS+OGztWj;%z*qp=qf<#YO%jU zyo^0%^U1(eq%C8=Et+fJ#}MD(v;53wJp>Fa1hl3WTi!i6FY`?LC15O4-vC%N*S_x} zem}nyWIA(s_j3-QHMQ7sKjBt@V@5%O!!{f_uK+BXYv1*VALFogrkrPBGA3IAt*ga; z3GsvT-SN{I1M(eS-XoQ9!}6U@_JwxHvLxg=P&?$eMFsL~QR3t|Bb!eKh9PbFJ}4L$ zqX|>NNioJ_DvUCgis=Mk6u|ERAc2hxkO4A42I`Z6wo=>ns80u1+V#Rm`;OI~ zPxfrtF$?=6uHFLeW)-jPCbeGMjUCpvL_@l@mqY_Ptk-sH)UU((O}QhnP_MRe4bs6M zc5{*+ybII8#(k@%DtaKQdqC;UQkf(82i7%(1JQCQhkMLUXYvw N3N3V(EA-U;+<$Gt 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 K&%&PZ&;I}yx)Lt{ literal 0 HcmV?d00001 diff --git a/WinJump/UI/Icons/Dark/6.ico b/WinJump/UI/Icons/Dark/6.ico new file mode 100644 index 0000000000000000000000000000000000000000..9f1b6b8b25590e9515813c96742823bc8818f7d5 GIT binary patch literal 67646 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 literal 0 HcmV?d00001 diff --git a/WinJump/UI/Icons/Dark/7.ico b/WinJump/UI/Icons/Dark/7.ico new file mode 100644 index 0000000000000000000000000000000000000000..0f6ada43dfb5049237e02ef4b68d8331b23effee GIT binary patch literal 67646 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 literal 0 HcmV?d00001 diff --git a/WinJump/UI/Icons/Dark/9.ico b/WinJump/UI/Icons/Dark/9.ico new file mode 100644 index 0000000000000000000000000000000000000000..5c2c4f444429e36cd4b3b6ed4038627fb3b90293 GIT binary patch literal 67646 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 literal 0 HcmV?d00001 diff --git a/WinJump/UI/Icons/Light/11.ico b/WinJump/UI/Icons/Light/11.ico new file mode 100644 index 0000000000000000000000000000000000000000..37fca330074fd0e1afb7d87990c2239ce8778921 GIT binary patch literal 67646 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 literal 0 HcmV?d00001 diff --git a/WinJump/UI/Icons/Light/13.ico b/WinJump/UI/Icons/Light/13.ico new file mode 100644 index 0000000000000000000000000000000000000000..0b9d9daca67ae050acea3bebfd9f5ee1b0e9cff6 GIT binary patch literal 67646 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 R&nENpGa#M@_D>lK{2xMmX%+wg literal 0 HcmV?d00001 diff --git a/WinJump/UI/Icons/Light/14.ico b/WinJump/UI/Icons/Light/14.ico new file mode 100644 index 0000000000000000000000000000000000000000..2086d811b0b350bbe454af0e55f2da030954b119 GIT binary patch literal 67646 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 l=|S}GDYQP6-{|Li3eiCH?WJxc_ejtbi4;0#?8Z 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 bpUuSQGw}nN_(CSW7~-*@>d|He23O#JVuepJ literal 0 HcmV?d00001 diff --git a/WinJump/UI/Icons/Light/15.ico b/WinJump/UI/Icons/Light/15.ico new file mode 100644 index 0000000000000000000000000000000000000000..6ebe3ea2c6bd2b46a540adbf149051528cf07a1e GIT binary patch literal 67646 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 HxR3u2(+WLJ literal 0 HcmV?d00001 diff --git a/WinJump/UI/Icons/Light/3.ico b/WinJump/UI/Icons/Light/3.ico new file mode 100644 index 0000000000000000000000000000000000000000..68ac055b55c06e95b6cd0a4f45df4883bed3af14 GIT binary patch literal 67646 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 Lb#oEwsr>%|#Xye^ literal 0 HcmV?d00001 diff --git a/WinJump/UI/Icons/Light/4.ico b/WinJump/UI/Icons/Light/4.ico new file mode 100644 index 0000000000000000000000000000000000000000..3190be60fe478ec186e6c53c9590e30a9e0fe5dd GIT binary patch literal 67646 zcmeI53yfS<8OLvtXN#o@w3Q%>@5;jpYPF=Y5(yYkVvHCuDMxGz3%_J0C#~<7MkMBXu4BY>i>c=13{g3ZK%naQBnCizL+x?I4LCg%? z|Cs8>AKU$p??KEA-2a&B#~<7MkMBXu4BY>i>c=13{g3ZK%naQBnCizL+x?I4LCg%? z|Cs8>AKU$p??KEA-2a&B#~<7MkMBXu4BY>i>c=13{g3ZK%naQBnCizL+x?I4LCg%? z|Muz!ZL~gqPtY|EgUDXYYc-UOfxZ3%LFB#oGTa54>!9&F=Cl#YhV&T7>wlP67WiIS zaVpTR3jTk$0zQ_VqWfP~xxr}X{tt$aWoJSyP?C3r6f1UeZ)`7U~dgT8v=tt{E<4&f{=wXij z`fmb#dgT9R`Z>1fYl2~-ZgT(I%U^g#X?gue^U)2Uzp>GNutoZSBjEGYba`&Gxc_au*XE#)%8$Vou!Oz`ItB86I}AHxyFxBC!=y%NJCN7^6ea|XcBA~3L8qeW z-P~9RMfXul2#$SiN_?vm;;r>tJy>Yd>8;$@7mDtqlo}u0|0${;^m;$#(_jg$ zxj74@{}e~>{oTU&?*9~}E&QUq7my9Uivo(M01CDdO1%Ug9gKm1<>hWt0okw&& zX&qulZfi_%|4XPHwA1sqp2sbr*AXj0UX;kBe3LMy`(HxB!Y|4*;lE&sr)j(xN@P;L ziN*u>zodjgKfRCF`*~~JNC*0Sp=37QH`7?){&z23-XiEH4RsxbKG(B^K3CM|i|Vgr zM%^_FW4Zs`$u{QRK|_6>|26m@SmO6I*7po!#+a^(#sT-=C~(km0i}K?(i&^%K;JhS zvzolQ#sK$!a;ZArBj_kCKLz@p-x6A9FbwLiU;a24ha2!gz_TzQkSUBXJHR$CrPEY zONYSulsmu@Ptf=i$f3{`gg#1D0|7AI1aVCE9@f?lyIz8`m0OYB}6oOxSQ~nUF@e?|DBUD1WL7Aldf+OK6u*7y6pACKv zNa)}w{Gz-Ntk;n~LLcu`pC0KbAiv*u}=*7zkIya_6= zYf1vhFL)RH2`r)S7A^ok2PAX=6n;^j1Nx575>L|ja7ai2PgQ<_zT001*7yM(>;*1t zA_~Z>z*5S;fhDxo=riEY1Bv*rG=pDS<5vUgn$i2{;~@3vk&XiL3$)hvUa*9YpKpSW zr*s6Uc&73ypf&$m`)`TA)A$(3`>eRM$xJ`^rPpuQgEiLEfv*`&#{GJtc&5j!uKjxg zEU|;e=YpRD5;_J7zbIG0t6+&g)A-$xkOH2n{DOIK8(3p49q4mr7d8F0oi4y(d1$}fOk&s#$8C*B7MDd4HfFVOq=TfiDO z(1CuZ%7slt0eKbp5anON68c>6bKuVdiTJNH3%@8Y1%1A6i3ezWFeIdarz*c-0o)1J zSWO4|zR`tEL;-mf_#~yi=eNXW8h;f0c_0!0m1giu>wB&RYiyu{{U9L)JXQGx?}10b z5?XJ#9Q+)R&|y{hMfpY0x?f8?PUH7NLJD}Q@(bp|t?(k~wq3F^%=%QKF^)@%5o``}Jp{TJOJe*ZOel{@1f?Or4&FTK7}0zr9_>Rz-b64Lzu9Ld|-8Ld|-8 zLalX8pjod^s96sa(olJX2^H1RFrn7^aHGNvCN$Jq4-;x`s!ym{4-PdpjN}t)R7ZnD zjSaz}mU`|;qaGY;)VrNfaHP?`4<@7+O3g@aLgO9TT7S$mW;E>+8h?n@A2W^dImGgl PLSw^Zhg`Q4Dmwl@+BF#~ literal 0 HcmV?d00001 diff --git a/WinJump/UI/Icons/Light/5.ico b/WinJump/UI/Icons/Light/5.ico new file mode 100644 index 0000000000000000000000000000000000000000..839ce7860b5cffbf0afdf082d0cfcc0aaa877ee7 GIT binary patch literal 67646 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 literal 0 HcmV?d00001 diff --git a/WinJump/UI/Icons/Light/6.ico b/WinJump/UI/Icons/Light/6.ico new file mode 100644 index 0000000000000000000000000000000000000000..0cd59975084cbeae1994970aead83a9f51c510ff GIT binary patch literal 67646 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 Kz~L6-$^QXwq5*0E literal 0 HcmV?d00001 diff --git a/WinJump/UI/Icons/Light/7.ico b/WinJump/UI/Icons/Light/7.ico new file mode 100644 index 0000000000000000000000000000000000000000..9e36c8c6ddc1afc882784376bfb77c081db32acd GIT binary patch literal 67646 zcmeI5*|S|m6~=c8lrYF35JHqh-hzfn96&y@HldfUj0r2w?`5 zlrmWHEyW;^FsdLAJfMOB6Cfpp1_@q&-_GiDht#c`oV9c6tkdgDeRrRI&slv|udloJ zy1l!1nr0sV#>X3eeyUkIe_peoX_}>wU+I_a@AXoz9^JEPx#c{6W&~nIU}9pTfd|18 z9XtWs17WPnr?=8xTn^X658!|M;AU)o1l#g5tZ^b>n(fYU74+bb7%zfxDxaQ~{;%Vu z@I%-yG;Uk~a|{U9u1vEf9AAbWti*Ue3>Ja9-IZR(pK|m-Im8!W-2n|)duaxbgYQ5O zevENm1A?^y(`+}6zw?NX?F%sTz?k~0-+mM7@40@VJn(#&V?fL{uo&$!4-BSV7w(om z%kd`Y!9OwnJnX`)EMtHXkY-Txf!;hY=K}|*TUTFY`YR877x28n`xs^(I2hSF{T6gn8~7xA5qj_| zj581HRGl4P7@Cza;Chev*uDvmQhAA35s+r^82A?S;D;Eu?I~6S>h{(&Qy%z$M|^BE z4-D9pOn>#;uS0JhcupxVU?{3}*);nE$3H_4eu?o%VYRW0SaIoPypE#>nFmH}0QijP z;?dBX2aa1=K_gM8+oqZJ`27}o@GQm$z&ZmNwARwgco9c$9(Z;sFK9TbblWsj9{4Bd zLFR!0TM&YmG2ka2@v(gaepclrVnslj!7suM(1S6I+x8SI0(E<9nkf(b4fNm{j57}m z)|}9+TmvrTwf6k>3*8q!3+5OQvkfdlJ3af!gZnVf`M{X^tKaIGA08+VJQ4wo zXC4@=4WU^X1N6)f57uFP1Pm5|bXS@|J@ZFfcAJ~9vDNmPQA1?ptZf)>*Yb_ft@Ndn8Fae zi~-{w@v&7Nc$msd#EO74gWBVxHJ%=PALF(?#fm`P-kN601GU%BgUka1HYEfv*8uJD z@Z^F2!TyX=UcgXP>#}L4JWzYRJh&6%oDU4vlF+P-0b?HVv3(t89vDo2xr=V1_V{@7 zz$>k+fKjN|Wz$S~p!WKC@D#@T!Fq!ku;$Xs_%)8+Jn-~VUcgXP>#}L4JziSl>p|v$ z0b3D*moY$V{5?<}cqj}Qg}PmqW>9;4w8qne?_u1wr@A8=tM;au@<6Te_TWj3GY^a< ze;NOE)E*D5@#`0w5Bv(uF(76e(3%dd@%7*ijB`FPrvB=;THEEpYZxzs!6K0EN;9an zow~R0!FMsvYe29jWtwU1zS|={wokx)VXz3)?XL7PYL7?effY6f(_eX@)^>PsJH|W1 z3Zode%F@fIwOt-45Bv;_8;nXlmu67+^fM2vunn00$^-9$9%LREu>Bx-xdv!$pC=Fe zH}+pH7u*B>9%{UMcRQ)^ zwOv2gl}}xjgI}tf?&)hz*Msk1+_tB#d>fW>tX|Up5l4W=RL-zLK0%G$x~Jp8;~0-* z9!R}P{~tO1)gG;Wn#=R%fv2jhQ9~vAtM6(~--BB*9=SY_I@QqsRmUr!2YzdvN~!m*P91^+sZ0Mhd)|%q4$Zu?I3LBQ%U^8J5`{>lSac*Mu{Q8*HLAoZxG|4(THr&4ck9(b~1 z7af)AufD4}eGh(tariutI@H+zRcFoRc%VFRDGWUX$bVSH^}h&Zbyg0qIb9Df!&vbv zMTa|@|268bv0H!3d!RgUZx~MZ;;?*&Rq3y~TfH@x=gk98Qdv=9ai~>)^YQ>%PePA>syn z*;f9G&w8*9hFtRbnDbVx`ICcQ-5FlqFd|#Ak6vS=qmQ_zw{6Ld-_kspJ$kxdA?-Y`{T`A=jt$%Sl!G#-(~Vm zUcK7Pb*|1giH*(7^SJ^x^Zg9qWHfoTan2T(0h|%AY=P4PQco9Ny=Cz0je)bC+n8tX z_r}29xs7>t-V5NY=houv{oWYZJGU-s=e>)@tmifc+WWnW$mIE0tFWE-E+Vs^A3yc; Wy^Dx_p7SO4ez^kHdEN%0{P;g~N7=am literal 0 HcmV?d00001 diff --git a/WinJump/UI/Icons/Light/8.ico b/WinJump/UI/Icons/Light/8.ico new file mode 100644 index 0000000000000000000000000000000000000000..3c2088ea4411c3a70466734419ef3c6f3cee0916 GIT binary patch literal 67646 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 literal 0 HcmV?d00001 diff --git a/WinJump/UI/Icons/Light/9.ico b/WinJump/UI/Icons/Light/9.ico new file mode 100644 index 0000000000000000000000000000000000000000..111633fcc75829cb19ca8ad901391d55024d48f0 GIT binary patch literal 67646 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 b?4WI~h_J&P?fm>`^P)l98{cP-;m`jEWT2q; literal 0 HcmV?d00001 diff --git a/WinJump/UI/TrayModel.cs b/WinJump/UI/TrayModel.cs new file mode 100644 index 0000000..072e8c4 --- /dev/null +++ b/WinJump/UI/TrayModel.cs @@ -0,0 +1,88 @@ +using System; +using System.Diagnostics; +using System.Windows; +using System.Windows.Input; +using WinJump.Core; + +namespace WinJump.UI; + +public class TrayModel { + public ICommand OnOpenConfig => new DelegateCommand { + CanExecuteFunc = () => true, + CommandAction = () => { + Config.EnsureCreated(); + + ProcessStartInfo processStartInfo = new ProcessStartInfo { + FileName = "notepad", + Arguments = Config.LOCATION + }; + + Process.Start(processStartInfo); + } + }; + + public ICommand ReloadConfig => new DelegateCommand { + CanExecuteFunc = () => true, + CommandAction = () => { + try { + App.SINGLE_INSTANCE_MUTEX.ReleaseMutex(); + } catch(Exception) { + // ignored + } + + string? currentExecutablePath = Process.GetCurrentProcess().MainModule?.FileName; + + if(currentExecutablePath == null) return; + + Process.Start(currentExecutablePath); + Application.Current.Shutdown(); + } + }; + + public ICommand ViewDocumentation => new DelegateCommand { + CanExecuteFunc = () => true, + CommandAction = () => { + // Open website + Process.Start(new ProcessStartInfo { + FileName = "https://github.com/widavies/WinJump", + UseShellExecute = true + }); + } + }; + + public ICommand Exit => new DelegateCommand { + CanExecuteFunc = () => true, + CommandAction = () => { + // Restart explorer to clean out any registrations that were present + + var killExplorer = Process.Start("cmd.exe", "/c taskkill /f /im explorer.exe"); + + killExplorer.WaitForExit(); + + Process.Start(Environment.SystemDirectory + "\\..\\explorer.exe"); + + Application.Current.Shutdown(); + } + }; + + public string Version => + $"WinJump {FileVersionInfo.GetVersionInfo(typeof(TrayModel).Assembly.Location).FileVersion}"; +} + +public class DelegateCommand : ICommand { + public required Action CommandAction { get; init; } + public required Func CanExecuteFunc { get; init; } + + public void Execute(object? parameter) { + CommandAction(); + } + + public bool CanExecute(object? parameter) { + return CanExecuteFunc(); + } + + public event EventHandler? CanExecuteChanged { + add => CommandManager.RequerySuggested += value; + remove => CommandManager.RequerySuggested -= value; + } +} \ No newline at end of file diff --git a/WinJump/UI/TrayResources.xaml b/WinJump/UI/TrayResources.xaml new file mode 100644 index 0000000..4dab87c --- /dev/null +++ b/WinJump/UI/TrayResources.xaml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/WinJump/WinJump.csproj b/WinJump/WinJump.csproj index 759c21d..e8fa737 100644 --- a/WinJump/WinJump.csproj +++ b/WinJump/WinJump.csproj @@ -1,127 +1,117 @@ - - - - - Debug - AnyCPU - {22774C1D-A813-493F-AB33-AE3A4049FADC} - WinExe - WinJump - WinJump - v4.8.1 - 512 - true - true - false - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 2 - 1.0.0.%2a - false - true - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - 6E443FEB1DE02B78AE4ABD396B5C71FCE97FA254 - - - WinJump_TemporaryKey.pfx - - - true - - - true - - - - ..\packages\Newtonsoft.Json.13.0.2\lib\net45\Newtonsoft.Json.dll - - - - - - - - - - - - - - - - - - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - - - - {153091d1-8af0-4609-aa38-db01f11d0c4a} - VirtualDesktop - - - - - False - Microsoft .NET Framework 4.8.1 %28x86 and x64%29 - true - - - False - .NET Framework 3.5 SP1 - false - - - - \ No newline at end of file + + + + WinExe + net7.0-windows + enable + true + + + + x86 + + + + x86 + + + + + + + + + + + MSBuild:Compile + Wpf + Designer + + + + + + MSBuild:Compile + Wpf + Designer + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 29df406f7eb8c5b07caf09b14d6b23336dda3dee Mon Sep 17 00:00:00 2001 From: Will Davies Date: Mon, 17 Apr 2023 16:37:58 -0500 Subject: [PATCH 2/3] Update README.md --- README.md | 48 ++++++++++++++++++------------------------------ 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 60c8831..f7aee1e 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,11 @@ # WinJump - + Ever wanted to jump directly to your `Nth` desktop on Windows 10 or 11 with a keyboard shortcut of your choice? WinJump enables you to create custom shortcuts to jump to any desktop and cycle between groups of desktops. +WinJump will also tell you what virtual desktop you're on in the system tray: + +![image](https://user-images.githubusercontent.com/11671115/232614847-1f8ccd7f-d5b8-429b-a67c-7f94cc5e18d9.png) + -Most other solutions use an [AutoHotKey](https://www.autohotkey.com/) based solution which automates pressing the Windows default shortuct Win + Ctrl + Left Arrow or Right Arrow multiple times. -This often results in glitchly visuals and lagging while jumping to the desktop you want. -WinJump uses the excellent [VirtualDesktop](https://github.com/MScholtes/VirtualDesktop) library which jumps directly to the desired desktop. ## Features @@ -21,6 +22,9 @@ Cycle through a group of desktops with a single shortcut *(there are no groups b Pressing the shortcut for the desktop you are currently on will jump back to the last desktop you were on. ## Installation +1. [Download] +2. Press Ctrl+R and type `shell:startup` +3. Drag the `WinJump.exe` to the shell startup folder ### Supported versions @@ -30,11 +34,9 @@ Currently, the following versions of Windows are supported: | Windows 10 | 1607-1709, 1803, 1809 - 21H2 | | Windows 11 | 21H2, 22H2 | -### How to install +> WinJump uses the reverse engineered Windows virtual desktop API. This means that the API often changes between Windows releases. Please see the [reverse engineering guide](https://github.com/github/codeql/blob/main/WinJump/Core/README.md) if you're interested in contributing reverse-engineering definitions for new Windows releases. -1. [Download](https://github.com/widavies/WinJump/releases/download/1.4.0/Release_1_4_0.zip) -2. Extract and run *setup.exe* -3. You're done! WinJump will start automatically and will register itself to start when your computer boots. +### How to install ### Config file @@ -42,12 +44,13 @@ You can optionally include a configuration file named *.winjump* in your home di #### Syntax -There are two blocks: +There are three blocks: - `toggle-groups` let you group desktops together and cycle through them with a keyboard shortcut +- `jump-current-goes-to-last` lets you decide whether jumping to the desktop you're already on does A) nothing or B) goes to your previous desktop - `jump-to` lets you define shortcuts that jump directly to a desktop -Both blocks contain a list of items, each item has a `shortcut` property. This shortcut must be a combination of: +The `toggle-groups` and `jump-to` blocks contain a list of items, each item has a `shortcut` property. This shortcut must be a combination of: `win`, `alt`, `shift`, and `ctrl`, it must be terminated by a key listed [here](https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.keys?view=windowsdesktop-7.0), and each token must be separated by `+`. @@ -57,11 +60,7 @@ Each `jump-to` item has the `desktop` property, which should be a single positiv > ⚠️ If no *.winjump* config file is found or a syntax error exists within it, WinJump will use default key mappings. -> ⚠️ WinJump does not auto-reload your configuration file. To apply changes, restart WinJump via one of the following methods: -> -> - launch task manager, kill WinJump, launch it again from the start menu -> - log out and back in -> - reboot +> ⚠️ WinJump does not auto-reload your configuration file. To apply changes, right click the system-tray icon and select `Reload configuration`. #### Example @@ -76,6 +75,7 @@ Below is an example configuration file that changes the shortcut to `alt+N` to j "desktops": [ 1, 5, 6 ] } ], + "jump-current-goes-to-last": false, "jump-to": [ { "shortcut": "alt+d1", @@ -121,18 +121,6 @@ Below is an example configuration file that changes the shortcut to `alt+N` to j } ``` -## Uninstallation - -WinJump can be uninstalled via the windows application manager - -1. Press the start button -2. Search for "Add or remove programs" -3. Find WinJump -4. Uninstall it - -## Known issues - -- Launching WinJump while it is already running will hang Windows explorer. To fix this you have to use `ctrl+shift+esc` to open task manager, kill all WinJump instances, use `Run new task` and type `explorer`, then start WinJump again - -## Attributions -[Icon created by Freepik](https://www.flaticon.com/free-icons/monitor) +# Uninstall +1. Press Ctrl+R and type `shell:startup` +2. Delete `WinJump.exe` From 95c8b56a9c5788f19b24a8ad88e0147bfa39f296 Mon Sep 17 00:00:00 2001 From: Will Davies Date: Mon, 17 Apr 2023 16:43:23 -0500 Subject: [PATCH 3/3] Fix version string in release builds & bump to 2.0.0 --- WinJump/UI/TrayModel.cs | 3 ++- WinJump/WinJump.csproj | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/WinJump/UI/TrayModel.cs b/WinJump/UI/TrayModel.cs index 072e8c4..c0015a6 100644 --- a/WinJump/UI/TrayModel.cs +++ b/WinJump/UI/TrayModel.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.Reflection; using System.Windows; using System.Windows.Input; using WinJump.Core; @@ -66,7 +67,7 @@ public class TrayModel { }; public string Version => - $"WinJump {FileVersionInfo.GetVersionInfo(typeof(TrayModel).Assembly.Location).FileVersion}"; + $"WinJump {Assembly.GetEntryAssembly()?.GetName().Version?.ToString()}"; } public class DelegateCommand : ICommand { diff --git a/WinJump/WinJump.csproj b/WinJump/WinJump.csproj index e8fa737..8734a19 100644 --- a/WinJump/WinJump.csproj +++ b/WinJump/WinJump.csproj @@ -5,6 +5,9 @@ net7.0-windows enable true + 2.0.0 + 2.0.0 + 2.0.0