For the complete documentation index, see llms.txt. This page is also available as Markdown.

Sample apps to test Windows pen behaviors

Most of the hard problems in these notes are OS-level behaviors whose fixes are subtle and window-level (a WndProc message, a feedback setting, a DPI awareness context). Diagnosing them inside a real application — a driver UI, a paint app, a calibration overlay — is slow and conflates concerns: you're fighting the app's own input plumbing, layout, and framework at the same time as the OS behavior.

The efficient approach is a set of minimal, single-purpose sample apps — one per gotcha — that reproduce the symptom in isolation and let us A/B candidate fixes, then bake the verified answer back into the corresponding note. Iterate in the sample, not in production.

What each sample should be

  • Reproduces the symptom out of the box (no fix applied) so the behavior is obvious.

  • Shows the problem and the solution together. A live toggle is ideal when a fix is runtime-reversible — but several of these fixes are latched per-window and cannot be un-applied at runtime (see Lessons learned). For those, use two windows born in different states (RAW vs FIXED) compared side by side, not a toggle.

  • Reports the baseline. Show the global settings + device state on-screen, so a missing symptom is self-explaining (setting off / indirect device) rather than a false "pass".

  • A short README: symptom → cause → each fix tried and its verified result (works / partial / no effect), with a link to the matching implementation note.

  • Per-framework where the fix differs (raw Win32, WinForms, WPF, WinUI 3, Avalonia) — the concept is one thing, the hook point differs per framework and that's where people get stuck.

  • Verified across input types + configs: pen and touch; single vs multi-monitor; 100% vs scaled DPI; Windows Ink vs WinTab where relevant. Note that an indirect pen device may not drive the shell behaviors at all — verify on a direct-touch device.

Backlog (seed from the implementation notes)

  • Tap / contact feedback rings — the ripple the shell draws on every pen/touch tap. Pure cosmetic; SetWindowFeedbackSetting per FEEDBACK_TYPE. (sample 01) (note)

  • Press-and-hold gesture — the ring + right-click on a stationary contact. WM_TABLET_QUERYSYSTEMGESTURESTATUSTABLET_DISABLE_PRESSANDHOLD. (sample 01) (note)

  • Cursor reappears during a still hold — the dwell's "other half"; no mouse-move to re-apply a hidden cursor. Compare SetWindowFeedbackSetting, SetCursor(NULL) on a timer, and handling WM_SETCURSOR (the zero-flicker one). (sample 01) (note)

  • Hiding the cursor over a full-screen pen window for all input types, without breaking clickable chrome.

  • DPI & pen coordinate mapping — Per-Monitor V2, ClientToScreen from the right awareness context. (note, note)

  • WM_POINTER coalescing — retrieving the full input history vs the single current point. (note)

  • WinTab vs Windows Ink coexistence — both active, driver conflict, which wins. (note)

  • Pressure normalization — raw range vs pre-normalized 0..1 across tablets.

  • Tilt representations — azimuth/altitude vs tiltX/tiltY round-trips. (note)

  • Flicks / other shell gestures — the rest of the TABLET_DISABLE_* flags.

Lessons learned

From building sample 01. These shaped the design and are worth carrying into the rest.

  • Some fixes are not runtime-reversible. Windows queries WM_TABLET_QUERYSYSTEMGESTURESTATUS once, early, and caches it — after it has seen TABLET_DISABLE_PRESSANDHOLD, clearing the flag does not re-enable the gesture. A per-fix "off" toggle silently keeps acting fixed. The reliable demo is a window born without the fix (RAW) next to one born with it (FIXED); the stickiness only comes from apply-then-unapply.

  • The symptom depends on settings and device. All the visualization settings can be ON and you still see nothing because the pen is an ExternalPen (indirect tablet / WinTab / OpenTabletDriver) that never drives the Ink shell. Read and display the baseline. → Windows pen & touch settings.

  • Unicode window ⇒ Unicode DefWindowProc. A RegisterClassW window that falls through to DefWindowProcA (i.e. compiled without UNICODE) reads its own wide title as ANSI and truncates it at the first \0 — the title "Windows…" becomes just "W". Define UNICODE/_UNICODE, or call DefWindowProcW explicitly.

  • Compile wide string literals as UTF-8. MSVC reads source in the system ANSI codepage by default, mangling // in L"…" literals (â€"). Build with /utf-8.

  • DPI is not optional. Under Per-Monitor V2 the client is physical pixels; a hard-coded pixel font is tiny at 200 %+. Scale text/layout by GetDpiForWindow (win32) / DeviceDpi (WinForms) and handle WM_DPICHANGED.

  • Per-framework hook points (same native fixes, different attach point): win32 WndProc in the class + SetProcessDpiAwarenessContext; WinForms override WndProc(ref Message) + this.Handle + Application.SetHighDpiMode(PerMonitorV2) (and <AllowUnsafeBlocks> for [LibraryImport]).

Feedback loop

Each sample is the source of truth for its note: when a sample proves which fix works (and which don't), update the note's "fixes tried → result" with the verified outcome. The samples are where we experiment; the notes are the distilled conclusion.

Location

github.com/TheSevenPens/WindowsPenBehaviors — one folder per sample under samples/, and within each, one subfolder per framework (win32/, winforms/, …), each building to a tiny runnable exe.

Last updated