chore: import upstream snapshot with attribution
Spell checking / Report (Push) (push) Blocked by required conditions
Spell checking / Report (PR) (push) Blocked by required conditions
Spell checking / Check Spelling (push) Has been cancelled
Spell checking / Update PR (push) Has been cancelled
Publish Dev Docs Website / build (push) Failing after 1s
Publish Dev Docs Website / deploy (push) Has been skipped

This commit is contained in:
wehub-resource-sync
2026-07-13 12:16:02 +08:00
commit 79031da543
8235 changed files with 1365916 additions and 0 deletions
+46
View File
@@ -0,0 +1,46 @@
# Advanced Paste
[Public overview - Microsoft Learn](https://learn.microsoft.com/en-us/windows/powertoys/advanced-paste)
## Quick Links
[All Issues](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3A%22Product-Advanced%20Paste%22)<br>
[Bugs](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3A%22Product-Advanced%20Paste%22%20label%3AIssue-Bug)<br>
[Pull Requests](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+is%3Aopen++label%3A%22Product-Advanced+Paste%22)
## Overview
Advanced Paste is a PowerToys module that provides enhanced clipboard pasting with formatting options and additional functionality.
## Implementation Details
[Source code](/src/modules/AdvancedPaste)
TODO: Add implementation details
### Paste with AI Preview
The "Show preview" setting (`ShowCustomPreview`) controls whether AI-generated results are displayed in a preview window before pasting. **The preview feature does not consume additional AI credits**—the preview displays the same AI response that was already generated, cached locally from a single API call.
The implementation flow:
1. User initiates "Paste with AI" action
2. A single AI API call is made via `ExecutePasteFormatAsync`
3. The result is cached in `GeneratedResponses`
4. If preview is enabled, the cached result is displayed in the preview UI
5. User can paste the cached result without any additional API calls
See the `ExecutePasteFormatAsync(PasteFormat, PasteActionSource)` method in `OptionsViewModel.cs` for the implementation.
## Debugging
TODO: Add debugging information
## Settings
| Setting | Description |
|---------|-------------|
| `ShowCustomPreview` | When enabled, shows AI-generated results in a preview window before pasting. Does not affect AI credit consumption. |
## Future Improvements
TODO: Add potential future improvements
+98
View File
@@ -0,0 +1,98 @@
# Always on Top
[Public overview - Microsoft Learn](https://learn.microsoft.com/en-us/windows/powertoys/always-on-top)
## Quick Links
[All Issues](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3A%22Product-Always%20On%20Top%22)<br>
[Bugs](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3AIssue-Bug%20%20label%3A%22Product-Always%20On%20Top%22)<br>
[Pull Requests](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+is%3Aopen++label%3A%22Product-Always+On+Top%22+)
## Overview
The Always on Top module allows users to pin windows on top of others, ensuring they remain visible even when switching between applications. The module provides visual indicators (customizable borders) to identify which windows are pinned.
## Features
- Pin any window to stay on top of other windows
- Customizable border color, opacity, and thickness around pinned windows
- User-defined keyboard shortcut for toggling window pinning
- Visual indicators to identify pinned windows
## Architecture
### Main Components
- **Hotkey Listener**: Detects the user-defined hotkey to toggle the Always on Top state
- **AlwaysOnTop**: Manages the state of windows, ensuring the selected window stays on top
- **Settings**: Stores user preferences and configurations
- **WindowHook**: Hooks all window events
### Data Flow
1. The Hotkey Listener detects the hotkey press and notifies the AlwaysOnTop
2. The AlwaysOnTop updates the window state and interacts with the operating system to keep the window on top
3. User preferences are saved and loaded from the Settings
## Code Structure
### Key Files
- **AlwaysOnTop.cpp**: Contains the core logic for the module, including initialization and event handling
- **Settings.cpp**: Defines the settings structure and provides methods to load and save settings
- **main.cpp**: Starts thread and initializes AlwaysOnTop
### Initialization
The module is initialized in the AlwaysOnTop class. During initialization, the following steps are performed:
1. **LoadSettings**: The module loads user settings from a configuration file
2. **RegisterHotkey**: The HotkeyManager registers the keyboard shortcut for pinning/unpinning windows
3. **SubscribeToEvents**: Event handlers are attached to respond to user actions, such as pressing the hotkey
### Pinning and Unpinning Windows
The AlwaysOnTop class handles the pinning and unpinning of windows. Key methods include:
- **PinTopmostWindow**: Pins the specified window on top of others and applies visual indicators
- **UnpinTopmostWindows**: Removes the pinning status and visual indicators from the specified window
- **AssignBorder**: Applies a colored border around the pinned window based on user settings
### Settings Management
The Settings class manages the module's settings. Key methods include:
- **LoadSettings**: Loads settings from a configuration file
- **NotifyObservers**: Distributes the data for the settings
- **GetDefaultSettings**: Returns the default settings for the module
## User Interface
The module provides a user interface for configuring settings in the PowerToys Settings UI. This interface is implemented using XAML and includes options for customizing the:
- Border color
- Border opacity
- Border thickness
- Keyboard shortcut
## Development Environment Setup
### Prerequisites
- Visual Studio 2019 or later
- Windows 10 SDK
- PowerToys repository cloned from GitHub
### Building and Testing
1. Clone the repository: `git clone https://github.com/microsoft/PowerToys.git`
2. Open PowerToys.slnx in Visual Studio
3. Select the Release configuration and build the solution
4. Run PowerToys.exe from the output directory to test the module
### Debug
1. build the entire project
2. launch the built Powertoys
3. select AlwaysOnTop as the startup project in VS
4. In the debug button, choose "Attach to process". ![image](https://github.com/user-attachments/assets/a7624ec2-63f1-4720-9540-a916b0ada282)
5. Attach to AlwaysOnTop.![image](https://github.com/user-attachments/assets/815c0f89-8fd1-48d6-b7fd-0e4a92e222d0)
+44
View File
@@ -0,0 +1,44 @@
# Awake
[Public overview - Microsoft Learn](https://learn.microsoft.com/en-us/windows/powertoys/awake)
## Quick Links
[All Issues](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3AProduct-Awake)<br>
[Bugs](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3AIssue-Bug%20%20label%3AProduct-Awake)<br>
[Pull Requests](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+is%3Aopen++label%3A%22Product-Awake%22+)
## Overview
Awake is a PowerToys utility designed to keep your computer awake without permanently modifying system power settings. It prevents the computer from sleeping and can keep the monitor on, providing a convenient alternative to changing system power configurations.
## Key Features
- Temporarily override system sleep settings
- Keep monitor on (prevent display from turning off)
- Set time intervals for keeping the system awake
- One-time setup with no need to revert power settings afterward
## Advantages Over System Power Settings
- **Convenience**: Easy UI for quick toggling of sleep prevention
- **Flexibility**: Support for different time intervals (indefinitely, for specific duration)
- **Non-persistent**: Changes are temporary and don't require manual reversion
- **Quick Access**: Available directly from the system tray
## Architecture
### Components
- **System Tray UI**: Provides user interface for controlling Awake settings
- **Backend Threads**: Manages the power state prevention functionality
- **Command Line Interface**: Supports various commands for controlling Awake functionality programmatically
## Technical Implementation
Awake works by preventing system sleep through Windows power management APIs. The module runs as a background process that interfaces with the Windows power management system to keep the device awake according to user preferences.
## User Experience
Users can access Awake through the PowerToys system tray icon. From there, they can:
1. Toggle Awake on/off
2. Set a specific duration for keeping the system awake
3. Choose whether to keep the display on or allow it to turn off
4. Access additional configuration options
## Command Line Support
Awake includes command-line functionality for power users and automation scenarios, allowing programmatic control of the utility's features.
+49
View File
@@ -0,0 +1,49 @@
# Color Picker
[Public overview - Microsoft Learn](https://learn.microsoft.com/en-us/windows/powertoys/color-picker)
## Quick Links
[All Issues](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3A%22Product-Color%20Picker%22)<br>
[Bugs](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3AIssue-Bug%20label%3A%22Product-Color%20Picker%22)<br>
[Pull Requests](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+is%3Aopen++label%3A%22Product-Color+Picker%22)
## Overview
Color Picker is a system-wide color picking utility for Windows that allows users to pick colors from any screen and copy them to the clipboard in a configurable format.
## Implementation Details
### Color Capturing Mechanism
The Color Picker works by following these steps to capture the color at the current mouse position:
1. Obtain the position of the mouse
2. Create a 1x1 size rectangle at that position
3. Create a Bitmap class and use it to initiate a Graphics object
4. Create an image associated with the Graphics object by leveraging the CopyFromScreen function, which captures the pixel information from the specified location
### Core Color Picking Function
The following code snippet demonstrates the core functionality of how a color is picked from the screen:
```csharp
private static Color GetPixelColor(System.Windows.Point mousePosition)
{
var rect = new Rectangle((int)mousePosition.X, (int)mousePosition.Y, 1, 1);
using (var bmp = new Bitmap(rect.Width, rect.Height, PixelFormat.Format32bppArgb))
{
var g = Graphics.FromImage(bmp);
g.CopyFromScreen(rect.Left, rect.Top, 0, 0, bmp.Size, CopyPixelOperation.SourceCopy);
return bmp.GetPixel(0, 0);
}
}
```
## Features
- Pick colors from any pixel on the screen
- View color information in various formats (RGB, HEX, HSL, etc.)
- Copy color values to clipboard in configurable formats
- Color history for quick access to previously selected colors
- Keyboard shortcuts for quick activation and operation
## User Experience
When activated, Color Picker displays a magnified view of the area around the cursor to allow for precise color selection. Once a color is selected, it can be copied to the clipboard in the user's preferred format for use in design tools, development environments, or other applications.
+43
View File
@@ -0,0 +1,43 @@
# Command Not Found
[Public overview - Microsoft Learn](https://learn.microsoft.com/en-us/windows/powertoys/cmd-not-found)
## Quick Links
[All Issues](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3AProduct-CommandNotFound)<br>
[Bugs](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3AIssue-Bug%20label%3AProduct-CommandNotFound)<br>
[Pull Requests](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+is%3Aopen+label%3AProduct-CommandNotFound)
## Overview
Command Not Found is a PowerToys module that suggests package installations when you attempt to run a command that isn't available on your system. It integrates with the Windows command line to provide helpful suggestions for installing missing commands through package managers.
## How it Works
When you attempt to execute a command in the terminal that isn't found, the Command Not Found module intercepts this error and checks if the command is available in known package repositories. If a match is found, it suggests the appropriate installation command.
## Installation
The Command Not Found module requires the Microsoft.WinGet.CommandNotFound PowerShell module to function properly. When enabling the module through PowerToys, it automatically attempts to install this dependency.
The installation is handled by the following script:
```powershell
# Located in PowerToys\src\settings-ui\Settings.UI\Assets\Settings\Scripts\EnableModule.ps1
Install-Module -Name Microsoft.WinGet.CommandNotFound -Force
```
## Usage
1. Enable the Command Not Found module in PowerToys settings.
2. Open a terminal and try to run a command that isn't installed on your system.
3. If the command is available in a package, you'll see a suggestion for how to install it.
Example:
```
C:\> kubectl
'kubectl' is not recognized as an internal or external command, operable program, or batch file.
Command 'kubectl' not found, but can be installed with:
winget install -e --id Kubernetes.kubectl
```
## Technical Details
The Command Not Found module leverages the Microsoft.WinGet.CommandNotFound PowerShell module, which is maintained in a separate repository: https://github.com/microsoft/winget-command-not-found
The module works by registering a command-not-found handler that intercepts command execution failures and provides installation suggestions based on available packages in the WinGet repository.
+49
View File
@@ -0,0 +1,49 @@
# Crop and Lock
[Public overview - Microsoft Learn](https://learn.microsoft.com/en-us/windows/powertoys/crop-and-lock)
## Quick Links
[All Issues](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3AProduct-CropAndLock)<br>
[Bugs](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3AIssue-Bug%20label%3AProduct-CropAndLock)<br>
[Pull Requests](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+is%3Aopen+label%3AProduct-CropAndLock)
## Overview
The Crop and Lock module in PowerToys allows users to crop a current application into a smaller window or create a thumbnail. This utility enhances productivity by enabling users to focus on specific parts of an application window.
## Features
### Thumbnail Mode
Creates a window showing the selected area of the original window. Changes in the original window are reflected in the thumbnail.
### Reparent Mode
Creates a window that replaces the original window, showing only the selected area. The application is controlled through the cropped window.
### Screenshot Mode
Creates a window showing a freezed snapshot of the original window.
## Code Structure
### Project Layout
The Crop and Lock module is part of the PowerToys solution. All the logic-related settings are in the main.cpp. The main implementations are in ThumbnailCropAndLockWindow and ReparentCropAndLockWindow. ChildWindow and OverlayWindow distinguish the two different modes of windows implementations.
### Key Files
- **ThumbnailCropAndLockWindow.cpp**: Defines the UI for the thumbnail mode.
- **OverlayWindow.cpp**: Thumbnail module type's window concrete implementation.
- **ReparentCropAndLockWindow.cpp**: Defines the UI for the reparent mode.
- **ChildWindow.cpp**: Reparent module type's window concrete implementation.
- **ScreenshotCropAndLockWindow.cpp**: Defines the UI for the screenshot mode.
## Known Issues
- Cropping maximized or full-screen windows in "Reparent" mode might not work properly.
- Some UWP apps may not respond well to being cropped in "Reparent" mode.
- Applications with sub-windows or tabs can have compatibility issues in "Reparent" mode.
## Debug
1. build the entire project
2. launch the built Powertoys
3. select CropAndLock as the startup project in VS
4. In the debug button, choose "Attach to process". ![image](https://github.com/user-attachments/assets/a7624ec2-63f1-4720-9540-a916b0ada282)
5. Attach to CropAndLock.![image](https://github.com/user-attachments/assets/08aa0465-596c-4494-9daa-e96b234f9997)
@@ -0,0 +1,71 @@
# Environment Variables
[Public overview - Microsoft Learn](https://learn.microsoft.com/en-us/windows/powertoys/environment-variables)
## Quick Links
[All Issues](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3A%22Product-Environment%20Variables%22)<br>
[Bugs](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3AIssue-Bug%20label%3A%22Product-Environment%20Variables%22)<br>
[Pull Requests](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+is%3Aopen+label%3A%22Product-Environment+Variables%22)
[Checklist](https://github.com/microsoft/PowerToys/blob/releaseChecklist/doc/releases/tests-checklist-template.md?plain=1#L744)
## Overview
Environment Variables is a PowerToys module that provides an easy and convenient way to manage Windows environment variables. It offers a modern user interface for viewing, editing, and managing both user and system environment variables.
## Features
- View and edit user and system environment variables in a unified interface
- Create profiles to group and manage sets of variables together
- Profile-based variable management with on/off toggles
- Automatic backup of existing variables when overridden by a profile
- Restoration of original values when profiles are disabled
## How It Works
### Profiles
Profiles are collections of environment variables that can be enabled or disabled together. When a profile is enabled:
1. Variables in the profile override existing User variables with the same name
2. Original values are automatically backed up for restoration when the profile is disabled
3. Only one profile can be active at a time
### Variable Precedence
The module follows this precedence order for environment variables:
1. Active profile variables (highest precedence)
2. User variables
3. System variables (lowest precedence)
## Architecture
The Environment Variables module is structured into three main components:
### Project Structure
```
EnvironmentVariables/ # Contains assets, main windows, and telemetry
EnvironmentVariablesModuleInterface # Interface definitions and package configurations
EnvironmentVariableUILib # Abstracted UI methods and implementations
```
### Key Components
- **Main Window Framework**: Builds the modern Windows desktop UI, handles Windows messages, resource loading, and window closing operations
- **Project Configuration**: Defines settings and configurations for the module
- **UI Implementation**: Contains the user interface components and the backend logic
## Implementation Details
### Key Functions
- **OpenEnvironmentKeyIfExists**: Accesses environment information through registry keys
- **SetEnvironmentVariableFromRegistryWithoutNotify**: Sets variables directly to registry instead of using Environment API, avoiding the 1-second timeout for settings change notifications
- **GetVariables**: Reads variables directly from registry instead of using Environment API to prevent automatic variable expansion
### Technical Notes
- The module reads and writes variables directly to the registry instead of using the Environment API
- This direct registry access approach is used because the Environment API automatically expands variables and has a timeout for notifications
- When a profile variable has the same name as an existing User variable, a backup is created with the naming pattern: `VARIABLE_NAME_powertoys_PROFILE_NAME`
+131
View File
@@ -0,0 +1,131 @@
# FancyZones Debugging Tools
## Overview
FancyZones has several specialized debugging tools to help diagnose issues with window management, zone detection, and rendering. These tools are designed to isolate and test specific components of the FancyZones functionality.
## Tools Summary
| Tool | Purpose | Key Functionality |
|------|---------|-------------------|
| FancyZones_HitTest | Tests zone hit detection | Shows which zone is under cursor with detailed metrics |
| FancyZones_DrawLayoutTest | Tests layout drawing | Renders zone layouts to debug display issues |
| FancyZones_zonable_tester | Tests window zonability | Determines if windows can be placed in zones |
| StylesReportTool | Analyzes window properties | Generates window style reports for debugging |
## FancyZones_HitTest
![Image of the FancyZones hit test tool](/doc/images/tools/fancyzones-hit-test.png)
### Purpose
Tests the FancyZones layout selection logic by displaying a window with zones and highlighting the zone under the mouse cursor.
### Functionality
- Displays a window with 5 sample zones
- Highlights the zone under the mouse cursor
- Shows metrics used for zone detection in a sidebar
- Helps diagnose issues with zone positioning and hit testing
### Usage
- Run the tool and move your mouse over the zones
- The currently detected zone will be highlighted
- The sidebar displays metrics used for determining the active zone
- Useful for debugging hit detection, positioning, and DPI issues
## FancyZones_DrawLayoutTest
### Purpose
Debug issues related to the drawing of zone layouts on screen.
### Functionality
- Simulates zone layouts (currently only column layout supported)
- Tests rendering of zones with different configurations
- Helps diagnose display issues across monitor configurations
### Usage
- Run the tool
- Press **W** key to toggle zone appearance on the primary screen
- Press **Q** key to exit the application
- The number of zones can be modified in the source code
### Technical Notes
The application is DPI unaware, meaning it doesn't scale for DPI changes and always assumes a scale factor of 100% (96 DPI). Scaling is automatically performed by the system.
## FancyZones_zonable_tester
![Image of the FancyZones zonable tester](/doc/images/tools/fancyzones-zonable-tester.png)
### Purpose
Tests if the window under the mouse cursor is "zonable" (can be placed in a FancyZones zone).
### Functionality
- Analyzes the window under the cursor
- Provides detailed window information:
* HWND (window handle)
* Process ID
* HWND of foreground window
* Window style flags
* Extended style flags
* Window class
* Process path
### Usage
- Run the command-line application
- Hover the mouse over a window to test
- Review the console output for detailed window information
- Check if the window is considered zonable by FancyZones
### Limitations
Note that this tool may not be fully up-to-date with the latest zonable logic in the main FancyZones codebase.
## StylesReportTool
### Purpose
Generates detailed reports about window styles that affect zonability.
### Functionality
- Creates comprehensive window style reports
- Focuses on style flags that determine if windows can be placed in zones
- Outputs report to "WindowStyles.txt" on the desktop
### Usage
- Run the tool
- Focus the window you want to analyze
- Press **Ctrl+Alt+S** to generate a report
- Review WindowStyles.txt to understand why a window might not be zonable
## Debugging Workflow
For most effective debugging of FancyZones issues:
1. Use **StylesReportTool** to analyze window properties of problematic windows
2. Use **FancyZones_zonable_tester** to check if specific windows can be zoned
3. Use **FancyZones_draw** for layout rendering issues on different monitors
4. Use **FancyZones_HitTest** for diagnosing zone detection problems
## Testing Considerations
When testing FancyZones with these tools, consider:
- Testing on different Windows versions
- Testing with multiple monitors with different:
* Resolutions
* Scaling settings
* Physical arrangements
- Testing with various window types:
* Standard applications
* Legacy applications
* UWP/WinUI applications
* Administrative windows
* Special windows (like Task Manager)
- Testing various layouts:
* Grid layouts
* Custom layouts
* Overlapping zones
## Initial Setup Issues
If encountering JSON token errors on first run:
1. Launch FancyZones Editor through PowerToys Settings UI
2. This initializes required configuration files
3. Direct project execution won't initialize configs properly
+489
View File
@@ -0,0 +1,489 @@
# FancyZones
[Public overview - Microsoft Learn](https://learn.microsoft.com/en-us/windows/powertoys/fancyzones)
## Quick Links
[All Issues](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3AProduct-FancyZones)<br>
[Bugs](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3AIssue-Bug%20label%3AProduct-FancyZones)<br>
[Pull Requests](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+is%3Aopen+label%3AProduct-FancyZones)
## Overview
FancyZones is a window manager utility that allows users to create custom layouts for organizing windows on their screen.
## Architecture Overview
FancyZones consists of several interconnected components:
### Directory Structure
- **src**: Contains the source code for FancyZones.
- **Editor**: Code for the zone editor.
- **Runner**: Code for the zone management and window snapping.
- **Settings**: Code for managing user settings.
- **tests**: Contains unit and integration tests for FancyZones and UI test code.
### Project Structure
FancyZones is divided into several projects:
- **FancyZones**: Used for thread starting and module initialization.
- **FancyZonesLib**: Contains the main backend logic, called by FancyZones (via COM).
- **FancyZonesData** folder: Contains classes and utilities for managing FancyZones data.
- **FancyZonesEditor**: Main UI implementation for creating and editing layouts.
- **FancyZonesEditorCommon**: Stores editor's data and provides shared functionality.
- **FancyZonesModuleInterface**: Interface layer between FancyZones and the PowerToys Runner.
### Interface Layer: FancyZonesModuleInterface
- Exposes interface between FancyZones and the Runner
- Handles communication and configuration exchange
- Contains minimal code, most logic implemented in other modules
### UI Layer: FancyZonesEditor and FancyZonesEditorCommon
- **FancyZonesEditor**: Main UI implementation with MainWindow.xaml as entry point
- **FancyZonesEditorCommon**: Provides data structures and I/O helpers for the Editor
- Acts as a visual config editor for layout configuration
![Editor Code Map](../images/fancyzones/editor_map.png)
![Editor Common Code Map](../images/fancyzones/editor_common_map.png)
### Backend Implementation: FancyZones and FancyZonesLib
- **FancyZonesLib**: Core logic implementation
- All drag-and-drop behavior
- Layout UI during dragging (generated in C++ via WorkArea.cpp, NewZonesOverlayWindow function)
- Core data structures
- **FancyZones**: Wrapper around FancyZonesLib
### Data Flow
- User interactions with the Editor are saved in the Settings
- The Runner reads the Settings to apply the zones and manage window positions
- Editor sends update events, which trigger FancyZones to refresh memory data
## Key Files
### FancyZones and FancyZonesLib Projects
- **FancyZonesApp.h/cpp**:
- **FancyZonesApp Class**: Initializes and manages the FancyZones application.
- **Constructor**: Initializes DPI awareness, sets up event hooks, creates the FancyZones instance.
- **Destructor**: Cleans up resources, destroys the FancyZones instance, unhooks event hooks.
- **Run Method**: Starts the FancyZones application.
- **InitHooks Method**: Sets up Windows event hooks to monitor system events.
- **DisableModule Method**: Posts a quit message to the main thread.
- **HandleWinHookEvent/HandleKeyboardHookEvent Methods**: Handle Windows event hooks.
- **Data Management Files**:
- **AppliedLayouts.h/cpp**: Manages applied layouts for different monitors and virtual desktops.
- **AppZoneHistory.h/cpp**: Tracks history of app zones.
- **CustomLayouts.h/cpp**: Handles user-created layouts.
- **DefaultLayouts.h/cpp**: Manages default layouts for different monitor configurations.
- **LayoutHotkeys.h/cpp**: Manages hotkeys for switching layouts.
- **LayoutTemplates.h/cpp**: Handles layout templates.
- **Core Functionality**:
- **FancyZonesDataTypes.h**: Defines data types used throughout FancyZones.
- **FancyZonesWindowProcessing.h/cpp**: Processes window events like moving and resizing.
- **FancyZonesWindowProperties.h/cpp**: Manages window properties like assigned zones.
- **JsonHelpers.h/cpp**: Utilities for JSON serialization/deserialization.
- **Layout.h/cpp**: Defines the Layout class for zone layout management.
- **LayoutConfigurator.h/cpp**: Configures different layout types (grid, rows, columns).
- **Settings.h/cpp**: Manages FancyZones module settings.
### FancyZonesEditor and FancyZonesEditorCommon Projects
- **UI Components**:
- **MainWindow.xaml/cs**: Main window of the FancyZones Editor.
- **EditorOverlay.xaml/cs**: Overlay window for editing zones.
- **EditorSettings.xaml/cs**: Settings window for the FancyZones Editor.
- **LayoutPreview.xaml/cs**: Provides layout preview.
- **ZoneSettings.xaml/cs**: Manages individual zone settings.
- **Data Components**:
- **EditorParameters.cs**: Parameters used by the FancyZones Editor.
- **LayoutData.cs**: Manages data for individual layouts.
- **LayoutHotkeys.cs**: Manages hotkeys for switching layouts.
- **LayoutTemplates.cs**: Manages layout templates.
- **Zone.cs**: Represents an individual zone.
- **ZoneSet.cs**: Manages sets of zones within a layout.
## Configuration Management
### Configuration Files Location
- Path: `C:\Users\[username]\AppData\Local\Microsoft\PowerToys\FancyZones`
- Files:
- EditorParameters
- AppliedLayouts
- CustomLayouts
- DefaultLayouts
- LayoutHotkeys
- LayoutTemplates
- AppZoneHistory
### Configuration Handling
- No central configuration handler
- Editor: Read/write handlers in FancyZonesEditorCommon project
- FancyZones: Read/write handlers in FancyZonesLib project
- Data synchronization: Editor sends update events, FancyZones refreshes memory data
## Window Management
### Monitor Detection and DPI Scaling
- Monitor detection handled in `FancyZones::MoveSizeUpdate` function
- DPI scaling: FancyZones retrieves window position without needing mouse DPI scaling info
- Window scaling uses system interface via `WindowMouseSnap::MoveSizeEnd()` function
### Zone Tracking
- Window-to-zone tracking implemented in `FancyZones::MoveSizeUpdate` function
- Maintains history of which windows belong to which zones
## Development History
- FancyZones was originally developed as a proof of concept
- Many configuration options were added based on community feedback after initial development
- Some options were added to address specific issues:
- Options for child windows or pop-up windows
- Some options were removed later
- Community feedback led to more interactions being implemented
## Admin Mode Considerations
- FancyZones can't move admin windows unless running as admin
- By default, all utilities run as admin if PowerToys is running as admin
## Development Environment Setup
### Prerequisites
- Visual Studio 2026 (or 2022 17.4+): Required for building and debugging
- Windows 10 SDK: Ensure the latest version is installed
- PowerToys Repository: Clone from GitHub
### Setup Steps
1. Clone the Repository:
```
git clone https://github.com/microsoft/PowerToys.git
```
2. Open `PowerToys.slnx` in Visual Studio
3. Select the Release configuration and build the solution
4. If you encounter build errors, try deleting the x64 output folder and rebuild
## Getting Started with FancyZones Development
### Step 1: Familiarize with the Feature
- Use the feature to understand its functionality
- Read the official documentation: [PowerToys FancyZones utility for Windows](https://learn.microsoft.com/en-us/windows/powertoys/fancyzones)
### Step 2: Build and Debug
- Ensure you can successfully compile and debug the module
- First-time setup may require running the Editor through PowerToys Settings UI to initialize configuration files
### Step 3: Learn through Bug Fixes
- Examine existing bugs and feature requests to understand code structure
- Use debugging to trace code execution for specific features
- Examine UI test code to understand how features are tested
## Debugging
### Setup for Debugging
1. In Visual Studio, set FancyZonesEditor as the startup project
2. Set breakpoints in the code where needed
3. Click Run to start debugging
### During Active Development
- You can perform breakpoint debugging to troubleshoot issues
- Attach to running processes if needed to debug the module in context
### Common Debugging Issues
- If encountering JSON errors on first run, launch the FancyZones Editor once through PowerToys Settings UI to initialize required configuration files
- For UI-related issues, use tools like AccessibilityInsights to inspect element properties
## Deployment and Release Process
### Deployment
#### Local Testing
1. Build the solution in Visual Studio
2. Run PowerToys.exe from the output directory
#### Packaging
- Use the MSIX packaging tool to create an installer
- Ensure all dependencies are included
### Release
#### Versioning
- Follow semantic versioning for releases
#### Release Notes
- Document all changes, fixes, and new features
#### Publishing
1. Create a new release on GitHub
2. Upload the installer and release notes
## Troubleshooting
### First Run JSON Error
**Error**: "The input does not contain any JSON tokens. Expected the input to start with a valid JSON token, when isFinalBlock is true. Path: $ | LineNumber: 0 | BytePositionInLine: 0."
**Solution**: Launch the FancyZones Editor once through PowerToys Settings UI. Running the Editor directly within the project will not initialize the required configuration files.
### Known Issues
- Potential undiscovered bugs related to data updates in the Editor
- Some automated tests pass in CI but fail on specific machines
- Complex testing requirements across different monitor configurations
## FancyZones UI Testing
UI tests are implemented using [Windows Application Driver](https://github.com/microsoft/WinAppDriver).
### Before running tests
- Install Windows Application Driver v1.2.1 from https://github.com/microsoft/WinAppDriver/releases/tag/v1.2.1.
- Enable Developer Mode in Windows settings
### Running tests
- Exit PowerToys if it's running
- Run WinAppDriver.exe from the installation directory. Skip this step if installed in the default directory (`C:\Program Files (x86)\Windows Application Driver`); in this case, it'll be launched automatically during tests.
- Open `PowerToys.slnx` in Visual Studio and build the solution.
- Run tests in the Test Explorer (`Test > Test Explorer` or `Ctrl+E, T`).
>Note: notifications or other application windows, that are shown above the window under test, can disrupt the testing process.
### UI Test Automation
FancyZones is currently undergoing a UI Test migration process to improve automated testing coverage. You can track the progress of this migration at:
[FancyZones UI Test Migration Progress](https://github.com/microsoft/PowerToys/blob/feature/UITestAutomation/src/modules/fancyzones/UITests-FancyZonesEditor/release-test-checklist.md)
### Testing Strategy
#### Unit Tests
- Build the unit test project
- Run using the Visual Studio Test Explorer (`Test > Test Explorer` or `Ctrl+E, T`)
#### Integration Tests
- Ensure the entire FancyZones module works as expected
- Test different window layouts and snapping behaviors
### Test Framework Structure
#### UI Test Requirements
All test cases require pre-configured user data and must reset this data before each test.
**Required User Data Files**:
- EditorParameters
- AppliedLayouts
- CustomLayouts
- DefaultLayouts
- LayoutHotkeys
- LayoutTemplates
- AppZoneHistory
#### Editor Test Suite
**ApplyLayoutTest.cs**
- Verifies layout application and selection per monitor
- Tests file updates and behavior under display switching
- Validates virtual desktop changes
**CopyLayoutTests.cs**
- Tests copying various layout types
- Validates UI and file correctness
**CreateLayoutTests.cs**
- Tests layout creation and cancellation
- Focuses on file correctness validation
**CustomLayoutsTests.cs**
- Tests user-created layout operations
- Covers renaming, highlight line changes, zone count changes
**DefaultLayoutsTest.cs**
- Validates default and user layout files
**DeleteLayoutTests.cs**
- Tests layout deletion across types
- Checks both UI and file updates
**EditLayoutTests.cs**
- Tests zone operations: add/delete/move/reset/split/merge
**FirstLaunchTest.cs**
- Verifies Editor launches correctly on first run
**LayoutHotkeysTests.cs**
- Tests hotkey configuration file correctness
- Note: Actual hotkey behavior tested in FancyZones backend
**TemplateLayoutsTests.cs**
- Tests operations on built-in layouts
- Covers renaming, highlight changes, zone count changes
#### FancyZones Backend Tests
**LayoutApplyHotKeyTests.cs**
- Focuses on hotkey-related functionality
- Tests actual hotkey behavior implementation
### UI Testing Tools
While working on tests, you may need tools to view element accessibility data:
- [AccessibilityInsights](https://accessibilityinsights.io/docs/windows/overview)
- [WinAppDriver UI Recorder](https://github.com/microsoft/WinAppDriver/wiki/WinAppDriver-UI-Recorder)
>Note: Close helper tools while running tests. Overlapping windows can affect test results.
2. FancyZones might have implemented certain screen resolution limits in the code that do not support such wide screens
3. User error — it can be seen that no layout has been applied to the screen, so it's normal that the far right is not displayed, as the user hasn't used the FancyZones feature
4. From the image, it appears the user is trying to maximize a game window, but some games may not support rendering windows at such high resolutions due to internal implementation
The **optimal solution** for this bug is to first comment on the user's usage issue. Let them correctly use the FancyZones feature before making further judgments. If the issue persists after proper usage, then investigate whether it's a code issue or a problem with the game itself.
To demonstrate a debugging example, I will assume it's a code issue, specifically an issue with the Editor. Please see the following debug process.
Let's first locate the corresponding code. Since the error is in the Editor, we'll start by checking the FancyZonesEditor shown in the image.
![Debug Step Image](../images/fancyzones/1.png)
However, I currently don't know where the code for this specific UI element in the Editor is located.
![Debug Step Image](../images/fancyzones/2.png)
We now have two approaches to find the exact code location.
**First approach:**
The main XAML page is usually named `App.xaml` or `MainWindow.xaml`. Let's start by locating these two files in the FancyZones Editor. Upon reviewing their contents, we find that `App.xaml` is primarily a wrapper file and doesn't contain much UI code. Therefore, it's highly likely that the UI code is located in `MainWindow.xaml`. In the preview of `MainWindow.xaml`, we can also see a rough outline of the UI elements.![Debug Step Image](../images/fancyzones/3.png)
By searching for "monitor", we found that only lines 82 and 338 contain the string "monitor".![Debug Step Image](../images/fancyzones/4.png)
Then, upon reviewing the code, we found that the line at 82 is part of a template. The UI element we're looking for is located within the code block around line 338.
**Second approach:**
We can use the **AccessibilityInsights** tool to inspect the specific information of the corresponding UI element.
![Debug Step Image](../images/fancyzones/5.png)
However, the current UI element does not have an AutomationId. Let's check whether its parent or child nodes have an AutomationId value. (In fact, using ClassName could also help locate it, but elements with the same ClassName might be numerous, making AutomationId a more accurate option.)
![Debug Step Image](../images/fancyzones/6.png)
We found that the parent node "List View" has an AutomationId value. Copy this value and search for it in the code.
![Debug Step Image](../images/fancyzones/7.png)
**Accurately located at line 338.**
Now that we've found the code for the UI element, let's look at where the size data for this UI element comes from. First, the text of this `Text` element is bound within the `MonitorItemTemplate`. The name of this `Text` element is `ResolutionText`, and it binds to a data property named `Dimensions`.
![Debug Step Image](../images/fancyzones/8.png)
Search for code related to `Dimensions` across all projects in FancyZones.
![Debug Step Image](../images/fancyzones/9.png)
We found that this string corresponds to a variable. However, the return value differs in Debug mode, so let's first examine the logic in Release mode.
We found that the variable `ScreenBoundsWidth` is located in the constructor of `MonitorInfoModel`.
![Debug Step Image](../images/fancyzones/10.png)
Then, by searching for `MonitorInfoModel`, we found that this class is instantiated in the constructor of the `MonitorViewModel` class.
![Debug Step Image](../images/fancyzones/11.png)
The width and height of the monitor, which are crucial, are also assigned at this point. Let's continue by checking where the data in `App.Overlay.Monitors` is initialized.
My idea is to examine all references to the `Monitors` variable and identify the initialization point based on those references.
![Debug Step Image](../images/fancyzones/12.png)
Finally, by tracing the `Add` function of `Monitors`, we found the `AddMonitor()` method. This method is only called by `ParseParams()`, which confirms that the data originates from there.
However, by examining the context around the `AddMonitor()` function, we can see that the data comes from the `editor-parameters.json` file. Next, we will continue to investigate how this file is initialized and modified.
![Debug Step Image](../images/fancyzones/13.png)
By searching, we found that the `editor-parameters.json` file has write functions in both the Editor and FancyZones projects.
![Debug Step Image](../images/fancyzones/14.png)
**The display information is retrieved through the following call stack:**
`UpdateWorkAreas()` → `IdentifyMonitors()` → `GetDisplays()` → `EnumDisplayDevicesW()`.
**How was the `UpdateWorkAreas()` function identified?**
It was discovered by searching for `EditorParameters` and noticing that when the `save` function is called on `EditorParameters`, the parameter passed is `m_workAreaConfiguration`.
![Debug Step Image](../images/fancyzones/15.png)
**Then, by checking the initialization location of the `m_workAreaConfiguration` variable, we found that it is initialized inside `UpdateWorkAreas`.**
With this, we have successfully identified the source of the monitor resolution data displayed in the Editor's `Monitors` section.
### Step Four
Familiarize yourself with the module code through the current tasks at hand.
Bug[Issues · microsoftPowerToys](https://github.com/microsoft/PowerToys/issues?q=is%3Aissue%20state%3Aopen%20type%3ABug%20label%3AProduct-FancyZones)
UITest Code
[Task 57329836: PowerToys UI Test FancyZone UI Test Override Windows Snap-1 - Boards](https://microsoft.visualstudio.com/OS/_workitems/edit/57329836/)
[Task 57329843: PowerToys UI Test FancyZone UI Test Override Windows Snap-2 - Boards](https://microsoft.visualstudio.com/OS/_workitems/edit/57329843/)
[Task 57329845: PowerToys UI Test FancyZone UI Test Override Windows Snap-3 - Boards](https://microsoft.visualstudio.com/OS/_workitems/edit/57329845/)
[Task 56940387: PowerToys UI Test FancyZone UI Test Override Windows Snap-4 - Boards](https://microsoft.visualstudio.com/OS/_workitems/edit/56940387/)
UI Test Check List:
PowerToys/doc/releases/tests-checklist-template.md at releaseChecklist · microsoft/PowerToys](https://github.com/microsoft/PowerToys/blob/releaseChecklist/doc/releases/tests-checklist-template.md)
## Q&A
- ### First Run FancyZones error
![Debug Step Image](../images/fancyzones/16.png)
If you encounter this situation, you need to launch the FancyZones Editor once in the powertoys settings UI:
![Debug Step Image](../images/fancyzones/17.png)
The reason is that running the Editor directly within the project will not initialize various configuration files.
- ### How are layouts stored and loaded? Is there a central configuration handler?
There is no central configuration handler.
Editor read/write config data handler is in FancyZonesEditorCommon project.
![Debug Step Image](../images/fancyzones/18.png)
FancyZones cpp project read/write config data handler is in FancyZonesLib project.
![Debug Step Image](../images/fancyzones/19.png)
However, the files read from and written to are those in `C:\Users\“xxxxxx”\AppData\Local\Microsoft\PowerToys\FancyZones`
You can think of the editor as a visual config editor, which is most of its functionality. Another feature is used to set the layout for the monitor displays.
When the Editor starts, it will load the config data, and when FancyZones starts, it will also load the config data. After the Editor updates the config data, it will send a data update event, and FancyZones will refresh the current data in memory upon receiving the event.
![Debug Step Image](../images/fancyzones/20.png)
- ### Which parts of the code are responsible for monitor detection and DPI scaling?
About monitor detection you can find "FancyZones::MoveSizeUpdate" function.
I believe that in the case without DPI scaling, FancyZones retrieves the window's position and does not need to know what the mouse's DPI scaling is like. If you are referring to window scaling, it is called through the system interface, and you can see the detailed code in "WindowMouseSnap::MoveSizeEnd()" function.
- ### How does FancyZones track which windows belong to which zones?
In "FancyZones::MoveSizeUpdate" function.
### Extra tools and information
**Test samples**: https://github.com/microsoft/WinAppDriver/tree/master/Samples
While working on tests, you may need a tool that helps you to view the element's accessibility data, e.g. for finding the button to click. For this purpose, you could use [AccessibilityInsights](https://accessibilityinsights.io/docs/windows/overview) or [WinAppDriver UI Recorder](https://github.com/microsoft/WinAppDriver/wiki/WinAppDriver-UI-Recorder).
>Note: close helper tools while running tests. Overlapping windows can affect test results.
+34
View File
@@ -0,0 +1,34 @@
# File Explorer Add-ons
[Public overview - Microsoft Learn](https://learn.microsoft.com/en-us/windows/powertoys/file-explorer)
## Quick Links
[All Issues](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3A%22Product-File%20Explorer%22)<br>
[Bugs](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3AIssue-Bug%20label%3A%22Product-File%20Explorer%22)<br>
[Pull Requests](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+is%3Aopen+label%3A%22Product-File+Explorer%22)
## Overview
File Explorer Add-ons are extensions that enhance Windows File Explorer functionality with additional features and context menu options.
## Links
- [Source code folder](https://github.com/microsoft/PowerToys/tree/main/src/modules/fileexplorerpreview)
- [Issue tracker](https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+label%3A%22File+Explorer%22)
## Implementation Details
TODO: Add implementation details
## Debugging
TODO: Add debugging information
## Settings
TODO: Add settings documentation
## Future Improvements
TODO: Add potential future improvements
+202
View File
@@ -0,0 +1,202 @@
# File Locksmith
[Public overview - Microsoft Learn](https://learn.microsoft.com/en-us/windows/powertoys/file-locksmith)
## Quick Links
[All Issues](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3A%22Product-File%20Locksmith%22)<br>
[Bugs](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3A%22Product-File%20Locksmith%22%20label%3AIssue-Bug)<br>
[Pull Requests](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+is%3Aopen+label%3A%22Product-File+Locksmith%22)
## Overview
File Locksmith is a utility in PowerToys that shows which processes are locking or using a specific file. This helps users identify what's preventing them from deleting, moving, or modifying files by revealing the processes that have handles to those files.
## Architecture
![Diagram](../images/filelocksmith/diagram.png)
File Locksmith follows a similar architecture to the ImageResizer and NewPlus modules. It consists of:
1. **Shell Extensions**:
- `FileLocksmithExt` - COM-based shell extension for Windows 10 and below
- `FileLocksmithContextMenu` - Shell extension for Windows 11 context menu
2. **Core Components**:
- `FileLocksmithLib` - Handles IPC between shell extensions and UI
- `FileLocksmithLibInterop` - Core functionality for finding processes locking files
- `FileLocksmithUI` - WinUI 3 user interface component
3. **Settings Integration**:
- Settings integration in the PowerToys settings application
## Implementation Details
### Shell Extensions
The module adds "Unlock with File Locksmith" to the context menu in File Explorer:
- For Windows 11, a context menu command is registered as a MSIX sparse package (compiled via appxmanifest.xml)
- For Windows 10 and below, a traditional shell extension is registered through registry keys during installation
### Process Communication Flow
1. User enables File Locksmith in PowerToys settings
2. User right-clicks on a file and selects "Unlock with File Locksmith"
3. The shell extension writes the selected file path to a temporary file (file-based IPC)
4. The shell extension launches `PowerToys.FileLocksmithUI.exe`
5. The UI reads the file path from the temporary file
6. The UI uses `FileLocksmithLibInterop` to scan for processes with handles to the file
7. Results are displayed in the UI, showing process information and allowing user action
### Core Functionality
The core functionality to find processes locking files is implemented in [FileLocksmith.cpp](/src/modules/FileLocksmith/FileLocksmithLibInterop/FileLocksmith.cpp), which:
- Uses low-level Windows APIs via `NtdllExtensions` to iterate through file handles
- Examines all running processes to find handles to the specified files
- Maps process IDs to the files they're locking
- Retrieves process information such as name, user context, and file paths
### User Interface
The UI is built with WinUI 3 and uses MVVM architecture:
- View models handle process data and user interactions
- Converters transform raw data into UI-friendly formats
- The interface shows which processes are locking files, along with icons and process details
## Code Structure
### Shell Extensions
- [ClassFactory.cpp](/src/modules/FileLocksmith/FileLocksmithExt/ClassFactory.cpp): COM class factory that creates instances of shell extension objects
- [ExplorerCommand.cpp](/src/modules/FileLocksmith/FileLocksmithExt/ExplorerCommand.cpp): Implements Windows Explorer context menu command for Windows 10 and below
- [PowerToysModule.cpp](/src/modules/FileLocksmith/FileLocksmithExt/PowerToysModule.cpp): PowerToys module interface implementation with settings management
- [dllmain.cpp](/src/modules/FileLocksmith/FileLocksmithExt/dllmain.cpp): DLL entry point for Windows 10 shell extension
- [dllmain.cpp](/src/modules/FileLocksmith/FileLocksmithContextMenu/dllmain.cpp): Windows 11 context menu integration through MSIX package
### Core Libraries
- [IPC.cpp](/src/modules/FileLocksmith/FileLocksmithLib/IPC.cpp): File-based inter-process communication between shell extensions and UI
- [Settings.cpp](/src/modules/FileLocksmith/FileLocksmithLib/Settings.cpp): Settings management for File Locksmith module
- [FileLocksmith.cpp](/src/modules/FileLocksmith/FileLocksmithLibInterop/FileLocksmith.cpp): Core process scanning implementation to find processes locking files
- [NativeMethods.cpp](/src/modules/FileLocksmith/FileLocksmithLibInterop/NativeMethods.cpp): Interop layer bridging native C++ with WinRT-based UI
- [NtdllBase.cpp](/src/modules/FileLocksmith/FileLocksmithLibInterop/NtdllBase.cpp): Interface to native Windows NT APIs
- [NtdllExtensions.cpp](/src/modules/FileLocksmith/FileLocksmithLibInterop/NtdllExtensions.cpp): Process and handle querying utilities using NtQuerySystemInformation
- [ProcessResult.cpp](/src/modules/FileLocksmith/FileLocksmithLibInterop/ProcessResult.cpp): Class for storing process information (name, PID, user, file list)
### UI Components
- [FileCountConverter.cs](/src/modules/FileLocksmith/FileLocksmithUI/Converters/FileCountConverter.cs): Converts file counts for UI display
- [FileListToDescriptionConverter.cs](/src/modules/FileLocksmith/FileLocksmithUI/Converters/FileListToDescriptionConverter.cs): Formats file lists for display
- [PidToIconConverter.cs](/src/modules/FileLocksmith/FileLocksmithUI/Converters/PidToIconConverter.cs): Extracts icons for processes
- [UserToSystemWarningVisibilityConverter.cs](/src/modules/FileLocksmith/FileLocksmithUI/Converters/UserToSystemWarningVisibilityConverter.cs): Shows warnings for system processes
- [MainWindow.xaml.cs](/src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithXAML/MainWindow.xaml.cs): Main application window implementation
- [App.xaml.cs](/src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithXAML/App.xaml.cs): Application entry point
- [ResourceLoaderInstance.cs](/src/modules/FileLocksmith/FileLocksmithUI/Helpers/ResourceLoaderInstance.cs): Localization resource helper
- [MainViewModel.cs](/src/modules/FileLocksmith/FileLocksmithUI/ViewModels/MainViewModel.cs): Main view model that handles loading processes asynchronously
### Settings Integration
- [FileLocksmithViewModel.cs](/src/settings-ui/Settings.UI/ViewModels/FileLocksmithViewModel.cs): ViewModel for File Locksmith in PowerToys settings
- [FileLocksmithLocalProperties.cs](/src/settings-ui/Settings.UI.Library/FileLocksmithLocalProperties.cs): Machine-level settings storage
- [FileLocksmithProperties.cs](/src/settings-ui/Settings.UI.Library/FileLocksmithProperties.cs): User-level settings storage
- [FileLocksmithSettings.cs](/src/settings-ui/Settings.UI.Library/FileLocksmithSettings.cs): Module settings definitions
## Debugging
To build and debug the File Locksmith module:
0. **Build FileLocksmith module**
- Shutdown the existing release builds of PowerToys
- Open the solution in Visual Studio
- Build the entire solution
- Build the `FileLocksmith` project
1. **Create certificate and import to Root (if you don't already have)**
```powershell
New-SelfSignedCertificate -Subject "CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" `
-KeyUsage DigitalSignature `
-Type CodeSigningCert `
-FriendlyName "PowerToys SelfCodeSigning" `
-CertStoreLocation "Cert:\CurrentUser\My"
$cert = Get-ChildItem -Path Cert:\CurrentUser\My | Where-Object { $_.FriendlyName -like "*PowerToys*" }
Export-Certificate -Cert $cert -FilePath "$env:TEMP\PowerToysCodeSigning.cer"
# under admin Terminal:
Import-Certificate -FilePath "$env:TEMP\PowerToysCodeSigning.cer" -CertStoreLocation Cert:\LocalMachine\Root
# get Thumbprint
Get-ChildItem -Path Cert:\CurrentUser\My | Where-Object { $_.FriendlyName -like "*PowerToys*" }
```
2. **Sign the MSIX package**
```
SignTool sign /fd SHA256 /sha1 <CERTIFICATE THUMBPRINT> "C:\Users\$env:USERNAME\source\repos\PowerToys\x64\Debug\WinUI3Apps\FileLocksmithContextMenuPackage.msix"
```
SignTool might be not in your PATH, so you may need to specify the full path to it, e.g., `C:\Program Files (x86)\Windows Kits\10\bin\<version>\x64\signtool.exe`.
**commands example**:
```powershell
PS C:\Users\developer> New-SelfSignedCertificate -Subject "CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" `
>> -KeyUsage DigitalSignature `
>> -Type CodeSigningCert `
>> -FriendlyName "PowerToys SelfSigned" `
>> -CertStoreLocation "Cert:\CurrentUser\My"
PSParentPath: Microsoft.PowerShell.Security\Certificate::CurrentUser\My
Thumbprint Subject EnhancedKeyUsageList
---------- ------- --------------------
1AA018C2B06B60EAFEE452ADE403306F39058FF5 CN=Microsoft Corpor… Code Signing
PS C:\Users\developer> Get-ChildItem -Path Cert:\CurrentUser\My | Where-Object { $_.FriendlyName -like "*PowerToys*" }
PSParentPath: Microsoft.PowerShell.Security\Certificate::CurrentUser\My
Thumbprint Subject EnhancedKeyUsageList
---------- ------- --------------------
1AA018C2B06B60EAFEE452ADE403306F39058FF5 CN=Microsoft Corpor… Code Signing
PS C:\Users\developer> & "C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\signtool.exe" sign /fd SHA256 /sha1 1AA018C2B06B60EAFEE452ADE403306F39058FF5 "%REPO_PATH%\PowerToys\x64\Debug\WinUI3Apps\FileLocksmithContextMenuPackage.msix"
Done Adding Additional Store
Successfully signed: C:\Users\developer\Develop\GitHub\PowerToys\x64\Debug\WinUI3Apps\FileLocksmithContextMenuPackage.msix
```
3. **Remove old version**
```powershell
Get-AppxPackage -Name Microsoft.PowerToys.FileLocksmithContextMenu*
Remove-AppxPackage Microsoft.PowerToys.FileLocksmithContextMenu_1.0.0.0_neutral__8wekyb3d8bbwe
```
4. **Install new signed MSIX**
```powershell
Add-AppxPackage -Path "%REPO_PATH%\PowerToys\x64\Debug\WinUI3Apps\FileLocksmithContextMenuPackage.msix" -ExternalLocation "%REPO_PATH%\PowerToys\x64\Debug\WinUI3Apps"
```
5. **Restart Explorer**
- Go to Task Manager and restart explorer.exe
6. **Debug Process**
- Set the breakpoint in [dllmain.cpp](/src/modules/FileLocksmith/FileLocksmithContextMenu/dllmain.cpp#L116)
- Open the **Attach to Process** dialog in Visual Studio
- Right-click a file in File Explorer
- Attach the debugger to `dllhost.exe` with **FileLocksmith** Title to debug the shell extension
![Attach to Process](../images/filelocksmith/debug.png)
- Right-click (fast) a file again and select *"Unlock with File Locksmith"*
- Attach the debugger to `PowerToys.FileLocksmithUI.exe` to debug the UI
7. **Alternative Debugging Method**
- You can set the `FileLocksmithUI` as startup project directly in Visual Studio, which will launch the UI without needing to go through the shell extension. This is useful for debugging the UI logic without the shell extension overhead.
## Known Issues
There is an open PR to change the IPC mechanism from file-based to pipe-based, but it has blockers:
- When restarting as admin, the context menu extension doesn't show
- The "Unlock with File Locksmith" option doesn't work when launched as admin
## Settings Integration
File Locksmith integrates with the PowerToys settings through:
- [FileLocksmithViewModel.cs](/src/settings-ui/Settings.UI/ViewModels/FileLocksmithViewModel.cs)
- [FileLocksmithLocalProperties.cs](/src/settings-ui/Settings.UI.Library/FileLocksmithLocalProperties.cs)
- [FileLocksmithProperties.cs](/src/settings-ui/Settings.UI.Library/FileLocksmithProperties.cs)
- [FileLocksmithSettings.cs](/src/settings-ui/Settings.UI.Library/FileLocksmithSettings.cs)
+114
View File
@@ -0,0 +1,114 @@
# Hosts File Editor
[Public overview - Microsoft Learn](https://learn.microsoft.com/en-us/windows/powertoys/hosts-file-editor)
## Quick Links
[All Issues](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3A%22Product-Hosts%20File%20Editor%22)<br>
[Bugs](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3A%22Product-Hosts%20File%20Editor%22%20label%3AIssue-Bug)<br>
[Pull Requests](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+is%3Aopen+label%3A%22Product-Hosts+File+Editor%22)
## Overview
The Hosts File Editor module provides a convenient way to edit the system's hosts file. The hosts file is a plain text file used by the operating system to map hostnames to IP addresses, allowing users to override DNS for specific domain names.
## Code Structure
![Diagram](../images/hostsfileeditor/diagram.png)
The Hosts File Editor module is structured into three primary components:
1. **Hosts** - Entry point for the Hosts File Editor. Manages core services and settings through helper utilities.
2. **HostsModuleInterface** - Interface for integrating the Hosts module with the PowerToys system.
3. **HostsUILib** - Implements the UI layer using WinUI 3.
This structure is similar to the Environment Variables for Windows module.
## Key Components
### Main Entry Points
- **Module Entry**: [Program.cs](/src/modules/Hosts/Program.cs) → [App.xaml.cs](/src/modules/Hosts/HostsXAML/App.xaml.cs)
- **Settings UI**:
- Main Window: [MainWindow.xaml.cs](/src/modules/Hosts/Hosts/HostsXAML/MainWindow.xaml.cs)
- View: [HostsMainPage.xaml](/src/modules/Hosts/HostsUILib/HostsMainPage.xaml)
- ViewModel: [HostsMainPage.xaml.cs](/src/modules/Hosts/HostsUILib/HostsMainPage.xaml.cs)
- **Runner Integration**: [HostsModuleInterface](/src/modules/Hosts/HostsModuleInterface)
### Runner Integration
The module is loaded by the PowerToys runner from:
- [main.cpp](/src/runner/main.cpp) (Lines 183-184): Loads Hosts Module using `L"WinUI3Apps/PowerToys.HostsModuleInterface.dll"`
### Settings Management
- [HostsViewModel.cs](/src/settings-ui/Settings.UI/ViewModels/HostsViewModel.cs): Hosts UI in PowerToys settings
- [HostsProperties.cs](/src/settings-ui/Settings.UI.Library/HostsProperties.cs): In settings UI
- [HostsSettings.cs](/src/settings-ui/Settings.UI.Library/HostsSettings.cs): Wrapper with HostsProperties
### Module Components
#### HostsModuleInterface
- Defines the interface for integrating the Hosts module with the PowerToys system.
#### Hosts (Main Project)
- [Program.cs](/src/modules/Hosts/Hosts/Program.cs): Launch app
- [HostsXAML](/src/modules/Hosts/Hosts/HostsXAML): Initialize service and loads the main window
- [Host.cs](/src/modules/Hosts/Hosts/Helpers/Host.cs): Access to services register
- [NativeEventWaiter.cs](/src/modules/Hosts/Hosts/Helpers/NativeEventWaiter.cs): Gets the dispatcher queue for posting UI updates from a background thread
- [UserSettings.cs](/src/modules/Hosts/Hosts/Settings/UserSettings.cs): Manages reading, tracking, and updating user settings from settings.json
#### HostsUILib
- [HostsMainPage.xaml.cs](/src/modules/Hosts/HostsUILib/HostsMainPage.xaml.cs): Main page
- [ViewModels](/src/modules/Hosts/HostsUILib/ViewModels): Contains view models that manage state and logic
- [Models](/src/modules/Hosts/HostsUILib/Models): Models for managing host entries
- [AddressType.cs](/src/modules/Hosts/HostsUILib/Models/AddressType.cs): Specifies whether an address is IPv4, IPv6, or Invalid
- [Entry.cs](/src/modules/Hosts/HostsUILib/Models/Entry.cs): Represents a single hosts file entry (IP address, hostnames, comment, flags)
- [HostsData.cs](/src/modules/Hosts/HostsUILib/Models/HostsData.cs): Converts the list of entries into a read-only collection
- [Settings](/src/modules/Hosts/HostsUILib/Settings): Settings configuration
- [Consts.cs](/src/modules/Hosts/HostsUILib/Consts.cs): Defines constants like max hosts IP length
- [Helpers](/src/modules/Hosts/HostsUILib/Helpers): Utilities for dealing with hosts IP, filter features, and file loading
## Call Flow
1. **Enable app**: runner/main.cpp → settings.ui/settings.ui.library
2. **Start app**: Program.cs → HostsXAML → HostsMainPage
3. **Load hosts data**: ViewModel → HostsData → Helpers (load and parse file)
4. **User edits**: UI bound to ViewModel updates entries
5. **Save changes**: ViewModel triggers file write through Helpers
6. **Settings management**: UserSettings.cs persists user preferences
## Key Features
| Feature | Key Function |
|---------|--------------|
| Adding a new entry | `Add(Entry entry)` |
| Filtering host file entries | `ApplyFilters()` |
| Open Hosts File | `ReadHosts()` |
| Additional Lines | `UpdateAdditionalLines(string lines)` |
## Settings
| Setting | Implementation |
|---------|---------------|
| Open as administrator | `UserSettings()` |
| Additional lines position | `UserSettings()->AdditionalLinesPosition` |
| Consider loopback addresses as duplicates | `UserSettings()->LoopbackDuplicates` |
| Encoding Setting | `UserSettings()->Encoding` |
## UI Test Automation
Hosts File Editor is currently undergoing a UI Test migration process to improve automated testing coverage. You can track the progress of this migration at:
[Hosts File Editor UI Test Migration Progress](https://github.com/microsoft/PowerToys/blob/feature/UITestAutomation/src/modules/Hosts/Hosts.UITests/Release-Test-Checklist-Migration-Progress.md)
## How to Build and Debug
1. Build PowerToys Project in debug mode
2. Set Hosts as the startup project
3. Launch Hosts File Editor in debug mode
4. Attach the debugger to PowerToys.Hosts.dll
5. Add breakpoints in the Hosts code
+132
View File
@@ -0,0 +1,132 @@
# Image Resizer
[Public overview - Microsoft Learn](https://learn.microsoft.com/en-us/windows/powertoys/image-resizer)
## Quick Links
[All Issues](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3A%22Product-Image%20Resizer%22)<br>
[Bugs](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3AIssue-Bug%20label%3A%22Product-Image%20Resizer%22)<br>
[Pull Requests](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+is%3Aopen+label%3A%22Product-Image+Resizer%22)
Image Resizer is a Windows shell extension that enables batch resizing of images directly from File Explorer.
## Overview
Image Resizer provides a convenient way to resize images without opening a dedicated image editing application. It is accessible through the Windows context menu and allows users to:
- Resize single or multiple images at once
- Choose from predefined sizing presets or enter custom dimensions
- Maintain or modify aspect ratios
- Create copies or overwrite original files
- Apply custom filename formats to resized images
- Select different encoding quality settings
## Architecture
Image Resizer consists of multiple components:
- Shell Extension DLL (context menu integration)
- WinUI 3 UI application
- Core image processing library
### Technology Stack
- C++/WinRT
- WPF (UI components)
- Windows Imaging Component (WIC) for image processing
- COM for shell integration
## Context Menu Integration
Image Resizer integrates with the Windows context menu following the [PowerToys Context Menu Handlers](../common/context-menus.md) pattern. It uses a dual registration approach to ensure compatibility with both Windows 10 and Windows 11.
### Registration Process
The context menu integration follows the same pattern as PowerRename, using:
- A traditional shell extension for Windows 10
- A sparse MSIX package for Windows 11 context menus
For more details on the implementation approach, see the [Dual Registration section](../common/context-menus.md#1-dual-registration-eg-imageresizer-powerrename) in the context menu documentation.
### Context Menu Appearance Logic
Image Resizer dynamically determines when to show the context menu option:
- `AppxManifest.xml` registers the extension for all file types (`Type="*"`)
- The shell extension checks if the selected files are images using `AssocGetPerceivedType()`
- The menu appears only for image files (returns `ECS_ENABLED`); otherwise, it remains hidden (returns `ECS_HIDDEN`)
This approach provides flexibility to support additional file types by modifying only the detection logic without changing the system-level registration.
## UI Implementation
Image Resizer uses WPF for its user interface, as evidenced by the App.xaml.cs file. The UI allows users to:
- Select from predefined size presets or enter custom dimensions
- Configure filename format for the resized images
- Set encoding quality and format options
- Choose whether to replace or create copies of the original files
From the App.xaml.cs file, we can see that the application:
- Supports localization through `LanguageHelper.LoadLanguage()`
- Processes command line arguments via `ResizeBatch.FromCommandLine()`
- Uses a view model pattern with `MainViewModel`
- Respects Group Policy settings via `GPOWrapper.GetConfiguredImageResizerEnabledValue()`
## Debugging
### Debugging the Context Menu
See the [Debugging Context Menu Handlers](../common/context-menus.md#debugging-context-menu-handlers) section for general guidance on debugging PowerToys context menu extensions.
For Image Resizer specifically, there are several approaches:
#### Option 1: Manual Registration via Registry
1. Create a registry file (e.g., `register.reg`) with the following content:
```
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\CLSID\{51B4D7E5-7568-4234-B4BB-47FB3C016A69}]
@="PowerToys Image Resizer Extension"
[HKEY_CLASSES_ROOT\CLSID\{51B4D7E5-7568-4234-B4BB-47FB3C016A69}\InprocServer32]
@="D:\\PowerToys\\x64\\Debug\\PowerToys.ImageResizerExt.dll"
"ThreadingModel"="Apartment"
[HKEY_CURRENT_USER\Software\Classes\SystemFileAssociations\.png\ShellEx\ContextMenuHandlers\ImageResizer]
@="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
```
2. Import the registry file:
```
reg import register.reg
```
3. Restart Explorer to apply changes:
```
taskkill /f /im explorer.exe && start explorer.exe
```
4. Attach the debugger to `explorer.exe`
5. Add breakpoints to relevant code in the Image Resizer shell extension
#### Option 2: Using regsvr32
1. Register the shell extension DLL:
```
regsvr32 "D:\PowerToys\x64\Debug\PowerToys.ImageResizerExt.dll"
```
2. Restart Explorer and attach the debugger as in Option 1
### Common Issues
- Context menu not appearing:
- Ensure the extension is properly registered
- Verify you're right-clicking on supported image files
- Restart Explorer to clear context menu cache
- For Windows 11, check AppX package registration:
- Use `get-appxpackage -Name *imageresizer*` to verify installation
- Use `Remove-AppxPackage` to remove problematic registrations
- Missing UI or processing failures:
- Check Event Viewer for application errors
- Verify file permissions for both source and destination folders
+134
View File
@@ -0,0 +1,134 @@
# Interface definition
```cpp
class PowertoyModuleIface {
public:
virtual const wchar_t* get_name() = 0;
virtual const wchar_t** get_events() = 0;
virtual bool get_config(wchar_t* buffer, int *buffer_size) = 0;
virtual void set_config(const wchar_t* config) = 0;
virtual void call_custom_action(const wchar_t* action) {};
virtual void enable() = 0;
virtual void disable() = 0;
virtual bool is_enabled() = 0;
virtual void destroy() = 0;
};
typedef PowertoyModuleIface* (__cdecl *powertoy_create_func)();
```
# Runtime logic
The PowerToys runner will, for each PowerToy DLL:
- load the DLL,
- call [`powertoy_create()`](#powertoy_create_func) to create the PowerToy.
On the received object, the runner will call:
- [`get_name()`](#get_name) to get the name of the PowerToy,
- [`enable()`](#enable) to initialize the PowerToy.
While running, the runner might call the following methods between create_powertoy()
and destroy():
- [`disable()`](#disable)/[`enable()`](#enable)/[`is_enabled()`](#is_enabled) to change or get the PowerToy's enabled state,
- [`get_config()`](#get_config) to get the available configuration settings,
- [`set_config()`](#set_config) to set settings after they have been edited in the Settings editor,
- [`call_custom_action()`](#call_custom_action) when the user selects a custom action in the Settings editor,
When terminating, the runner will:
- call [`disable()`](#disable),
- call [`destroy()`](#destroy) which should free all the memory and delete the PowerToy object,
- unload the DLL.
# Method definition
This section contains a more detailed description of each of the interface methods.
## powertoy_create_func
```cpp
typedef PowertoyModuleIface* (__cdecl *powertoy_create_func)()
```
Typedef of the factory function that creates the PowerToy object.
Must be exported by the DLL as `powertoy_create()`.
Called by the PowerToys runner to initialize each PowerToy.
It will be called only once before a call to [`destroy()`](#destroy) is made.
The returned PowerToy should be in the disabled state. The runner will call the [`enable()`](#enable) method to start the PowerToy.
In case of errors returns `nullptr`.
## get_name
```cpp
virtual const wchar_t* get_name()
```
Returns the name of the PowerToy, it will be cached by the runner.
## get_config
```
virtual bool get_config(wchar_t* buffer, int *buffer_size)
```
Fills a buffer with the available configuration settings.
If `buffer` is a null pointer or the buffer size is not large enough sets the required buffer size in 'buffer_size' and return false.
Returns true if successful.
## set_config
```cpp
virtual void set_config(const wchar_t* config)
```
After the user has changed the module settings in the Settings editor, the runner calls this method to pass the updated values to the module. It's a good place to save the settings as well.
## call_custom_action
```cpp
virtual void call_custom_action(const wchar_t* action)
```
Calls a custom action in response to the user pressing the custom action button in the Settings editor.
This can be used to spawn custom editors defined by the PowerToy.
## enable
```cpp
virtual void enable()
```
Enables the PowerToy.
## disable
```cpp
virtual void disable()
```
Disables the PowerToy, should free as much memory as possible.
## is_enabled
```cpp
virtual bool is_enabled() = 0;
```
Returns the PowerToy state.
## destroy
```cpp
virtual void destroy()
```
Destroy the PowerToy and free all memory.
# Code organization
### [`powertoy_module_interface.h`](/src/modules/interface/powertoy_module_interface.h)
Contains the PowerToys interface definition.
@@ -0,0 +1,17 @@
# Table of Contents
[Public overview - Microsoft Learn](https://learn.microsoft.com/en-us/windows/powertoys/keyboard-manager)
## Quick Links
[All Issues](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3A%22Product-Keyboard%20Shortcut%20Manager%22)<br>
[Bugs](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3AIssue-Bug%20label%3A%22Product-Keyboard%20Shortcut%20Manager%22)<br>
[Pull Requests](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+is%3Aopen+label%3A%22Product-Keyboard+Shortcut+Manager%22+)
The devdocs for Keyboard Manager have been divided into the following modules:
1. [Keyboard Manager Module](keyboardmanager.md)
2. [Keyboard Event Handlers](keyboardeventhandlers.md)
3. [Keyboard Manager UI](keyboardmanagerui.md)
4. [Keyboard Manager Common](keyboardmanagercommon.md)
5. [Debugging Guide](debug.md)
@@ -0,0 +1,94 @@
# Keyboard Manager Debugging Guide
This document provides guidance on debugging the Keyboard Manager module in PowerToys.
## Module Overview
Keyboard Manager consists of two main components:
- **Keyboard Manager Editor**: UI application for configuring key and shortcut remappings
- **Keyboard Manager Engine**: Background process that intercepts and handles keyboard events
## Development Environment Setup
1. Clone the PowerToys repository
2. Open `PowerToys.slnx` in Visual Studio
3. Ensure all NuGet packages are restored
4. Build the entire solution in Debug configuration
## Debugging the Editor (UI)
### Setup
1. In Visual Studio, right-click on the `KeyboardManagerEditor` project
2. Select "Set as Startup Project"
### Common Debugging Scenarios
#### UI Rendering Issues
Breakpoints to consider:
- `EditKeyboardWindow.cpp`: `CreateWindow()` method
- `EditShortcutsWindow.cpp`: `CreateWindow()` method
#### Configuration Changes
When debugging configuration changes:
1. Set breakpoints in `KeyboardManagerState.cpp` around the `SetRemappedKeys()` or `SetRemappedShortcuts()` methods
2. Monitor the JSON serialization process in the save functions
### Testing UI Behavior
The `KeyboardManagerEditorTest` project contains tests for the UI functionality. Run these tests to validate UI changes.
## Debugging the Engine (Remapping Logic)
### Setup
1. In Visual Studio, right-click on the `KeyboardManagerEngine` project
2. Select "Set as Startup Project"
3. Press F5 to start debugging
### Key Event Flow
The keyboard event processing follows this sequence:
1. Low-level keyboard hook captures an event
2. `KeyboardEventHandlers.cpp` processes the event
3. `KeyboardManager.cpp` applies remapping logic
4. Event is either suppressed, modified, or passed through
### Breakpoints to Consider
- `main.cpp`: `StartLowlevelKeyboardHook()` - Hook initialization
- `KeyboardEventHandlers.cpp`: `HandleKeyboardEvent()` - Entry point for each keyboard event
- `KeyboardManager.cpp`: `HandleKeyEvent()` - Processing individual key events
- `KeyboardManager.cpp`: `HandleShortcutRemapEvent()` - Processing shortcut remapping
### Logging and Trace
Enable detailed logging by setting the `_DEBUG` and `KBM_VERBOSE_LOGGING` preprocessor definitions.
## Common Issues and Troubleshooting
### Multiple Instances
If you encounter issues with multiple instances, check the mutex logic in `KeyboardManagerEditor.cpp`. The editor uses `PowerToys_KBMEditor_InstanceMutex` to ensure single instance.
### Key Events Not Being Intercepted
1. Verify the hook is properly installed by setting a breakpoint in the hook procedure
2. Check if any other application is capturing keyboard events at a lower level
3. Ensure the correct configuration is being loaded from the settings JSON
### UI Freezes or Crashes
1. Check XAML Islands initialization in the Editor
2. Verify UI thread is not being blocked by IO operations
3. Look for exceptions in the event handling code
## Advanced Debugging
### Debugging Both Components Simultaneously
To debug both the Editor and Engine:
1. Launch the Engine first in debug mode
2. Attach the debugger to the Editor process when it starts
Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

@@ -0,0 +1,84 @@
# Keyboard Event Handlers (Remapping Logic)
This file contains documentation for all the methods involved in key/shortcut remapping.
## Table of Contents:
1. [HandleSingleKeyRemapEvent](#HandleSingleKeyRemapEvent)
2. [HandleShortcutRemapEvent](#HandleShortcutRemapEvent)
3. [HandleOSLevelShortcutRemapEvent](#HandleOSLevelShortcutRemapEvent)
4. [HandleAppSpecificShortcutRemapEvent](#HandleAppSpecificShortcutRemapEvent)
5. [HandleSingleKeyToggleToModEvent (Obsolete))](#HandleSingleKeyToggleToModEvent-(Obsolete---Code-from-PoC-which-is-commented-out))
6. [Tests](#Tests)
1. [MockedInput](#MockedInput)
2. [Tests for single key remaps and shortcut remaps](#Tests-for-single-key-remaps-and-shortcut-remaps)
## HandleSingleKeyRemapEvent
[This method](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp#L13-L124) is used for handling the key to key and key to shortcut remapping logic. The general logic is as follows:
- Check if the `dwExtraInfo` field contains the `KEYBOARDMANAGER_INJECTED_FLAG` bit set. This bit is used to indicate that the key event was generated by KBM using `SendInput`. This ensures that we don't read events generated by the key or shortcut remap methods.
- Check if the current key is present in the list of remaps. If it isn't, return 0 (i.e. do not suppress the event).
- If it is remapped to Disable, suppress the event.
- If it is remapped to a key, we send the key down/up message for the target key and suppress the current key event. We have a check for filtering artificial keys, such as `VK_WIN` (which is a keycode added by us), so that it is translated to `VK_LWIN` instead.
- If it is remapped to a shortcut, for key down we set the target modifiers first, followed by the target action key, and for key up we release the action key first, followed by the modifiers.
- All the remapped key events that we send above are sent with `KEYBOARDMANAGER_SINGLEKEY_FLAG` on the `dwExtraInfo` field.
## HandleShortcutRemapEvent
[This method](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp#L178-L739) is used for handling the shortcut to shortcut and shortcut to key remapping logic. The general logic is as follows:
- Check if any shortcut remap is currently invoked. This is required to ensure that two remaps don't occur simultaneously at a time, and we send key up events for the shortcuts only if they are actually invoked and not for artificial key up events. In addition to that, while a remap is in the middle of execution, the keyboard state will not match the physical keys, so we do not want a remap <kbd>Ctrl+A</kbd> to <kbd>Ctrl+V</kbd> to also trigger the remap from <kbd>Ctrl+V</kbd> to <kbd>Alt+V</kbd> on pressing <kbd>Ctrl+A</kbd> on the keyboard.
- Get the remap table as per the `activatedApp` argument (i.e. if it is empty, we get the global shortcut remap table and otherwise we get the corresponding app-specific shortcut remap table).
- Iterate over the list of remaps in descending order of number of keys in the shortcut. This is required **for shortcut to key remaps** to ensure that if a user has both <kbd>Ctrl+A</kbd> and <kbd>Ctrl+Shift+A</kbd> remapped to some keys, and the user presses <kbd>Ctrl+Shift+A</kbd>, then we prefer the <kbd>Ctrl+Shift+A</kbd> remap. This logic would not be required if there were only shortcut to shortcut remaps, as they are invoked only on exact match.
- If any shortcut was found to be invoked (from the first step), then we skip till we find the matching shortcut remap. If not we check if the modifiers of the original shortcut are pressed down. If they are, we check if the current key event is a key down event and it matches the action key of the original shortcut. For shortcut to shortcut and for disabling a shortcut [we have an additional step](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp#L208-L212) where we check if any other key is pressed apart from the original shortcut. This is required because for these two features we allow the remaps only if those exact keys are pressed. The method used for this is [described in detail](keyboardmanagercommon.md#IsKeyboardStateClearExceptShortcut). If a win key was pressed, we store whether it was the left or the right one, in order to determine which key to set for remaps from/to the common Win key code which we added. This is so that pressing and releasing Left Win key results in that Win key getting modified and not the Right Win key.
- If the remap is to a key, we send a dummy key event followed by releasing the original shortcut's modifiers and setting the target key (or doing nothing if it is remapped to disable) and we suppress the event.
- If the remap is to a shortcut, if the modifiers in the original shortcut are present in the target, we only set the additional modifiers and the action key of the target. If it isn't, we send a dummy key event followed by releasing the modifiers which are not common, and setting the remaining ones in the target along with the action key.
- For both cases, we set the `isShortcutInvoked` flag to true, and set the `KeyboardManagerState.activatedApp` if it is an app-specific shortcut remap.
- For the `isShortcutInvoked` is true scenario (i.e. the initial remap keydown section is done) there are several cases depending on the key pressed or released:
- [**Case 1:**](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp#L339-L430) If a modifier in the original shortcut is released, we need to reset back to the physical keys pressed.
- For remap to shortcut, we release the target action key if it is currently pressed, and depending on whether all the modifiers of the original shortcut are present in the target, we release the target modifiers that are not common, and set the remaining original shortcut modifiers except the one that was released. We do not need to send the original action key as that will get generate its own key event if it is held down.
- For remap to key, we release the target key if it is pressed (and it is not remapped to Disable), and we set the original shortcut modifiers.
- For both the cases we send a dummy key event at the end, since we are setting modifiers without any other key after that, and we reset all the remap variables.
- [**Case 2:**](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp#L435-L461) If the original shortcut's action key is pressed again, we send the target shortcut's action key or the target key again (or for disable we just suppress the event).
- [**Case 3:**](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp#L463-L527) If the original shortcut's action key is released
- For remap to shortcut, we just release the target shortcut's action key
- For remap to disable, we suppress the event
- For remap to key, we check if any other keys are pressed apart from the target key. If not, we just release the target key. If there are, we reset back to the physical keys by releasing the target key and setting the original shortcut's modifiers along with a dummy key, and we reset all the remap variables. This behavior is different from remap to shortcut because if the action key is released while other keys are pressed the remap should be inactive, but such a state can't occur for shortcut to shortcut remaps since they happen only when the exact keys are pressed.
- [**Case 4:**](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp#L529-L551) If a modifier in the original shortcut is pressed, suppress the event
- [**Case 5:**](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp#L553-L732) If any other key is pressed
- For remap to shortcut, we need to reset back to physical keys as the shortcut remaps can't be pressed in combination with other keys. We release the target action key if it was pressed, and we release the modifier keys of the target shortcut that are not common and set the remaining ones in the original shortcut. We then send the original shortcut's action key if the target action key was found to be pressed, and we send the current key press at the end.
- For remap to key, if it is remapped to disable or if the target key is not found to be pressed, we reset to the physical keys, we set the original shortcut's modifiers and if is remap to Disable and the original shortcut's action key is physically pressed (this is checked by the `isOriginalActionKeyPressed` flag which we keep track of whenever the action key is pressed or released for remap to Disable), then we set the original shortcut's action key, followed by the current key press. If it is not remapped to disable and the target key is pressed, then we don't suppress the event as we allow shortcut to key remappings to be pressed along with other keys.
- For all the above cases, dummy key isn't required as we want the current key press to behave like a normal key.
- [**Case 6:**](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp#L733) If any other key is released, do not suppress the event as this event didn't appear with a corresponding key down event (such as an app sending a key up event) or we processed the key down and let it continue (for shortcut to key scenario).
- All the remapped key events that we send above are sent with `KEYBOARDMANAGER_SHORTCUT_FLAG` on the `dwExtraInfo` field, except the usage of the current key press in Case 5, for which we don't send any extra info so that it is considered as a normal key event which may in turn invoke some other remap.
**Note:** Shortcuts are considered valid if they have modifiers and an action key. The reason why we haven't supported key combinations of just modifiers (which is requested in this [issue](https://github.com/microsoft/PowerToys/issues/5670)) (like remapping <kbd>Ctrl+Alt</kbd>) is because this would require more cases and handling as these remappings have to take place only on press and release and if there is no key pressed in between similar to what Start Menu does. The remapping would have to be invoked only for this specific sequence <kbd>Ctrl</kbd> key down, <kbd>Alt</kbd> key down, <kbd>Alt</kbd> key up, <kbd>Ctrl</kbd> key up (ordering between Ctrl and Alt can be swapped). If any other key is pressed in between it shouldn't be invoked, and since this logic requires tracking exact states instead of using GetAsyncKeyStates, this could cause false positives if a user is not running as admin.
## HandleOSLevelShortcutRemapEvent
[This method](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp#L741-L752) is used for handling global shortcut to shortcut and shortcut to key remaps. The general logic is as follows:
- Check if the `dwExtraInfo` field is set to `KEYBOARDMANAGER_SHORTCUT_FLAG`. This indicates that the key event was generated by the KBM shortcut remap method using `SendInput`. This ensures that we don't read events generated by the shortcut remap method, but we still read events which are generated by the key remap method.
- Call `HandleShortcutRemapEvent` without the `activatedApp` argument so that global shortcut remapping takes place if it applies for the current key event.
## HandleAppSpecificShortcutRemapEvent
[This method](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp#L754-L809) is used for handling app-specific shortcut to shortcut and shortcut to key remaps. The general logic is as follows:
- Check if the `dwExtraInfo` field is set to `KEYBOARDMANAGER_SHORTCUT_FLAG`. This indicates that the key event was generated by the KBM shortcut remap method using `SendInput`. This ensures that we don't read events generated by the shortcut remap method, but we still read events which are generated by the key remap method.
- Get the name of the process in the foreground. This is done using `GetCurrentApplication` which uses `GetForegroundWindow` to get the window handle and `get_process_path` from the common lib. This approach can fail for UWP apps in full screen, so for that scenario we use the `GetGUIThreadInfo` approach to find the correct window handle, and hence the correct process name. This method is [described in more detail](keyboardmanagercommon.md#Foreground-app-detection)
- By checking `KeyboardManagerState.GetActivatedApp` we check if an app-specific shortcut is currently invoked. If so, we consider this application to be the activated app. This is required because some shortcut remaps could cause the current app to lose focus and hence until the shortcut is completely released we should allow that remap to continue; otherwise, the user could end up in a state where some keys do not get released. For example: remap <kbd>Ctrl+A</kbd> to <kbd>Alt+Tab</kbd> for Edge, when a user presses <kbd>Ctrl+A</kbd> the window loses focus as <kbd>Alt+Tab</kbd> gets executed.
- If there is no app-specific shortcut currently invoked, we check if the foreground process is present in the list of app-specific remaps, either with or without the file extension and case-insensitive. If it is, this is considered to be the activated app.
- Call `HandleShortcutRemapEvent` with the `activatedApp` argument so that app-specific shortcut remapping takes place if it applies for the current key event.
## HandleSingleKeyToggleToModEvent (Obsolete - Code from PoC which is commented out)
[This method](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp#L126-L176) was added to support a feature for converting the behavior of a key from behaving like a toggle (like Caps Lock/Num Lock) to a modifier (like Ctrl), such that when you hold Caps Lock it would behave as if Caps Lock was active, and when it was not pressed Caps Lock would be off. For Caps Lock this would be similar to behaving like Shift, but for Num Lock there is no existing key which can substitute for this. This was added while testing out remapping for the KBM PoC, but wasn't added as a feature since it wasn't a priority.
## Tests
In order to test the remapping logic, a mocked keyboard input handler had to be created because otherwise the tests would process and send actual key events. For this the [`InputInterface`](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/common/InputInterface.h) was made, and in production code the methods are implemented using [`SendInput`](https://learn.microsoft.com/windows/win32/api/winuser/nf-winuser-sendinput) and [`GetAsyncKeyState`](https://learn.microsoft.com/windows/win32/api/winuser/nf-winuser-getasynckeystate). In addition to this, [`GetCurrentApplication`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/Helpers.cpp#L226-L268) had to be mocked so that app-specific remapping can be tested.
### MockedInput
The [`MockedInput`](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/test/MockedInput.h) class uses a 256 size `bool` vector to store the key state for each key code. Identifying the foreground process is mocked by simply setting and getting a string value for the name of the current process.
[To mock the `SendInput` method](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/test/MockedInput.cpp#L10-L110), the steps for processing the input are as follows. This implementation is based on public documentation for SendInput and the behavior of key messages and keyboard hooks:
- Iterate over all the inputs in the `INPUT` vector argument.
- If the event is a key up event, then it is considered [`WM_SYSKEYUP`](https://learn.microsoft.com/windows/win32/inputdev/wm-syskeyup) if Alt is held down; otherwise, it is `WM_KEYUP`.
- If the event is a key down event, then it is considered [`WM_SYSKEYDOWN`](https://learn.microsoft.com/windows/win32/inputdev/wm-syskeydown) if either Alt is held down or if it is F10; otherwise, it is `WM_KEYDOWN`.
- An optional function which can be set on the `MockedInput` handler can be used to test for the number of times a key event is received by the system with a particular condition using [`sendVirtualInputCallCondition`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/test/MockedInput.cpp#L48-L52).
- The hook logic for a low level hook which returns 0 or 1 can be set on the `MockedInput` handler such that it behaves like a low level hook would behave with actual keyboard input. If the method returns 1, then the keyboard state is not updated, and if it returns 0 the corresponding key event is used to update the key state. This works in the recursive way as well similar to low level hooks, as `SendVirtualInput` can be called from within the hook, thus simulating identical behavior to calling `SendInput` in a low level hook (as soon as SendInput is called, the low level hook is called for the new input event, and only after those are processed it returns back to the current event, check this [blog](https://devblogs.microsoft.com/oldnewthing/20140213-00/?p=1773) for more details).
- For updating the keyboard state, KEYUP messages result in the state for that key code being set to false, and KEYDOWN result in the state for that key code being set to true.
- For modifiers the behavior is slightly different as if the key state of the L/R version is modified, it should also modify the common version, and if a common version is released, it should release both the L and R versions.
### Tests for single key remaps and shortcut remaps
Using the MockedInput handler, all the expected (and known) key scenarios that can occur for while pressing a [remapped key](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/test/SingleKeyRemappingTests.cpp) or [remapped shortcut](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/test/OSLevelShortcutRemappingTests.cpp) are tested. The foreground app behavior which is specific to app-specific shortcuts is tested in [AppSpecificShortcutRemappingTests.cpp](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/test/AppSpecificShortcutRemappingTests.cpp).
@@ -0,0 +1,197 @@
# Keyboard Manager module
This file contains the documentation for the KeyboardManager PowerToy module which is called by the runner.
## Table of Contents:
- [Keyboard Manager module](#keyboard-manager-module)
- [Table of Contents:](#table-of-contents)
- [Class members](#class-members)
- [Enable/Disable](#enabledisable)
- [Settings format](#settings-format)
- [Loading settings](#loading-settings)
- [Low level keyboard hook handler](#low-level-keyboard-hook-handler)
- [HandleKeyboardHookEvent](#handlekeyboardhookevent)
- [Custom Action to launch KBM UI](#custom-action-to-launch-kbm-ui)
- [SendInput Special Scenarios](#sendinput-special-scenarios)
- [Extended keys](#extended-keys)
- [Scan code](#scan-code)
- [Special Scenarios](#special-scenarios)
- [Dummy key events](#dummy-key-events)
- [Suppressing Num Lock in a keyboard hook](#suppressing-num-lock-in-a-keyboard-hook)
- [Modifier-Caps Lock interaction on Japanese IME keyboards](#modifier-caps-lock-interaction-on-japanese-ime-keyboards)
- [UIPI Issues (not resolved)](#uipi-issues-not-resolved)
- [Other remapping approaches](#other-remapping-approaches)
- [Registry approach](#registry-approach)
- [Driver approach](#driver-approach)
- [Telemetry](#telemetry)
## Class members
The `KeyboardManager` module has [3 main class members](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L54-L61):
- A static pointer to the current object of `KeyboardManager`. This is required for using the `KeyboardManager` object in the low level keyboard hook handler as that method must be static. This is described in more detail in [this section](#Low-level-keyboard-hook-handler).
- An object of type `Input`, which is used for all the operations that involving getting or setting keyboard states. This is wrapped in an object to allow testing the remapping methods.
- An object of type `KeyboardManagerState`. This object contains all the data related to remappings and is also used in the sense of a [View Model as it used to communicate common data that is shared between the KBM UI and the backend](keyboardmanagercommon.md#keyboardmanagerstate).
## Enable/Disable
On enabling KBM, the low level keyboard hook is started, and it is unhooked on disable. This is done to allow users to manually restart KBM if some other application which registers a keyboard hook was launched after PowerToys, so that it can be brought back to the highest priority hook (as the [last hook to be registered receives the input first](https://learn.microsoft.com/windows/win32/winmsg/about-hooks#hook-procedures)).
In addition to stopping the hook, any active KBM UI windows are also closed on disabling. This is done because the KBM UI uses the same keyboard hook for the Type button where you can type a key/shortcut, so if KBM is disabled the windows would not be completely functional.
The [enable/disable code can be found in dllmain.cpp](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L301-L322)
## Settings format
KBM uses two sets of settings files.
- The main `settings.json` is of the following format:
{
"properties":
{
"activeConfiguration":
{
"value":"default"
},
"keyboardConfigurations":
{
"value":["default"]
}
},
"name":"Keyboard Manager",
"version":"1"
}
- The `activeConfiguration` attribute stores the current remapping profile, while `keyboardConfigurations` stores all the profiles that the user has. This was added to avoid any future breaking changes for the [profiles feature](https://github.com/microsoft/PowerToys/issues/1881), which would allow users to switch between remappings
- The profile format (`default.json`) is of the following format:
{
"remapKeys":
{
"inProcess":
[
{
"originalKeys":"91",
"newRemapKeys":"162;70"
},
{
"originalKeys":"92",
"newRemapKeys":"162;70"
}
]
},
"remapShortcuts":
{
"global":
[
{
"originalKeys":"164;37",
"newRemapKeys":"162;65"
},
{
"originalKeys":"162;68",
"newRemapKeys":"91"
}
],
"appSpecific":
[
{
"originalKeys":"91;162;65",
"newRemapKeys":"162;86",
"targetApp":"msedge"
}
]
}
}
- `originalKeys` stores the key/shortcut which is to be pressed for the remap, and `newKeys` stores the key/shortcut which is to be executed.
- Both contain semi-colon separated virtual key codes. For `remapKeys`, `originalKeys` must have only one key code, whereas for `remapShortcuts` it must have at least two key codes.
- `inProcess` sub-key was added in `remapKeys` because there was a possibility of adding the registry based remapping approach (used by [SharpKeys](https://github.com/randyrants/sharpkeys)), so that would be under a separate sub-key while `inProcess` would be for keyboard hook based remaps. This was deprioritized as there weren't enough requests for it.
- `remapShortcuts` is split into `global` and `appSpecific`, where `global` remaps would apply to all applications, whereas `appSpecific` would apply on when the `targetApp` is in focus. `targetApp` must be the process name of the app (with or without its extension), e.g. `msedge` or `msedge.exe` for Microsoft Edge.
## Loading settings
KBM settings are loaded only on the C++ side only at start up, in the [constructor](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L67-L68). The settings file may get modified from the KBM UI on applying new remappings, but the file is not read again. The files are read from the PowerToys Settings process whenever a change is made to the file (using a FileWatcher) or whenever the KBM page is opened. The settings are updated only when the user presses the OK button from either of the Remap Keys or Remap Shortcuts windows. This is described in more detail [keyboardmanagerui: OK and Cancel button](keyboardmanagerui.md#ok-and-cancel-button).
## Low level keyboard hook handler
Since the [`hook_proc`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L330-L349) cannot be a member function in the class, this is declared `static` and a `static pointer` to the `KeyboardManager` project is used ([`keyboardmanager_object_ptr`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L54-L55)).
As seen in the code for `hook_proc`, similar to other keyboard hooks in PowerToys it consists of a main method `HandleKeyboardHookEvent` which computes whether the key needs to be suppressed and accordingly returns 1 or calls the `CallNextHook` method.
`HandleKeyboardHookEvent` is covered in the [next section](#HandleKeyboardHookEvent). The `SetNumLockToPreviousState` code in the above snippet is required for a special scenario with keyboard input, which is covered in [this section](#Suppressing-Num-Lock-in-a-keyboard-hook).
## HandleKeyboardHookEvent
The [`HandleKeyboardHookEvent`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L384-L458) is the method which calls the corresponding remapping methods in the required order. The following checks are executed in order:
- **`KeyboardManagerState.AreRemappingsEnabled`:** This returns false while the KBM remap tables are getting updated. If it is in this state, `HandleKeyboardHookEvent` returns `0`, i.e. the key event is not suppressed and is forwarded normally.
- **Check for `KEYBOARDMANAGER_SUPPRESS_FLAG`:** If the key event has the suppress flag, the method returns 1 to suppress the key event.
- **[`KeyboardManagerState.DetectSingleRemapKeyUIBackend`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L399-L408):** This method is used for handling hook operations for the single key Type UI in the Remap keys window. If the Remap keys window is open, then `HandleKeyboardHookEvent` returns `0` and the key event is forwarded normally. If the left column Type button is clicked on the Remap keys window and the window is in focus, then the key event is suppressed and the UI is updated with the latest key from the recent key events. This method is described in more detail in [DetectSingleRemapKeyUIBackend and DetectShortcutUIBackend](keyboardmanagercommon.md#DetectSingleRemapKeyUIBackend-and-DetectShortcutUIBackend).
- **[`KeyboardManagerState.DetectShortcutUIBackend(data, true)`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L410-L419):** This method is used for handling hook operations for the shortcut Type UI in the Remap keys window (when `isRemapKey` arg is `true`). If the Remap keys window is open, then `HandleKeyboardHookEvent` returns `0` and the key event is forwarded normally. If the right column Type button is clicked on the Remap keys window and the window is in focus, then the key event is suppressed and the UI is updated with the shortcut from the recent key events. This method is also described in more detail in [DetectSingleRemapKeyUIBackend and DetectShortcutUIBackend](keyboardmanagercommon.md#DetectSingleRemapKeyUIBackend-and-DetectShortcutUIBackend).
- **`HandleSingleKeyRemapEvent`:** This method handles the single key remap logic. If a remapping takes place, the key event is suppressed. This method is described in more detail in [HandleSingleKeyRemapEvent](keyboardeventhandlers.md#HandleSingleKeyRemapEvent).
- **[`KeyboardManagerState.DetectShortcutUIBackend(data, false)`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L430-L439):** This method is used for handling hook operations for the shortcut Type UI in the Remap shortcuts window (when `isRemapKey` arg is `false`). If the Remap shortcuts window is open, then `HandleKeyboardHookEvent` returns `0` and the key event is forwarded normally. If the Type button is clicked on the Remap shortcuts window and the window is in focus, then the key event is suppressed and the UI is updated with the shortcut from the recent key events. **Since this is executed after the single key remap method, all single key remappings are applied when the user is on the Remap shortcuts window.**
- **`HandleAppSpecificShortcutRemapEvent`:** This method handles the app-specific shortcut remap logic. If a remapping takes place, the key event is suppressed. This method is described in more detail in [HandleAppSpecificShortcutRemapEvent](keyboardeventhandlers.md#HandleAppSpecificShortcutRemapEvent). **Since this is executed after the single key remap method, single key remappings have precedence over shortcut remaps and are correspondingly reflected in shortcut remaps.**
- **`HandleOSLevelShortcutRemapEvent`:** This method handles the global shortcut remap logic. If a remapping takes place, the key event is suppressed. This method is described in more detail under [HandleOSLevelShortcutRemapEvent](keyboardeventhandlers.md#HandleOSLevelShortcutRemapEvent). The app-specific remap method is executed before this because if a shortcut is remapped to different keys/shortcuts for a particular app and globally, the app-specific variant should be preferred if that app is in focus. **Since this is executed after the single key remap method, single key remappings have precedence over shortcut remaps and are correspondingly reflected in shortcut remaps.**
**Note:** Single key remaps need to be executed before shortcut remaps, because otherwise there can be several logical issues. For example if a user has Ctrl remapped to X and Ctrl+A remapped to Y, we can't detect Ctrl+A because the moment Ctrl is pressed it would be remapped to X before the system ever sees Ctrl+A. This is why the design decision was made to separate Remap keys and Remap shortcuts, and all key remaps are reflected in the shortcut remaps.
## Custom Action to launch KBM UI
KBM uses the [`call_custom_action`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L249-L280) method from the `PowertoyModuleIface` in order to launch the KBM UI when the user clicks "Remap a key" or "Remap a shortcut" from the KBM settings page. On clicking the button, we check if there is already any active KBM UI window, and if there is it is brought to the foreground. If not, the corresponding KBM UI window is launched on a separate detached thread. The UI is described in more detail in [Keyboard Manager UI](keyboardmanagerui.md).
## SendInput Special Scenarios
### Extended keys
Certain keys such as the arrow keys, <kbd>right Ctrl/Alt</kbd>, and <kbd>Del/Home/Ins</kbd>, etc. need to be sent with the `KEYEVENTF_EXTENDEDKEY` flag because otherwise the NumPad versions get sent, which can cause weird behavior when NumLock is on. The code can be found where [`SetKeyEvent` checks `IsExtendedKey(keyCode)`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/Helpers.cpp#L190-L194) and the list of extended keys in code can be found in [`IsExtendedKey`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/Helpers.cpp#L73-L98). Docs about extended keys can be found in [Keyboard Input Overview: Extended-Key Flag
](https://learn.microsoft.com/windows/win32/inputdev/about-keyboard-input#extended-key-flag).
The weird behavior that is caused by this can be found at these issues:
- https://github.com/microsoft/PowerToys/issues/3478
- https://github.com/microsoft/PowerToys/issues/3647
- https://github.com/microsoft/PowerToys/issues/3981
### Scan code
Certain applications (such as Windows Terminal) may filter out key events which are set to scan code 0. Even though the `KEYEVENTF_SCANCODE` flag is not set, the `wScan` field is still sent, which defaults to 0. To avoid this issue we use the `MapVirtualKey` API to find the scan code from the virtual key code. Code can be found in [`SetKeyEvent`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/Helpers.cpp#L196-L198).
## Special Scenarios
Since we are using low level keyboard hooks and not actual OS level input handling certain scenarios with input require workarounds as do they not interact well with the OS input logic directly. These are covered in the sub-sections below.
### Dummy key events
To prevent the behavior that some modifiers have that occur when you press and release the modifier without pressing any key in between, we need to send a dummy key event in between the two states. Some examples of this behavior are Win key for Start Menu and Alt key to focus the menu bar. We need to send the dummy key events at any point where an unintentional modifier press/release sequence may occur. We use the undocumented `0xFF` virtual key code for this as we haven't found any side effects of using this key code yet. Initially we used only a key up message, but it has been tweaked now to send a key down followed by key up (code can be found (here)[https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/Helpers.cpp#L201-L208]), as without the key down there were [compatibility issues with some apps](https://github.com/microsoft/PowerToys/issues/7133) (like Slack).
The dummy key event is currently used in the following places (the linked code snippets contains an example scenario of why it is required):
- https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp#L251-L253
- https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp#L289-L291
- https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp#L382-L383
- https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp#L409-L410
- https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp#L509-L510
### Suppressing Num Lock in a keyboard hook
The <kbd>Num Lock</kbd> key state is updated by the OS before it is intercepted by low level hooks. This causes the issue that even if you suppress a <kbd>Num Lock</kbd> key event, <kbd>Num Lock</kbd> will still get toggled. In order to work around this, in the [`hook_proc`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L340-L344) whenever we suppress a <kbd>Num Lock</kbd> key down event, we send an additional <kbd>Num Lock</kbd> key up followed by key down so that the <kbd>Num Lock</kbd> state is reverted to its previous value before the suppressed event. These are sent with a `KEYBOARDMANAGER_SUPPRESS_FLAG` in the `dwExtraInfo` field, so that we suppress them at the start of the hook (see code in [`SetNumLockToPreviousState`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp#L811-L825)). Since these events will update the <kbd>Num Lock</kbd> state before the low level hooks, by suppressing them we ensure that these are not sent to any other hooks/applications and hence are only processed by the OS.
This assumes that KBM is the last hook to be registered (since another hook-based app like AutoHotkey could remap NumLock to some other key which could mess up this logic).
### Modifier-Caps Lock interaction on Japanese IME keyboards
While using Japanese IME on Windows, shortcuts like <kbd>Shift/Alt/Ctrl</kbd> + <kbd>Caps Lock</kbd> can be used to switch IME options.
![japanese-ime](japanese-ime.png)
These shortcuts are detected before low level hooks, and hence cause issues while remapping <kbd>Caps Lock</kbd> to <kbd>Shift/Alt/Ctrl</kbd> or vice-versa, as there could be an intermediate state where the system detects both the keys as being pressed. This results in a state where the modifier key does not get released since the OS suppresses the key up messages before they reach the low level hooks.
In order to work around this when a key down for the modifier is being processed, we send a key up for the modifier key with the `KEYBOARDMANAGER_SUPPRESS_FLAG` in the `dwExtraInfo` field, so that we suppress them at the start of the hook, and this key event would only be processed by the OS, without getting forwarded to other hooks/apps. The approach is described in more detail at [this comment](https://github.com/microsoft/PowerToys/issues/3397#issuecomment-640136416), as discussed with the AutoHotkey team. The code for the workaround can be found in [`ResetIfModifierKeyForLowerLevelKeyHandlers`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp#L827-L846). Tests for these scenarios have also been added at:
- [Tests for workaround on single key remaps](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/test/SingleKeyRemappingTests.cpp#L110-L219)
- [Tests for workaround on shortcut remaps](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/test/OSLevelShortcutRemappingTests.cpp#L1935-L2144)
For example, while [remapping <kbd>Ctrl</kbd> to <kbd>Caps Lock</kbd>](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp#L59-L63), before sending the <kbd>Caps Lock</kbd> key down message and suppressing <kbd>Ctrl</kbd>, we send a suppressed <kbd>Ctrl</kbd> key up, so that the OS doesn't see <kbd>Ctrl</kbd> and <kbd>Caps Lock</kbd> pressed together at any point. For the [<kbd>Caps Lock</kbd> to <kbd>Ctrl</kbd> scenario](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp#L104-L116), we send a suppressed C<kbd>Ctrl</kbd>trl key up message after sending <kbd>Ctrl</kbd> key down before <kbd>Caps Lock</kbd> suppressed. Similar logic is added for such scenarios in shortcut remapping.
While the above work around fixes most of the cases, there are still some scenarios where the modifier can get stuck, mentioned at this [comment](https://github.com/microsoft/PowerToys/issues/3397#issuecomment-663729278), which is why the issue is still open. This occurs if a modifier is pressed after the remap has been invoked before releasing the remapped key and it is a harder scenario to solve which requires refactoring the single key remap code.
### UIPI Issues (not resolved)
`SendInput` does not work directly with certain key codes such as Play/Pause Media, Calculator key, etc. as it requires UAC privileges to be injected to the OS and accordingly play the active media app or launch the Calculator app. In order to resolve this the correct approach is that the executable which calls `SendInput` needs to have the [UIAccess flag](https://learn.microsoft.com/windows/win32/winauto/uiauto-securityoverview) set to true, which will also avoid the requirement of KBM having to run as administrator to intercept key events when an elevated window is in focus. The UIAccess flag has many constraints such as it must be a signed executable and must be located in a protected path like Program Files. Since KBM currently runs out of the runner process, it would make more sense to do this work after KBM is moved to a separate executable, and it could be enabled by a separate toggle in settings only if PowerToys is installed in Program Files. [This comment](https://github.com/microsoft/PowerToys/issues/3192#issuecomment-646323661) has more details on this approach and (this)[https://github.com/microsoft/PowerToys/issues/3255] is the tracking issue.
## Other remapping approaches
Other approaches for remapping which were deprioritized are:
### Registry approach
This method is used by [SharpKeys](https://github.com/randyrants/sharpkeys) and involves using the [Microsoft Keyboard Scancode mapper registry key](https://github.com/randyrants/sharpkeys) to remap keys based on their scan codes. This has the advantage of being applied in all scenarios and not facing any elevation or UAC issues, however the disadvantages are that for modifying the settings, the process must run elevated (as it modifies HKLM registry) and it requires a reboot to get applied. Another issue which is an advantage/disadvantage for users is that the process does not need to be running, so the remaps are applied all the time, including at the password prompt on logging into the user's Windows account, which could get a user stuck if they orphaned a key in their password. This registry doesn't have any support for remapping shortcuts either, so the hook approach was prioritized over this.
### Driver approach
Using a driver approach has the benefit of not depending on precedence orders as KBM could always run before low level hooks, and it also has the benefit of differentiating between different keyboards, allowing [multi keyboard-specific remaps](https://github.com/microsoft/PowerToys/issues/1460). The disadvantages are however that any bug or crash could have system level consequences. [Interception](https://github.com/oblitum/Interception) is an open source driver that could be used for implementing this. The approach was deprioritized due to the potential side effects.
## Telemetry
Keyboard Manager emits the following telemetry events (implemented in [trace.h](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/common/trace.h) and [trace.cpp](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/common/trace.cpp)):
- **`KeyboardManager_EnableKeyboardManager`:** Logs a `boolean` value storing the KBM toggle state. It is logged whenever KBM is enabled or disabled (emitted in [`enable`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L305-L306) and [`disable`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L315-L316)).
- **`KeyboardManager_KeyRemapCount`:** Logs the number of key to key and key to shortcut remaps (i.e. all the remaps on the "Remap a key" window). This gets logged on saving new settings in the "Remap a key" window (emitted at [the end of `ApplySingleKeyRemappings`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/LoadingAndSavingRemappingHelper.cpp#L159-L163)).
- **`KeyboardManager_OSLevelShortcutRemapCount`:** Logs the number of global shortcut to shortcut and shortcut to key remaps. This gets logged on saving new settings in the "Remap a shortcut" window (emitted at [the end of `ApplyShortcutRemappings`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/LoadingAndSavingRemappingHelper.cpp#L220)).
- **`KeyboardManager_AppSpecificShortcutRemapCount`:** Logs the number of app-specific shortcut to shortcut and shortcut to key remaps. This gets logged on saving new settings in the "Remap a shortcut" window (emitted [after calling `OSLevelShortcutRemapCount` in `ApplyShortcutRemappings`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/LoadingAndSavingRemappingHelper.cpp#L221)).
- **`KeyboardManager_Error`:** Logs the occurrence of an error in KBM with the name of the method, error code and the corresponding error message. This is currently used only for logging `SetWindowsHookEx` failures (emitted [at the end of `start_lowlevel_keyboard_hook`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L364-L369)).
@@ -0,0 +1,63 @@
# Keyboard Manager Common
This project contains any code that is to be shared between the backend and UI projects. This file covers any functionality in this project which hasn't been covered along with the other modules.
## Table of Contents
1. [KeyboardManagerState](#KeyboardManagerState)
1. [UI States](#UI-States)
2. [DetectSingleRemapKeyUIBackend and DetectShortcutUIBackend](#DetectSingleRemapKeyUIBackend-and-DetectShortcutUIBackend)
3. [HandleKeyDelayEvent](#HandleKeyDelayEvent)
4. [Saving remappings to file](#Saving-remappings-to-file)
5. [Concurrent Access to remap tables](#Concurrent-Access-to-remap-tables)
2. [KeyDelay](#KeyDelay)
3. [Shortcut and RemapShortcut classes](#Shortcut-and-RemapShortcut-classes)
1. [IsKeyboardStateClearExceptShortcut](#IsKeyboardStateClearExceptShortcut)
2. [CheckModifiersKeyboardState](#CheckModifiersKeyboardState)
3. [Tests](#Tests)
4. [Helpers](#Helpers)
1. [Foreground App Detection](#Foreground-App-Detection)
## KeyboardManagerState
[This class](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/common/KeyboardManagerState.cpp) stores all the data related to remappings and is also used in the sense of a View Model as it used to communicate common data that is shared between the KBM UI and the backend. They are accessed on the UI controls using static class members of `SingleKeyRemapControl` and `ShortcutControl`.
### UI States
[UI states](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/KeyboardManagerState.h#L27-L42) are used to keep track in which step of the UI flow is the user at, such as which Remap window they are on, or if they have one of the Type windows open. This is required because the hook needs to suppress input and update UI in some cases, and in some cases remappings have to be disabled altogether.
### DetectSingleRemapKeyUIBackend and DetectShortcutUIBackend
[These methods](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/KeyboardManagerState.cpp#L374-L446) are [called on the low level hook](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L399-L408) in the main keyboard event handler. When the user opens any UI window the UI states are updated and in this case some remappings have to be disabled. On the Remap keys window, all remappings are disabled, while on the Remap shortcuts window, shortcut remappings are disabled.
In addition to this, if the user has opened the Type window, and the window is in focus, [whenever a key event is received we have to update the set of selected keys in the TextBlock](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/KeyboardManagerState.cpp#L266-L329) in the ContentDialog. These methods also call the `KeyDelay` handlers to check if the input is Esc/Enter and accordingly handle the Accessibility events for the Type window. When the user clicks the Type button, [variables in the KeyboardManagerState store the corresponding TextBlocks](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/SingleKeyRemapControl.cpp#L375-L376) which appear in the ContentDialog, so that these UI controls can be updated from the hook on the dispatcher thread.
### HandleKeyDelayEvent
[This method](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/KeyboardManagerState.cpp#L482-L498) checks if the UI is in the foreground, and if so runs the key delay handlers that have been registered.
### Saving remappings to file
The [`SaveConfigToFile`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/KeyboardManagerState.cpp#L500-L607) method is called on clicking the OK button on the EditKeyboardWindow or EditShortcutsWindow. Since PowerToys Settings also reads the config JSON file, [a named mutex is used](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/KeyboardManagerState.cpp#L582-L600) before accessing the file, with a 1 second timeout. If the mutex is obtained the settings are written to the default.json file.
### Concurrent Access to remap tables
To prevent the UI thread and low level hook thread from concurrently accessing the remap tables we use an [`atomic bool` variable](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/KeyboardManagerState.h#L91-L92), which is set to `true` while the tables are getting updated. When this is `true` the hook will skip all remappings. Use of mutexes in the hook were removed to prevent reentrant mutex bugs.
## KeyDelay
The [KeyDelay class](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyDelay.cpp) implements a queue based approach for processing key events and based on the time difference between key down and key up events [executes separate methods for `ShortPress`, `LongPress` or `LongPressReleased`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/KeyDelay.h#L69-L72). The class is used for the hold Enter/Esc functionality required for making the Type window accessible and prevent keyboard traps (see [this call to `keyboardManagerState.RegisterKeyDelay`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/SingleKeyRemapControl.cpp#L273-L292) for an example of its usage). The `KeyEvents` are added to the queue from the hook thread of KBM, and a separate [`DelayThread`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/KeyDelay.cpp#L142-L166) is used to process the key events by checking the `time` member in the key event. The thresholds for short vs long press and hold wait timeouts are `static` constants, but if the module is extended for other purposes these could be made into arguments.
**Note:** [Deletion of the `KeyDelay`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/KeyDelay.cpp#L4-L12) object should never be called from the `DelayThread` i.e. from within one of the 3 handlers, as it can re-enter the mutex and would lead to a deadlock. This can be avoided by either deleting it on a separate thread or as done in the KBM UI, on the dispatcher thread. See [this PR](https://github.com/microsoft/PowerToys/pull/6959#issue-496583547) for more details on this issue.
## Shortcut and RemapShortcut classes
The [`Shortcut` class](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/common/Shortcut.h) is a data structure for storing key combinations which are valid shortcuts and it contains several methods which are used for shortcut specific operations. [`RemapShortcut`](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/common/RemapShortcut.h) consists of a shortcut/key union (`std::variant`), along with other boolean flags which are required on the hook side for storing any relevant keyboard states mid-execution.
### IsKeyboardStateClearExceptShortcut
[This method](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/Shortcut.cpp#L665-L813) is used by the `HandleShortcutRemapEvent` to check if any other keys on the keyboard have been pressed apart from the keys in the shortcut. This is required because shortcut to shortcut remaps should not be applied if the shortcut is pressed with other keys. The method iterates over all the possible key codes, except any keys that are considered reserved, unassigned, OEM-specific or undefined, as well as mouse buttons (see [list in `IgnoreKeyCode`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/Shortcut.cpp#L628-L663)).
### CheckModifiersKeyboardState
[This method](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/Shortcut.cpp#L517-L614) uses `GetVirtualKeyState` (internally calls `GetAsyncKeyState` in production code), to check if all the modifiers of the current shortcut are being pressed. Since Win doesn't have a non-L/R key code we check this by checking both LWIN and RWIN.
### Tests
Tests for some methods in the `Shortcut` class can be found in [`OSLevelShortcutRemappingTests.cpp`](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/KeyboardManagerEngineTest/OSLevelShortcutRemappingTests.cpp) and [`AppSpecificShortcutRemappingTests.cpp`](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/KeyboardManagerEngineTest/AppSpecificShortcutRemappingTests.cpp).
## Helpers
[This namespace](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/common/Helpers.cpp) has any methods which are used across either UI or the backend which aren't specific to either. Some of these methods have tests in [`SetKeyEventTests.cpp`](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/KeyboardManagerEngineTest/SetKeyEventTests.cpp).
### Foreground App Detection
[`GetCurrentApplication`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/Helpers.cpp#L226-L268) is used for detecting the foreground process for App-specific shortcuts. The logic is very similar to that used for FZ's app exception feature, involving `GetForegroundWindow` and `get_process_path`. The one additional case which has been added is for full-screen UWP apps, where the above method fails and returns `ApplicationFrameHost.exe`. The [`GetFullscreenUWPWindowHandle`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/Helpers.cpp#L210-L224) uses `GetGUIThreadInfo` API to find the window linked to the GUI thread. This logic is based on [this stackoverflow answer](https://stackoverflow.com/questions/39702704/connecting-uwp-apps-hosted-by-applicationframehost-to-their-real-processes/55353165#55353165).
**Note:** The [`GetForegroundProcess` method](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/Input.cpp#L17-L21) performs string allocation in a weird way because of exceptions that were occurring while running tests as a result of memory being allocated or deallocated across dll boundaries. Here's the comment from the PR where this was added
> To make app-specific logic test-able, a GetForegroundProcess was added to the input interface which internally calls GetCurrentApplication. This allows us to mock this method in the test project by just setting some process name as the foreground process for that function. When I set this to just return the string name, it would goes runtime errors on the test project in debug_heap with `__acrt_first_block == header`. Based on [this stackoverflow answer](https://stackoverflow.com/a/35311928), this would happen if allocation happens in one dll's code space and deallocation happens in another. One way to avoid this is to change both the projects to MD (multi threaded dll) instead of MT(multi threaded), however that results in many compile-time errors since all the PT projects are configured as MT. To solve this, the GetForegroundProcess was rewritten such that its argument is the output variable, and we allocate memory for that string within the AppSpecificHandler method rather than in that function.
@@ -0,0 +1,114 @@
# Keyboard Manager UI
## Table of Contents:
1. [XAML Implementation](#xaml-implementation)
2. [UI Structure](#ui-structure)
3. [EditKeyboardWindow / EditShortcutsWindow](#editkeyboardwindow--editshortcutswindow)
1. [OK and Cancel button](#ok-and-cancel-button)
2. [Delete button](#delete-button)
3. [Handling common modifiers in EditKeyboardWindow](#handling-common-modifiers-in-editkeyboardwindow)
4. [SingleKeyRemapControl](#singlekeyremapcontrol)
5. [ShortcutControl](#shortcutcontrol)
6. [KeyDropDownControl](#keydropdowncontrol)
1. [Localized key names](#localized-key-names)
2. [Single Key ComboBox Selection Handler](#single-key-combobox-selection-handler)
3. [Shortcut ComboBox Selection Handler](#shortcut-combobox-selection-handler)
## XAML Implementation
The KBM UI was originally implemented as a XAML Island, but in order to easily support Mica, which is [still an issue](https://github.com/microsoft/microsoft-ui-xaml/issues/5319), the [`XamlBridge`](https://github.com/microsoft/PowerToys/blob/b3f27057d43445abc59aa04405f7c24bb895a61c/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/XamlBridge2.cpp) was rewritten to use a [`FrameworkView`](https://github.com/microsoft/PowerToys/blob/b3f27057d43445abc59aa04405f7c24bb895a61c/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/XamlBridge2.cpp#L47C11-L49) object which is how a traditional UWP app behaves:
1. A `CoreWindow` is created by [calling a function inside of `Windows.UI.dll` with an ordinal number of `1500`](https://github.com/microsoft/PowerToys/blob/b3f27057d43445abc59aa04405f7c24bb895a61c/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/XamlBridge2.cpp#L35-L42).
2. A [stubbed implementation of `CoreApplicationView`](https://github.com/microsoft/PowerToys/blob/b3f27057d43445abc59aa04405f7c24bb895a61c/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/XamlBridge2.cpp#L10-L18) was created.
3. Then [both objects are passed on to the `FrameworkView`](https://github.com/microsoft/PowerToys/blob/b3f27057d43445abc59aa04405f7c24bb895a61c/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/XamlBridge2.cpp#L47-L49) to initialize the XAML framework.
4. Lastly, the `CoreWindow` is [attached to the editor window](https://github.com/microsoft/PowerToys/blob/b3f27057d43445abc59aa04405f7c24bb895a61c/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/XamlBridge2.cpp#L54-L61) and its `HWND` is used in-place of `DesktopWindowXamlSource`'s.
Mica is then achieved by calling [`BackdropMaterial::SetApplyToRootOrPageBackground()`](https://github.com/microsoft/PowerToys/blob/b3f27057d43445abc59aa04405f7c24bb895a61c/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditKeyboardWindow.cpp#L388-L400) in both of the editor windows, or falls back to the `ApplicationPageBackgroundThemeBrush` background if Mica isn't available.
The UI was also updated to use WinUI 2.8 to match the look and feel of the Fluent design language of Windows 11 and the rest of PowerToys. There has been talk about [migrating the implementation to XAML files instead of code-behind](https://github.com/microsoft/PowerToys/issues/2027) and [utilizing WinUI 3 going forward](https://github.com/microsoft/PowerToys/issues/15870). More about the update can be read in [[Keyboard Manager] Modernize the editor UI - PR#28473](https://github.com/microsoft/PowerToys/pull/28473).
[**Link to the original documentation**](https://github.com/microsoft/PowerToys/blob/b3f27057d43445abc59aa04405f7c24bb895a61c/doc/devdocs/modules/keyboardmanager/keyboardmanagerui.md#c-xaml-islands)
## UI Structure
The KBM UI consists of a [`Grid` with several columns](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/EditKeyboardWindow.cpp#L200-L218). Rows are added dynamically when [the add button is pressed](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/EditKeyboardWindow.cpp#L305-L309). [A vector of vector of unique pointers to `SingleKeyRemapControl`/`ShortcutControl`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/EditKeyboardWindow.cpp#L248-L249) is created so that references to the UI components and their data are not lost until the window is closed. [`SingleKeyRemapControl`](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/ui/SingleKeyRemapControl.cpp) is the UI class for each row of the Remap keys table, and [`ShortcutControl`](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/ui/ShortcutControl.cpp) is the UI class for each row of the Remap shortcuts table. [`KeyDropDownControl`](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/ui/KeyDropDownControl.cpp) is used for handling the ComboBox operations. Each of these two classes [have vectors of unique pointers to the `KeyDropDownControl` objects](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/ShortcutControl.h#L44-L45) so that references to the objects are active until the control is deleted.
When the UI windows are activated the `KeyboardManagerState` object [sets the `UIState` variable](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/EditKeyboardWindow.cpp#L251-L252) which is used for distinguishing if the UI is up from the keyboard hook thread. The [states are also updated](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/SingleKeyRemapControl.cpp#L53) on opening and closing the Type window.
Clicking the Type Button [opens a content dialog](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/SingleKeyRemapControl.cpp#L206-L380) which registers key delays using the [`KeyDelay` class](keyboardmanagercommon.md#KeyDelay) for Enter and Esc keys and sets the UI states such that when a key event occurs the TextBlocks on the ContentDialog are updated accordingly. On accepting the dialog, the selected keys are copied into the ComboBoxes from the TextBlocks, and on closing the window, the key delays are unregistered and UI states are reset.
Since ComboBoxes are added dynamically, handlers have been added which [update the accessible names for these controls](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/KeyDropDownControl.cpp#L69-L74), which get executed whenever a drop down is added or removed.
When the `EditKeyboardWindow`/`EditShortcutsWindow` is created, [we iterate through the remappings](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/EditKeyboardWindow.cpp#L254-L262) stored in `KeyboardManagerState` and add rows to the UI Grid. For both the windows we have `static` buffers [`singleKeyRemapBuffer`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/SingleKeyRemapControl.h#L39-L40) and [`shortcutRemapBuffer`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/ShortcutControl.h#L42-L43) which store the corresponding key/shortcuts as per the selections in the UI if they are valid with no warnings.
## EditKeyboardWindow / EditShortcutsWindow
### OK and Cancel button
[On pressing the OK button](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/EditKeyboardWindow.cpp#L66-L89) in `EditKeyboardWindow`, first the [`CheckIfRemappingsAreValid` method](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/LoadingAndSavingRemappingHelper.cpp#L10-L44) is executed which performs basic validity checks on the current remappings in the remap buffer (`static SingleKeyRemapControl::singleKeyRemapBuffer`), such as if there are no NULL columns and none of the source keys are repeated. All other validity checks are assumed to happen while the user adds the remapping. If this is found to be invalid a ContentDialog is displayed which shows that some remappings are invalid and if the user proceeds only the valid ones will be applied. If it is valid [`GetOrphanedKeys`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/LoadingAndSavingRemappingHelper.cpp#L46-L75) is executed which checks if any keys are orphaned (i.e. the key has been remapped and no other key has been remapped to it, so there is no way to send that key code), and a dialog is shown for notifying the user with a list of orphaned keys. After this the settings are [applied by adding it to the `KeyboardManagerState.singleKeyReMap` member](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/LoadingAndSavingRemappingHelper.cpp#L102-L164) and they are saved to the JSON file. `EditShortcutsWindow` differs slightly from this, as there is no orphaned keys check, and [on pressing OK](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/EditShortcutsWindow.cpp#L32-L47) both the global and app-specific shortcuts are validated and [updated](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/LoadingAndSavingRemappingHelper.cpp#L166-L223).
The code used for updating the remapping tables in `KeyboardManagerState` can be found in [KeyboardManagerState.cpp lines 104-183](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/KeyboardManagerState.cpp#L104-L183). For shortcut remaps, the `sortedKeys` vectors are updated and re-sorted whenever an element is added to them (like [this code in `KeyboardManagerState::AddOSLevelShortcut`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/KeyboardManagerState.cpp#L135-L136)).
On pressing OK (after confirmation dialogs) or Cancel, the window is closed and UI states are reset.
### Delete button
Since there is no single method to delete the elements in a row for a Grid, [the logic](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/SingleKeyRemapControl.cpp#L143-L188) we use involves decrementing the rowIndex for all the UI controls that appearing after the row to be deleted, and removing each of the items of the row from the Grid, followed by deleting that row definition. We also update the accessible names for all the rows since the indexing has changed. After this the corresponding row in the remap buffer is also deleted, and `SingleKeyRemapControl`/`ShortcutControl` objects are deleted from the vector.
### Handling common modifiers in EditKeyboardWindow
In the SingleKeyRemap table for a remapping of the form Ctrl->X, where Ctrl is the common version and not L/R, we can't store it directly as Ctrl->X because when the hook receives the key event it only gets LCtrl or RCtrl specifically and not `VK_CONTROL`. To simplify the backend code, when [single key remappings are applied](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/LoadingAndSavingRemappingHelper.cpp#L116-L143), any remapping of the form Ctrl->X is split into Ctrl(L)->X and Ctrl(R)->X (i.e. both L and R versions are remapped to the same target), and when remappings are loaded in EditKeyboardWindow, we [pre-process the remap table](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/LoadingAndSavingRemappingHelper.cpp#L77-L100) such that if the L and R versions of a modifier are remapped to the same key/shortcut we combine them. This also results in the behavior where a user adds LCtrl->X and RCtrl->X and after closing and re-opening KBM UI it appears combined as Ctrl->X.
## SingleKeyRemapControl
The left drop down column uses a single ComboBox and the Type button is linked to `createDetectKeyWindow`, whereas the right column is linked to `createDetectShortcutWindow` as the column can accept a key or a shortcut (required to support key to key and key to shortcut). The `KeyDropDownControl` for the left column in the window uses a smaller key list, without `None`.
## ShortcutControl
Both the columns in are linked to `createDetectShortcutWindow`, however the drop down selection handlers differ in their logic as the left column only allows shortcuts, and the drop downs do not contain the `Disable` key, whereas the right column allows you to select both shortcuts and keys (to support shortcut to shortcut and shortcut to key), and it allows selection of `Disable`.
For the app-specific shortcut target app text-box, we had to validate that the shortcut row is still valid when the target app is changed (for example, <kbd>Ctrl+A</kbd> is remapped for Chrome, and another remapping for <kbd>Ctrl+A</kbd> was remapped to Edge, but the target was changed to Chrome.). For this we didn't use the TextChanged handler as every time a letter is typed it would get executed. Instead we used the [`LostFocus` handler](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/ShortcutControl.cpp#L108-L167) which gets executed whenever you focus into the box (by clicking or tabbing) and then tabbing or clicking out. In the method we perform the same shortcut buffer validation used for selections in the drop down menus and update the buffers accordingly.
## KeyDropDownControl
Each ComboBox has a linked flyout, which is used to show warnings to the users whenever a user selects an invalid key from the drop down. When the warning is displayed the ComboBox is also reset to -1, i.e. no selection.
For selection handlers on the ComboBoxes we couldn't use just the SelectionChanged handler directly as it gets executed even on searching for elements in the drop down. Instead we used DropDownClosed (when a user opens the drop down and searches and selects something) and SelectionChanged when the drop down is not open (for setting selections programmatically or selection made by searching with tab focus on the drop down without opening it). This was required because if we execute the selection handlers while users are searching, it could cause false positive flyout warnings if the search causes an invalid value to be selected, and flyouts cause the drop down to close leading to bad UI experience.
### Localized key names
For getting localized key names and symbols for each virtual key code, whenever the key lists are accessed, i.e. [whenever the drop down is opened](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/KeyDropDownControl.cpp#L56-L60) or when `GetKeyName` is called in the Type window, the current `KeyboardLayout` is retrieved to ensure that the displayed key names are always updated. Since the `WM_INPUTLANGCHANGED` event was having some issues with XAML islands we weren't able to use this to update the keyboard layout. In addition to this we do not refresh the UI, so the key lists get updated only on opening/interacting with them.
### Single Key ComboBox Selection Handler
On making a selection in the drop down, [the selection handler](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/KeyDropDownControl.cpp#L91-L130) validates the input with the buffer from the other column and other rows. Error messages are shown using flyouts if the selection is not considered valid and the drop down and buffer for that entry are reset to empty selection. The errors that can occur on the single key ComboBox are:
- Remap to same key (A->A)
- Same key previously remapped (A->B and A->C)
- Conflicting modifier previously remapped (Ctrl->A and Ctrl(left)->B, since Ctrl also includes Ctrl(left))
If the selection is found to be valid, the `singleKeyRemapBuffer` is updated accordingly.
For handling `Shortcut` and key in the remap buffer for the right column, we use `std::variant`, which allows us to store either of the two types and check which one of them is present in the buffer by using the `index` method.
[`ValidateAndUpdateKeyBufferElement`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/BufferValidationHelpers.cpp#L8-L66) does not reference any UI components and instead takes all the relevant data as arguments. This method [has tests](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/test/BufferValidationTests.cpp) which covers all the cases that could arise from making selections on the UI.
### Shortcut ComboBox Selection Handler
On making a selection in the drop down, [the selection handler](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/KeyDropDownControl.cpp#L215-L295) validates the input with the buffer from the other column and other rows. Error messages are shown using flyouts if the selection is not considered valid and the drop down and buffer for that entry are reset to empty selection.
This differs from the Single Key ComboBox handler in the sense that after validating the current selection we may perform [a set of actions](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/KeyDropDownControl.cpp#L160-L204) such as:
- Adding a drop down (if a modifier is selected)
- Removing a drop down (if None is selected)
- Clearing terminal empty drop downs (if an action key is selected in a non-last drop down and the remaining ones are empty)
After performing the corresponding action, if any, we check if the drop down resulted in an error, in which case we do [a second level of validation](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/KeyDropDownControl.cpp#L224-L229) on all the drop downs in that list of drop downs. This is done because there can be cases where an error in one drop down results us setting it to empty, and the remaining selection is also invalid. For example, you have Ctrl+A -> Ctrl+Shift+A, and you change Shift to Ctrl. This would show a warning for having two Ctrls in the shortcut being invalid, after which setting that to empty would result in Ctrl+A->Ctrl+Empty+A, which is a case of remapping a shortcut to itself.
Once this second level of validation is done, we proceed with [updating the buffer](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/KeyDropDownControl.cpp#L231-L251). Depending on the number of drop downs with valid values, this could be either a key or a shortcut (for the right columns). We also [set the buffer value for the target app](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/KeyDropDownControl.cpp#L252-L267) while doing this.
Unlike the Single Key handler, there is a different set of errors that can occur here which are related to making a selection that is considered as a valid shortcut. The `isHybridControl` argument is used to distinguish between the differing behaviors for the two types of columns (shortcut only or shortcut/key column). The errors that can occur for this handler are:
- Shortcut must start with modifier (selecting A on the first drop for the left column is invalid)
- Shortcut can't have a repeated modifier (Ctrl+Ctrl(left)+A is not a shortcut)
- Shortcut can only have up to 2 modifiers (Ctrl+Shift+Alt is not supported as we have enforced a 3 key constraint (**not a backend limitation, there is [an issue](https://github.com/microsoft/PowerToys/issues/3936) requesting to remove this**))
- Shortcut must contain an action key (Ctrl+A and change A to None, only for left column)
- Shortcut must have at least two keys (Ctrl+A and change Ctrl to None, only for left column)
- Disable can't be a modifier or action key (Ctrl+Disable is invalid)
- Shortcut can't have more than one action key (Ctrl+Shift+A, change Shift to B)
- Remap to same shortcut(Ctrl+A->Ctrl+A)
- Same shortcut previously remapped for same target app (Ctrl+A->B and Ctrl+A->C)
- Conflicting shortcut previously remapped for same target app (Ctrl+A->B and Ctrl(left)+A->C, since Ctrl also includes Ctrl(left))
- Illegal shortcut remaps like Win+L or Ctrl+Alt+Del (since these cannot be remapped using LL hooks)
[`ValidateShortcutBufferElement`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/BufferValidationHelpers.cpp#L68-L304) does not reference any UI components and instead takes all the relevant data as arguments. This method [has tests](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/test/BufferValidationTests.cpp) which covers all the cases that could arise from making selections on the UI.
**Note:** After updating the buffer we have [code to handle a special case](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/KeyDropDownControl.cpp#L269-L279), which was required to prevent scenarios where a drop down can get deleted but the corresponding `KeyDropDownControl` object isn't deleted. The code checks if the drop down is still linked to the parent and accordingly deletes the `KeyDropDownControl` object from the vector.
**IgnoreKeyToShortcutWarning special case:**
[An additional](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/KeyDropDownControl.cpp#L177-L181) check was added to ignore the Map to Same key error when an existing remapping is loaded.
This was because a remapping like Ctrl->Ctrl+A has an intermediate step of Ctrl->Ctrl, which could lead to an error of invalid input, even though Ctrl+A is valid.
The only way to actually add this is from the Type button or by adding them in a different order (like typing Shift+A and then changing Shift to Ctrl).
Since the intermediate check could fail, this was causing the app to crash since the Xaml Island wouldn't be completely loaded at that point and the Flyout can't be displayed.
[Issue #6695](https://github.com/microsoft/PowerToys/issues/6695) is the linked issue which describes the repro scenario.
@@ -0,0 +1,48 @@
# Architecture
## Overview
`PowerToys Run` is a plugin-based .net core desktop application. It is written in WPF using `Model-View-ViewModel (MVVM)` structural design pattern. This article provides an overview of `PowerToys Run` architecture and introduces major components in the data flow.
Note : We refer to base application without plugins as `PowerLauncher`, which is same as the name of startup WPF project.
## UI
PowerToys Run UI is written in the WPF framework. The UI code is present in the Powerlauncher project and is spanned across three high-level components: [`MainWindow.xaml`](/src/modules/launcher/PowerLauncher/MainWindow.xaml), [`LauncherControl.xaml`](/src/modules/launcher/PowerLauncher/LauncherControl.xaml) and [`ResultList.xaml`](/src/modules/launcher/PowerLauncher/LauncherControl.xaml). These components are discussed below.
![Image of PowerToys Run UI](/doc/images/launcher/pt_run_ui.png)
**Fig 1: PowerToys Run UI architecture**
1. **[`MainWindow.xaml`](/src/modules/launcher/PowerLauncher/MainWindow.xaml)**: This is the outermost-level UI control. It is composed of lower-level UI components such as [`LauncherControl.xaml`](/src/modules/launcher/PowerLauncher/LauncherControl.xaml) and [`ResultList.xaml`](/src/modules/launcher/PowerLauncher/LauncherControl.xaml). The corresponding code-behind file implements all the UI related functionalities such as autosuggest, key-bindings, toggling visibility of WPF window and animations.
2. **[`LauncherControl.xaml`](/src/modules/launcher/PowerLauncher/LauncherControl.xaml)**: This control implements the UI component for editing query text.(marked in red in Fig 1) It consists of two overlapping WPF controls, `TextBox` and `TextBlock`. The outer `TextBox` is used for editing query whereas the inner `TextBlock` is used to display autosuggest text.
3. **[`ResultList.xaml`](/src/modules/launcher/PowerLauncher/LauncherControl.xaml)**: This control implements the UI component for displaying results (marked in green in Fig 1). It consists of a `ListView` WPF control with a custom `ItemTemplate` to display application logo, name, tooltip text, and context menu.
## Data flow
The backend code is written using the `Model-View-ViewModel (MVVM)` structural design pattern. Plugins act as `Model` in this project. A detailed overview of the project's structure is given in [Project Structure](/doc/devdocs/modules/launcher/project_structure.md).
#### Flow of data between UI(view) and ViewModels
Data flow between View and ViewModel follows typical `MVVM` scheme. Properties in viewModels are bound to WPF controls and when these properties are updated, `INotifyPropertyChanged` handler is invoked, which in turn updates UI. The diagram below provides a rough sketch of the components involved.
![Flow of data between UI(view) and ViewModels](/doc/images/launcher/ui_vm_interaction.PNG)
**Fig 2: Flow of data between UI and ViewModels.**
#### Flow of data between ViewModels and Plugins(Model)
`PowerLauncher` interact with plugins using [`IPlugin`](/src/modules/launcher/Wox.Plugin/IPlugin.cs) and `IDelayedExecutionPlugin` interface. [`IPlugin`](/src/modules/launcher/Wox.Plugin/IPlugin.cs) is used for initialization and making queries which are fast (typically return results in less than 100ms).[`IDelayedExecutionPlugin`](/src/modules/launcher/Wox.Plugin/IDelayedExecutionPlugin.cs) is used for long-running queries and is implemented only when required. For example, [`IDelayedExecutionPlugin`](/src/modules/launcher/Wox.Plugin/IDelayedExecutionPlugin.cs) is implemented by indexer plugin for searching files with names of form \*abc\*.
```
public interface IPlugin
{
// Query plugin
List<Result> Query(Query query);
// Initialize plugin
void Init(PluginInitContext context);
}
public interface IDelayedExecutionPlugin : IFeatures
{
// Query plugin
List<Result> Query(Query query, bool delayedExecution);
}
```
![Flow of data between UI(view) and ViewModels](/doc/images/launcher/vm_plugin_interaction.PNG)
**Fig 3: Flow of data between ViewModels and Plugins.**
#### Requesting services from powerlauncher
Plugins could use the [`IPublicAPI`](/src/modules/launcher/Wox.Plugin/IPublicAPI.cs) interface to request services such as getting the current theme (for deciding logo background), displaying messages to the user, and toggling the visibility of PowerLauncher.
+20
View File
@@ -0,0 +1,20 @@
# Debugging
`PowerToys Run` is a single exe file associated with `launcher.exe` process and debugger should be attached to this process. There are two approaches to debug `PowerToys Run`. Both these approaches differ in the compile-time and the range of functionalities that could be debugged. These methods are discussed in detail in the following sections.
## Debugging Prerequisite
Setup development environment for PowerToys by following instruction [here.](https://github.com/microsoft/PowerToys/tree/main/doc/devdocs#prerequisites-for-compiling-powertoys)
## Direct debugging
This approach is used to test UI, plugins, and core `PowerToys Run` functionality. This **cannot** be used to test `PowerToys Run` settings. The approach is significantly faster compared to `Debugging with runner`, as it requires compiling projects relevant to `PowerToys Run`. Please follow the steps below for direct debugging.
1. Right-click on `modules->launcher->PowerLauncher` and select `Set as startup Project`.
2. Press `F5` to start debugging.
## Debugging with runner
This approach can be used to test UI, plugins, core `PowerToys Run` functionality and `PowerToys Run` settings. This approach **cannot** be used to debug functions that execute on starting `launcher.exe` process. This requires building runner along with all the other modules on first compile, making it slower than `Direct debugging` approach. The subsequent compilations should be fast.
1. Right-click on `runner` and select `Set as startup Project`.
2. Press `F5` to start debugging.
3. Attach debugger to `launcher.exe` process.
1. Go to `Debug->Attach to process..`
2. Filter and select `launcher.exe` process.
3. Click on `Attach`.
@@ -0,0 +1,45 @@
# New plugin checklist
- [ ] The plugin is a project under `modules\launcher\Plugins`
- [ ] Microsoft plugin project name pattern: `Microsoft.PowerToys.Run.Plugin.{PluginName}`
- [ ] Community plugin project name pattern: `Community.PowerToys.Run.Plugin.{PluginName}`
- [ ] The plugin target framework should be `net10.0-windows10.0.22621.0`
- [ ] If the plugin uses any 3rd party dependencies the project file should import `DynamicPlugin.props`
- [ ] 3rd party dependencies must be compatible with .NET 10
- [ ] The plugin has to contain a `plugin.json` file of the following format in its root folder:
```json
{
"ID": string, // GUID string
"ActionKeyword": string, // Direct activation phrase
"IsGlobal": boolean,
"Name": string, // Has to be unique, same as 'PluginName' in the project name pattern
"Author": string,
"Version": "1.0.0", // For future compatibility
"Language": "csharp", // So far we support only csharp
"Website": "https://aka.ms/powertoys", // Has to be an absolute uri starting with "http://" or "https://".
"ExecuteFileName": string, // Should be {Type}.PowerToys.Run.Plugin.{PluginName}.dll
"IcoPathDark": string, // Path to dark theme icon. The path is relative to the root plugin folder
"IcoPathLight": string // Path to light theme icon. The path is relative to the root plugin folder
"DynamicLoading": bool // Sets whether the plugin should dynamically load any dependencies isolated from the core application.
}
```
- [ ] Make sure your `Main` class contains a public, static string property for the `PluginID`. The plugin id has to be the same as the one in the `plugin.json`file.
```csharp
public static string PluginID => "xxxxxxx"; // The part xxxxxxx stands for the plugin ID.
```
- [ ] Do not use plugin name or PowerToys as prefixes for entities inside of the plugin project
- [ ] The plugin has to have Unit tests. Use MSTest framework
- [ ] Plugin's output code and assets have to be included in the installer [`Product.wxs`](/installer/PowerToysSetup/Product.wxs)
- [ ] Test the plugin with a local build. Build the installer, install, check that the plugin works as expected
- [ ] All plugin's binaries have to be included in the signed build [`pipeline.user.windows.yml`](/.pipelines/pipeline.user.windows.yml)
Some localization steps can only be done after the first pass by the localization team to provide the localized resources.
In the PR that adds a new plugin, reference a new issue to track the work for fully enabling localization for the new plugin.
- [ ] Add the resource folder to https://github.com/microsoft/PowerToys/blob/21247c0bb09a1bee3d14d6efa53d0c247f7236af/installer/PowerToysSetup/Product.wxs#L825
- [ ] Add the resource files under the section https://github.com/microsoft/PowerToys/blob/21247c0bb09a1bee3d14d6efa53d0c247f7236af/installer/PowerToysSetup/Product.wxs#L882
- [ ] Your plugin's executable file (DLL) has to have correct version information after building it. (This version information will be shown on the settings page.)
@@ -0,0 +1,64 @@
# Calculator Plugin
The Calculator plugin as the name suggests is used to perform calculations on the user entered query.
![Image of Calculator plugin](/doc/images/launcher/plugins/calculator.png)
## Optional plugin settings
* We have the following settings that the user can configure to change the behavior of the plugin:
| Key | Default value | Name | Description |
|--------------|-----------|------------|------------|
| `InputUseEnglishFormat` | `false` | Use English (United States) number format for input | Ignores your system setting and expects numbers in the format '1,000.50' |
| `OutputUseEnglishFormat` | `false` | Use English (United States) number format for output | Ignores your system setting and returns numbers in the format '1000.50' |
* The optional plugin settings are implemented via the [`ISettingProvider`](/src/modules/launcher/Wox.Plugin/ISettingProvider.cs) interface from `Wox.Plugin` project. All available settings for the plugin are defined in the [`Main`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Main.cs) class of the plugin.
## Technical details
### [`BracketHelper`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/BracketHelper.cs)
- This helper validates the bracket usage in the input string.
### [`CalculateHelper`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateHelper.cs)
- The [`CalculateHelper.cs`](src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateHelper.cs) class checks to see if the user entered query is a valid input to the calculator and only if the input is valid does it perform the operation.
- It does so by matching the user query to a valid regex.
- This class also handles some human multiplication expression like `2(1+2)` and `(2+3)(3+4)` in order to be computed by `Mages` lib.
- It does so by matching some regex and inserting `'*'` where appropriate, e.g: `2(1+2) -> 2 * (1+2)`
- It takes into account the combination of numbers (`num`), constants (`const`), functions (`func`) and expressions in parentheses (`(exp)`).
- The blank spaces between them are also considered.
- Some combinations were not handled as they are not common such as `'const num'` or `'func const'`
### [`CalculateEngine`](src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateEngine.cs)
- The main computation is done in the [`CalculateEngine.cs`](src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateEngine.cs) file using the `Mages` library.
```csharp
var result = CalculateEngine.Interpret(query.Search, CultureInfo.CurrentUICulture);
```
### [`CalculateResult`](src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateResult.cs)
- The class which encapsulates the result of the computation.
- It comprises of the `Result` and `RoundedResult` properties.
### [`ErrorHandler`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/ErrorHandler.cs)
- The class which encapsulates the code to log errors and format the user message.
- It returns an error result if the user searches with the activation command. This error result is shown to the user.
### Score
The score of each result from the calculator plugin is `300`.
## [Unit Tests](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests)
We have a [Unit Test project](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests) that executes various test to ensure that the plugin works as expected.
### [`BracketHelperTests`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests/BracketHelperTests.cs)
- The [`BracketHelperTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests/BracketHelperTests.cs) class contains tests to validate that brackets are handled correctly.
### [`ExtendedCalculatorParserTests`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests/ExtendedCalculatorParserTests.cs)
- The [`ExtendedCalculatorParserTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests/ExtendedCalculatorParserTests.cs) class contains tests to validate that the input is parsed correctly and the result is correct.
### [`NumberTranslatorTests`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests/NumberTranslatorTests.cs)
- The [`NumberTranslatorTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests/NumberTranslatorTests.cs) class contains tests to validate that each number is converted correctly based on the defined locals.
### [`QueryTests`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests/QueryTests.cs)
- The [`QueryTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests/QueryTests.cs) class contains tests to validate that the user gets the correct results when searching.
@@ -0,0 +1,31 @@
# Unit Converter Plugin
The Unit Convert plugin as the name suggests is used to perform unit conversion on the user entered query.
This plugin uses a package called [UnitsNet](https://github.com/angularsen/UnitsNet).
![Image of Calculator plugin](/doc/images/launcher/plugins/community.unitconverter.png)
### Currently Supported Units
- [Acceleration](https://github.com/angularsen/UnitsNet/blob/master/UnitsNet/GeneratedCode/Units/AccelerationUnit.g.cs)
- [Angle](https://github.com/angularsen/UnitsNet/blob/master/UnitsNet/GeneratedCode/Units/AngleUnit.g.cs)
- [Area](https://github.com/angularsen/UnitsNet/blob/master/UnitsNet/GeneratedCode/Units/AreaUnit.g.cs)
- [Duration](https://github.com/angularsen/UnitsNet/blob/master/UnitsNet/GeneratedCode/Units/DurationUnit.g.cs)
- [Energy](https://github.com/angularsen/UnitsNet/blob/master/UnitsNet/GeneratedCode/Units/EnergyUnit.g.cs)
- [Information](https://github.com/angularsen/UnitsNet/blob/master/UnitsNet/GeneratedCode/Units/InformationUnit.g.cs)
- [Length](https://github.com/angularsen/UnitsNet/blob/master/UnitsNet/GeneratedCode/Units/LengthUnit.g.cs)
- [Mass](https://github.com/angularsen/UnitsNet/blob/master/UnitsNet/GeneratedCode/Units/MassUnit.g.cs)
- [Power](https://github.com/angularsen/UnitsNet/blob/master/UnitsNet/GeneratedCode/Units/PowerUnit.g.cs)
- [Pressure](https://github.com/angularsen/UnitsNet/blob/master/UnitsNet/GeneratedCode/Units/PressureUnit.g.cs)
- [Speed](https://github.com/angularsen/UnitsNet/blob/master/UnitsNet/GeneratedCode/Units/SpeedUnit.g.cs)
- [Temperature](https://github.com/angularsen/UnitsNet/blob/master/UnitsNet/GeneratedCode/Units/TemperatureUnit.g.cs)
- [Volume](https://github.com/angularsen/UnitsNet/blob/master/UnitsNet/GeneratedCode/Units/VolumeUnit.g.cs)
These are the ones that are currently enabled (though UnitsNet supports many more). They are defined in [`UnitHandler.cs`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/UnitHandler.cs).
### [`InputInterpreter`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/InputInterpreter.cs)
- Class which manipulates user input such that it may be interpreted correctly and thus converted.
- Uses a regex amongst other things to do this.
### [`UnitHandler`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/UnitHandler.cs)
- Class that does the actual conversion.
- Supports abbreviations in user input (single, double, or none).
@@ -0,0 +1,85 @@
# Value Generator Plugin
The Value Generator plugin is used to generate hashes for strings, to calculate base64 encodings, escape and encode URLs/URIs and to generate GUIDs of version 1, 3, 4, 5, and 7.
![Image of Value Generator plugin](/doc/images/launcher/plugin/community.valuegenerator.png)
### [`IComputeRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/IComputeRequest.cs)
- Interface for a request for computation
- the `bool Compute()` method must populate the `IsSuccessful` and one of the `Result` and `ErrorMessage` fields
- The result of `string ResultToString()` will be used for the Result's title
- The `Description` field will be used for the Result's subtitle
### [`HashRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Hashing/HashRequest.cs)
- Implements IComputeRequest
- Supports the hashing algorithms from System.Security.Cryptography:
- MD5
- SHA1
- SHA256
- SHA384
- SHA512
- If other algorithms are added to System.Security.Cryptography, they can be added to the `_algorithms` dictionary. [`InputParser.ParseInput()`](#inputparser) will need to return a `HashRequest` for the algorithm in the query
### [`Base64Request`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Base64/Base64Request.cs)
- Implements IComputeRequest
- `Compute()` will populate `Result` with the base64 encoding of the byte array passed in the constructor
### [`Base64DecodeRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Base64/Base64DecodeRequest.cs)
- Implements IComputeRequest
- `Compute()` will populate `Result` with the decoded byte array of the base64 string passed in the constructor
### [`GUIDRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/GUID/GUIDRequest.cs)
- Implements IComputeRequest
- Uses the [`GUIDGenerator`](#guidgenerator) class to generate or compute the requested GUID
### [`GUIDGenerator`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/GUID/GUIDGenerator.cs)
- Utility class for generating or calculating GUIDs
- Generating GUID versions 1, 4, and 7 is done using builtin APIs:
- [`UuidCreateSequential`](https://learn.microsoft.com/en-us/windows/win32/api/rpcdce/nf-rpcdce-uuidcreatesequential) for version 1
- `System.Guid.NewGuid()` for version 4
- `System.Guid.CreateVersion7()` for version 7
- Versions 3 and 5 take two parameters: a namespace and a name
- The namespace must be a valid GUID or one of the [predefined ones](https://datatracker.ietf.org/doc/html/rfc4122#appendix-C)
- The `PredefinedNamespaces` dictionary contains aliases for the predefined namespaces
- The name can be any string
### [`UrlEncodeRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/UrlEncodeRequest.cs)
- Implements IComputeRequest
- `Compute()` will populate `Result` with the encoded url converted using `HttpUtility.UrlEncode()`.
### [`UrlDecodeRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/UrlDecodeRequest.cs)
- Implements IComputeRequest
- `Compute()` will populate `Result` with the decoded url converted using `HttpUtility.UrlDecode()`.
### [`DataEscapeRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/DataEscapeRequest.cs)
- Implements IComputeRequest
- `Compute()` will populate `Result` with the escaped data string converted using `System.Uri.EscapeDataString()`.
### [`DataUnescapeRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/DataUnescapeRequest.cs)
- Implements IComputeRequest
- `Compute()` will populate `Result` with the unescaped data string converted using `System.Uri.UnescapeDataString()`.
### [`HexEscapeRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/HexEscapeRequest.cs)
- Implements IComputeRequest
- `Compute()` will populate `Result` with the escaped data string converted using `System.Uri.HexEscape()`.
- Only single characters are supported as input.
### [`HexUnescapeRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/HexUnescapeRequest.cs)
- Implements IComputeRequest
- `Compute()` will populate `Result` with the unescaped data string converted using `System.Uri.HexUnescape()`.
- Only the first hexadecimal character in the string gets unescaped. The rest of the user input is ignored.
### [`InputParser`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/InputParser.cs)
- It is responsible only for parsing the query from the user
- Based on the user query, the `ParseInput()` method must return an object that implements the `IComputeRequest` interface or it must throw one of `FormatException` or `ArgumentException`
- Throwing an `ArgumentException` should signal the fact that the query contains a mistake that the user can fix (e.g. an unsupported hash function, an invalid GUID version, an invalid namespace, etc.)
> The error message will be shown to the user and no log message will be created
- Throwing a `FormatException` should signal either:
- that the query may become valid, and so it does not make sense to show an error just yet (e.g. the query does not contain a request yet, a hash request without a string to hash)
- that the query is completely invalid
> The error message will not be shown to the user but a log message will be created
### Adding a new value generator
1. To add a new value generator, create a folder under `/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/` and inside it add a class that implements `IComputeRequest`.
2. Add any utility classes that are specific to the new generator inside the same folder to keep them separated from the other generators.
3. Modify the `InputParser.ParseInput()` to handle a request for the new generator and return an instance of the class you created in step 1
@@ -0,0 +1,17 @@
# Folder Plugin
The Folder plugin is used to navigate the directory structure and display the sub-folders and files within a folder.
![Image of Folder plugin](/doc/images/launcher/plugins/folder.png)
### [`FolderHelper.cs`](src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Path/FolderHelper.cs)
- The [`FolderHelper`](src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Path/FolderHelper.cs) class leverages the `DriveInformation` and `folderLinks` to get the folder results for a user query.
- The [`DriveInformation`](src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Path/DriveInformation.cs) class gets the list of all drives on the system.
- The [`FolderLink`](src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/FolderLink.cs) object corresponds to a user created link for frequently accessed projects. This was inherited from Wox but is presently not functional as we don't have the UI setup in settings to get this user input. Each folderLink object has a `nickname`, which is the name of the folder and this can be used to directly access that folder instead of entering the entire path.
### [`IFolderProcessor.cs`](src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/IFolderProcessor.cs)
The `IFolderProcessor` utilizes the `FolderHelper` class to extract the folders and return the results.
There are two types of Folder Processors, based on the type of information they are processing -
1. [`UserFolderProcessor`](src/modules/launcher/Plugins/Microsoft.Plugin.Folder/UserFolderProcessor.cs) - This Processor is currently not used in PT Run but it is used to process the user created folder links.
2. [`InternalDirectoryProcessor`](src/modules/launcher/Plugins/Microsoft.Plugin.Folder/InternalDirectoryProcessor.cs) - This processor is used to retrieve the files and folders located within the current drive or shared folder.
### Score
The first result is of score 500 and the following results are scored 10.
@@ -0,0 +1,103 @@
# History Plugin
The History Plugin allows users to search or display results they have used (selected).
## How it works
The plugin uses data that was already being captured which is, what results were clicked, and how many times. We do add a little more data to this set now.
When this plugin is queried, it creates results based on this previously selected results data.
In order to make sure selected results in the history are still valid, we re-query the relevant plugin using the PluginManager. If there are no results,
this history item is not included. This usually means that the result is no longer valid. For instance, if a file was deleted, but it's still in the selected history
we don't want to show it as a selectable result.
Because the results from the History Plugin are actually created from calls to the PluginManager, they will be exactly the same is if they did not come from the History Plugin.
## Special notes
While the results returned from the plugin are from the PluginManager, they are sometimes modified before returning. One example is the Calculator plugin.
Since the Calculator plugin operates on the current query input by the user, the results from Calculator plugin don't include that in the title. However, as a history item,
the query is very important. In this case, and maybe others in the future, we modify the tile to also include the search.
### Modified title example:
This is what the Calculator plugin normally might show:
![image](https://user-images.githubusercontent.com/4396667/184661303-4f8cf0da-2956-46b9-bdc1-ed879cd0b7cc.png)
But this is how it will look returned from the History plugin
![image](https://user-images.githubusercontent.com/4396667/184661450-9ec3c416-66df-40c8-b004-da8b0cebc5c5.png)
As you can see, here and maybe other places, other non-history plugin might be able to include extra data for the History plugin to use later.
For example, in future, plugins might be able to also set a "History Title", "History Icon", etc... But for now, it's not needed.
## Duplicates from the History Plugin in global results
If the History plugin is set to show in the global results, it might return a result that is also returned from another plugin. If a match is found,
the result from the history plugin is discarded.
## Removing items from history
A new context menu item is added to each History result, which can be used to delete it from the history.
![image](https://user-images.githubusercontent.com/4396667/184656195-6d9f1a49-652c-4027-a424-535e9fb1f2a8.png)
## Context menus
Because these results are coming from the History plugin, this plugin must invoke each menu items `LoadContextMenus` method.
We then also add the "Remove this from history" context menu action.
## Results score
When the plugin is used with the activation command, the scores are configured so the results show with the more recently selected items at the top.
If the history results are shown in the global results, the scores are not modified from that the original plugin set.
## Old Data
Items selected before this plugin was created will not show in the history because they don't contain enough data.
## Important for developers
### Important plugin values (meta-data)
| Name | Value |
| --------------- | ---------------------------------------------------- |
| ActionKeyword | `!!` |
| ExecuteFileName | `Microsoft.PowerToys.Run.Plugin.History.dll` |
| ID | `C88512156BB74580AADF7252E130BA8D` |
### Interfaces used by this plugin
The plugin uses only these interfaces (all inside the `Main.cs`):
* `Wox.Plugin.IPlugin`
* `Wox.Plugin.IContextMenu`
* `Wox.Plugin.IPluginI18n`
### Program files
| File | Content |
| ------------------------------------- | ----------------------------------------------------------------------- |
| `Images\history.dark.png` | Symbol for the results for the dark theme |
| `Images\history.light.png` | Symbol for the results for the light theme |
| `Properties\Resources.Designer.resx` | File that contain all translatable keys |
| `Properties\Resources.resx` | File that contains all translatable strings in the neutral language |
| `Main.cs` | Main class, the only place that implements the WOX interfaces |
| `ErrorHandler.cs` | Class to build error result on plugin failure |
| `plugin.json` | All meta-data for this plugin |
### Important project values (*.csproj)
| Name | Value |
| --------------- | ------------------------------------------------- |
| TargetFramework | `net6.0-windows10.0.19041.0` |
### Project dependencies
#### Projects
* `Wox.Infrastructure`
* `Wox.Plugin`
* `PowerToys.PowerLauncher`
#### Build Dependency
Access to PluginManager was needed to make this plugin work. Because of this a reference to PowerToys.PowerLauncher was needed.
Since History Plugin needs a reference to PowerToys.PowerLauncher, it cannot be set as a dependency reference in PowerToys.PowerLauncher project (else a circular reference would exist).
This means that if you build PowerToys.PowerLauncher only it will not build History Plugin. You will need to manually build History Plugin at least once and again manually if you change it.
### Caching
Right now, there is no caching. But since this plugin does cause more queries than expected to many plugins, the `BuildResult` method is likely to be improved with some level of caching.
@@ -0,0 +1,39 @@
# Indexer Plugin
The indexer plugin is used to search for files within the indexed locations of the system.
![Image of Indexer plugin](/doc/images/launcher/plugins/indexer.png)
### [Drive Detection](src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/DriveDetection)
- There are two indexing modes in Windows:
1. **Classic mode**: Only the desktop and certain customizable locations in the system are indexed. All the systems have the classic mode enabled by default.
2. **Enhanced Mode**: This mode indexes the entire PC when enabled. The user can exclude certain locations from being indexed in this mode from the Windows Search settings options.
- A drive detection warning is displayed to the users when only the custom mode is enabled on the system informing the user that not all the locations on their PC are indexed as this could lead to some results not showing up.
- The [`IndexerDriveDetection.cs`](src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/DriveDetection/IndexerDriveDetection.cs) file gets the status of the drive detection checkbox in the settings UI and depending on whether the enhanced mode is enabled or disabled, displays the warning.
- To determine whether the `EnhancedMode` is enabled or not, we check the local machine registry entry for `EnableFindMyFiles`. If it is set to 1, the enhanced mode is enabled.
### [`OleDBSearch`](src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/SearchHelper/OleDBSearch.cs)
- The `Query` function within the [`OleDBSearch.cs`](src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/SearchHelper/OleDBSearch.cs) class takes in the query and the connection string to the SystemIndex catalog as arguments and returns a list of results.
- It first opens a [connection][OLEDBConnection] to the Windows Indexer database, creates an [OleDB command][OLEDBCommand] and executes the command to get a list of results.
### [`WindowsSearchAPI`](src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/SearchHelper/WindowsSearchAPI.cs)
- The [`WindowsSearchAPI`](src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/SearchHelper/WindowsSearchAPI.cs) class leverages the [`OleDBSearch.cs`](src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/SearchHelper/OleDBSearch.cs) class to execute the query.
- It initializes the `QueryHelper` in the `InitQueryHelper()` function by creating a catalog manager to the SystemIndex catalog.
- The metadata is initialized within the query helper, such as the number of results to retrieve, the type of information to retrieve for each file (currently we retrieve the item URL, the file name and the file attributes).
- The query helper matches results using the name of the file only and they are sorted by the last modified date, ensuring that the recently modified files are ranked higher.
- The File attributes are utilized to filter out hidden files from being displayed.
### Additional Information
- There are two major types of queries generated by the indexer plugin:
1. Full Text predicates - eg: [CONTAINS][Contains]
2. Non-Full Text predicates - eg: [LIKE][Like]
- The Full text predicates are much faster than non-full text predicates as they are based on finding matches rather than comparing the query with each item in the indexer database. Hence, queries which have the `CONTAINS` keyword are much faster than those which contain the `LIKE` keyword.
- To prevent the indexer query from taking a long time and blocking the UI thread, there are two types of indexer queries which are executed. A simplified query and a full query, without and with the `LIKE` keyword respectively.
- The result list is updated with the results of the full query once they are obtained.
### Score
Each of the indexer plugin results has a score set to 0 so they are present at the bottom of the list.
[OLEDBCommand]: https://learn.microsoft.com/dotnet/api/system.data.oledb.oledbcommand?view=dotnet-plat-ext-3.1
[OLEDBConnection]: https://learn.microsoft.com/dotnet/api/system.data.oledb.oledbconnection?view=dotnet-plat-ext-3.1
[Contains]: https://learn.microsoft.com/windows/win32/search/-search-sql-contains
[Like]: https://learn.microsoft.com/windows/win32/search/-search-sql-like
@@ -0,0 +1,38 @@
# OneNote Plugin
The OneNote plugin searches your locally synced OneNote notebooks based on the user query.
![Image of OneNote plugin](/doc/images/launcher/plugins/onenote.png)
The code itself is very simple, basically just a call into OneNote interop via the https://github.com/scipbe/ScipBe-Common-Office library.
```csharp
var pages = OneNoteProvider.FindPages(query.Search);
```
The query results will be cached for 1 day, and if cached results are found they'll be returned in the initial `Query()` call; otherwise, OneNote itself will be queried in the `delayedExecution:true` overload.
If the user actions on a result, it'll open it in the OneNote app, and restore and/or focus the app as well if necessary.
```csharp
if (PInvoke.IsIconic(handle))
{
PInvoke.ShowWindow(handle, SHOW_WINDOW_CMD.SW_RESTORE);
}
PInvoke.SetForegroundWindow(handle);
```
The plugin attempts to call the library in the constructor, and if it fails with a COMException then it'll note that OneNote isn't available and not attempt to query it again.
```csharp
try
{
_ = OneNoteProvider.PageItems.Any();
_oneNoteInstalled = true;
}
catch (COMException)
{
// OneNote isn't installed, plugin won't do anything.
_oneNoteInstalled = false;
}
```
@@ -0,0 +1,38 @@
# Structural Overview
The following basic functions are common to each of the plugins. They perform some rudimentary operations such as initialization of the plugin, executing the query that has been entered, loading context menu icons, updating settings when configurations are altered in the settings UI, and updating the theme of the icons when the theme changed event is triggered.
## IPlugin Interface
Each plugin implements the `IPlugin` interface which comprises of the `Init()` and `Query()` functions.
### `Init`
- The `Init()` function initializes the context, storage and settings of each plugin. This is equivalent to a constructor and is the first function to be called in the `Main.cs` file for each plugin.
### `Query`
- For every query that the user enters into PT Run, the `PluginManager.cs` executes the `Query()` function in the `Main.cs` file corresponding to each Plugin.
### Context Menu Icons
- The `ContextMenus` are loaded for each result based on the type of the result.
- The various types of `ContextMenu` functionalities are:
- Open containing folder
- Run as Administrator
- Open in console
- Copy path
### UpdateSettings
- This function updates the settings of each plugin based on the changes made by the user in the settings UI.
- Eg: To disable drive detection in the indexer plugin, when the user checks or unchecks the drive detection check box, the `UpdateSettings()` function dispatches the changes in the check box to the plugin.
### ThemeChanged
- This function is invoked when there is a change in the theme of PT Run.
- It is used to update the `IconPath` for each plugin based on the theme.
### Save
- This function saves the configurations of each plugin so that they can be loaded the next time.
### Score
- The user query is executed against each of the plugins and the result list view is updated with results from each of the plugins.
- The ordering of the results is based on the `Score` of each Result.
- Each plugin assigns a score to a result based on its relevance. The results with higher scores are displayed higher in the list view and vice versa.
## Plugin settings
Plugin settings that are editable from the settings are stored in `PowerToys Run\settings.json`. In the very first run, those settings are populated from plugin' `plugin.json` file. Unlike Wox we do not support multiple action keywords. Instead, we have `ActionKeyword` and `IsGlobal` options.
@@ -0,0 +1,47 @@
# Program Plugin
The program plugin as the name suggests is used to search for programs installed on the system.
![Image of Program plugin](/doc/images/launcher/plugins/program.png)
There are broadly two different categories of applications:
1. Packaged applications
2. Win32 applications
### [UWP](src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/UWP.cs)
- The logic for indexing Packaged applications is present within the [`UWP.cs`](src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/UWP.cs) file.
- There can be multiple applications present within a package. The [`UWPApplication.cs`](src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/UWPApplication.cs) file encapsulates the properties of a packaged application.
- To index packaged applications, the `PackageManager` retrieves all the packages for the current user and indexes all the applications.
- To retrieve the app icon for packaged applications, the assets path is retrieved from the `Application Manifest` file. There are multiple icons corresponding to each scale, target size and theme. The best icon is chosen given the theme of powerToys Run.
### [Win32Program](src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32Program.cs)
- Win32 programs in the following locations are indexed by PT Run-
1. Desktop
2. Public Desktop (Applications present on the desktop of all the users)
3. Registry (Some programs)
4. Start Menu
5. Common start menu (Applications which are common to all users)
8. Locations pointed to by the PATH environment variable.
- To prevent applications and shortcuts present in multiple locations from showing up as duplicate results, we consider apps with the same name, executable name and full path to be the same.
- The subtitle of the application result is set based on its application type. It could be one of the following:
1. Lnk Shortcuts
2. Appref files
3. Internet shortcut - steam and epic games
4. PWAs
5. Run commands - these are indexed by the PATH environment variable
### Score
- The score for each application result is based on the how many letters are matched, how close the matched letters are to the actual word and the index of the matched characters.
- There is a threshold score to decide the apps which are to be displayed and applications which have a lower score are not displayed by PT Run.
### Update Program List in Runtime
- Packaged and Win32 app helpers exist to reflect changes in the list of indexed apps when applications are installed on the system while PT Run is executing.
- Packaged applications trigger events when the package is being installed and uninstalled. PT Run listens to those events to index applications which are newly installed or to delete an app which no longer exists from the database.
- No such events exist for Win32 applications. We therefore use FileSystem Watchers to monitor the locations that we index for newly created, deleted or renamed application files and update the indexed Win32 catalog accordingly.
### Additional Notes
- Arguments can be provided to the program plugin by entering them after `--` (a double dash).
- The localization is done using the `Localization Helper`from `Wox.Plugin.Common` hosted at runtime in a variable of plugin's main class.
- The `Run commands` differ in two points from the normal `Win32Programs`:
- The result title contains the executable type.
- The file types `.cpl` and `.msc` are supported too.
@@ -0,0 +1,92 @@
# Registry Plugin
The registry plugin allows users to search the Windows registry.
## Special functions (differ from the regular functions)
* Support full base keys and short base keys (e.g. `HKLM` for `HKEY_LOCALE_MACHINE`).
* Show count of subkeys and count of values in the second result line.
* Search for value names and value data inside a registry key (syntax: `[RegistryKey]\\[ValueName]` and `[RegistryKey]\\[ValueData]`)
## The Windows Registry
The registry contains all settings for the Windows operating system and many settings of the installed (Windows only) programs.
*Note: Linux and macOS program ports typical store the settings in their own configuration files and not in the Windows registry.*
For more information about the Windows registry, see [the official documentation](https://learn.microsoft.com/windows/win32/sysinfo/registry).
For advanced information about the Windows registry, see [Windows registry information for advanced users](https://learn.microsoft.com/troubleshoot/windows-server/performance/windows-registry-advanced-users).
## Score
The score is currently not set on the results.
## Important for developers
### General
* The assembly name is cached into `_assemblyName` (to avoid to many calls of `Assembly.GetExecutingAssembly()`)
### Results
* All results override the visible search result via `QueryTextDisplay` to avoid problems with short registry base keys (e.g. `HKLM`).
* The length of a `Title` and `Subtitle` is automatic truncated, when it is to long.
## Microsoft.Plugin.Registry project
### Important plugin values (meta-data)
| Name | Value |
| --------------- | --------------------------------------------- |
| ActionKeyword | `:` |
| ExecuteFileName | `Microsoft.PowerToys.Run.Plugin.Registry.dll` |
| ID | `303417D927BF4C97BCFFC78A123BE0C8` |
### Interfaces used by this plugin
The plugin use only these interfaces (all inside the `Main.cs`):
* `Wox.Plugin.IPlugin`
* `Wox.Plugin.IContextMenu`
* `Wox.Plugin.IPluginI18n`
* `System.IDisposable`
### Program files
| File | Content |
| ------------------------------------ | ------------------------------------------------------------------------ |
| `Classes\RegistryEntry.cs` | Wrapper class for a registry key with a possible exception on access |
| `Constants\KeyName.cs` | Static used short registry key names (to avoid code and string doubling) |
| `Constants\MaxTextLength.cs` | Contain all maximum text lengths (for truncating) |
| `Enumeration\TruncateSide.cs` | Contain the possible truncate sides |
| `Helper\ContextMenuHelper.cs` | All functions to build the context menu (for each result entry) |
| `Helper\QueryHelper.cs` | All functions to analyze the search query |
| `Helper\RegistryHelper.cs` | All functions to search into the Windows registry (via `Win32.Registry`) |
| `Helper\ResultHelper.cs` | All functions to convert internal results into WOX results |
| `Helper\ValueHelper.cs` | All functions to convert values into human readable values |
| `Images\reg.dark.png` | Symbol for the results for the dark theme |
| `Images\reg.light.png` | Symbol for the results for the light theme |
| `Properties\Resources.Designer.resx` | File that contain all translatable keys |
| `Properties\Resources.resx` | File that contain all translatable strings in the neutral language |
| `Main.cs` | Main class, the only place that implement the WOX interfaces |
| `plugin.json` | All meta-data for this plugin |
### Important project values (*.csproj)
| Name | Value |
| --------------- | ------------------------------------------------------------------------------ |
| TargetFramework | `net6.0-windows` (.NET 5) or `net6.0-windows10.0.19041.0` (OS version specific)|
| LangVersion | `8.0` (mean C# 8.0) |
| Platforms | `x64` |
| Nullable | `true` |
| Output | `..\..\..\..\..\x64\Debug\modules\launcher\Plugins\Microsoft.Plugin.Registry\` |
| RootNamespace | `Microsoft.PowerToys.Run.Plugin.Registry` |
| AssemblyName | `Microsoft.PowerToys.Run.Plugin.Registry` |
### Project dependencies
#### Projects
* `Wox.Infrastructure`
* `Wox.Plugin`
@@ -0,0 +1,14 @@
# Shell Plugin
- Shell plugin emulates the Windows Run Prompt (Win+R).
- Shell Plugin is one of the non-global plugins which has an action keyword set to `>`.
![Image of Shell plugin](/doc/images/launcher/plugins/shell.png)
### Functionality
- The Shell command expands environment variables, so `>%appdata%` works as expected.
- On inheriting the Shell plugin from Wox, there are three different ways of executing a command, using the command prompt, powershell or the run prompt. To uphold the name of PT Run, the Shell plugin always executes commands as the Run prompt would.
- The Shell plugin has a concept of history where the previously executed commands show up in the drop down list along with the number of times they have been executed.
- The Run prompt has the folder plugin function where we can navigate to different locations and entering the path to a directory displays all the sub-directories. To prevent reimplementing this logic, the shell plugin references the folder plugin to implement this functionality.
### Score
The Shell plugin results have a very high score of 5000. Hence, they are one of the first results in the list.
@@ -0,0 +1,85 @@
# Windows System Commands Plugin
As the name suggests, the Windows System Commands Plugin is used to directly run Windows system commands that have been entered by the user as a query. This is done by parsing the entry and validating the command, followed by executing it.
The user can change the behavior of the plugin (language, confirmation dialog, ...) with optional plugin settings.
![Image of System Commands plugin](/doc/images/launcher/plugins/sys.gif)
Available commands:
* Shutdown
* Restart
* Sign Out
* Lock
* Sleep
* Hibernate
* Open / Empty Recycle Bin
* UEFI Firmware Settings (Only available on systems, that boot in UEFI mode.)
* IP / MAC / Address => Show information about network connections.
## Optional plugin settings
* We have the following settings that the user can configure to change the behavior of the plugin:
| Key | Default value | Name/Description |
|--------------|-----------|------------|
| `ConfirmSystemCommands` | `false` | Show a dialog to confirm system commands |
| `LocalizeSystemCommands` | `true` | Use localized system commands instead of English ones |
| `ReduceNetworkResultScore` | `true` | Reduce the priority of 'IP' and 'MAC' results to improve the order in the global results |
* The optional plugin settings are implemented via the [`ISettingProvider`](/src/modules/launcher/Wox.Plugin/ISettingProvider.cs) interface from `Wox.Plugin` project. All available settings for the plugin are defined in the [`Main`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Main.cs) class of the plugin.
## Technical details
### [`Main`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Main.cs)
* Tries to parse the user input and returns a specific Windows system command by using a [`Result`](/src/modules/launcher/Wox.Plugin/Result.cs) list.
* While parsing, the plugin uses [`FuzzyMatch`](/src/modules/launcher/Wox.Infrastructure/StringMatcher.cs) to get characters matching a result in the list.
### [`Commands.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/Commands.cs)
- The [`Commands`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/Commands.cs) class contains the definition of all available commands/results.
### [`ResultHelper.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/ResultHelper.cs)
- The [`ResultHelper`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/ResultHelper.cs) class contains methods for working with the results and some of the result features (tool tip, copy to clipboard, execute command).
- **Recycle Bin command:** The context menu action to empty the Recycle Bin is executed as an async task to not block PowerToys Run. (While the task is running the static class variable `executingEmptyRecycleBinTask` is set to true, to block multiple executions at the same time)
### [`NetworkConnectionProperties.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/NetworkConnectionProperties.cs)
- The [`NetworkConnectionProperties`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/NetworkConnectionProperties.cs) class contains methods to get the properties of a network interface/connection.
- An instance of this class collects/provides all required information about one connection/adapter.
### [`SystemPluginContext.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/SystemPluginContext.cs)
- An instance of the class [`SystemPluginContext`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/SystemPluginContext.cs) contains/defines the context data of a system plugin result. We select the context menu based on the defined properties.
- It is used for the `ContextData` property of the [`Wox.Plugin.Result`](/src/modules/launcher/Wox.Plugin/Result.cs).
### UEFI command
* The UEFI command is only available on systems, that boot in UEFI mode.
* This is validated by checking the result of the method [`GetSystemFirmwareType`](/src/modules/launcher/Wox.Plugin/Common/Win32/Win32Helpers.cs), which uses the native method [`GetFirmwareType`](/src/modules/launcher/Wox.Plugin/Common/Win32/NativeMethods.cs) in `kernel32.dll`.
## Search
### Score
* [`CalculateSearchScore`](/src/modules/launcher/Wox.Infrastructure/StringMatcher.cs) A match found near the beginning of a string is scored more than a match found near the end. A match is scored more if the characters in the patterns are closer to each other, while the score is lower if they are more spread out.
* For network results (IP address and MAC address) the score is reduced by 25 percent.
### Network results on global queries
- The network results (IP and MAC address) are only shown on global queries, if the search term starts with either IP, MAC or Address. (We compare case-insensitive.)
### Returning results
We return the results in two steps:
1. All results which we can create very fast like shutdown or logoff via [`Main.Query(Query query)`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Main.cs).
2. All results which need some time to create like the network results (IP, MAC) via [`Main.Query(Query query, bool delayedExecution)`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Main.cs).
## [Unit Tests](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System.UnitTests)
We have a [Unit Test project](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System.UnitTests) that executes various test to ensure that the plugin works as expected.
### [`ImageTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System.UnitTests/ImageTests.cs)
- The [`ImageTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System.UnitTests/ImageTests.cs) class contains tests to validate that each result shows the expected and correct image.
### [`QueryTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System.UnitTests/QueryTests.cs)
- The [`QueryTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System.UnitTests/QueryTests.cs) class contains tests to validate that the user gets the correct results when searching.
@@ -0,0 +1,178 @@
# 'Time and Date' plugin
The 'Time and Date' plugin shows the date and time in different formats. For the date and time formats the plugin uses the culture setting in Windows, if the format is not commonly defined. The user can search for the system date/time or a custom date/time. The value of each result can be copied to clipboard.
**Query examples:**
- Format: `time`
- Date/time: `10:30 AM`
- Format and date/time: `Week number::10/10/2022`
!['Time and Date' plugin](/doc/images/launcher/plugins/timedate.png)
![Search for a date in a specified format](/doc/images/launcher/plugins/timedate2.png)
## Formats
### Available formats
**Remarks**
- The following formats requires a prefix in the query when using them as date input:
- Unix Timestamp: `u`
- Unix Timestamp in milliseconds: `ums`
- Windows file time: `ft`
- OLE Automation date: `oa`
- Excel 1900 date value: `exc`
- Excel 1904 date value: `exf`
- On invalid number inputs we show a warning that tells the user which prefixes are allowed/required.
**List of available formats**
The following formats are currently available:
| Format | Example (Based on default settings) | As result | As input | Result as custom format only
|--------------|-----------|------------|------------|
| Time | 5:10 PM | x | x | |
| Date | 3/5/2022 | x | x | |
| Now | 3/5/2022 5:10 PM | x | x | |
| Time UTC | 4:10 PM | x | x | |
| Now UTC | 3/5/2022 4:10 PM | x | x | |
| Unix Timestamp | 1646496622 | x | x | |
| Unix Timestamp in milliseconds | 1646496622500 | x | x | |
| Hour | 10 | x | | |
| Minute | 30 | x | | |
| Second | 45 | x | | |
| Millisecond | 678 | x | | |
| Day (Week day) | Saturday | x | | |
| Day of the week | 6 | x | | |
| Day of the month | 5 | x | | |
| Day of the year | 64 | x | | |
| Week of the month | 1 | x | | |
| Week of the year (Calendar week, Week number) | 10 | x | | |
| Month | March | x | | |
| Month of the year | 3 | x | | |
| Month and day | March 7 | x | x | |
| Year | 2022 | x | | |
| Era | AD | x | | |
| Era abbreviation | A | x | | |
| Month and year | March 2022 | x | x | |
| Windows file time (Int64 number) | 637820976123938199 | x | x | |
| Universal time format: YYYY-MM-DD hh:mm:ss| 2022-03-05 16:20:12Z | x | x | |
| ISO 8601 | 2022-03-05T17:23:04 | x | x | |
| ISO 8601 UTC | 2022-03-05T16:23:04 | x | x | |
| ISO 8601 with time zone | 2022-03-05T17:23:04+01:00 | x | x | |
| ISO 8601 UTC with time zone | 2022-03-05T16:23:04Z | x | x | |
| RFC1123 | Sat, 05 Mar 2022 16:23:04 GMT | x | x | |
| OLE Automation date | 45723.44143763889 | | x | x |
| Excel's 1900 date value | 45723.44143763889 | | x | x |
| Excel's 1904 date value | 44261.44143763889 | | x | x |
**Custom format definition**
The user can create its own formats. One per line in the settings text box. The format of each line is `<name>=<syntax pattern>`.
If the syntax pattern starting with `UTC:` then we use the UTC time instead of the local time.
As syntax pattern the pattern from `DateTime.ToString()` and the following custom pattern are available:
- DOW: Number of the day in the week.
- WOM: Number of week in the month.
- WOY: Number of the week in the year.
- EAB: Era abbreviation.
- WFT: Windows file time.
- UXT: Unix time stamp.
- UMS: Unix time stamp in milliseconds.
- OAD: OLE Automation date.
- EXC: Excel's 1900 based date value.
- EXF: Excel's 1904 based date value.
### Add new formats
- To add a new formats you have to add them to the method `GetList()` of the [`AvailableResultsList`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/AvailableResultsList.cs) class.
- Please add the new formats in the second range. The first one is reserved for the three main formats (Time, Date, Now).
- After adding the new formats you have to update the Unit Tests!
## Optional plugin settings
- The optional plugin settings are implemented via the [`ISettingProvider`](/src/modules/launcher/Wox.Plugin/ISettingProvider.cs) interface from `Wox.Plugin` project.
- All available settings for the plugin are defined in the [`TimeDateSettings`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/TimeDateSettings.cs) class of the plugin. The settings can be accessed everywhere in the plugin code via the static class instance `TimeDateSettings.Instance`.
- We have the following settings that the user can configure to change the behavior of the plugin:
| Key | Type | Default value | Name | Description |
|--------------|--------------|-----------|------------|------------|
| `OnlyDateTimeNowGlobal` | Checkbox | `true` | Show only 'Time', 'Date', and 'Now' result for system time on global queries | Regardless of this setting, for global queries the first word of the query has to be a complete match. |
| `TimeWithSeconds` | Checkbox | `false` | Show time with seconds | This setting applies to the 'Time' and 'Now' result. |
| `DateWithWeekday` | Checkbox | `false` | Show date with weekday and name of month | This setting applies to the 'Date' and 'Now' result. |
| `HideNumberMessageOnGlobalQuery` | Checkbox | `false` | Hide 'Invalid number input' error message on global queries | |
| `CalendarFirstWeekRule` | Combo box | `-1` (Use system settings) | First week of the year | Configure the calendar rule for the first week of the year. |
| `FirstDayOfWeek` | Combo box | `-1` (Use system settings) | First day of the week | |
| `CustomFormats` | Multiline text box | `string.Empty` | Custom formats | Use date and time string format syntax and DOW (Day of Week), WOM (Week of Month), WOY (Week of the year), EAB (Era abbreviation), WFT (Windows File Time), UXT (Unix Time), UMS (Unix Time in milliseconds), OAD (OLE Automation date), EXC (Excel's 1900 based date value), EXF (Excel's 1904 based date value). If the format starts with UTC:, then Universal Time (UTC) is used. (Use a backslash to escape format sequences and the backslash character as text.) |
## Classes
### [`AvailableResult.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/AvailableResult.cs)
- Each instance of the [`AvailableResult`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/AvailableResult.cs) class represents a time/date result/format that the user can search for.
- The results/formats are defined in the `AvailableResultsList` class.
### [`AvailableResultsList.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/AvailableResultsList.cs)
- The [`AvailableResultsList`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/AvailableResultsList.cs) class contains the list of available formats/results in its method `GetList()`.
### [`ResultHelper.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/ResultHelper.cs)
- The [`ResultHelper`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/ResultHelper.cs) class contains methods for some of the result features (tool tip, copy to clipboard) and the error result on incorrect number input.
- And it contains the `SelectStringFromResources()` method for getting the resource strings based on the user input.
- The method has a parameter for the `stringId` which is the name of the string in the resource file. By default the word `Now` is automatically added at the end to get the string for a system time/date search.
- If a different/custom string is needed for a system time/date search the parameter `stringIdNow` can be used to override the default behavior of the method.
- If only a string for the system time/date search is required, you can set `stringId` to `string.Empty` and only `stringIdNow` to a valid string id.
### [`TimeAndDateHelper.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/TimeAndDateHelper.cs)
- The [`TimeAndDateHelper`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/TimeAndDateHelper.cs) class contains methods to format/convert date and time formats/strings.
- And it contains methods to return the `first week day` and `first week of the year rule` based on the current plugin settings.
### [`TimeDateSettings.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/TimeDateSettings.cs)
- The [`TimeDateSettings`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/TimeDateSettings.cs) class provides access to all optional plugin settings.
- The class has a static property called `Instance` that holds an instance of the class itself. This allows us to access the settings from everywhere in the plugin code without having additional parameters in our methods.
### [`SearchController.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/SearchController.cs)
- The [`SearchController`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/SearchController.cs) encapsulates the methods needed to search and find matches.
## Search
### Tags
- We compare the user input with the label of each results. If it doesn't match we search the tags of the result too.
- For each result two tag strings are defined. One for a search with system time/date and one for a search with a custom time/date. Most of the results (except the era results) are using one of the generic tag lists: Date, Time or Format
- The selection of the tag (for "system time/date" or "custom time/date") is happening at search time in the `AvailableResultsList.cs` class.
- The different tags in a list are split by the `;` character.
### Score
- The plugin uses `FuzzyMatching` to get the matching formats, if the user searches for a specific format. The score is set based on the `FuzzySearch` result.
- To achieve a better balance between sub title matches and tag matches the score of tag matches is divided by two.
### Match requirements for global queries
On global queries the high score returned by `FuzzySearch` has negative impacts on the user experience and the search results priority/order of other plugins. To mitigate this we defined some matching requirements:
- If the query is a word of the following conjunction list, we don't return any results: for, and, nor, but, or, so
- We don't have 'yet' (synonym of 'now') on the list, because this could block results in some languages.
- The first word of the query has to be a full match with a word in the label or tag list.
- For both requirements we compare case-insensitive.
## [Unit Tests](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests)
We have a [Unit Test project](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests) that executes various test to ensure that the plugin works as expected.
### [`ImageTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/ImageTests.cs)
- The [`ImageTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/ImageTests.cs) class contains tests to validate that each result shows the expected and correct image.
- That we can execute the tests at any time on any machine, we set the thread culture always to `en-us` while executing the tests.
### [`PluginSettingsTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/PluginSettingsTests.cs)
- The [`PluginSettingsTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/PluginSettingsTests.cs) class contains tests to validate that all settings exist and that they have the correct default values.
### [`QueryTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/QueryTests.cs)
- The [`QueryTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/QueryTests.cs) class contains tests to validate that the user gets the correct results when searching.,
- That we can execute the tests at any time on any machine, we set the thread culture always to `en-us` while executing the tests.
### [`StringParserTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/StringParserTests.cs)
- The [`StringParserTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/StringParserTests.cs) class contains tests to validate that the typed string gets converted correctly into a `DateTime` object.
- That we can execute the tests at any time on any machine, we set the thread culture always to `en-us` while executing the tests.
### [`TimeAndDateHelperTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/TimeAndDateHelperTests.cs)
- The [`TimeAndDateHelperTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/TimeAndDateHelperTests.cs) class contains tests to validate important methods form the `TimeAndDateHelper` class that are not used for string parsing.
### [`TimeDateResultTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/TimeDateResultTests.cs)
- The [`TimeDateResultTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/TimeDateResultTests.cs) class contains tests to validate that the time and date values are correctly formatted/calculated.
- That we can execute the tests at any time on any machine, we use a specified date/time value and set the thread culture always to `en-us` while executing the tests.
- Some tests use custom settings for the first day of week and the first week of year. (This is done in the tests for the affected results to validate them for different settings/cultures.)
- Some tests contain checks that calculate the expected result at runtime instead of using an expected value written fix in the code. This is done to get valid results on every machine at any time.
@@ -0,0 +1,19 @@
# URI Plugin
The URI Plugin, as the name suggests is used to directly run the URI that has been entered by the user as a query. This is done by parsing the entry and validating the URI, followed by executing it.
![Image of URI plugin](/doc/images/launcher/plugins/uri.png)
### [`URI Parser`](src/modules/launcher/Plugins/Microsoft.Plugin.Uri/UriHelper/ExtendedUriParser.cs)
- The [`ExtendedUriParser.cs`](src/modules/launcher/Plugins/Microsoft.Plugin.Uri/UriHelper/ExtendedUriParser.cs) file tries to parse the user input and returns a `System.Uri` result by using the `UriBuilder`.
- It also captures other cases which the UriBuilder does not handle such as when the input ends with a `:`, `.` or `:/`.
### [`URI Resolver`](src/modules/launcher/Plugins/Microsoft.Plugin.Uri/UriHelper/UriResolver.cs)
- The [`UriResolver.cs`](src/modules/launcher/Plugins/Microsoft.Plugin.Uri/UriHelper/UriResolver.cs) file returns true for Valid hosts.
- Currently there is no additional logic for filtering out invalid hosts and it always returns true for a valid Uri that was created by parsing the user query. It can be expanded in the future to filter out certain hosts.
### Default Browser Icon
- The icon for each uri result is that of the default browser set by the user.
- These details are obtained from the user registry and updated each time the theme of PT Run is changed.
### Score
- All uri plugin results have a score of 0 which indicates that they would show up after each of the other plugins, other than the indexer plugin which also has a score of 0.
@@ -0,0 +1,11 @@
## Web Search Plugin
The Web Search Plugin, as the name suggests, is used to perform a web search - in the default search engine in the default browser - on the query that has been entered by the user.
![Image of Web Search plugin](/doc/images/launcher/plugins/WebSearch.png)
## Default Browser Icon
- The icon for each web search result is that of the default browser set by the user.
- It, and the browser path, are obtained from the user registry and updated each time the theme of PT Run is changed.
## Score
- The web search result always has a score of 0 which indicates that it would show up after each of the other plugins, other than the indexer plugin and possibly the uri plugin which both have a score of 0.
@@ -0,0 +1,154 @@
# Windows Settings Plugin
The Windows settings Plugin allows users to search the Windows settings.
## Special functions (differ from the regular functions)
* Support modern Windows settings (Windows 10+)
* Support legacy Windows settings (Windows 7, 8.1)
* Support extra programs for setting (like ODBC)
* Support search by the area of the setting (like `Privacy`)
* Support search for alternative names of a setting
## How to add a new Windows Setting or change one
All Windows settings are located in `WindowsSettings.json` in root folder of the project.
The `WindowsSettings.json` use a JSON schema file that make it easier to edit it.
| Key | Optional | Value type | String prefix |
| ------------------- | -------- | ----------------- | ------------- |
| `Name` | **No** | String | |
| `Type` | **No** | String | `App` |
| `Command` | **No** | String | |
| `Areas` | Yes | List with strings | `Area` |
| `AltNames` | Yes | List with strings | |
| `Note` | Yes | String | `Note` |
| `IntroducedInBuild` | Yes | Integer | |
| `DeprecatedInBuild` | Yes | Integer | |
| `ShowAsFirstResult` | Yes | Boolean | |
A minimum entry for the `WindowsSettings.json` looks like:
```json
{
"Name": "mySetting",
"Type": "AppSettingsApp",
"Command": "ms-settings:mySetting"
}
```
A full entry for the `WindowsSettings.json` looks like:
```json
{
"Name": "mySetting",
"Type": "AppSettingsApp",
"Command": "ms-settings:mySetting",
"Areas": [ "AreaMySettingArea" ],
"AltNames": [ "NiceSetting" ],
"Note": "NoteMySettingNote",
"IntroducedInBuild" : 1903,
"DeprecatedInBuild" : 2004,
"ShowAsFirstResult" : true
}
```
### Remarks
* The `Command` for modern Windows settings should start with `ms-settings:`
* The `Command` for legacy Windows settings should start with `control`
* The integer value for `IntroducedInBuild` and `DeprecatedInBuild` must be in range of `0` to `4294967295`
* The strings for `Name`, `AltNames`, `Areas`, `Type` and `Note` must not contain whitespace(s) or special characters (#, €, $, etc.)
* The strings for `Name`, `AltNames`, `Areas`, `Type` and `Note` are used as ids for the resource file under `Properties\Resources.resx`
* When you add new strings make sure you have added all translations for it.
* If a result has `mmc.exe` as command and the note property is filled, the note is shown in the sub title too. (This is for special MMC results where we don't have a .msc file.)
## Scores
There are three different score types with different start values.
| Score type | Start value |
| ------------------ | ------------ |
| First result score | 10500 |
| High score | 10000 |
| Medium score | 5000 |
| Low score | 1000 |
Each score will be decreased by one when a condition match.
| Priority | Condition | Score type |
| -------- | ----------------------------------------------------------------- | ------------ |
| 1. | Settings name starts with the search value | High score |
| 2. | Settings name contain the search value | Medium score |
| 3. | Setting has no area | Low score |
| 4. | One area of the settings starts with the search value | Low score |
| 5. | Setting has no alternative name | Low score |
| 6. | One alternative name of the settings starts with the search value | Medium score |
| x. | no condition match | Low score |
### Remarks
* For each score condition we check if the property "ShowAsFirstResult" of the setting is true. If yes we use the firstResultScore instead of condition`s score.
## Important for developers
### General
* The assembly name is cached into `_assemblyName` (to avoid to many calls of `Assembly.GetExecutingAssembly()`)
## Microsoft.PowerToys.Run.Plugin.WindowsSettings project
### Important plugin values (meta-data)
| Name | Value |
| --------------- | ---------------------------------------------------- |
| ActionKeyword | `$` |
| ExecuteFileName | `Microsoft.PowerToys.Run.Plugin.WindowsSettings.dll` |
| ID | `5043CECEE6A748679CBE02D27D83747A` |
### Interfaces used by this plugin
The plugin use only these interfaces (all inside the `Main.cs`):
* `Wox.Plugin.IPlugin`
* `Wox.Plugin.IContextMenu`
* `Wox.Plugin.IPluginI18n`
### Program files
| File | Content |
| ------------------------------------- | ----------------------------------------------------------------------- |
| `Classes\WindowsSetting.cs` | A class that represent one Windows setting |
| `Classes\WindowsSettings.cs` | A wrapper class that only contains a list with Windows settings (see 1) |
| `Helper\ContextMenuHelper.cs` | All functions to build the context menu (for each result entry) |
| `Helper\JsonSettingsListHelper.cs` | All functions to load the windows settings from a JSON file |
| `Helper\ResultHelper.cs` | All functions to convert internal results into WOX results |
| `Helper\TranslationHelper.cs` | All functions to translate the result in the surface language |
| `Helper\UnsupportedSettingsHelper.cs` | All functions to filter not supported Windows settings out |
| `Helper\WindowsSettingsPathHelper.cs` | All functions to build the area paths |
| `Images\WindowsSettings.dark.png` | Symbol for the results for the dark theme |
| `Images\WindowsSettings.light.png` | Symbol for the results for the light theme |
| `Properties\Resources.Designer.resx` | File that contain all translatable keys |
| `Properties\Resources.resx` | File that contain all translatable strings in the neutral language |
| `Main.cs` | Main class, the only place that implement the WOX interfaces |
| `plugin.json` | All meta-data for this plugin |
1. We need this extra wrapper class to make it possible that the JSON file can have and use a JSON schema file.
Because the JSON file must have an object as root type, instead of an array.
### Important project values (*.csproj)
| Name | Value |
| --------------- | --------------------------------------------------------------------------------------------------- |
| TargetFramework | `net6.0-windows` (.NET 5) or `net6.0-windows10.0.19041.0` (OS version specific) |
| Platforms | `x64` |
| Output | `..\..\..\..\..\x64\Debug\modules\launcher\Plugins\Microsoft.PowerToys.Run.Plugin.WindowsSettings\` |
| RootNamespace | `Microsoft.PowerToys.Run.Plugin.WindowsSettings` |
| AssemblyName | `Microsoft.PowerToys.Run.Plugin.WindowsSettings` |
### Project dependencies
#### Projects
* `Wox.Infrastructure`
* `Wox.Plugin`
@@ -0,0 +1,84 @@
# Window Walker plugin
The window walker plugin matches the user entered query with the open windows on the system.
The user can switch to the found windows, close them or kill their process.
![Image of Window Walker plugin](/doc/images/launcher/plugins/windowwalker.png)
## Remarks
### UWP Apps
- The process of an UWP app can't be detected correctly for windows that are minimized while searching. At this time they are assigned to the generic process `ApplicationFrameHost.exe`. If the user searches for such a window while it is not minimized, then the process gets assigned correctly/updated.
### Killing processes
- Killing the Explorer process is only allowed, if each folder window is running in its own process. (See section `File Explorer setting` below.)
- You can only kill elevated processes, if you have admin permissions (UAC).
- If you kill the process of an UWP app window, you kill all instances of the app. All windows are assigned to the same process.
- Windows of UWP apps don't know their process, until they are searched in non-minimized state.
### File Explorer setting
- To kill the Process of an Explorer window, each window has to run in a separate process. Otherwise, the process is the same one as the shell process and killing the shell process will crash the shell (Windows ui).
- To enable this behavior the setting `Launch folder windows in a separate process` under `Folder Options > View` has to be enabled.
- From PowerToys Run you can open the `Folder options` dialog by clicking the information message in the search results. The information message is only shown when searching with action keyword for explorer windows and can be hidden in the plugin settings.
- Note: The folder option/process is evaluated in real time. After changing the setting it is enough to search again for the windows.
![Folder options for Window Walker](/doc/images/launcher/plugins/windowwalker_folder_options.png)
## Optional plugin settings
- The optional plugin settings are implemented via the [`ISettingProvider`](/src/modules/launcher/Wox.Plugin/ISettingProvider.cs) interface from `Wox.Plugin` project.
- All available settings for the plugin are defined in the [`WindowWalkerSettings`](/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/WindowWalkerSettings.cs) class of the plugin. The settings can be accessed everywhere in the plugin code via the static class instance `WindowWalkerSettings.Instance`.
- We have the following settings that the user can configure to change the behavior of the plugin:
| Key | Default value | Name/Description |
|--------------|-----------|------------|
| `ResultsFromVisibleDesktopOnly` | `false` | Show only results from visible desktop |
| `SubtitleShowPid` | `false` | Show process id in subtitle |
| `SubtitleShowDesktopName` | `true` | Show desktop name in subtitle (If two or more desktops exist) |
| `ConfirmKillProcess` | `true` | Request confirmation when killing a process |
| `KillProcessTree` | `false` | Kill process and its child processes |
| `OpenAfterKillAndClose` | `false` | Stay open after closing windows and killing processes (Not working with kill process confirmation) |
| `HideKillProcessOnElevatedProcesses` | `false` | Hide "kill process" button if additional permissions required |
| `HideExplorerSettingInfo` | `false` | Hide Explorer process information |
## Technical details
### [`OpenWindows.cs`](/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/OpenWindows.cs)
- The window walker plugin uses the `EnumWindows` function to enumerate all the open windows in the [`OpenWindows.cs`](/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/OpenWindows.cs) class.
### [`SearchController.cs`](/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/SearchController.cs)
- The [`SearchController`](/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/SearchController.cs) encapsulates the functions needed to search and find matches.
- It is responsible for updating the search text and performing a fuzzy search on all the open windows.
### [`Window.cs`](/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/Window.cs)
- The [`Window`](/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/Window.cs) class represents a specific window and has functions to get the name of the window, the state of the window (whether it is visible or not), the `SwitchToWindow` function which switches the desktop focus to the selected window and the `CloseThisWindow` function which closes the window. The `switchToWindow` action is performed when the user clicks on a window walker plugin result.
- The `Window` class holds a static cache with the process information of all windows we know so far and each window instance has a property which holds its process information (name, file, ...). The process data in the cache and the window property are of the type `WindowProcess`.
- To get the desktop information for a window, we use the common [`VirtualDesktopHelper`](/src/modules/launcher/Wox.Plugin/Common/VirtualDesktop/VirtualDesktopHelper.cs) in `Wox.Plugin` project. The instance of `VirtualDesktopHelper` is cached in the [`Main`](/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Main.cs) class of the plugin at runtime. The desktop information is stored in a property of the type [`VDesktop`](/src/modules/launcher/Wox.Plugin/Common/VirtualDesktop/VDesktop.cs).
### [`WindowProcess.cs`](/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/WindowProcess.cs)
- The [`WindowProcess`](/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/WindowProcess.cs) class represents a specific process for a window.
- It contains static methods to query process information from the system and instance methods/properties to hold/retrieve the process information we want to know about a window's process.
- Additionally, it contains the method `KillThisProcess` to kill the process. (If the user has not enough permissions to kill a process they are requested via UAC.)
### [`ResultHelper.cs`](/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/ResultHelper.cs)
- The [`ResultHelper`](/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/ResultHelper.cs) class contains the code to create the list with all results for PT Run based on the data returned from `SearchController` class.
- There is a special result that is added if the folder windows doesn't run in separate processes and the user searches for Explorer windows using the action keyword.
- This result informs the user that there is a setting that must be enabled to be able to kill Explorer processes.
- The result can be disabled in plugin options. When it is clicked it opens the folder options.
### [`ContextMenuHelper.cs`](/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/ContextMenuHelper.cs)
- The [`ContextMenuHelper`](/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/ContextMenuHelper.cs) class provides the code for the context menu items.
### [`WindowWalkerSettings.cs`](/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/WindowWalkerSettings.cs)
- The [`WindowWalkerSettings`](/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/WindowWalkerSettings.cs) class provides access to all optional plugin settings.
- The class has a static property called `Instance` that holds an instance of the class itself. This allows us to access the settings from everywhere in the plugin code without having additional parameters in our methods.
### Score
The window walker plugin uses [`FuzzyMatching`](/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/FuzzyMatching.cs) to get the matching indices and calculates the score by creating a 2 dimensional array of the window and the query text.
## [Unit Tests](/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker.UnitTests)
We have a [Unit Test project](/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker.UnitTests) that executes various test to ensure that the plugin works as expected.
### [`PluginSettingsTests.cs`](/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker.UnitTests/PluginSettingsTests.cs)
- The [`PluginSettingsTests.cs`](/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker.UnitTests/PluginSettingsTests.cs) class contains tests to validate that all settings exist and that they have the correct default values.
@@ -0,0 +1,24 @@
# Project Structure
## Overview
`PowerToys Run` is divided across several projects to keep a logical separation between plugins and core functionality. The following sections provide a brief overview of each project.
![Image of project dependency](/doc/images/launcher/launcher_dependency.PNG)
Fig 1. Project along with their dependencies in `PowerToys Run` ecosystem.
## Project Description
#### [`PowerLauncher`](/src/modules/launcher/PowerLauncher)
This is the startup project for the `PowerToys Run.` It is a WPF desktop application and follows the `Model-View-ViewModel (MVVM)` design pattern. Plugins play the role of `Model` and provide data to `ViewModel.`
#### [`PowerLauncher.Telemetry`](/src/modules/launcher/PowerLauncher.Telemetry)
[`PowerLauncher.Telemetry`](/src/modules/launcher/PowerLauncher.Telemetry) is a .net core project that contains telemetry events generated by `PowerLauncher.` These events have been discussed in detail in [Launcher Telemetry](/doc/devdocs/modules/launcher/telemetry.md).
#### [`Wox.Core`](/src/modules/launcher/Wox.Core)
[`Wox.Core`](/src/modules/launcher/Wox.Core) is a .net core project that contains helper classes required by the `PowerLauncher` project. Two major functionalities encapsulated in this project are [`PluginManager`](/src/modules/launcher/Wox.Core/Plugin/PluginManager.cs) and [`Query Builder.`](/src/modules/launcher/Wox.Core/Plugin/QueryBuilder.cs) [`PluginManager`](/src/modules/launcher/Wox.Core/Plugin/PluginManager.cs) provides an interface for managing C# plugins. [`Query Builder.`](/src/modules/launcher/Wox.Core/Plugin/QueryBuilder.cs) decimate user-typed query string and creates a [`Query`](/src/modules/launcher/Wox.Plugin/Query.cs) object. [`Query`](/src/modules/launcher/Wox.Plugin/Query.cs) object contains the action keyword and cleaned query, which is then sent to all plugins.
#### [`Wox.Infrastructure`](/src/modules/launcher/Wox.Infrastructure)
[`Wox.Infrastructure`](/src/modules/launcher/Wox.Infrastructure) is a .net core project that contains helper classes required for image manipulation and storage by the `PowerLauncher` project and the plugins. [`ImageLoader.cs`](/src/modules/launcher/Wox.Infrastructure/Image/ImageLoader.cs) class is used to load icons for `Win32` program. It also provides caching functionality to speed up image loading for frequently queried programs.
#### [`Wox.Plugin`](/src/modules/launcher/Wox.Plugin)
[`Wox.Plugin`](/src/modules/launcher/Wox.Plugin) contains interfaces that facilitate communication between `PowerLauncher` and plugins. These interfaces have been discussed in detail in [Flow of data between ViewModels and Plugins(Model)](/doc/devdocs/modules/launcher/architecture.md#flow-of-data-between-viewmodels-and-pluginsmodel). It also contains a helper class for logging. [`Log.cs`](/src/modules/launcher/Wox.Plugin/Logger/Log.cs) provides an abstraction for logging error, information, and output to text files. These files are stored at `%userprofile%/appdata/local/microsoft/powertoys/powertoys run/Logs.`
+24
View File
@@ -0,0 +1,24 @@
# Table of Contents
1. [Architecture](/doc/devdocs/modules/launcher/architecture.md)
2. [Debugging](/doc/devdocs/modules/launcher/debugging.md)
3. [New Plugin Checklist](/doc/devdocs/modules/launcher/new-plugin-checklist.md)
4. [Project Structure](/doc/devdocs/modules/launcher/project_structure.md)
5. [Telemetry](/doc/devdocs/modules/launcher/telemetry.md)
6. Plugins
- [Overview](/doc/devdocs/modules/launcher/plugins/overview.md)
- [Calculator](/doc/devdocs/modules/launcher/plugins/calculator.md)
- [Folder](/doc/devdocs/modules/launcher/plugins/folder.md)
- [History](/doc/devdocs/modules/launcher/plugins/history.md)
- [Indexer](/doc/devdocs/modules/launcher/plugins/indexer.md)
- [OneNote](/doc/devdocs/modules/launcher/plugins/onenote.md)
- [Program](/doc/devdocs/modules/launcher/plugins/program.md)
- [Registry](/doc/devdocs/modules/launcher/plugins/registry.md)
- [Shell](/doc/devdocs/modules/launcher/plugins/shell.md)
- [Time and Date](/doc/devdocs/modules/launcher/plugins/timedate.md)
- [Unit Converter](/doc/devdocs/modules/launcher/plugins/community.unitconverter.md)
- [Uri](/doc/devdocs/modules/launcher/plugins/uri.md)
- [Value Generator](/doc/devdocs/modules/launcher/plugins/community.valuegenerator.md)
- [Web Search](/doc/devdocs/modules/launcher/plugins/WebSearch.md)
- [Windows Settings](/doc/devdocs/modules/launcher/plugins/windowssettings.md)
- [Windows System Commands](/doc/devdocs/modules/launcher/plugins/system.md)
- [Window Walker](/doc/devdocs/modules/launcher/plugins/windowwalker.md)
+14
View File
@@ -0,0 +1,14 @@
# Telemetry
## Overview
`PowerLauncher.Telemetry` project contains telemetry events generated by `PowerToys Run.` These event classes are derived from the [`EventBase`](/src/common/ManagedTelemetry/Telemetry/Events/EventBase.cs) class and [`IEvent`](/src/common/ManagedTelemetry/Telemetry/Events/IEvent.cs) class. [`IEvent`](/src/common/ManagedTelemetry/Telemetry/Events/IEvent.cs) class provides the lowest level abstraction, containing attributes such as privacy tags needed for every telemetry data. [`EventBase`](/src/common/ManagedTelemetry/Telemetry/Events/EventBase.cs) class provides a higher-level abstraction, having attributes common to all `PowerToys` telemetry events.
## Events
The following events are generated by `PowerLauncher`:
1. [`LauncherBootEvent`](/src/modules/launcher/PowerLauncher.Telemetry/Events/LauncherBootEvent.cs): This event captures the time taken by `PowerLauncher` to load all plugins, perform cold start, and setup UI environment.
2. [`LauncherHideEvent`](/src/modules/launcher/PowerLauncher.Telemetry/Events/LauncherHideEvent.cs): This event is generated when the `PowerLauncher` window is hidden.
3. [`LauncherColdStateHotkeyEvent`](/src/modules/launcher/PowerLauncher.Telemetry/Events/LauncherColdStateHotkeyEvent.cs): This event logs time from the first Alt+Space press till the `PowerLauncher` window is visible.
4. [`LauncherFirstDeleteEvent`](/src/modules/launcher/PowerLauncher.Telemetry/Events/LauncherFirstDeleteEvent.cs): This event is generated after the first delete is pressed after `PowerLauncher` is visible.
5. [`LauncherQueryEvent`](/src/modules/launcher/PowerLauncher.Telemetry/Events/LauncherQueryEvent.cs): This event is generated for every query that is typed in the searchbox. It logs query time, number of results, and query length.
6. [`LauncherResultActionEvent`](/src/modules/launcher/PowerLauncher.Telemetry/Events/LauncherResultActionEvent.cs): This event is generated when a context menu action is triggered.
7. [`LauncherShowEvent`](/src/modules/launcher/PowerLauncher.Telemetry/Events/LauncherShowEvent.cs): This event is generated when the `PowerLauncher` window is shown.
8. [`LauncherWarmStateHotkeyEvent`](/src/modules/launcher/PowerLauncher.Telemetry/Events/LauncherWarmStateHotkeyEvent.cs): This event logs time from the Alt+Space press until the PT Run window is visible.
+110
View File
@@ -0,0 +1,110 @@
# Light Switch
[Public Overview Microsoft Learn](https://learn.microsoft.com/en-us/windows/powertoys/light-switch)
## Quick Links
* [All Issues](https://github.com/microsoft/PowerToys/issues?q=is%3Aissue%20state%3Aopen%20label%3AProduct-LightSwitch)
* [Bugs](https://github.com/microsoft/PowerToys/issues?q=is%3Aissue%20state%3Aopen%20label%3AProduct-LightSwitch%20label%3AIssue-Bug)
* [Pull Requests](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+is%3Aopen+label%3AProduct-LightSwitch)
## Overview
The **Light Switch** module lets users automatically transition between light and dark mode using a timed schedule or a keyboard shortcut.
## Features
* Set custom times to start and stop dark mode.
* Use geolocation to determine local sunrise and sunset times.
* Apply offsets in sunrise mode (e.g., 15 minutes before sunset).
* Quickly toggle between modes with a keyboard shortcut (`Ctrl+Shift+Win+D` by default).
* Choose whether theme changes apply to:
* Apps only
* System only
* Both apps and system
## Architecture
### Main Components
* **Shortcut/Hotkey**
Listens for a hotkey event. Calling `onHotkey()` flips the theme flags.
> **Note:** Using the shortcut overrides the current schedule until the next transition event.
* **LightSwitchService.cpp**
is the heart beat of the module. Controls ticking every minute and depending on user actions (manual override, settings changing, etc) triggers the state manager to perform the corresponding operation.
* **LightSwitchStateManager.cpp**
handles updating the state based on the signals sent by LightSwitchService.
* **SettingsXAML/LightSwitch**
Provides the settings UI for configuring schedules, syncing location, and customizing shortcuts.
* **Settings.UI/ViewModels/LightSwitchViewModel.cs**
Handles updates to the settings file and communicates changes to the front end.
* **modules/LightSwitch/Tests**
Contains UI tests that verify interactions between the settings UI, system state, and `settings.json`.
### Data Flow
1. User configures settings in the UI (default: manual mode, light mode from 06:0018:00).
2. Every minute, the service checks the time.
* If its not a threshold, the service sleeps until the next minute.
* If it matches a threshold, the service applies the theme based on settings and returns to sleep.
3. At **midnight**, when in *Sunrise to Sunset* mode, the service updates daily sunrise and sunset times.
4. If the machine was asleep during a scheduled event, the service applies the correct settings at the next check.
## User Interface
The modules settings are exposed in the PowerToys Settings UI. Options include:
* Shortcut customization
* Mode selection (Manual or Sunrise to Sunset)
* Manual start/stop times (manual mode only)
* Automatic sunrise/sunset calculation (location-based)
* Time offsets (sunrise mode)
* Target scope (system, apps, or both)
## Development Environment Setup
### Prerequisites
* Visual Studio 2019 or later
* Windows 10 SDK
* PowerToys repository cloned from GitHub
### Building and Testing
1. Clone the repo:
```sh
git clone https://github.com/microsoft/PowerToys.git
```
2. Initialize submodules:
```sh
git submodule update --init --recursive
```
3. Build the solution:
```sh
msbuild -restore -p:RestorePackagesConfig=true -p:Platform=ARM64 -m PowerToys.slnx
```
> Note: This may take some time.
4. Set `runner` as the startup project and press **F5**.
5. Enable Light Switch in PowerToys Settings.
6. To debug the service:
* Press `Ctrl+Alt+P` or go to **Debug > Attach to Process**.
* Select `LightSwitchService.exe` and click **Attach**.
* You can now set breakpoints in the service files.
7. To debug the Settings UI:
* Set the startup project to `PowerToys.Settings` and press **F5**.
* Note: Light Switch settings will not persist in this mode (they depend on the service executable).
* Alternatively, you can attach `PowerToys.Settings.exe` to the debugger while `runner` is running to test the full flow with breakpoints.
@@ -0,0 +1,95 @@
# Find My Mouse
Find My Mouse is a utility that helps users locate their mouse pointer by creating a spotlight effect when activated. It is based on Raymond Chen's SuperSonar utility.
## Implementation
Find My Mouse displays a spotlight effect centered on the cursor location when activated via a keyboard shortcut (typically a double-press of the Ctrl key).
### Key Files
- `src/modules/MouseUtils/FindMyMouse/FindMyMouse.cpp` - Contains the main implementation
- Key function: `s_WndProc` - Handles window messages for the utility
### Enabling Process
When the utility is enabled:
1. A background thread is created to run the Find My Mouse logic asynchronously:
```cpp
// Enable the PowerToy
virtual void enable()
{
m_enabled = true; // Mark the module as enabled
Trace::EnableFindMyMouse(true); // Enable telemetry
std::thread([=]() { FindMyMouseMain(m_hModule, m_findMyMouseSettings); }).detach(); // Run main logic in background
}
```
2. The `CompositionSpotlight` instance is initialized with user settings:
```cpp
CompositionSpotlight sonar;
sonar.ApplySettings(settings, false); // Apply settings
if (!sonar.Initialize(hinst))
{
Logger::error("Couldn't initialize a sonar instance.");
return 0;
}
m_sonar = &sonar;
```
3. The utility listens for raw input events using `WM_INPUT`, which provides more precise and responsive input detection than standard mouse events.
### Activation Process
The activation process works as follows:
1. **Keyboard Hook Detects Shortcut**
- A global low-level keyboard hook is set up during initialization
- The hook monitors for the specific activation pattern (double Ctrl press)
- Once matched, it sends a `WM_PRIV_SHORTCUT` message to the sonar window:
```cpp
virtual void OnHotkeyEx() override
{
Logger::trace("OnHotkeyEx()");
HWND hwnd = GetSonarHwnd();
if (hwnd != nullptr)
{
PostMessageW(hwnd, WM_PRIV_SHORTCUT, NULL, NULL);
}
}
```
2. **Message Handler Triggers Action**
- The custom message is routed to `BaseWndProc()`
- The handler toggles the sonar animation:
```cpp
if (message == WM_PRIV_SHORTCUT)
{
if (m_sonarStart == NoSonar)
StartSonar(); // Trigger sonar animation
else
StopSonar(); // Cancel if already running
}
```
3. **Sonar Animation**
- `StartSonar()` uses `CompositionSpotlight` to display a highlight (ripple/pulse) centered on the mouse pointer
- The animation is temporary and fades automatically or can be cancelled by user input
### Event Handling
The Find My Mouse utility handles several types of events:
- **Mouse Events**: Trigger sonar animations (e.g., after a shake or shortcut)
- **Keyboard Events**: May cancel or toggle the effect
- **Custom Shortcut Messages**: Handled to allow toggling Find My Mouse using a user-defined hotkey
When the main window receives a `WM_DESTROY` message (on shutdown or disable), the sonar instance is properly cleaned up, and the message loop ends gracefully.
## Debugging
To debug Find My Mouse:
- Attach to the PowerToys Runner process directly
- Set breakpoints in the `FindMyMouse.cpp` file
- When debugging the spotlight effect, visual artifacts may occur due to the debugger's overhead
@@ -0,0 +1,92 @@
# Mouse Highlighter
Mouse Highlighter is a utility that visualizes mouse clicks by displaying a highlight effect around the cursor when clicked.
## Implementation
Mouse Highlighter runs within the PowerToys Runner process and draws visual indicators (typically circles) around the mouse cursor when the user clicks.
### Key Files
- `src/modules/MouseUtils/MouseHighlighter/MouseHighlighter.cpp` - Contains the main implementation
- Key function: `WndProc` - Handles window messages and mouse events
### Enabling Process
When the utility is enabled:
1. A background thread is created to run the mouse highlighter logic asynchronously:
```cpp
std::thread([=]() { MouseHighlighterMain(m_hModule, m_highlightSettings); }).detach();
```
2. The Highlighter instance is initialized and configured with user settings:
```cpp
Highlighter highlighter;
Highlighter::instance = &highlighter;
highlighter.ApplySettings(settings);
highlighter.MyRegisterClass(hInstance);
```
3. A highlighter window is created:
```cpp
instance->CreateHighlighter();
```
4. The utility:
- Registers a custom window class
- Creates a transparent window for drawing visuals
- Handles the `WM_CREATE` message to initialize the Windows Composition API (Compositor, visuals, and target)
### Activation Process
The activation process works as follows:
1. **Shortcut Detection**
- The system detects when the activation shortcut is pressed
- A global hotkey listener (registered with `RegisterHotKey` or similar hook) detects the shortcut
2. **Message Transmission**
- A message (like `WM_SWITCH_ACTIVATION_MODE`) is sent to the highlighter window via `PostMessage()` or `SendMessage()`
3. **Window Procedure Handling**
- The `WndProc` of the highlighter window receives the message and toggles between start and stop drawing modes:
```cpp
case WM_SWITCH_ACTIVATION_MODE:
if (instance->m_visible)
instance->StopDrawing();
else
instance->StartDrawing();
```
4. **Drawing Activation**
- If turning ON, `StartDrawing()` is called, which:
- Moves the highlighter window to the topmost position
- Slightly offsets the size to avoid transparency bugs
- Shows the transparent drawing window
- Hooks into global mouse events
- Starts drawing visual feedback around the mouse
- If turning OFF, `StopDrawing()` is called, which:
- Hides the drawing window
- Removes the mouse hook
- Stops rendering highlighter visuals
### Drawing Process
When the mouse highlighter is active:
1. A low-level mouse hook detects mouse button events
2. On click, the highlighter draws a circle (or other configured visual) at the cursor position
3. The visual effect fades over time according to user settings
4. Each click can be configured to show different colors based on the mouse button used
## Debugging
To debug Mouse Highlighter:
- Attach to the PowerToys Runner process directly
- Set breakpoints in the `MouseHighlighter.cpp` file
- Be aware that visual effects may appear different or stuttery during debugging due to the debugger's overhead
## Known Issues
- There is a reported bug where the highlight color stays on after toggling opacity to 0
- This issue has been present for more than six months and can still be reproduced in recent PowerToys releases
@@ -0,0 +1,90 @@
# Mouse Jump
Mouse Jump is a utility that allows users to quickly move their cursor to any location on screen using a grid-based overlay interface.
## Implementation
Unlike the other Mouse Utilities that run within the PowerToys Runner process, Mouse Jump operates as a separate process that communicates with the Runner via events.
### Key Files
- `src/modules/MouseUtils/MouseJump` - Contains the Runner interface for Mouse Jump
- `src/modules/MouseUtils/MouseJumpUI` - Contains the UI implementation
- `src/modules/MouseUtils/MouseJumpUI/MainForm.cs` - Main UI form implementation
- `src/modules/MouseUtils/MouseJump.Common` - Shared code between the Runner and UI components
### Enabling Process
When the utility is enabled:
1. A separate UI process is launched for Mouse Jump:
```cpp
void launch_process()
{
Logger::trace(L"Starting MouseJump process");
unsigned long powertoys_pid = GetCurrentProcessId();
std::wstring executable_args = L"";
executable_args.append(std::to_wstring(powertoys_pid));
SHELLEXECUTEINFOW sei{ sizeof(sei) };
sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI };
sei.lpFile = L"PowerToys.MouseJumpUI.exe";
sei.nShow = SW_SHOWNORMAL;
sei.lpParameters = executable_args.data();
if (ShellExecuteExW(&sei))
{
Logger::trace("Successfully started the Mouse Jump process");
}
else
{
Logger::error(L"Mouse Jump failed to start. {}", get_last_error_or_default(GetLastError()));
}
m_hProcess = sei.hProcess;
}
```
2. The Runner creates shared events for communication with the UI process:
```cpp
m_hInvokeEvent = CreateDefaultEvent(CommonSharedConstants::MOUSE_JUMP_SHOW_PREVIEW_EVENT);
m_hTerminateEvent = CreateDefaultEvent(CommonSharedConstants::TERMINATE_MOUSE_JUMP_SHARED_EVENT);
```
### Activation Process
The activation process works as follows:
1. **Shortcut Detection**
- When the activation shortcut is pressed, the Runner signals the shared event `MOUSE_JUMP_SHOW_PREVIEW_EVENT`
2. **UI Display**
- The MouseJumpUI process listens for this event and displays a screen overlay when triggered
- The overlay shows a grid or other visual aid to help select a destination point
3. **Mouse Movement**
- User selects a destination point on the overlay
- The UI process moves the mouse cursor to the selected position
4. **Termination**
- When the utility needs to be disabled or PowerToys is shutting down, the Runner signals the `TERMINATE_MOUSE_JUMP_SHARED_EVENT`
- The UI process responds by cleaning up and exiting
### User Interface
The Mouse Jump UI is implemented in C# using Windows Forms:
- Displays a semi-transparent overlay over the entire screen
- May include grid lines, quadrant divisions, or other visual aids to help with precision selection
- Captures mouse and keyboard input to allow for selection and cancellation
- Moves the mouse cursor to the selected location upon confirmation
## Debugging
To debug Mouse Jump:
1. Start by debugging the Runner process directly
2. Then attach the debugger to the MouseJumpUI process when it launches
3. Note: Debugging MouseJumpUI directly is challenging because it requires the Runner's process ID to be passed as a parameter at launch
## Community Contributions
Mouse Jump was initially contributed by Michael Clayton (@mikeclayton) and is based on his FancyMouse utility.
@@ -0,0 +1,114 @@
# Mouse Pointer Crosshairs
Mouse Pointer Crosshairs is a utility that displays horizontal and vertical lines that intersect at the mouse cursor position, making it easier to track the cursor location on screen.
## Implementation
Mouse Pointer Crosshairs runs within the PowerToys Runner process and draws crosshair lines that follow the cursor in real-time.
### Key Files
- `src/modules/MouseUtils/MousePointerCrosshairs/InclusiveCrosshairs.cpp` - Contains the main implementation
- Key function: `WndProc` - Handles window messages and mouse events
### Enabling Process
When the utility is enabled:
1. A background thread is created to run the crosshairs logic asynchronously:
```cpp
std::thread([=]() { InclusiveCrosshairsMain(hInstance, settings); }).detach();
```
2. The InclusiveCrosshairs instance is initialized and configured with user settings:
```cpp
InclusiveCrosshairs crosshairs;
InclusiveCrosshairs::instance = &crosshairs;
crosshairs.ApplySettings(settings, false);
crosshairs.MyRegisterClass(hInstance);
```
3. The utility:
- Creates the crosshairs visuals using Windows Composition API inside `CreateInclusiveCrosshairs()`
- Handles the `WM_CREATE` message to initialize the Windows Composition API (Compositor, visuals, and target)
- Creates a transparent, layered window for drawing the crosshairs with specific extended window styles (e.g., `WS_EX_LAYERED`, `WS_EX_TRANSPARENT`)
### Activation Process
The activation process works as follows:
1. **Shortcut Detection**
- When the activation shortcut is pressed, the window procedure (`WndProc`) receives a custom message `WM_SWITCH_ACTIVATION_MODE`
2. **Toggle Drawing State**
```cpp
case WM_SWITCH_ACTIVATION_MODE:
if (instance->m_drawing)
{
instance->StopDrawing();
}
else
{
instance->StartDrawing();
}
break;
```
3. **Start Drawing Function**
- The `StartDrawing()` function is called to:
- Log the start of drawing
- Update the crosshairs position
- Check if the cursor should be auto-hidden, and set a timer for auto-hide if enabled
- Show the crosshairs window if the cursor is visible
- Set a low-level mouse hook to track mouse movements asynchronously
```cpp
void InclusiveCrosshairs::StartDrawing()
{
Logger::info("Start drawing crosshairs.");
UpdateCrosshairsPosition();
m_hiddenCursor = false;
if (m_crosshairs_auto_hide)
{
CURSORINFO cursorInfo{};
cursorInfo.cbSize = sizeof(cursorInfo);
if (GetCursorInfo(&cursorInfo))
{
m_hiddenCursor = !(cursorInfo.flags & CURSOR_SHOWING);
}
SetAutoHideTimer();
}
if (!m_hiddenCursor)
{
ShowWindow(m_hwnd, SW_SHOWNOACTIVATE);
}
m_drawing = true;
m_mouseHook = SetWindowsHookEx(WH_MOUSE_LL, MouseHookProc, m_hinstance, 0);
}
```
4. **Stop Drawing Function**
- The `StopDrawing()` function is called to:
- Remove the mouse hook
- Kill the auto-hide timer
- Hide the crosshairs window
- Log the stop of drawing
### Cursor Tracking
While active, the utility:
1. Uses a low-level mouse hook (`WH_MOUSE_LL`) to track cursor movement
2. Updates crosshair positions in real-time as the mouse moves
3. Supports auto-hiding functionality when the cursor is inactive for a specified period
## Debugging
To debug Mouse Pointer Crosshairs:
- Attach to the PowerToys Runner process directly
- Set breakpoints in the `InclusiveCrosshairs.cpp` file
- Be aware that during debugging, moving the mouse may cause unexpected or "strange" visual behavior because:
- The mouse hook (`MouseHookProc`) updates the crosshairs position on every `WM_MOUSEMOVE` event
- This frequent update combined with the debugger's overhead or breakpoints can cause visual glitches or stutters
+129
View File
@@ -0,0 +1,129 @@
# Mouse Utilities
[Public overview - Microsoft Learn](https://learn.microsoft.com/en-us/windows/powertoys/mouse-utilities)
## Quick Links
[All Issues](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3A%22Product-Mouse%20Utilities%22)<br>
[Bugs](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3AIssue-Bug%20label%3A%22Product-Mouse%20Utilities%22)<br>
[Pull Requests](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+is%3Aopen+label%3A%22Product-Mouse+Utilities%22)
Mouse Utilities is a collection of tools designed to enhance mouse and cursor functionality on Windows. The module contains four sub-utilities that provide different mouse-related features.
## Overview
Mouse Utilities includes the following sub-modules:
- **[Find My Mouse](findmymouse.md)**: Helps locate the mouse pointer by creating a visual spotlight effect when activated
- **[Mouse Highlighter](mousehighlighter.md)**: Visualizes mouse clicks with customizable highlights
- **[Mouse Jump](mousejump.md)**: Allows quick cursor movement to specific screen locations
- **[Mouse Pointer Crosshairs](mousepointer.md)**: Displays crosshair lines that follow the mouse cursor
## Architecture
Most of the sub-modules (Find My Mouse, Mouse Highlighter, and Mouse Pointer Crosshairs) run within the PowerToys Runner process as separate threads. Mouse Jump is more complex and runs as a separate process that communicates with the Runner via events.
### Code Structure
#### Settings UI
- [MouseUtilsPage.xaml](/src/settings-ui/Settings.UI/SettingsXAML/Views/MouseUtilsPage.xaml)
- [MouseJumpPanel.xaml](/src/settings-ui/Settings.UI/SettingsXAML/Panels/MouseJumpPanel.xaml)
- [MouseJumpPanel.xaml.cs](/src/settings-ui/Settings.UI/SettingsXAML/Panels/MouseJumpPanel.xaml.cs)
- [MouseUtilsViewModel.cs](/src/settings-ui/Settings.UI/ViewModels/MouseUtilsViewModel.cs)
- [MouseUtilsViewModel_MouseJump.cs](/src/settings-ui/Settings.UI/ViewModels/MouseUtilsViewModel_MouseJump.cs)
#### Runner and Module Implementation
- [FindMyMouse](/src/modules/MouseUtils/FindMyMouse)
- [MouseHighlighter](/src/modules/MouseUtils/MouseHighlighter)
- [MousePointerCrosshairs](/src/modules/MouseUtils/MousePointerCrosshairs)
- [MouseJump](/src/modules/MouseUtils/MouseJump)
- [MouseJumpUI](/src/modules/MouseUtils/MouseJumpUI)
- [MouseJump.Common](/src/modules/MouseUtils/MouseJump.Common)
## Community Contributors
- **Michael Clayton (@mikeclayton)**: Contributed the initial version of the Mouse Jump tool and several updates based on his FancyMouse utility
- **Raymond Chen (@oldnewthing)**: Find My Mouse is based on Raymond Chen's SuperSonar
## Known Issues
- Mouse Highlighter has a reported bug where the highlight color stays on after toggling opacity to 0
## UI Test Automation
Mouse Utilities is currently undergoing a UI Test migration process to improve automated testing coverage. You can track the progress of this migration at:
[Mouse Utils UI Test Migration Progress](https://github.com/microsoft/PowerToys/blob/feature/UITestAutomation/src/modules/MouseUtils/MouseUtils.UITests/Release-Test-Checklist-Migration-Progress.md)
## See Also
For more detailed implementation information, please refer to the individual utility documentation pages linked above.
#### Activation Process
1. A keyboard hook detects the activation shortcut (typically double-press of Ctrl)
2. A `WM_PRIV_SHORTCUT` message is sent to the sonar window
3. `StartSonar()` is called to display a spotlight animation centered on the mouse pointer
4. The animation automatically fades or can be cancelled by user input
### Mouse Highlighter
Mouse Highlighter visualizes mouse clicks by displaying a highlight effect around the cursor when clicked.
#### Key Components
- Uses Windows Composition API for rendering
- Main implementation in `MouseHighlighter.cpp`
- Core logic handled by the `WndProc` function
#### Activation Process
1. When activated, it creates a transparent overlay window
2. A mouse hook monitors for click events
3. On click detection, the highlighter draws a circle or other visual indicator
4. The highlight effect fades over time based on user settings
### Mouse Pointer Crosshairs
Displays horizontal and vertical lines that intersect at the mouse cursor position.
#### Key Components
- Uses Windows Composition API for rendering
- Core implementation in `InclusiveCrosshairs.cpp`
- Main logic handled by the `WndProc` function
#### Activation Process
1. Creates a transparent, layered window for drawing crosshairs
2. When activated via shortcut, calls `StartDrawing()`
3. Sets a low-level mouse hook to track cursor movement
4. Updates crosshairs position on every mouse movement
5. Includes auto-hide functionality for cursor inactivity
### Mouse Jump
Allows quick mouse cursor repositioning to any screen location through a grid-based UI.
#### Key Components
- Runs as a separate process (`PowerToys.MouseJumpUI.exe`)
- Communicates with Runner process via events
- UI implemented in `MainForm.cs`
#### Activation Process
1. When shortcut is pressed, Runner triggers the shared event `MOUSE_JUMP_SHOW_PREVIEW_EVENT`
2. The MouseJumpUI process displays a screen overlay
3. User selects a destination point on the overlay
4. Mouse cursor is moved to the selected position
5. The UI process can be terminated via the `TERMINATE_MOUSE_JUMP_SHARED_EVENT`
## Debugging
### Find My Mouse, Mouse Highlighter, and Mouse Pointer Crosshairs
- Debug by attaching to the Runner process directly
- Set breakpoints in the respective utility code files (e.g., `FindMyMouse.cpp`, `MouseHighlighter.cpp`, `InclusiveCrosshairs.cpp`)
- Call the respective utility by using the activation shortcut (e.g., double Ctrl press for Find My Mouse)
- During debugging, visual effects may appear glitchy due to the debugger's overhead
### Mouse Jump
- Start by debugging the Runner process
- Then attach the debugger to the MouseJumpUI process
- Note: Debugging MouseJumpUI directly is challenging as it requires the Runner's process ID as a parameter
## Known Issues
- Mouse Highlighter has a reported bug where the highlight color stays on after toggling opacity to 0
@@ -0,0 +1,31 @@
# Mouse Without Borders module
[Public overview - Microsoft Learn](https://learn.microsoft.com/en-us/windows/powertoys/mouse-without-borders)
## Quick Links
[All Issues](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3A%22Product-Mouse%20Without%20Borders%22)<br>
[Bugs](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3AIssue-Bug%20label%3A%22Product-Mouse%20Without%20Borders%22)<br>
[Pull Requests](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+is%3Aopen+label%3A%22Product-Mouse+Without+Borders%22)
This file contains the documentation for the Mouse Without Borders PowerToy module.
## Table of Contents:
- [Mouse Without Borders module](#mouse-without-borders-module)
- [Table of Contents](#table-of-contents)
- [Status colors](#status-colors)
## Status colors
The following colors are used to indicate the connection status to the user when trying to connect to another computer:
| Connection Status | Color | Hex Code |
| :-----: | :---: | :---: |
| NA | Dark Grey | `#00717171` |
| Resolving | Yellow | `#FFFFFF00` |
| Connecting | Orange | `#FFFFA500` |
| Handshaking | Blue | `#FF0000FF` |
| Error | Red | `#FFFF0000` |
| ForceClosed | Purple | `#FF800080` |
| InvalidKey | Brown | `#FFA52A2A` |
| Timeout | Pink | `#FFFFC0CB` |
| SendError | Maroon | `#FF800000` |
| Connected | Green | `#FF008000` |
+150
View File
@@ -0,0 +1,150 @@
# NewPlus Module
[Public overview - Microsoft Learn](https://learn.microsoft.com/en-us/windows/powertoys/newplus)
## Quick Links
[All Issues](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3AProduct-New%2B)<br>
[Bugs](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3AIssue-Bug%20label%3AProduct-New%2B)<br>
[Pull Requests](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+is%3Aopen+label%3AProduct-New%2B+)
## Overview
NewPlus is a PowerToys module that provides a context menu entry for creating new files directly from File Explorer. Unlike some other modules, NewPlus implements a different approach to context menu registration to avoid duplication issues in Windows 11.
## Context Menu Implementation
NewPlus implements two separate context menu handlers:
1. **Windows 10 Handler** (`NewPlus.ShellExtension.win10.dll`)
- Implements "old-style" context menu handler for Windows 10 compatibility
- Not shown in Windows 11 (this is intentional and controlled by a condition in `QueryContextMenu`)
- Registered via registry keys
2. **Windows 11 Handler** (`NewPlus.ShellExtension.dll`)
- Implemented as a sparse MSIX package for Windows 11's modern context menu
- Only registered and used on Windows 11
This implementation differs from some other modules like ImageResizer which register both handlers on Windows 11, resulting in duplicate menu entries. NewPlus uses selective registration to provide a cleaner user experience, though it can occasionally lead to issues if the Windows 11 handler fails to register properly.
## Project Structure
- **NewPlus.ShellExtension** - Windows 11 context menu handler implementation
- **NewPlus.ShellExtension.win10** - Windows 10 "old-style" context menu handler implementation
## Debugging NewPlus Context Menu Handlers
### Debugging the Windows 10 Handler
1. Update the registry to point to your debug build:
```
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\CLSID\{<NewPlus-CLSID>}]
@="PowerToys NewPlus Extension"
[HKEY_CLASSES_ROOT\CLSID\{<NewPlus-CLSID>}\InprocServer32]
@="x:\GitHub\PowerToys\x64\Debug\PowerToys.NewPlusExt.win10.dll"
"ThreadingModel"="Apartment"
[HKEY_CURRENT_USER\Software\Classes\Directory\Background\shellex\ContextMenuHandlers\NewPlus]
@="{<NewPlus-CLSID>}"
```
2. Restart Explorer:
```
taskkill /f /im explorer.exe && start explorer.exe
```
3. Attach the debugger to explorer.exe
4. Add breakpoints in the NewPlus code
5. Right-click in File Explorer to trigger the context menu handler
### Debugging the Windows 11 Handler
Debugging the Windows 11 handler requires signing the MSIX package:
1. Build PowerToys to get the MSIX packages
2. **Create certificate** (if you don't already have one):
```powershell
New-SelfSignedCertificate -Subject "CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" `
-KeyUsage DigitalSignature `
-Type CodeSigningCert `
-FriendlyName "PowerToys SelfCodeSigning" `
-CertStoreLocation "Cert:\CurrentUser\My"
```
3. **Get the certificate thumbprint**:
```powershell
$cert = Get-ChildItem -Path Cert:\CurrentUser\My | Where-Object { $_.FriendlyName -like "*PowerToys*" }
$cert.Thumbprint
```
4. **Install the certificate in the Trusted Root** (requires admin Terminal):
```powershell
Export-Certificate -Cert $cert -FilePath "$env:TEMP\PowerToysCodeSigning.cer"
Import-Certificate -FilePath "$env:TEMP\PowerToysCodeSigning.cer" -CertStoreLocation Cert:\LocalMachine\Root
```
Alternatively, you can manually install the certificate using the Certificate Import Wizard:
![wizard 1](../images/newplus/wizard1.png)
![wizard 2](../images/newplus/wizard2.png)
![wizard 3](../images/newplus/wizard3.png)
![wizard 4](../images/newplus/wizard4.png)
5. Sign the MSIX package:
```powershell
SignTool sign /fd SHA256 /sha1 <THUMBPRINT> "x:\GitHub\PowerToys\x64\Debug\WinUI3Apps\NewPlusPackage.msix"
```
Note: SignTool might not be in your PATH, so you may need to specify the full path, e.g.:
```powershell
& "C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\signtool.exe" sign /fd SHA256 /sha1 <THUMBPRINT> "x:\GitHub\PowerToys\x64\Debug\WinUI3Apps\NewPlusPackage.msix"
```
6. Check if the NewPlus package is already installed and remove it if necessary:
```powershell
Get-AppxPackage -Name Microsoft.PowerToys.NewPlusContextMenu
Remove-AppxPackage Microsoft.PowerToys.NewPlusContextMenu_<VERSION>_neutral__8wekyb3d8bbwe
```
7. Install the new signed MSIX package (optional if launching PowerToys settings first):
```powershell
Add-AppxPackage -Path "x:\GitHub\PowerToys\x64\Debug\WinUI3Apps\NewPlusPackage.msix" -ExternalLocation "x:\GitHub\PowerToys\x64\Debug\WinUI3Apps"
```
Note: If you prefer, you can simply launch PowerToys settings and enable the NewPlus module, which will install the MSIX package for you.
8. Restart Explorer to ensure the new context menu handler is loaded:
```powershell
taskkill /f /im explorer.exe && start explorer.exe
```
9. Run Visual Studio as administrator (optional)
10. Set breakpoints in the code (e.g., in [shell_context_menu.cpp#L45](/src/modules/NewPlus/NewShellExtensionContextMenu/shell_context_menu.cpp#L45))
11. Right-click in File Explorer and attach the debugger to the `DllHost.exe` process (with NewPlus title) that loads when the context menu is invoked
![alt text](../images/newplus/debug.png)
12. Right-click again (quickly) after attaching the debugger to trigger the breakpoint
Note: The DllHost process loads the DLL only when the context menu is triggered and unloads after, making debugging challenging. For easier development, consider using logging or message boxes instead of breakpoints.
## Common Issues
- If the Windows 11 context menu entry doesn't appear, it may be due to:
- The package not being properly registered
- Explorer not being restarted after registration
- A signature issue with the MSIX package
- For development and testing, using the Windows 10 handler can be easier since it doesn't require signing.
## Restoring Built-in Windows New context menu
If the Windows 11 built-in New context menu doesn't reappear on uninstalling PowerToys, some issue with settings etc. here's how to restore the built-in New context menu.
1. Open Registry Editor
1. Go to the key "Computer\HKEY_CURRENT_USER\Software\Classes\Directory\background\ShellEx\ContextMenuHandlers"
1. Delete the "New" subkey (i.e. fullpath "Computer\HKEY_CURRENT_USER\Software\Classes\Directory\background\ShellEx\ContextMenuHandlers\New")
+16
View File
@@ -0,0 +1,16 @@
# PowerToys Peek
[Public overview - Microsoft Learn](https://learn.microsoft.com/en-us/windows/powertoys/peek)
## Quick Links
[All Issues](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3AProduct-Peek)<br>
[Bugs](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3AIssue-Bug%20label%3AProduct-Peek)<br>
[Pull Requests](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+is%3Aopen+label%3AProduct-Peek)
> Documentation is currently under construction
## Dev file previewer
The previewer for developer files uses the project in [FileExplorerCommon] common project to render monaco. You can find its documentation here: [/doc/devdocs/common/FilePreviewCommon.md](/doc/devdocs/common/FilePreviewCommon.md).
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,223 @@
# MCCS Capabilities String Parser - Recursive Descent Design
## Overview
This document describes the recursive descent parser implementation for DDC/CI MCCS (Monitor Control Command Set) capabilities strings.
### Attention!
This document and the code implement are generated by Copilot.
## Grammar Definition (BNF)
```bnf
capabilities ::= ['('] segment* [')']
segment ::= identifier '(' segment_content ')'
segment_content ::= text | vcp_entries | hex_list
vcp_entries ::= vcp_entry*
vcp_entry ::= hex_byte [ '(' hex_list ')' ]
hex_list ::= hex_byte*
hex_byte ::= [0-9A-Fa-f]{2}
identifier ::= [a-z_A-Z]+
text ::= [^()]+
```
## Example Input
```
(prot(monitor)type(lcd)model(PD3220U)cmds(01 02 03 07)vcp(10 12 14(04 05 06) 16 60(11 12 0F) DC DF)mccs_ver(2.2)vcpname(F0(Custom Setting)))
```
## Parser Architecture
### Component Hierarchy
```
MccsCapabilitiesParser (main parser)
├── ParseCapabilities() → MccsParseResult
├── ParseSegment() → ParsedSegment?
├── ParseBalancedContent() → string
├── ParseIdentifier() → ReadOnlySpan<char>
├── ApplySegment() → void
│ ├── ParseHexList() → List<byte>
│ ├── ParseVcpEntries() → Dictionary<byte, VcpCodeInfo>
│ └── ParseVcpNames() → void
├── VcpEntryParser (sub-parser for vcp() content)
│ └── TryParseEntry() → VcpEntry
├── VcpNameParser (sub-parser for vcpname() content)
│ └── TryParseEntry() → (byte code, string name)
└── WindowParser (sub-parser for windowN() content)
├── Parse() → WindowCapability
└── ParseSubSegment() → (name, content)?
```
### Design Principles
1. **ref struct for Zero Allocation**
- Main parser uses `ref struct` to avoid heap allocation
- Works with `ReadOnlySpan<char>` for efficient string slicing
- No intermediate string allocations during parsing
2. **Recursive Descent Pattern**
- Each grammar rule has a corresponding parse method
- Methods call each other recursively for nested structures
- Single-character lookahead via `Peek()`
3. **Error Recovery**
- Errors are accumulated, not thrown
- Parser attempts to continue after errors
- Returns partial results when possible
4. **Sub-parsers for Specialized Content**
- `VcpEntryParser` for VCP code entries
- `VcpNameParser` for custom VCP names
- Each sub-parser handles its own grammar subset
## Parse Methods Detail
### ParseCapabilities()
Entry point. Handles optional outer parentheses and iterates through segments.
```csharp
private MccsParseResult ParseCapabilities()
{
// Handle optional outer parens
// while (!IsAtEnd()) { ParseSegment() }
// Return result with accumulated errors
}
```
### ParseSegment()
Parses a single `identifier(content)` segment.
```csharp
private ParsedSegment? ParseSegment()
{
// 1. ParseIdentifier()
// 2. Expect '('
// 3. ParseBalancedContent()
// 4. Expect ')'
}
```
### ParseBalancedContent()
Extracts content between balanced parentheses, handling nested parens.
```csharp
private string ParseBalancedContent()
{
int depth = 1;
while (depth > 0) {
if (char == '(') depth++;
if (char == ')') depth--;
}
}
```
### ParseVcpEntries()
Delegates to `VcpEntryParser` for the specialized VCP entry grammar.
```csharp
vcp_entry ::= hex_byte [ '(' hex_list ')' ]
Examples:
- "10" code=0x10, values=[]
- "14(04 05 06)" code=0x14, values=[4, 5, 6]
- "60(11 12 0F)" code=0x60, values=[0x11, 0x12, 0x0F]
```
## Comparison with Other Approaches
| Approach | Pros | Cons |
|----------|------|------|
| **Recursive Descent** (this) | Clear structure, handles nesting, extensible | More code |
| **Regex** (DDCSharp) | Concise | Hard to debug, limited nesting |
| **Mixed** (original) | Pragmatic | Inconsistent, hard to maintain |
## Performance Characteristics
- **Time Complexity**: O(n) where n = input length
- **Space Complexity**: O(1) for parsing + O(m) for output where m = number of VCP codes
- **Allocations**: Minimal - only for output structures
## Supported Segments
| Segment | Description | Parser |
|---------|-------------|--------|
| `prot(...)` | Protocol type | Direct assignment |
| `type(...)` | Display type (lcd/crt) | Direct assignment |
| `model(...)` | Model name | Direct assignment |
| `cmds(...)` | Supported commands | ParseHexList |
| `vcp(...)` | VCP code entries | VcpEntryParser |
| `mccs_ver(...)` | MCCS version | Direct assignment |
| `vcpname(...)` | Custom VCP names | VcpNameParser |
| `windowN(...)` | PIP/PBP window capabilities | WindowParser |
### Window Segment Format
The `windowN` segment (where N is 1, 2, 3, etc.) describes PIP/PBP window capabilities:
```
window1(type(PIP) area(25 25 1895 1175) max(640 480) min(10 10) window(10))
```
| Sub-field | Format | Description |
|-----------|--------|-------------|
| `type` | `type(PIP)` or `type(PBP)` | Window type (Picture-in-Picture or Picture-by-Picture) |
| `area` | `area(x1 y1 x2 y2)` | Window area coordinates in pixels |
| `max` | `max(width height)` | Maximum window dimensions |
| `min` | `min(width height)` | Minimum window dimensions |
| `window` | `window(id)` | Window identifier |
All sub-fields are optional; missing fields default to zero values.
## Error Handling
```csharp
public readonly struct ParseError
{
public int Position { get; } // Character position
public string Message { get; } // Human-readable error
}
public sealed class MccsParseResult
{
public VcpCapabilities Capabilities { get; }
public IReadOnlyList<ParseError> Errors { get; }
public bool HasErrors => Errors.Count > 0;
public bool IsValid => !HasErrors && Capabilities.SupportedVcpCodes.Count > 0;
}
```
## Usage Example
```csharp
// Parse capabilities string
var result = MccsCapabilitiesParser.Parse(capabilitiesString);
if (result.IsValid)
{
var caps = result.Capabilities;
Console.WriteLine($"Model: {caps.Model}");
Console.WriteLine($"MCCS Version: {caps.MccsVersion}");
Console.WriteLine($"VCP Codes: {caps.SupportedVcpCodes.Count}");
}
if (result.HasErrors)
{
foreach (var error in result.Errors)
{
Console.WriteLine($"Parse error at {error.Position}: {error.Message}");
}
}
```
## Edge Cases Handled
1. **Missing outer parentheses** (Apple Cinema Display)
2. **No spaces between hex bytes** (`010203` vs `01 02 03`)
3. **Nested parentheses** in VCP values
4. **Unknown segments** (logged but not fatal)
5. **Malformed input** (partial results returned)
+108
View File
@@ -0,0 +1,108 @@
# PowerRename
[Public overview - Microsoft Learn](https://learn.microsoft.com/en-us/windows/powertoys/powerrename)
## Quick Links
[All Issues](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3AProduct-PowerRename)<br>
[Bugs](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3AIssue-Bug%20label%3AProduct-PowerRename)<br>
[Pull Requests](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+is%3Aopen+label%3AProduct-PowerRename)
PowerRename is a Windows shell extension that enables batch renaming of files using search and replace or regular expressions.
## Overview
PowerRename provides a powerful and flexible way to rename files in File Explorer. It is accessible through the Windows context menu and allows users to:
- Preview changes before applying them
- Use search and replace with regular expressions
- Filter items by type (files or folders)
- Apply case-sensitive or case-insensitive matching
- Save and reuse recent search/replace patterns
## Architecture
PowerRename consists of multiple components:
- Shell Extension DLL (context menu integration)
- WinUI 3 UI application
- Core renaming library
### Technology Stack
- C++/WinRT
- WinUI 3
- COM for shell integration
## Context Menu Integration
PowerRename integrates with the Windows context menu following the [PowerToys Context Menu Handlers](../common/context-menus.md) pattern. It uses a dual registration approach to ensure compatibility with both Windows 10 and Windows 11.
### Registration Process
The context menu registration entry point is in `PowerRenameExt/dllmain.cpp::enable`, which registers:
- A traditional shell extension for Windows 10
- A sparse MSIX package for Windows 11 context menus
For more details on the implementation approach, see the [Dual Registration section](../common/context-menus.md#1-dual-registration-eg-imageresizer-powerrename) in the context menu documentation.
## Code Components
### [`dllmain.cpp`](/src/modules/powerrename/dll/dllmain.cpp)
Contains the DLL entry point and module activation/deactivation code. The key function `RunPowerRename` is called when the context menu option is invoked, which launches the PowerRenameUI.
### [`PowerRenameExt.cpp`](/src/modules/powerrename/dll/PowerRenameExt.cpp)
Implements the shell extension COM interfaces required for context menu integration, including:
- `IShellExtInit` for initialization
- `IContextMenu` for traditional context menu support
- `IExplorerCommand` for Windows 11 context menu support
### [`Helpers.cpp`](/src/modules/powerrename/lib/Helpers.cpp)
Utility functions used throughout the PowerRename module, including file system operations and string manipulation.
### [`PowerRenameItem.cpp`](/src/modules/powerrename/lib/PowerRenameItem.cpp)
Represents a single item (file or folder) to be renamed. Tracks original and new names and maintains state.
### [`PowerRenameManager.cpp`](/src/modules/powerrename/lib/PowerRenameManager.cpp)
Manages the collection of items to be renamed and coordinates the rename operation.
### [`PowerRenameRegEx.cpp`](/src/modules/powerrename/lib/PowerRenameRegEx.cpp)
Implements the regular expression search and replace functionality used for renaming.
### [`Settings.cpp`](/src/modules/powerrename/lib/Settings.cpp)
Manages user preferences and settings for the PowerRename module.
### [`trace.cpp`](/src/modules/powerrename/lib/trace.cpp)
Implements telemetry and logging functionality.
## UI Implementation
PowerRename uses WinUI 3 for its user interface. The UI allows users to:
- Enter search and replace patterns
- Preview rename results in real-time
- Access previous search/replace patterns via MRU (Most Recently Used) lists
- Configure various options
### Key UI Components
- Search/Replace input fields with x:Bind to `SearchMRU`/`ReplaceMRU` collections
- Preview list showing original and new filenames
- Settings panel for configuring rename options
- Event handling for `SearchReplaceChanged` to update the preview in real-time
## Debugging
### Debugging the Context Menu
See the [Debugging Context Menu Handlers](../common/context-menus.md#debugging-context-menu-handlers) section for general guidance on debugging PowerToys context menu extensions.
### Debugging the UI
To debug the PowerRename UI:
1. Add file paths manually in `\src\modules\powerrename\PowerRenameUILib\PowerRenameXAML\App.xaml.cpp`
2. Set the PowerRenameUI project as the startup project
3. Run in debug mode to test with the manually specified files
### Common Issues
- Context menu not appearing: Ensure the extension is properly registered and Explorer has been restarted
- UI not launching: Check Event Viewer for errors related to WinUI 3 application activation
- Rename operations failing: Verify file permissions and check for locked files
+144
View File
@@ -0,0 +1,144 @@
# Quick Accent
[Public overview - Microsoft Learn](https://learn.microsoft.com/en-us/windows/powertoys/quick-accent)
## Quick Links
[All Issues](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3A%22Product-Quick%20Accent%22)<br>
[Bugs](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3AIssue-Bug%20label%3A%22Product-Quick%20Accent%22)<br>
[Pull Requests](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+is%3Aopen+label%3A%22Product-Quick+Accent%22)
## Overview
Quick Accent (formerly known as Power Accent) is a PowerToys module that allows users to quickly insert accented characters by holding a key and pressing an activation key (like the Space key or arrow keys). For example, holding 'a' might display options like 'à', 'á', 'â', etc. This tool enhances productivity by streamlining the input of special characters without the need to memorize keyboard shortcuts.
## Architecture
The Quick Accent module consists of five projects:
```
poweraccent/
├── PowerAccent.Common/ # Language data, character mappings, LetterKey enum
├── PowerAccent.Core/ # Accent logic, settings, positioning, usage statistics
├── PowerAccent.UI/ # WinUI 3 character selector app (PowerToys.PowerAccent.exe)
├── PowerAccentKeyboardService/ # WinRT keyboard-hook component
└── PowerAccentModuleInterface/ # Native runner module DLL
```
### Module Interface (PowerAccentModuleInterface)
The Module Interface, implemented in `PowerAccentModuleInterface/dllmain.cpp`, is responsible for:
- Handling communication between PowerToys Runner and the PowerAccent process
- Managing module lifecycle (enable/disable/settings)
- Launching and terminating the PowerToys.PowerAccent.exe process
### Shared Data (PowerAccent.Common)
`PowerAccent.Common` holds the UI- and runtime-agnostic data the other projects share:
- The language / character-set definitions and per-letter accent mappings
- The managed `LetterKey` enum (kept in sync with the WinRT `LetterKey` in `PowerAccentKeyboardService/KeyboardListener.idl`)
It has no UI or WinRT dependencies and is unit-tested in isolation (`PowerAccent.Common.UnitTests`).
### Core Logic (PowerAccent.Core)
The Core component contains:
- Main accent character logic, consuming the language data from `PowerAccent.Common`
- Toolbar positioning math (9 anchor points with per-monitor DPI) and settings handling
- Management of special characters (currency, math symbols, etc.) and usage statistics
Core carries no UI-framework dependency: it raises events and accepts a UI-thread marshaller delegate instead of touching WPF/WinUI directly, and its positioning math is covered by `PowerAccent.Core.UnitTests`.
### UI Layer (PowerAccent.UI)
The UI component is a self-contained **WinUI 3 (Windows App SDK)** app, migrated from WPF.
It is responsible for:
- Displaying the accent toolbar — a non-activating, always-on-top `TransparentWindow` overlay shown with `SW_SHOWNA` so it never steals focus from the app being typed into
- Handling selection and the toolbar's sizing / positioning
- Following the system theme while the long-lived process runs
It builds to `PowerToys.PowerAccent.exe` together with its `.pri` and the bundled Windows App SDK runtime, all under the `WinUI3Apps` output folder.
### Keyboard Service (PowerAccentKeyboardService)
This component:
- Implements keyboard hooks to detect key presses
- Manages the trigger mechanism for displaying the accent toolbar
- Handles keyboard input processing
## Implementation Details
### Activation Mechanism
Quick Accent supports two activation styles, selected by the **Activation key** setting.
**Trigger-key modes** (`Left/Right arrow`, `Space`, or `Both` — the default):
1. A user presses and holds a character key (e.g., 'a')
2. User presses the trigger key
3. After a brief delay (around 300ms per setting), the accent toolbar appears
4. The user can select an accented variant using the trigger key
5. Upon releasing the keys, the selected accented character is inserted
**Press-and-hold mode** (`Press and hold the letter`, iOS/macOS style, opt-in):
1. A user presses and holds an accent-capable character key (e.g., 'a'); the base
letter is typed immediately
2. After the configured **Hold duration** (around 500ms per setting), the accent
toolbar appears automatically — no separate trigger key is required
3. The user navigates the options with the arrow keys or Space
4. Upon releasing the letter, the selected accent replaces the base letter; if no
option was selected, the base letter that was already typed simply remains
5. A quick tap (shorter than the Hold duration) types the base letter only, and
modifier combinations (Ctrl/Alt/AltGr/Win + letter) are left untouched
### Character Sets
The module includes multiple language-specific character sets and special character sets:
- Various language sets for different alphabets and writing systems
- Special character sets (currency symbols, mathematical notations, etc.)
- These sets are defined in the core component and can be extended
### Known Behaviors
- The module has a specific timing mechanism for activation that users have become accustomed to. Initially, this was considered a bug (where the toolbar would still appear even after quickly tapping and releasing keys), but it has been maintained as expected behavior since users rely on it.
- Multiple rapid key presses can trigger multiple background tasks.
## Future Considerations
- Potential refinements to the activation timing mechanism
- Additional language and special character sets
- Improved UI positioning in different application contexts
## Debugging
To debug the Quick Accent module via **runner** approach, follow these steps:
0. Get familiar with the overall [Debugging Process](../development/debugging.md) for PowerToys.
1. **Build** the entire PowerToys solution in Visual Studio
2. Navigate to the **PowerAccent** folder in Solution Explorer
3. Open the file you want to debug and set **breakpoints** at the relevant locations
4. Find the **runner** project in the root of the solution
5. Right-click on the **runner** project and select "*Set as Startup Project*"
6. Start debugging by pressing `F5` or clicking the "*Start*" button
7. When the PowerToys Runner launches, **enable** the Quick Accent module in the UI
8. Use the Visual Studio Debug menu or press `Ctrl+Alt+P` to open "*Reattach to Process*"
9. Find and select "**PowerToys.PowerAccent.exe**" in the process list
10. Trigger the action in Quick Accent that should hit your breakpoint
11. Verify that the debugger breaks at your breakpoint and you can inspect variables and step through code
This process allows you to debug the Quick Accent module while it's running as part of the full PowerToys application.
### Alternative Debugging Approach
To directly debug the Quick Accent UI component:
0. Get familiar with the overall [Debugging Process](../development/debugging.md) for PowerToys.
1. **Build** the entire PowerToys solution in Visual Studio
2. Navigate to the **PowerAccent** folder in Solution Explorer
3. Open the file you want to debug and set **breakpoints** at the relevant locations
4. Right-click on the **PowerAccent.UI** project and select "*Set as Startup Project*"
5. Start debugging by pressing `F5` or clicking the "*Start*" button
6. Verify that the debugger breaks at your breakpoint and you can inspect variables and step through code
**Known issue**: A first incremental build can surface transient errors (for example from CsWinRT projection / WinUI XAML codegen ordering).<br>
**Solution**: Right-click the **PowerAccent** folder in Solution Explorer and select "*Rebuild*", then start debugging again.
+43
View File
@@ -0,0 +1,43 @@
# PowerToys Modules
This section contains documentation for individual PowerToys modules, including their architecture, implementation details, and debugging tools.
## Available Modules
| Module | Description |
|--------|-------------|
| [Advanced Paste](advancedpaste.md) | Tool for enhanced clipboard pasting with formatting options |
| [Always on Top](alwaysontop.md) | Tool for pinning windows to stay on top of other windows |
| [Awake](awake.md) | Tool to keep your computer awake without modifying power settings |
| [Color Picker](colorpicker.md) | Tool for selecting and managing colors from the screen |
| [Command Not Found](commandnotfound.md) | Tool suggesting package installations for missing commands |
| [Crop and Lock](cropandlock.md) | Tool for cropping application windows into smaller windows or thumbnails |
| [Environment Variables](environmentvariables.md) | Tool for managing user and system environment variables |
| [FancyZones](fancyzones.md) ([debugging tools](fancyzones-tools.md)) | Window manager utility for custom window layouts |
| [File Explorer add-ons](fileexploreraddons.md) | Extensions for enhancing Windows File Explorer functionality |
| [File Locksmith](filelocksmith.md) | Tool for finding processes that lock files |
| [Hosts File Editor](hostsfileeditor.md) | Tool for managing the system hosts file |
| [Image Resizer](imageresizer.md) | Tool for quickly resizing images within File Explorer |
| [Keyboard Manager](keyboardmanager/README.md) | Tool for remapping keys and keyboard shortcuts |
| [Mouse Utilities](mouseutils/readme.md) | Collection of tools to enhance mouse and cursor functionality |
| [Mouse Without Borders](mousewithoutborders.md) | Tool for controlling multiple computers with a single mouse and keyboard |
| [NewPlus](newplus.md) | Context menu extension for creating new files in File Explorer |
| [Peek](peek/readme.md) | File preview utility for quick file content viewing |
| [Power Rename](powerrename.md) | Bulk file renaming tool with search and replace functionality |
| [PowerToys Run (deprecation soon)](launcher/readme.md) | Quick application launcher and search utility |
| [Quick Accent](quickaccent.md) | Tool for quickly inserting accented characters and special symbols |
| [Registry Preview](registrypreview.md) | Tool for visualizing and editing Registry files |
| [Screen Ruler](screenruler.md) | Tool for measuring pixel distances and color boundaries on screen |
| [Shortcut Guide](shortcut_guide.md) | Tool for displaying Windows keyboard shortcuts when holding the Windows key |
| [Text Extractor](textextractor.md) | Tool for extracting text from images and screenshots |
| [Workspaces](workspaces.md) | Tool for saving and restoring window layouts for different projects |
| [ZoomIt](zoomit.md) | Screen zoom and annotation tool |
## Adding New Module Documentation
When adding documentation for a new module:
1. Create a dedicated markdown file for the module (e.g., `modulename.md`)
2. If the module has specialized debugging tools, consider creating a separate tools document (e.g., `modulename-tools.md`)
3. Update this index with links to the new documentation
4. Follow the existing documentation structure for consistency
+85
View File
@@ -0,0 +1,85 @@
# Registry Preview Module
[Public overview - Microsoft Learn](https://learn.microsoft.com/en-us/windows/powertoys/registry-preview)
## Quick Links
[All Issues](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3A%22Product-Registry%20Preview%22)<br>
[Bugs](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3AIssue-Bug%20label%3A%22Product-Registry%20Preview%22)<br>
[Pull Requests](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+is%3Aopen+label%3A%22Product-Registry+Preview%22)
[CheckList](https://github.com/microsoft/PowerToys/blob/releaseChecklist/doc/releases/tests-checklist-template.md?plain=1#L641)
## Overview
Registry Preview simplifies the process of visualizing and editing complex Windows Registry files. It provides a powerful interface to preview, edit, and write changes to the Windows Registry. The module leverages the [Monaco Editor](../common/monaco-editor.md) to provide features like syntax highlighting and line numbering for registry files.
## Technical Architecture
Registry Preview is built using WinUI 3 with the [Monaco Editor](../common/monaco-editor.md) embedded for text editing capabilities. Monaco was originally designed for web environments but has been integrated into this desktop application to leverage its powerful editing features.
The module consists of several key components:
1. **Main Windows Interface** - Handles the UI interactions, window messaging, and resource loading
2. **Monaco Editor Integration** - Embeds the Monaco web-based editor into WinUI 3 (see [Monaco Editor documentation](../common/monaco-editor.md) for details)
3. **Registry Parser** - Parses registry files and builds a tree structure for visualization
4. **Editor Control** - Manages the editing capabilities and syntax highlighting
## Code Structure
The Registry Preview module is organized into the following projects:
- **RegistryPreview** - Main window implementation, including Windows message handling, resource loading, and service injection
- **RegistryPreviewUILib** - UI implementation details and backend logic
- **RegistryPreviewExt** - Project configuration and setup
- **RegistryPreview.FuzzTests** - Fuzzing tests for the module
Key files and components:
1. **MonacoEditorControl** - Handles the embedding of [Monaco](../common/monaco-editor.md) into WinUI 3 and sets up the WebView container
2. **MainWindow** - Manages all event handling in one place
3. **Utilities** - Contains shared helper methods and utility classes
## Main Functions
- **MonacoEditorControl**: Controls editing in Monaco
- **GetRuntimeMonacoDirectory**: Gets the current directory path
- **OpenRegistryFile**: Opens and processes a registry file (first-time open)
- **RefreshRegistryFile**: Re-opens and processes an already opened file
- **ParseRegistryFile**: Parses text from the editor
- **AddTextToTree**: Creates TreeView nodes from registry keys
- **ShowMessageBox**: Wrapper method for displaying message boxes
## Debugging Registry Preview
### Setup Debugging Environment
1. Set the PowerToys Runner as the parent process
2. Set the RegistryPreviewUILib project as the child process for debugging
3. Use the PowerToys Development Utility tool to configure debugging
### Debugging Tips
1. The main application logic is in the RegistryPreviewUILib project
2. Monaco-related issues may require debugging the WebView component (see [Monaco Editor documentation](../common/monaco-editor.md) for details)
3. For parsing issues, add breakpoints in the ParseRegistryFile method
4. UI issues are typically handled in the main RegistryPreview project
## UI Automation
Currently, Registry Preview does not have UI automation tests implemented. This is a potential area for future development.
## Recent Updates
Registry Preview has received community contributions, including:
- UI improvements
- New buttons and functionality
- Data preview enhancements
- Save button improvements
## Future Considerations
- Adding UI automation tests
- Further [Monaco editor](../common/monaco-editor.md) updates
- Enhanced registry parsing capabilities
- Improved visualization options
+68
View File
@@ -0,0 +1,68 @@
# Screen Ruler
[Public overview - Microsoft Learn](https://learn.microsoft.com/en-us/windows/powertoys/screen-ruler)
## Quick Links
[All Issues](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3A%22Product-Screen%20Ruler%22)<br>
[Bugs](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3AIssue-Bug%20label%3A%22Product-Screen%20Ruler%22)<br>
[Pull Requests](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+is%3Aopen+label%3A%22Product-Screen+Ruler%22)
## Overview
Screen Ruler (project name: MeasureTool or Measure 2) is a PowerToys module that allows users to measure pixel distances and detect color boundaries on the screen. The tool renders an overlay UI using DirectX and provides several measurement utilities.
## Features
- **Bounce Utility**: Measure a rectangular zone by dragging with a left click
- **Spacing Tool**: Measure the length of a line with the same color with the same pixel value both horizontally and vertically
- **Horizontal Spacing**: Measure the line with the same color in the horizontal direction
- **Vertical Spacing**: Measure the line with the same color in the vertical direction
## Architecture & Implementation
The Screen Ruler module consists of several components:
### MeasureToolModuleInterface
- **Dllmain.cpp**: Provides functionality to start and stop the Measure Tool process based on hotkey events, manage settings, and handle events.
### MeasureToolUI
- **App.xaml.cs**: Main entrance of the app. Initializes MeasureToolCore and activates a new main window.
- **MainWindow.xaml.cs**: Sets properties and behaviors for the window, and handles user click interactions.
- **NativeMethods.cs**: Interacts with the Windows API to manipulate window properties, such as positioning and sizing.
- **Settings.cs**: Gets the default measure style from settings.
### PowerToys.MeasureToolCore
- **PowerToys.MeasureToolCore**: Handles initialization, state management, and starts the measure tool and bounds tool.
- **BGRATextureView.h**: Manages and interacts with BGRA textures in a Direct3D 11 context.
- **Measurement.cpp**: Defines a Measurement struct that represents a rectangular measurement area, including methods for converting and printing measurement details in various units.
- **Clipboard.cpp**: Copies measurement data to the clipboard.
- **D2DState.cpp**: Manages Direct2D rendering state and draws text boxes.
- **DxgiAPI.cpp**: Creates and manages Direct3D and Direct2D devices.
- **EdgeDetection.cpp**: Detects edges in a BGRA texture.
- **OverlayUI.cpp**: Creates and manages overlay windows for tools like MeasureTool and BoundsTool.
- **BoundsToolOverlayUI.cpp**: UI implementation for bounds feature. Handles mouse and touch events to draw measurement rectangles on the screen and display their pixels.
- **MeasureToolOverlayUI.cpp**: UI implementation for measure feature. Draws measurement lines on the screen and displays their pixels.
- **ScreenCapturing.cpp**: Continuously captures the screen, detects edges, and updates the measurement state for real-time drawing of measurement lines.
- **PerGlyphOpacityTextRender.cpp**: Renders text with varying opacity on a Direct2D render target.
## Building & Debugging
### Building
1. Open PowerToys.slnx in Visual Studio
2. In the Solutions Configuration drop-down menu, select Release or Debug
3. From the Build menu, choose Build Solution
4. The executable app for Screen Ruler is named PowerToys.MeasureToolUI.exe
### Debugging
1. Right-click the project MeasureToolUI and click 'Set as Startup Project'
2. Right-click the project MeasureToolUI and click 'Debug'
## Known Issues
There are several open bugs for the Screen Ruler module, most of which are related to crashing issues. These can be found in the [PowerToys issues list](https://github.com/microsoft/PowerToys/issues?q=is%3Aissue%20state%3Aopen%20Screen%20ruler%20type%3ABug).
+115
View File
@@ -0,0 +1,115 @@
# Shortcut Guide
[Public overview - Microsoft Learn](https://learn.microsoft.com/en-us/windows/powertoys/shortcut-guide)
## Quick Links
[All Issues](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3A%22Product-Shortcut%20Guide%22)<br>
[Bugs](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3AIssue-Bug%20label%3A%22Product-Shortcut%20Guide%22)<br>
[Pull Requests](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+is%3Aopen+label%3A%22Product-Shortcut+Guide%22+)
## Overview
Shortcut Guide is a PowerToy that displays an overlay of available keyboard shortcuts when a user-set keyboard shortcut is pressed. It helps users discover and remember keyboard shortcuts for Windows and apps.
> [!NOTE]
> The spec for the manifest files is in development and will be linked here once available.
## Usage
- Press the user-defined hotkey to display the overlay
- Press the hotkey again or press ESC to dismiss the overlay
## Build and Debug Instructions
### Build
1. Open PowerToys.slnx in Visual Studio
2. Select Release or Debug in the Solutions Configuration drop-down menu
3. From the Build menu, choose Build Solution
4. The executable is named PowerToys.ShortcutGuide.exe
### Debug
1. Right-click the ShortcutGuide.Ui project and select 'Set as Startup Project'
2. Right-click the project again and select 'Debug'
> [!NOTE]
> When run in debug mode, the window behaves differently than in release mode. It will not automatically close when loosing focus, it will be displayed on top of all other windows, and it is not hidden from the taskbar.
## Project Structure
The Shortcut Guide module consists of the following 4 projects:
### [`ShortcutGuide.Ui`](/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuide.Ui.csproj
This is the main UI project for the Shortcut Guide module. Upon startup it does the following tasks:
1. Copies the built-in manifest files to the users manifest directory (overwriting existing files).
2. Generate the `index.yml` manifest file.
3. Populate the PowerToys shortcut manifest with the user-defined shortcuts.
4. Starts the UI.
### Related files in PowerToys.Interop
#### [`excluded_app.cpp`](/src/modules/ShortcutGuide/ShortcutGuide.CPPProject/excluded_app.cpp)
This file contains one function with the following signature:
```cpp
__declspec(dllexport) bool IsCurrentWindowExcludedFromShortcutGuide()
```
This function checks if the current window is excluded from the Shortcut Guide overlay. It returns `true` if the current window is excluded otherwise it returns `false`.
#### [`tasklist_positions.cpp`](/src/modules/ShortcutGuide/ShortcutGuide.CPPProject/tasklist_positions.cpp)
This file contains helper functions to retrieve the positions of the taskbar buttons. It exports the following function:
```cpp
__declspec(dllexport) TasklistButton* get_buttons(HMONITOR monitor, int* size)
```
This function retrieves the positions of the taskbar buttons for a given monitor. It returns an array of `TasklistButton` structures (max 10), which contain the position and size of each button.
`monitor` must be the monitor handle of the monitor containing the taskbar instance of which the buttons should be retrieved.
`size` will contain the resulting array size.
It determines the positions through Windows `FindWindowEx` function.
For the primary taskbar it searches for:
* A window called "Shell_TrayWnd"
* that contains a window called "ReBarWindow32"
* that contains a window called "MSTaskSwWClass"
* that contains a window called "MSTaskListWClass"
For any secondary taskbar it searches for:
* A window called "Shell_SecondaryTrayWnd"
* that contains a window called "WorkerW"
* that contains a window called "MSTaskListWClass"
It then enumerates all the button elements inside "MSTaskListWClass" while skipping such with a same name (which implies the user does not use combining taskbar buttons)
If this method fails, which it will for newer versions of Windows, it falls back to searching for:
* A window called "Shell_TrayWnd" or "Shell_SecondaryTrayWnd"
* that contains a window called "Windows.UI.Composition.DesktopWindowContentBridge"
* that contains a window called "Windows.UI.Input.InputSite.WindowClass"
* the first child element
It then enumerates all the button elements inside the selected while skipping such with a same name (which implies the user does not use combining taskbar buttons) and such that do not start with "Appid:" (which are not actual taskbar buttons related to apps, but others like the widgets or the search button).
### [`ShortcutGuide.IndexYmlGenerator`](/src/modules/ShortcutGuide/ShortcutGuide.IndexYmlGenerator/)
This application generates the `index.yml` manifest file.
It is a separate project so that its code can be easier ported to WinGet in the future.
### [`ShortcutGuideModuleInterface`](/src/modules/ShortcutGuide/ShortcutGuideModuleInterface/ShortcutGuideModuleInterface.vcxproj)
The module interface that handles opening and closing the user interface.
## Features and Limitations
- Currently the displayed shortcuts (Except the ones from PowerToys) are not localized.
- It's currently rated as a P3 (lower priority) module
## Future Development
- Implementing with WinGet to get new shortcut manifest files
- Adding localization support for the built-in manifest files
+34
View File
@@ -0,0 +1,34 @@
# Text Extractor
[Public overview - Microsoft Learn](https://learn.microsoft.com/en-us/windows/powertoys/text-extractor)
## Quick Links
[All Issues](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3A%22Product-Text%20Extractor%22)<br>
[Bugs](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3AIssue-Bug%20label%3A%22Product-Text%20Extractor%22)<br>
[Pull Requests](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+is%3Aopen+label%3A%22Product-Text+Extractor%22)
## Overview
Text Extractor is a PowerToys utility that enables users to extract and copy text from anywhere on the screen, including inside images and videos. The module uses Optical Character Recognition (OCR) technology to recognize text in visual content. This module is based on Joe Finney's Text Grab.
## How it works
Text Extractor captures the screen content and uses OCR to identify and extract text from the selected area. Users can select a region of the screen, and Text Extractor will convert any visible text in that region into copyable text.
## Architecture
### Components
- **EventMonitor**: Handles the `ShowPowerOCRSharedEvent` which triggers the OCR functionality
- **OCROverlay**: The main UI component that provides:
- Language selection for OCR processing
- Canvas for selecting the screen area to extract text from
- **Screen Capture**: Uses `CopyFromScreen` to capture the screen content as the overlay background image
### Activation Methods
- **Global Shortcut**: Activates Text Extractor through a keyboard shortcut
- **LaunchOCROverlayOnEveryScreen**: Functionality to display the OCR overlay across multiple monitors
## Technical Implementation
Text Extractor is implemented using Windows Presentation Foundation (WPF) technology, which provides the UI framework for the selection canvas and other interface elements.
## User Experience
When activated, Text Extractor displays an overlay on the screen that allows users to select an area containing text. Once selected, the OCR engine processes the image and extracts any text found, which can then be copied to the clipboard.
+34
View File
@@ -0,0 +1,34 @@
# Workspaces
[Public overview - Microsoft Learn](https://learn.microsoft.com/en-us/windows/powertoys/workspaces)
## Quick Links
[All Issues](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3AProduct-Workspaces)<br>
[Bugs](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3AIssue-Bug%20label%3AProduct-Workspaces)<br>
[Pull Requests](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+is%3Aopen+label%3AProduct-Workspaces)
## Overview
Workspaces is a PowerToys module that allows users to save and restore window layouts for different projects or workflows.
## Links
- [Source code folder](https://github.com/microsoft/PowerToys/tree/main/src/modules/Workspaces)
- [Issue tracker](https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+label%3AWorkspaces)
## Implementation Details
TODO: Add implementation details
## Debugging
TODO: Add debugging information
## Settings
TODO: Add settings documentation
## Future Improvements
TODO: Add potential future improvements
+194
View File
@@ -0,0 +1,194 @@
# ZoomIt Module
[Public overview - Microsoft Learn](https://learn.microsoft.com/en-us/windows/powertoys/zoomit)
## Quick Links
[All Issues](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3AProduct-ZoomIt)<br>
[Bugs](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen%20label%3AProduct-ZoomIt%20label%3AIssue-Bug%20)<br>
[Pull Requests](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+is%3Aopen+label%3AProduct-ZoomIt)
## Overview
ZoomIt is a screen zoom and annotation tool originally from Microsoft's Sysinternals suite. It allows users to:
- Zoom in on specific areas of the screen
- Draw and annotate on the screen while zoomed in
- Use a timer for presentations or breaks
- Pan and move while zoomed in
- Record screen activity with audio
ZoomIt runs in the background and is activated via customizable hotkeys.
## Special Integration Considerations
Unlike other PowerToys modules, ZoomIt has some unique integration aspects:
1. **Registry-based Settings**: ZoomIt uses registry settings instead of JSON files (which is the standard for other PowerToys modules). This was required to maintain compatibility with the standalone Sysinternals version.
2. **Dual Source of Truth**: The PowerToys repository serves as the source of truth for both the PowerToys version and the standalone Sysinternals version, with build flags to differentiate between them.
3. **Settings Integration**: A special WinRT/C++ interop library was developed to bridge between PowerToys' JSON-based settings system and ZoomIt's registry-based settings.
## Technical Architecture
The ZoomIt module consists of the following components:
1. **ZoomIt Executable** (`PowerToys.ZoomIt.exe`): The main ZoomIt application that provides the zooming and annotation functionality.
2. **Module Interface** (`PowerToys.ZoomItModuleInterface.dll`): Implements the PowerToys module interface to integrate with the PowerToys runner.
3. **Settings Interop** (`ZoomItSettingsInterop`): A WinRT/C++ interop library that enables communication between PowerToys settings and ZoomIt's registry settings.
![key functions](../images/zoomit/functions.png)
### Directory Structure
```
src/
├── modules/
│ └── ZoomIt/
│ ├── ZoomIt/ # Main ZoomIt application code
│ ├── ZoomItModuleInterface/ # PowerToys module interface implementation
│ └── ZoomItSettingsInterop/ # WinRT/C++ interop for settings
├── settings-ui/
│ └── Settings.UI/
│ ├── SettingsXAML/
│ │ └── Views/
│ │ └── ZoomItPage.xaml # ZoomIt settings page UI
│ └── ViewModels/
│ └── ZoomItViewModel.cs # ZoomIt settings view model
└── common/
└── sysinternals/ # Common code from Sysinternals
```
## Settings Management
ZoomIt's settings are stored in the Windows registry instead of JSON files to maintain compatibility with the standalone version. The settings include:
- Hotkey combinations for different modes (zoom, draw, etc.)
- Drawing options (colors, line thickness, etc.)
- Font settings for text annotations
- Microphone selection for recording
- Custom file paths for demo mode and break backgrounds
The `ZoomItSettingsInterop` library handles:
1. Loading settings from registry and converting to JSON for PowerToys settings UI
2. Saving changes from the settings UI back to the registry
3. Notifying the ZoomIt application when settings change
![interop](../images/zoomit/interop.png)
## Integration Steps
The integration of ZoomIt into PowerToys involved these key steps:
1. **Code Migration**:
- Moving code from the Sysinternals ZoomIt repository to `src/modules/ZoomIt/ZoomIt`
- Adding required common libraries to `src/common/sysinternals`
- Sanitizing code for open source (removing private APIs, undocumented details, etc.)
- Ensuring no private APIs (validated through APIScan)
- Removing references to undocumented implementation details, constants, and names
- Standardizing dependencies with other PowerToys utilities
2. **Module Interface Implementation**:
- Creating the PowerToys module interface
- Adding process management (start/terminate)
- Implementing event-based communication for settings updates
- Adding named events for communication between PowerToys and ZoomIt
3. **Settings Integration**:
- Extracting ZoomIt settings code to a shareable component
- Creating a WinRT/C++ interop library for registry-JSON conversion
- Implementing all settings UI controls in PowerToys settings
- Building `ZoomItSettingsInterop` as a bridge between registry and JSON settings
4. **PowerToys Integration**:
- Adding ZoomIt to the PowerToys runner
- Adding GPO rules for ZoomIt
- Implementing telemetry and logging
- Creating OOBE (out-of-box experience) page with animated tutorial
- Adding ZoomIt to process termination list for proper cleanup
- Adding telemetry events documentation
5. **UI/UX Adjustments**:
- Redirecting ZoomIt's settings UI to PowerToys settings
- Handling hotkey conflicts with warning notifications
- Modifying tray icon behavior
- Removing original ZoomIt options menu entries
- Adding Sysinternals attribution on the settings page
6. **Build System Updates**:
- Adding ZoomIt to the PowerToys solution
- Implementing build flags for standalone vs. PowerToys versions
- Adding signing for new binaries
- Fixing analyzer errors and code quality issues
## Debug Instructions
1. Build the entire PowerToys solution at least once.
2. Set `runner` as the startup project and start debugging.
3. Once the PowerToys Settings app is running and ensure ZoomIt is activated.
4. Set `ZoomIt` as the startup project in Visual Studio.
5. Press `Ctrl + Alt + P` and attach ZoomIt to the process.
6. You should now be able to set breakpoints and step through the code.
## Special Implementation Details
### Font Selection
ZoomIt requires storing font information as a binary LOGFONT structure in the registry. This required special handling:
- Creating P/Invoke declarations for Windows font APIs
- Base64 encoding the binary data for transfer through JSON
- Using native Windows dialogs for font selection
### Hotkey Management
ZoomIt registers hotkeys through the Windows RegisterHotKey API. Special handling was needed to:
- Detect and notify about hotkey conflicts
- Update hotkeys when settings change
- Support modifier keys
### Process Communication
Communication between PowerToys and ZoomIt uses:
- Command-line arguments to pass PowerToys process ID
- Named events for signaling settings changes and exit requests
- Windows messages for internal ZoomIt state management
## Implementation Challenges
Several challenges were encountered during ZoomIt integration:
1. **First-Run Behavior**:
- Font loading crashed when no ZoomIt data existed in registry
- Hotkeys weren't registered on first run with no existing data
- Implemented safeguards to handle missing registry data
2. **Settings Synchronization**:
- Modifier keys for shortcuts weren't correctly updated when settings changed
- Implemented proper event notification for settings changes
- Added hotkey conflict detection and warnings
3. **File Interaction**:
- ZoomIt file pickers changed the working directory of the Settings project
- Fixed to maintain proper directory context
4. **Drawing Issues**:
- Color settings lacking opacity caused drawing functionality to fail
- Removed internal state settings that weren't truly editable
5. **Dual-Build Support**:
- Added build flags to support both PowerToys and standalone Sysinternals versions
- Implemented different executable properties based on build target
## Source Code Management
The PowerToys repository serves as the source of truth for both PowerToys and Sysinternals standalone versions of ZoomIt. Key repositories involved:
- Utility repo: `https://dev.azure.com/sysinternals/Tools/_git/ZoomIt`
- Common library repo: `https://dev.azure.com/sysinternals/Tools/_git/Common`
The integration process can be tracked through [PR #35880](https://github.com/microsoft/PowerToys/pull/35880) which contains the complete history of changes required to properly integrate ZoomIt.