Log the machine's wheel-lines setting alongside injection (#95)

Scroll is injected as wheel events, 120 units to a notch, and Windows
multiplies each notch by the user's lines-per-notch before anything
moves. Identical injection therefore scrolls wildly different amounts on
two machines, and a "scroll is far too fast" report gives no way to tell
that apart from a bug in our own arithmetic.

The default is 3 lines. WHEEL_PAGESCROLL makes a notch a whole page,
which turns the gentlest flick into a page jump — "I barely touch the pad
and it scrolls all the way down a page" describes that setting exactly,
and it is not something a sensitivity control could tune away, since with
a page as the smallest unit the granularity is the problem rather than
the scale.

Recorded in the injection environment line, which already fires at game
mode takeover whenever a pad is driving the desktop, so it costs nothing
and arrives attached to every future report of this kind.

The reporter's log and ours are otherwise identical — both id=0x42
len=54 — so the report layout is ruled out.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Dylan DeverillandClaude Opus 5 committed 2026-09-06 21:26:09 -07:00
1 parent 71a50df221
commit a46db6cc1e
1 file changed
+25 -6
+25 -6
View File
@@ -282,24 +282,43 @@ std::wstring DescribeCursor() {
namespace InputInjection {
// How far one wheel notch scrolls on this machine, which is not ours to
// choose. Scroll is injected as wheel events — 120 units to a notch — and
// Windows multiplies each notch by this before anything moves, so identical
// injection scrolls wildly different amounts on two machines. The default is
// 3 lines; WHEEL_PAGESCROLL means a notch is a whole page, which turns the
// gentlest flick of a pad into a page jump and is not something a sensitivity
// setting can tune away, the granularity being the problem rather than the
// scale. Worth a line in the log, since a scroll-feels-wrong report otherwise
// gives no way to tell this apart from a bug in our own arithmetic.
static std::wstring DescribeWheelLines() {
UINT lines = 3;
if (!SystemParametersInfoW(SPI_GETWHEELSCROLLLINES, 0, &lines, 0))
return L"unknown";
if (lines == WHEEL_PAGESCROLL) return L"one page per notch (!)";
return std::to_wstring(lines) + L" lines per notch"
+ (lines == 3 ? L"" : L" (not the default 3)");
}
void LogEnvironment(const char* reason) {
DWORD fgIntegrity = kIntegrityUnknown;
const std::wstring fg = DescribeForeground(fgIntegrity);
const DWORD own = OwnIntegrity();
const std::wstring clip = DescribeClip();
const std::wstring cur = DescribeCursor();
const std::wstring fg = DescribeForeground(fgIntegrity);
const DWORD own = OwnIntegrity();
const std::wstring clip = DescribeClip();
const std::wstring cur = DescribeCursor();
const std::wstring wheel = DescribeWheelLines();
const bool outranked = fgIntegrity != kIntegrityUnknown
&& own != kIntegrityUnknown
&& fgIntegrity > own;
EventLog::Write("INJECT: %s — foreground=%ls, us=integrity=%ls%s, "
"cursorClip=%ls, cursor=%ls",
"cursorClip=%ls, cursor=%ls, wheel=%ls",
reason, fg.c_str(), IntegrityName(own),
outranked ? " — FOREGROUND OUTRANKS US, injected input is "
"blocked (UIPI) until focus moves or it closes"
: "",
clip.c_str(), cur.c_str());
clip.c_str(), cur.c_str(), wheel.c_str());
}
void SetAlertCallback(AlertFn fn) {