Ready for Review - [Mouse Jump] - port upstream WinUI3 code to Mouse Jump (microsoft#48290) (#48393)

<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

Changes for #48290 to convert Mouse Jump to a WinUI app and remove all
remaining dependencies on WinForms, based on work already done in the
original "FancyMouse" project
(https://github.com/mikeclayton/FancyMouse).

## Notes for reviewers

* the new WinUI build of the app is output into the "/WinUI3Apps"
subfolder

* there's 2 new assemblies that need to be added to the installation as
well - MouseJump.HotKeys.dll and MouseJump.Models.dll. I'm not sure how
to add those to the installer for signing / shipping...

---

### Summary of changes

* New thumbnail layout and rendering code
* WinUI rewrite (winforms version still committed)
* MouseJump.Kicker (dev launch tool)
* CsWin32 for interop
* New assemblies - code reorganised

---

### 1. New layout code

Incorporates latest FancyMouse core layout and rendering logic into
Mouse Jump:

* includes **support** for multiple devices in layout algorithms
* preview still only shows local machine though
* prerequisite for long-term goal #34126

<img width="650" height="709" alt="image"
src="https://github.com/user-attachments/assets/9d1d996d-ed05-4471-b8a5-bd93442f70dc"
/>

### 2. WinUI rewrite

Port latest stable FancyMouse WinUI implementation into Mouse Jump.

Existing WinForms UI left in-situ side-by-side for now - easy to delete
if not needed.

### 3. MouseJump.Kicker

A small dev utility to start Mouse Jump without needing to build the
runner project:

<img width="283" height="274" alt="image"
src="https://github.com/user-attachments/assets/c81bb3f5-5008-48ce-8bc0-eef18413dee6"
/>

### 4. Cswin32 for interop

All win32 interop is now accessed via CsWin32 bindings.

The original win32 bindings were heavily influenced by CsWin32 generated
code (e.g. ```BOOL```, ```HWND```, etc structs), so there's not actually
much change other than deleting a lot of boilerplate code.

### 5. New assemblies

Some code has been reorganised into new assemblies to make it easier to
keep Mouse Jump in sync with upstream FancyMouse

* MouseJump.HotKeys
* MouseJump.Models




<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes #48290
- [x] **Communication:** I've discussed this with core contributors
already. If work hasn't been agreed, this work might be rejected
- [x] **Tests:** Added/updated and all pass
- [x] **Localization:** All end user facing strings can be localized
  -  all ui strings come from Resources.resw
- [x] **Dev docs:** Added/updated
  -  no changes required
- [x] **New binaries:** Added on the required places
  -  no changes required
- [x] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [x] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [x] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [x] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [x] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx
  -  no changes required
<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

- **Workflow tests**
  - [x] Automated tests passing locally
  - [x] Minimal actions workflow (spelling check) passing for PR
  - [ ] Full actions workflow (msbuild) passing for PR
- **UI tests**
  - [x] Happy path
    - [x] preview image appears when activated
- [x] clicking the preview image moves the mouse cursor to the correct
location
- [x] right-click dismisses the preview image without moving the mouse
- [x] pressing escape dismisses the preview image without moving the
mouse
- [x] left or right clicking another application / desktop dismisses the
preview image without moving the mouse
- [x] Works on multiple monitors with different dpi scaling settings
(e.g. 100% vs 150%)
*
https://github.com/microsoft/PowerToys/pull/23566#issuecomment-1411869418
*
https://github.com/microsoft/PowerToys/pull/23566#issuecomment-1412834413
- [x] Handling negative coordinates on non-primary monitors if higher or
"lefter" than primary monitor
*
https://github.com/microsoft/PowerToys/pull/23566#issuecomment-1404931694
- [x] Mouse crosshair moves when Mouse Jump moves the cursor (mouse
clicks *and* keyboard shortcuts)
    * #24523
    * #24527
- [x] Activating when the preview window is already visible moves the
form to the new mouse position
- [x] Number and key shortcuts (1-9, Home / End, Left / Right Arrow)
jump to the appropriate monitor
  - [x] Number-pad shortcuts (1-9 jump to the appropriate monitor
- **Settings tests**
- [x] Changing thumbnail size settings updates the size of the thumbnail
- [x] Changing preview type between Compact, Bezelled and Custom shows
the correct preview type
  - [x] Changing custom preview settings shows the correct settings
- [ ] Launching with settings version 1.0 upgrades settings to version
1.1, with "Bezelled" as the default style and the "Custom" settings
preconfigured to match "Bezelled"
- **Lifecycle tests**
- [x] Starting PowerToys Runner launches MouseJump exe when enabled, and
not when disabled
- [x] Enabling / disabling Mouse Jump in settings starts / stops
MouseJump exe
  - [x] Exiting PowerToys Runner stops MouseJump exe
  - [x] Killing runner exe via Task Manager stops MouseJump exe
  - [x] Stopping Visual Studio local debug run stops MouseJump exe
- note - runner needs to be in *non*-admin mode otherwise Visual Studio
debugger disconnects at launch
- [x] Hotkey and size settings are automatically reloaded when config
file is modified from Settings UI
- [ ] ~~Hotkey and size settings are automatically reloaded when config
file is modified manually (e.g. in notepad) while runner and
MouseJumpUI.exe are running~~
- **[Internal Test
Suite](5bc7201ae2/doc/releases/tests-checklist-template.md (mouse-utils))**
  - [x] Enable Mouse Jump. Then:
- [x] Press the activation shortcut and verify the screens preview
appears.
- [x] Change activation shortcut and verify that new shortcut triggers
Mouse Jump.
- [x] Click around the screen preview and ensure that mouse cursor
jumped to clicked location.
- [x] Reorder screens in Display settings and confirm that Mouse Jump
reflects the change and still works correctly.
- [x] Change scaling of screens and confirm that Mouse Jump still works
correctly.
- [ ] Unplug additional monitors and confirm that Mouse Jump still works
correctly.
- [x] Disable Mouse Jump and verify that the module is not activated
when you press the activation shortcut.

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Boliang Zhang (from Dev Box) <bozhang@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: moooyo <42196638+moooyo@users.noreply.github.com>
Co-authored-by: Yu Leng (from Dev Box) <yuleng@microsoft.com>
Copilot-Session: 8e04a72e-3b0f-4ac4-8156-d04ea9b8bb85
This commit is contained in:
Michael Clayton
2026-08-07 06:34:15 +01:00
committed by GitHub
parent 9fcb8faac5
commit e0010c5642
154 changed files with 6325 additions and 3464 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,96 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Drawing;
using System.Windows.Forms;
namespace MouseJump.Kicker
{
partial class KickerForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(KickerForm));
ActivationHotkey = new Button();
CloseMouseJump = new Button();
StartWinUI3 = new Button();
SuspendLayout();
//
// ActivationHotkey
//
ActivationHotkey.Location = new Point(12, 89);
ActivationHotkey.Name = "ActivationHotkey";
ActivationHotkey.Size = new Size(260, 70);
ActivationHotkey.TabIndex = 1;
ActivationHotkey.Text = "Activate Hotkey";
ActivationHotkey.UseVisualStyleBackColor = true;
ActivationHotkey.Click += ActivationHotkey_Click;
//
// CloseMouseJump
//
CloseMouseJump.Location = new Point(12, 165);
CloseMouseJump.Name = "CloseMouseJump";
CloseMouseJump.Size = new Size(260, 70);
CloseMouseJump.TabIndex = 2;
CloseMouseJump.Text = "Close MouseJump";
CloseMouseJump.UseVisualStyleBackColor = true;
CloseMouseJump.Click += CloseMouseJump_Click;
//
// StartWinUI3
//
StartWinUI3.Location = new Point(12, 12);
StartWinUI3.Name = "StartWinUI3";
StartWinUI3.Size = new Size(260, 70);
StartWinUI3.TabIndex = 3;
StartWinUI3.Text = "Start MouseJump (WinUI3)";
StartWinUI3.UseVisualStyleBackColor = true;
StartWinUI3.Click += StartWinUI3_Click;
//
// KickerForm
//
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(281, 242);
Controls.Add(StartWinUI3);
Controls.Add(CloseMouseJump);
Controls.Add(ActivationHotkey);
FormBorderStyle = FormBorderStyle.FixedSingle;
Icon = (Icon)resources.GetObject("$this.Icon");
MaximizeBox = false;
MinimizeBox = false;
Name = "KickerForm";
Text = "MouseJump Kicker";
ResumeLayout(false);
}
#endregion
private Button ActivationHotkey;
private Button CloseMouseJump;
private Button StartWinUI3;
}
}

View File

@@ -0,0 +1,131 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using MouseJump.Kicker.NativeMethods;
using static MouseJump.Kicker.NativeMethods.Core;
using static MouseJump.Kicker.NativeMethods.Kernel32;
using static MouseJump.Kicker.NativeMethods.User32;
namespace MouseJump.Kicker;
public partial class KickerForm : Form
{
public KickerForm()
{
InitializeComponent();
}
private Process? Process
{
get;
set;
}
private void StartWinUI3_Click(object sender, EventArgs e)
{
// this project builds into "{RepoRoot}\tools\MouseJump.Kicker\bin\x64\Debug"
var kickerDir = AppContext.BaseDirectory;
// step up to the repo root
var repoRoot = Path.Combine(kickerDir, "..", "..", "..", "..", "..");
// and back down to the mouse jump exe
var mouseJumpExe = Path.Combine(repoRoot, "x64", "Debug", "WinUI3Apps", "PowerToys.MouseJump.WinUI3.exe");
this.Process = this.StartMouseJump(mouseJumpExe);
}
private Process StartMouseJump(string mouseJumpExe)
{
if (!File.Exists(mouseJumpExe))
{
throw new FileNotFoundException($"Could not find {mouseJumpExe}");
}
var args = new List<string>
{
Environment.ProcessId.ToString(CultureInfo.InvariantCulture),
};
return Process.Start(mouseJumpExe, args);
}
private void ActivationHotkey_Click(object sender, EventArgs e)
{
// find the winui3 window and call SetForegroundWindow on it before signalling
// the event. this mirrors the approach used in on_hotkey() in
// src/modules/MouseUtils/MouseJump/dllmain.cpp - see the comment there for a
// full explanation.
//
// in brief: the kicker is the foreground process right now (the user just
// clicked the button), so it has permission to call SetForegroundWindow and
// transfer foreground status directly to the winui3 window. doing this before
// signalling the event means that by the time ShowWindowAsync() calls
// SetForegroundWindow on its own window, the winui3 process already owns the
// foreground and the call succeeds.
//
// using AllowSetForegroundWindow here instead would be unreliable — the
// permission it grants is revoked as soon as the user generates any input
// (e.g. moving the mouse), which is almost certain to happen while the
// preview is rendering.
if (this.Process is not null)
{
var targetPid = (uint)this.Process.Id;
var winUI3Hwnd = HWND.Null;
EnumWindows(
(hwnd, _) =>
{
GetWindowThreadProcessId(hwnd, out var windowPid);
if (windowPid == targetPid)
{
winUI3Hwnd = hwnd;
return false; // stop enumeration
}
return true; // continue enumeration
},
LPARAM.Zero);
if (!winUI3Hwnd.IsNull)
{
SetForegroundWindow(winUI3Hwnd);
}
}
const string MOUSE_JUMP_SHOW_PREVIEW_EVENT = "Local\\MouseJumpEvent-aa0be051-3396-4976-b7ba-1a9cc7d236a5";
var attributes = new LPSECURITY_ATTRIBUTES(
new SECURITY_ATTRIBUTES(
(uint)Marshal.SizeOf<SECURITY_ATTRIBUTES>(),
LPVOID.Null,
false));
var hEvent = Kernel32.CreateEventW(
attributes,
false,
false,
MOUSE_JUMP_SHOW_PREVIEW_EVENT);
if (hEvent.IsNull)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
var result = Kernel32.SetEvent(hEvent);
if (!result)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
private void CloseMouseJump_Click(object sender, EventArgs e)
{
this.Process?.Kill();
}
}

View File

@@ -0,0 +1,145 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAMd2C0LGdQpWxHMJVMRyEFS6ZABUsE4AVcBsA1S+awhUvWoGVLxpBlS8aQZUvGkGVLxp
BlS8aQZUvGkGVrxpBkLKegz/yXgL/8h3C//GcwD/xIta/8aljP+7aAD/wnAI/8BuCP+/bQf/vmsH/71q
B/+9agb/vGkG/7xpBv+8aQb/0IAO/85/Df/Nfg3/ynUA/9SqhP/w8O7/yKGD/8JsAP/HdQ3/xXMK/8Ny
Cf/CcQn/wW8I/8BtCP++bAf/vmsH/9WHEP/UhhD/04QP/9F9AP/Vpnj/8/X1/+zp5v/EjVv/yXYA/8t6
Dv/JeAv/yHcL/8Z1Cv/FdAr/xHMK/8NxCf/bjhL/2owS/9mLE//TgAD/0KV5//Pz8//3+fr/5t3U/8V/
HP/QfgD/zn8O/81+Df/MfAz/y3sM/8p5DP/IeAv/35QV/9+TFv/ZiwD/ypZb/+Pc1f/19fb/9vb2//r7
/P/fzb3/y34A/9SFCv/ThBD/0oMG/9GAAP/QgAz/zn4O/+KZGP/hlgD/16dv/+nn5P/09/n/9ff4//f5
+v/6/P3/+/7//9y4kv/YhwD/14kA/8qAAP/Hhzj/zH4A/9SFD//knhz/4poA/+S3e//s2L//69O0/+zT
s//s0K3/7tGu/+7Pq//lsXT/3o8A/9WTOv/n2s7/9PHs/9zCqv/ThQD/5qMg/+WiIP/loAr/45wA/+Ob
AP/jmgD/4pkA/+GXAP/hlQD/4ZcA/+CTAP/kuYf////////////59Oz/25k7/+epJP/npyL/56Yi/+al
If/mpCD/5aMf/+WhHv/koB3/5J8d/+SfHP/jmwD/5a1f//z27v//////9OPP/+CYBP/qrif/6a0m/+ms
Jf/oqyX/6Kkk/+ioJP/npyP/56Yi/+alIf/mpCD/5aMf/+SfCP/osV//7MGH/+SkPP/jnA7/67EoQuqv
KFbqryhU6q4nVOqtJ1TprCZU6KolVOipJFTnqCNU56ciVOamIlTmpSFU5JwAVOKVAFTkngBW5KAhQgAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAA//+sQf//rEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAA
rEH//6xB//+sQQ==
</value>
</data>
</root>

View File

@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<!-- Look at Directory.Build.props in root for common stuff as well -->
<Import Project="$(RepoRoot)src\Common.Dotnet.CsWinRT.props" />
<Import Project="$(RepoRoot)src\Common.SelfContained.props" />
<PropertyGroup>
<AssemblyName>PowerToys.MouseJump.Kicker</AssemblyName>
<AssemblyTitle>PowerToys.MouseJump.Kicker</AssemblyTitle>
<AssemblyDescription>PowerToys MouseJump.Kicker</AssemblyDescription>
<OutputType>WinExe</OutputType>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<ApplicationHighDpiMode>PerMonitorV2</ApplicationHighDpiMode>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Nullable>enable</Nullable>
<UseWindowsForms>true</UseWindowsForms>
<ImplicitUsings>disable</ImplicitUsings>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,10 @@
<Solution>
<Configurations>
<Platform Name="ARM64" />
<Platform Name="x64" />
</Configurations>
<Project Path="MouseJump.Kicker.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
</Solution>

View File

@@ -0,0 +1,44 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace MouseJump.Kicker.NativeMethods;
internal static partial class Core
{
/// <summary>
/// A Boolean variable (should be TRUE or FALSE).
/// This type is declared in WinDef.h as follows:
/// typedef int BOOL;
/// </summary>
/// <remarks>
/// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types
/// </remarks>
internal readonly struct BOOL
{
public readonly int Value;
public BOOL(int value)
{
this.Value = value;
}
public BOOL(bool value)
{
this.Value = value ? 1 : 0;
}
public static implicit operator bool(BOOL value) => value.Value != 0;
public static implicit operator BOOL(bool value) => new(value);
public static implicit operator int(BOOL value) => value.Value;
public static implicit operator BOOL(int value) => new(value);
public override string ToString()
{
return $"{this.GetType().Name}({this.Value})";
}
}
}

View File

@@ -0,0 +1,40 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Runtime.InteropServices;
namespace MouseJump.Kicker.NativeMethods;
internal static partial class Core
{
/// <summary>
/// A 32-bit unsigned integer. The range is 0 through 4294967295 decimal.
/// This type is declared in IntSafe.h as follows:
/// typedef unsigned long DWORD;
/// </summary>
/// <remarks>
/// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types
/// </remarks>
internal readonly struct DWORD
{
public readonly uint Value;
public DWORD(uint value)
{
this.Value = value;
}
public static int Size =>
Marshal.SizeOf<DWORD>();
public static implicit operator uint(DWORD value) => value.Value;
public static implicit operator DWORD(uint value) => new(value);
public override string ToString()
{
return $"{this.GetType().Name}({this.Value})";
}
}
}

View File

@@ -0,0 +1,41 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
namespace MouseJump.Kicker.NativeMethods;
internal static partial class Core
{
/// <summary>
/// A handle to an object.
/// This type is declared in WinNT.h as follows:
/// typedef PVOID HANDLE;
/// </summary>
/// <remarks>
/// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types
/// </remarks>
internal readonly struct HANDLE
{
public static readonly HANDLE Null = new(IntPtr.Zero);
public readonly IntPtr Value;
public HANDLE(IntPtr value)
{
this.Value = value;
}
public bool IsNull => this.Value == HANDLE.Null.Value;
public static implicit operator IntPtr(HANDLE value) => value.Value;
public static explicit operator HANDLE(IntPtr value) => new(value);
public override string ToString()
{
return $"{this.GetType().Name}({this.Value})";
}
}
}

View File

@@ -0,0 +1,41 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
namespace MouseJump.Kicker.NativeMethods;
internal static partial class Core
{
/// <summary>
/// A handle to a window.
/// This type is declared in WinDef.h as follows:
/// typedef HANDLE HWND;
/// </summary>
/// <remarks>
/// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types
/// </remarks>
internal readonly struct HWND
{
public static readonly HWND Null = new(IntPtr.Zero);
public readonly IntPtr Value;
public HWND(IntPtr value)
{
this.Value = value;
}
public bool IsNull => this.Value == HWND.Null.Value;
public static implicit operator IntPtr(HWND value) => value.Value;
public static explicit operator HWND(IntPtr value) => new(value);
public override string ToString()
{
return $"{this.GetType().Name}({this.Value})";
}
}
}

View File

@@ -0,0 +1,37 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace MouseJump.Kicker.NativeMethods;
internal static partial class Core
{
/// <summary>
/// A message parameter. This type contains information about a message.
/// This type is declared in WinDef.h as follows:
/// typedef LONG_PTR LPARAM;
/// </summary>
/// <remarks>
/// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types
/// </remarks>
internal readonly struct LPARAM
{
public static readonly LPARAM Zero = new(0);
public readonly nint Value;
public LPARAM(nint value)
{
this.Value = value;
}
public static implicit operator nint(LPARAM value) => value.Value;
public static explicit operator LPARAM(nint value) => new(value);
public override string ToString()
{
return $"{this.GetType().Name}({this.Value})";
}
}
}

View File

@@ -0,0 +1,56 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
namespace MouseJump.Kicker.NativeMethods;
internal static partial class Core
{
/// <summary>
/// A pointer to a constant null-terminated string of 16-bit Unicode characters.For more information, see Character Sets Used By Fonts.
/// This type is declared in WinNT.h as follows:
/// typedef CONST WCHAR* LPCWSTR;
/// </summary>
/// <remarks>
/// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types
/// </remarks>
internal readonly struct LPCWSTR
{
public static readonly LPCWSTR Null = new(IntPtr.Zero);
public readonly IntPtr Value;
public LPCWSTR(IntPtr value)
{
this.Value = value;
}
public LPCWSTR(string value)
{
this.Value = Marshal.StringToHGlobalAuto(value);
}
public bool IsNull => this.Value == LPCWSTR.Null.Value;
public static implicit operator IntPtr(LPCWSTR value) => value.Value;
public static explicit operator LPCWSTR(IntPtr value) => new(value);
public static implicit operator string?(LPCWSTR value) => Marshal.PtrToStringUni(value.Value);
public static implicit operator LPCWSTR(string value) => new(value);
public void Free()
{
Marshal.FreeHGlobal(this.Value);
}
public override string ToString()
{
return $"{this.GetType().Name}({this.Value})";
}
}
}

View File

@@ -0,0 +1,56 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
namespace MouseJump.Kicker.NativeMethods;
internal static partial class Core
{
/// <summary>
/// A pointer to any type.
/// This type is declared in WinDef.h as follows:
/// typedef void* LPVOID;
/// </summary>
/// <remarks>
/// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types
/// </remarks>
internal readonly struct LPVOID
{
public static readonly LPVOID Null = new(IntPtr.Zero);
public readonly IntPtr Value;
public LPVOID(IntPtr value)
{
this.Value = value;
}
public static implicit operator IntPtr(LPVOID value) => value.Value;
public static explicit operator LPVOID(IntPtr value) => new(value);
public static LPVOID Allocate(int length)
{
var ptr = Marshal.AllocHGlobal(length);
return new LPVOID(ptr);
}
public string? PtrToStringUni()
{
return Marshal.PtrToStringUni(this.Value);
}
public void Free()
{
Marshal.FreeHGlobal(this.Value);
}
public override string ToString()
{
return $"{this.GetType().Name}({this.Value})";
}
}
}

View File

@@ -0,0 +1,56 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
namespace MouseJump.Kicker.NativeMethods;
internal static partial class Kernel32
{
internal readonly struct LPSECURITY_ATTRIBUTES
{
public static readonly LPSECURITY_ATTRIBUTES Null = new(IntPtr.Zero);
public readonly IntPtr Value;
public LPSECURITY_ATTRIBUTES(IntPtr value)
{
this.Value = value;
}
public LPSECURITY_ATTRIBUTES(SECURITY_ATTRIBUTES value)
{
this.Value = LPSECURITY_ATTRIBUTES.ToPtr(value);
}
public bool IsNull => this.Value == LPSECURITY_ATTRIBUTES.Null.Value;
private static IntPtr ToPtr(SECURITY_ATTRIBUTES value)
{
var ptr = Marshal.AllocHGlobal(SECURITY_ATTRIBUTES.Size);
Marshal.StructureToPtr(value, ptr, false);
return ptr;
}
public SECURITY_ATTRIBUTES ToStructure()
{
return Marshal.PtrToStructure<SECURITY_ATTRIBUTES>(this.Value);
}
public void Free()
{
Marshal.FreeHGlobal(this.Value);
}
public static implicit operator IntPtr(LPSECURITY_ATTRIBUTES value) => value.Value;
public static implicit operator LPSECURITY_ATTRIBUTES(IntPtr value) => new(value);
public override string ToString()
{
return $"{this.GetType().Name}({this.Value})";
}
}
}

View File

@@ -0,0 +1,34 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using static MouseJump.Kicker.NativeMethods.Core;
namespace MouseJump.Kicker.NativeMethods;
internal static partial class Kernel32
{
[SuppressMessage("SA1304", "SA1304:NonPrivateReadonlyFieldsMustBeginWithUpperCaseLetter", Justification = "Names match Win32 api")]
[SuppressMessage("SA1307", "SA1307:AccessibleFieldsMustBeginWithUpperCaseLetter", Justification = "Names match Win32 api")]
[StructLayout(LayoutKind.Sequential)]
internal readonly struct SECURITY_ATTRIBUTES
{
internal readonly DWORD nLength;
internal readonly LPVOID lpSecurityDescriptor;
internal readonly BOOL bInheritHandle;
internal SECURITY_ATTRIBUTES(
DWORD nLength, LPVOID lpSecurityDescriptor, BOOL bInheritHandle)
{
this.nLength = nLength;
this.lpSecurityDescriptor = lpSecurityDescriptor;
this.bInheritHandle = bInheritHandle;
}
public static int Size =>
Marshal.SizeOf<SECURITY_ATTRIBUTES>();
}
}

View File

@@ -0,0 +1,32 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Runtime.InteropServices;
using static MouseJump.Kicker.NativeMethods.Core;
namespace MouseJump.Kicker.NativeMethods;
internal static partial class Kernel32
{
/// <summary>
/// Creates or opens a named or unnamed event object.
/// To specify an access mask for the object, use the CreateEventEx function.
/// </summary>
/// <returns>
/// If the function succeeds, the return value is a handle to the event object.
/// If the named event object existed before the function call, the function returns
/// a handle to the existing object and GetLastError returns ERROR_ALREADY_EXISTS.
/// If the function fails, the return value is NULL.
/// To get extended error information, call GetLastError.
/// </returns>
/// <remarks>
/// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumdisplaymonitors
/// </remarks>
[LibraryImport(Libraries.Kernel32, StringMarshalling = StringMarshalling.Utf16, SetLastError = true)]
internal static partial HANDLE CreateEventW(
LPSECURITY_ATTRIBUTES lpEventAttributes,
BOOL bManualReset,
BOOL bInitialState,
LPCWSTR lpName);
}

View File

@@ -0,0 +1,27 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Runtime.InteropServices;
using static MouseJump.Kicker.NativeMethods.Core;
namespace MouseJump.Kicker.NativeMethods;
internal static partial class Kernel32
{
/// <summary>
/// Sets the specified event object to the signaled state.
/// </summary>
/// <returns>
/// If the function succeeds, the return value is nonzero.
/// If the function fails, the return value is zero.
/// To get extended error information, call GetLastError.
/// </returns>
/// <remarks>
/// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumdisplaymonitors
/// </remarks>
[LibraryImport(Libraries.Kernel32, SetLastError = true)]
internal static partial Core.BOOL SetEvent(
HANDLE hEvent);
}

View File

@@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace MouseJump.Kicker.NativeMethods;
internal static class Libraries
{
public const string Gdi32 = "gdi32";
public const string Kernel32 = "kernel32";
public const string Shell32 = "shell32";
public const string User32 = "user32";
}

View File

@@ -0,0 +1,38 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Runtime.InteropServices;
using static MouseJump.Kicker.NativeMethods.Core;
namespace MouseJump.Kicker.NativeMethods;
internal static partial class User32
{
/// <summary>
/// An application-defined callback function for use with EnumWindows.
/// Return TRUE to continue enumeration, FALSE to stop.
/// </summary>
/// <remarks>
/// See https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms633498(v=vs.85)
/// </remarks>
internal delegate BOOL EnumWindowsProc(HWND hwnd, LPARAM lParam);
/// <summary>
/// Enumerates all top-level windows on the screen, including hidden windows,
/// by passing the handle to each window in turn to an application-defined callback function.
/// </summary>
/// <returns>
/// If the function succeeds, the return value is nonzero.
/// If the function fails, the return value is zero.
/// To get extended error information, call GetLastError.
/// </returns>
/// <remarks>
/// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumwindows
/// </remarks>
[DllImport(Libraries.User32, SetLastError = true)]
internal static extern BOOL EnumWindows(
EnumWindowsProc lpEnumFunc,
LPARAM lParam);
}

View File

@@ -0,0 +1,27 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Runtime.InteropServices;
using static MouseJump.Kicker.NativeMethods.Core;
namespace MouseJump.Kicker.NativeMethods;
internal static partial class User32
{
/// <summary>
/// Retrieves the identifier of the thread that created the specified window and,
/// optionally, the identifier of the process that created the window.
/// </summary>
/// <returns>
/// The return value is the identifier of the thread that created the window.
/// </returns>
/// <remarks>
/// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowthreadprocessid
/// </remarks>
[LibraryImport(Libraries.User32, SetLastError = true)]
internal static partial uint GetWindowThreadProcessId(
HWND hWnd,
out uint lpdwProcessId);
}

View File

@@ -0,0 +1,27 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Runtime.InteropServices;
using static MouseJump.Kicker.NativeMethods.Core;
namespace MouseJump.Kicker.NativeMethods;
internal static partial class User32
{
/// <summary>
/// Brings the thread that created the specified window into the foreground and
/// activates the window.
/// </summary>
/// <returns>
/// If the window was brought to the foreground, the return value is nonzero.
/// If the window was not brought to the foreground, the return value is zero.
/// </returns>
/// <remarks>
/// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setforegroundwindow
/// </remarks>
[LibraryImport(Libraries.User32, SetLastError = true)]
internal static partial BOOL SetForegroundWindow(
HWND hWnd);
}

View File

@@ -0,0 +1,23 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Windows.Forms;
namespace MouseJump.Kicker;
internal static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
public static void Main()
{
// To customize application configuration such as set high DPI settings or default font,
// see https://aka.ms/applicationconfiguration.
ApplicationConfiguration.Initialize();
Application.Run(new KickerForm());
}
}

View File

@@ -0,0 +1,11 @@
## MouseJump.Kicker
This is a simple "kicker" app that can be used to launch the Mouse Jump background process
and activate the hotkey without needing to build and run the entire "runner" app.
It's intended to be used for testing and development purposes, and is not meant to be
included in the main PowerToys release footprint.
When launching Mouse Jump via the kicker, it will look in the build output folder and
launch the last successful build. Make sure to build the Mouse Jump project before
launching the kicker - otherwise it won't run the latest version of the code.