Implementing DPI-Aware Functionality in PowerToys Modules Using DPIAware

PowerToys modules achieve per-monitor DPI awareness by calling DPIAware::EnableDPIAwarenessForThisProcess() at startup and using conversion helpers like DPIAware::Convert() to scale UI coordinates between logical (96 DPI) and physical pixels.

PowerToys requires pixel-perfect rendering across displays with varying scaling factors. The centralized DPIAware utility in src/common/Display abstracts Win32 DPI APIs, providing a testable interface that modules use instead of calling GetDpiForMonitor or SetProcessDpiAwarenessContext directly.

Understanding the DPIAware Architecture

The DPIAware namespace in src/common/Display/dpi_aware.h encapsulates all DPI-related functionality. The implementation in src/common/Display/dpi_aware.cpp wraps the modern Shell Scaling API, handling per-monitor awareness and coordinate conversion.

Key components include:

  • EnableDPIAwarenessForThisProcess: Sets process-wide DPI awareness
  • GetScreenDPIForMonitor and GetScreenDPIForWindow: Retrieve scaling factors
  • Convert and InverseConvert: Transform between logical and physical coordinates
  • ConvertByCursorPosition: Scale based on the monitor under the cursor

Enabling Per-Monitor DPI Awareness

Every PowerToys executable must opt into per-monitor-v2 DPI awareness before creating any UI. This is done exactly once per process.

In src/runner/main.cpp at line 184, the Runner initializes awareness:

#include "dpi_aware.h"

int WINAPI wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
{
    DPIAware::EnableDPIAwarenessForThisProcess();  // Required for per-monitor-v2
    // ... create window and run message loop
}

The implementation calls SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) as seen in lines 31-33 of src/common/Display/dpi_aware.cpp.

Converting Coordinates and Dimensions

After enabling awareness, modules must convert logical coordinates (based on 96 DPI) to physical pixels for the specific monitor displaying the window.

Window-Based Scaling

Use DPIAware::Convert when you have a window handle and need to scale dimensions or rectangles to the monitor containing that window.

From src/modules/fancyzones/FancyZonesLib/WindowUtils.cpp at line 317:

#include "dpi_aware.h"

void ScaleWindowRect(HWND hwnd, RECT& rect)
{
    // Convert logical coordinates (96 DPI) to physical pixels
    DPIAware::Convert(hwnd, rect);
}

The Convert method accepts both HWND and HMONITOR, allowing usage even before window creation if you have a monitor handle.

Cursor-Based Scaling

When positioning UI based on the current mouse location (such as context menus or floating toolbars), use cursor-based helpers to get the DPI of the monitor under the cursor.

From src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditShortcutsWindow.cpp at line 128:

#include "dpi_aware.h"

void ResizeBasedOnCursor(float& width, float& height)
{
    DPIAware::ConvertByCursorPosition(width, height);
}

Similarly, src/modules/alwaysontop/AlwaysOnTop/ScalingUtils.cpp at line 10 retrieves DPI for an existing window:

auto res = DPIAware::GetScreenDPIForWindow(window, dpi);

Integration Examples from PowerToys Modules

The DPIAware utility provides a consistent pattern across all PowerToys modules:

Summary

  • Enable awareness early: Call DPIAware::EnableDPIAwarenessForThisProcess() once at process startup before creating any windows.
  • Use conversion helpers: Scale logical coordinates to physical pixels using DPIAware::Convert for window-based operations or DPIAware::ConvertByCursorPosition for cursor-based positioning.
  • Centralized implementation: All DPI logic lives in src/common/Display/dpi_aware.h and src/common/Display/dpi_aware.cpp, ensuring consistent behavior across PowerToys modules.
  • Testable abstraction: The helper functions wrap Win32 APIs like GetDpiForMonitor and SetProcessDpiAwarenessContext, providing a mockable interface for unit testing.

Frequently Asked Questions

When should I call EnableDPIAwarenessForThisProcess?

You must call DPIAware::EnableDPIAwarenessForThisProcess() exactly once per executable, before any UI thread creates windows or accesses display metrics. In PowerToys, this happens in src/runner/main.cpp at line 184 and in each module's entry point that creates its own process.

How do I handle DPI changes when moving a window between monitors?

Use DPIAware::Convert with the window handle when positioning or sizing the window. The function automatically retrieves the DPI for the monitor containing the window. For operations based on mouse position (like showing a context menu), use DPIAware::ConvertByCursorPosition to get the DPI of the monitor currently under the cursor.

What is the difference between Convert and InverseConvert?

DPIAware::Convert transforms logical coordinates (based on 96 DPI) into physical pixels for the target monitor. DPIAware::InverseConvert performs the reverse operation, converting physical pixel measurements back to logical coordinates. FancyZones uses both when calculating zone layouts and converting them to window positions.

Can I use DPIAware in a new PowerToys module?

Yes. Include #include "dpi_aware.h" from src/common/Display in your module's source files. Call DPIAware::EnableDPIAwarenessForThisProcess() at startup if your module runs in its own process, then use DPIAware::Convert or DPIAware::GetScreenDPIForWindow whenever you need to scale UI elements to the current monitor's DPI.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →