diff --git a/docs/architecture/05-transform-engine.md b/docs/architecture/05-transform-engine.md index a3eeb5d..4964e87 100644 --- a/docs/architecture/05-transform-engine.md +++ b/docs/architecture/05-transform-engine.md @@ -226,7 +226,7 @@ Defences (wall arming in `RunTick`'s `ReadForegroundAndArmWall`, write clamp in DWM magnifies with nearest neighbour unless something calls `MagSetFullscreenUseBitmapSmoothing` (Magnification.dll, undocumented, resolved BY NAME). -`txSamplingMode`: 0 nearest, 1 smooth (default since #508). +`txSamplingMode`: 0 nearest (default), 1 smooth. - Until 0.24.0 it was resolved by ordinal 1, which does not exist (the export ordinals start at 100), so Wind never set the filter: the image showed whatever state another process had left. diff --git a/docs/architecture/08-config-profiles.md b/docs/architecture/08-config-profiles.md index 45f0145..da1ab7e 100644 --- a/docs/architecture/08-config-profiles.md +++ b/docs/architecture/08-config-profiles.md @@ -198,7 +198,7 @@ Every key works in the ini whether or not Settings shows it. Keys hot-reload unl | Keys | Meaning | |---|---| -| `maxLevel` (30), `zoomInSpeed` and `zoomOutSpeed` (1.5), `zoomEaseOutMs` (115), `smoothZoom*` | Range and feel of the zoom | +| `maxLevel` (30), `zoomInSpeed` and `zoomOutSpeed` (1.75), `zoomEaseOutMs` (115), `smoothZoom*` | Range and feel of the zoom | | `cursorSensitivity` (1.0), `panSpeed` (1.0), `panGlideMaxPx` (0) | Mouse and arrow-key pan speed; the pan glide's distance, which also sets its ease (#430, #434) | | `gameCursorLagMs` (-1) | How late the view follows the pointer while a full-screen game hides it and draws its own cursor; -1 = one display frame, 0 = off (#443). Ini only | | `mouseAlign` (0 centred, 1 within the edges), `mouseMarginPct` | Where the pointer sits while the view moves | @@ -210,7 +210,7 @@ Every key works in the ini whether or not Settings shows it. Keys hot-reload unl | Keys | Meaning | |---|---| -| `txSamplingMode` (1) | Transform sampling: 0 nearest, 1 smooth (Settings: Transform scaling, formerly "High resolution cursor", [05](05-transform-engine.md)) | +| `txSamplingMode` (0) | Transform sampling: 0 nearest, 1 smooth (Settings: Transform scaling, formerly "High resolution cursor", [05](05-transform-engine.md)) | | `edgeClip` (1, hot) | While a transform session is zoomed, clips the pointer 1 px inside the monitor's left and top edges so it cannot rest on the contested outermost pixel (cursor-shape flicker there). The right and bottom stay open so the cursor can slide off the screen, as at 1x. A tighter existing clip (game confine, Inspect) always wins; 0 turns it off. Rationale: `Config::edgeClip` in `src/config/config.h`, mechanics in `TransformModel::edgeClipManage` | | `renderScaling` (0), `upscaleTiming` (0) | Render engine scaling: 0 nearest, 1 xBR, 2 NVIDIA Image Scaling (Settings: Render scaling, [04](04-render-engine.md)); `bilinear` is retired and only migrates (0 keeps nearest). `upscaleTiming=1` logs the filter's GPU time | | `sharpness`, `brightness`, `hdrTonemap` (1) | Render engine image | diff --git a/src/config/config.cpp b/src/config/config.cpp index 78929ed..5afb80b 100644 --- a/src/config/config.cpp +++ b/src/config/config.cpp @@ -526,9 +526,9 @@ std::string DefaultIniText() { "panUpVk=0\npanUpMods=0\npanDownVk=0\npanDownMods=0\npanSpeed=1.0\n" "; maxLevel: how far you can zoom (does not affect zoom speed)\n" "maxLevel=30.0\n" - "; zoomInSpeed/zoomOutSpeed: zoom rate multipliers (1.5=default, 3.0=twice as fast, 0.75=half);\n" + "; zoomInSpeed/zoomOutSpeed: zoom rate multipliers (1.75=default, 3.5=twice as fast, 0.875=half);\n" "; speed is independent of maxLevel\n" - "zoomInSpeed=1.5\nzoomOutSpeed=1.5\n" + "zoomInSpeed=1.75\nzoomOutSpeed=1.75\n" "; smoothZoom: 0=linear constant speed; 1=zoom-IN soft-starts, easing up to linear (shipped on)\n" "smoothZoom=1\n" "; smoothZoomAccel: ease-in depth - zoom-in starts at zoomInSpeed/this and climbs to\n" diff --git a/src/config/config.h b/src/config/config.h index 34139d6..9fc9302 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -66,8 +66,8 @@ struct Config { // --- Zoom experience (see docs/specs/2026-05-26-configurable-zoom-design.md) --- // Per-direction rate multipliers (1.0 = default speed); apply in BOTH linear and smooth modes. // Speed is independent of maxLevel (a fixed doublings/sec base inside ZoomController). - double zoomInSpeed = 1.5; // 0.25-4.0 - double zoomOutSpeed = 1.5; // 0.25-4.0 + double zoomInSpeed = 1.75; // 0.25-4.0 + double zoomOutSpeed = 1.75; // 0.25-4.0 int zoomTrace = 0; // 1 = log a zoom-in/zoom-out timeline per zoom (#310, diagnostics) // Hitch recorder (#361): log every long zoomed frame with its classified cause, plus a // per-minute summary. On by default: the record costs ~0.5 us per tick and logs nothing on a @@ -210,7 +210,7 @@ struct Config { // to come and go between builds; it is re-applied per magnification context. KNOWN // INTERACTION: the 1px warm pulse (txWarmMode=1) puts the view 1px off-true on rest ticks; // nearest masks that as sub-block noise, smoothing can render it as visible shaking. - int txSamplingMode = 1; + int txSamplingMode = 0; // nearest (owner pick 2026-10-24, #510) // MPO buster (issue #191, hot): 1 (default) = during transform GAME sessions on MPO-ENABLED // machines, show a fullscreen alpha-1 click-through ghost that demotes the game off its // hardware overlay plane - off the plane there is no 16-bit translation field to overflow, diff --git a/src/tray_app/flyout_model.h b/src/tray_app/flyout_model.h index 3fb8c03..0fcc588 100644 --- a/src/tray_app/flyout_model.h +++ b/src/tray_app/flyout_model.h @@ -204,8 +204,8 @@ inline const SliderSpec* SliderSpecs(int* count) { { "colorWarmPct", L"Warmth", 0, 100, 0, ValueFmt::Percent, "warm", 5 }, { "colorDimPct", L"Brightness", 1, 100, 100, ValueFmt::Percent, "bright", 1 }, { "maxLevel", L"Max zoom", 2, 30, 30, ValueFmt::TimesInt, "maxz", 1 }, - { "zoomInSpeed", L"Zoom-in speed", 0.25, 4, 1.5, ValueFmt::Times2, "zin", 0.05 }, - { "zoomOutSpeed", L"Zoom-out speed", 0.25, 4, 1.5, ValueFmt::Times2, "zout", 0.05 }, + { "zoomInSpeed", L"Zoom-in speed", 0.25, 4, 1.75, ValueFmt::Times2, "zin", 0.05 }, + { "zoomOutSpeed", L"Zoom-out speed", 0.25, 4, 1.75, ValueFmt::Times2, "zout", 0.05 }, { "panSpeed", L"Arrow key speed", 0.25, 4, 1, ValueFmt::Times2, "pan", 0.05 }, { "panGlideMaxPx", L"Pan glide", 0, 200, 0, ValueFmt::Pixels, "smooth", 5 }, { "zoomEaseOutMs", L"Release glide", 0, 300, 115, ValueFmt::Millis, "glide", 5 }, diff --git a/tests/fixtures/config_defaults.txt b/tests/fixtures/config_defaults.txt index ec8a903..d4f6a72 100644 --- a/tests/fixtures/config_defaults.txt +++ b/tests/fixtures/config_defaults.txt @@ -49,8 +49,8 @@ panKeysOn - - 1 hideCursorOn - - 1 cursorLockOn - - 1 maxLevel 1 30 30 -zoomInSpeed 0.25 4 1.5 -zoomOutSpeed 0.25 4 1.5 +zoomInSpeed 0.25 4 1.75 +zoomOutSpeed 0.25 4 1.75 smoothZoom - - 1 smoothZoomAccel 1 8 3 smoothZoomRamp 0.1 3 0.6 @@ -96,7 +96,7 @@ mouseMarginPct 0 40 0 trackLog - - 0 ixDecimate 1 16 4 mpoBuster - - 1 -txSamplingMode - - 1 +txSamplingMode - - 0 txWarmMode 0 1 1 txTrace - - 0 txRestLevel 1 1.01 1 diff --git a/tests/test_config.cpp b/tests/test_config.cpp index 65f425a..5e6ce15 100644 --- a/tests/test_config.cpp +++ b/tests/test_config.cpp @@ -7,7 +7,7 @@ TEST_CASE("defaults when text is empty") { Config c = ParseConfig(""); CHECK(c.zoomInButton == 0); // shipped unbound (onboarding captures it) CHECK(c.zoomOutButton == 0); // shipped unbound - CHECK(c.maxLevel == doctest::Approx(30.0)); // owner pick (#508) + CHECK(c.maxLevel == doctest::Approx(30.0)); // owner pick (#508, #510) CHECK(c.diagnostics == 0); // Ramp step cap ships ON (issue #219): uncapped, ~15% of high-level zoom-ins stall 35-43ms // then snap 1.2-1.9 levels; capped at 2.5%/tick every measured ramp was even. 0 would @@ -81,8 +81,8 @@ TEST_CASE("renderer knobs have sane defaults") { CHECK(c.multiMonitor == 1); // follows the cursor's monitor (#508) CHECK(c.cropCapture == 0); // shipped off: always copy all changed regions (no edge staleness) CHECK(c.smoothZoom == 1); // shipped on (eased-in zoom) - CHECK(c.zoomInSpeed == doctest::Approx(1.5)); // owner pick (#508) - CHECK(c.zoomOutSpeed == doctest::Approx(1.5)); + CHECK(c.zoomInSpeed == doctest::Approx(1.75)); // owner pick (#508, #510) + CHECK(c.zoomOutSpeed == doctest::Approx(1.75)); CHECK(c.zoomEaseOutMs == 115); CHECK(c.smoothZoomAccel == doctest::Approx(3.0)); CHECK(c.smoothZoomRamp == doctest::Approx(0.6)); @@ -174,7 +174,7 @@ TEST_CASE("parses overrides and ignores comments/blank lines") { CHECK(c.maxLevel == doctest::Approx(12.5)); CHECK(c.zoomInButton == 1); CHECK(c.cursorSensitivity == doctest::Approx(0.5)); - CHECK(c.zoomInSpeed == doctest::Approx(1.5)); // untouched default + CHECK(c.zoomInSpeed == doctest::Approx(1.75)); // untouched default } TEST_CASE("malformed lines are ignored, keep defaults") { Config c = ParseConfig("garbage line\nmaxLevel\n=5\n"); @@ -640,7 +640,7 @@ TEST_CASE("the first-run ini carries the new keys and parses to the struct defau } TEST_CASE("txSamplingMode folds the nearest aliases 2..4 to 0 (review 2026-10-09 #23)") { - CHECK(ParseConfig("").txSamplingMode == 1); // Smooth by default (#508) + CHECK(ParseConfig("").txSamplingMode == 0); // Nearest by default (#510) CHECK(ParseConfig("txSamplingMode=0\n").txSamplingMode == 0); CHECK(ParseConfig("txSamplingMode=1\n").txSamplingMode == 1); CHECK(ParseConfig("txSamplingMode=-1\n").txSamplingMode == -1); // leave DWM alone stays valid diff --git a/tests/test_profiles.cpp b/tests/test_profiles.cpp index 8751ba6..9289bbd 100644 --- a/tests/test_profiles.cpp +++ b/tests/test_profiles.cpp @@ -272,8 +272,8 @@ TEST_CASE("profile names: a device name is reserved with any extension, and the TEST_CASE("SessionDiffers: UI keys outside the first-run template read as their built-in default (review #70, #76)") { CHECK_FALSE(SessionDiffers("zoomEaseOutMs=115\n", "maxLevel=30\n")); CHECK(SessionDiffers("zoomEaseOutMs=100\n", "maxLevel=30\n")); - CHECK_FALSE(SessionDiffers("txSamplingMode=1\n", "")); - CHECK(SessionDiffers("txSamplingMode=0\n", "")); + CHECK_FALSE(SessionDiffers("txSamplingMode=0\n", "")); + CHECK(SessionDiffers("txSamplingMode=1\n", "")); CHECK_FALSE(SessionDiffers("lockApps=\n", "")); CHECK(SessionDiffers("lockApps=game.exe\n", "")); // The core's missing-key engine is transform, the template's too; the retired hybrid reads as it. diff --git a/ui/src/settings-schema.js b/ui/src/settings-schema.js index 4de7c76..c9bd048 100644 --- a/ui/src/settings-schema.js +++ b/ui/src/settings-schema.js @@ -60,8 +60,8 @@ export const groups = [ cards: [ { caption: 'Level and speed', rows: [ { key: 'maxLevel', type: 'slider', label: 'Max zoom', keywords: ['maximum', 'limit', 'highest', 'magnification', 'level', 'cap', 'zoom level', 'factor', 'times', 'upper'], desc: 'The highest zoom level', min: 2, max: 30, step: 1, def: 30.0, unit: 'times' }, - { key: 'zoomInSpeed', type: 'slider', label: 'Zoom-in speed', keywords: ['faster', 'slower', 'rate', 'magnify speed', 'zoom rate', 'acceleration', 'velocity', 'how fast'], desc: 'How fast the view zooms in', min: 0.25, max: 4, step: 0.05, def: 1.5, unit: 'times' }, - { key: 'zoomOutSpeed', type: 'slider', label: 'Zoom-out speed', keywords: ['faster', 'slower', 'rate', 'zoom rate', 'velocity', 'how fast'], desc: 'How fast the view zooms out', min: 0.25, max: 4, step: 0.05, def: 1.5, unit: 'times' }, + { key: 'zoomInSpeed', type: 'slider', label: 'Zoom-in speed', keywords: ['faster', 'slower', 'rate', 'magnify speed', 'zoom rate', 'acceleration', 'velocity', 'how fast'], desc: 'How fast the view zooms in', min: 0.25, max: 4, step: 0.05, def: 1.75, unit: 'times' }, + { key: 'zoomOutSpeed', type: 'slider', label: 'Zoom-out speed', keywords: ['faster', 'slower', 'rate', 'zoom rate', 'velocity', 'how fast'], desc: 'How fast the view zooms out', min: 0.25, max: 4, step: 0.05, def: 1.75, unit: 'times' }, { key: 'zoomEaseOutMs', type: 'slider', label: 'Release glide', keywords: ['inertia', 'coast', 'momentum', 'ease out', 'easing', 'smooth stop', 'slow down', 'deceleration', 'stop', 'let go', 'release', 'delay', 'milliseconds'], desc: 'How long the zoom coasts after you let go', min: 0, max: 300, step: 5, def: 115, unit: 'ms' }, ] }, { caption: 'Easing', rows: [ @@ -79,7 +79,7 @@ export const groups = [ // Transform: txSamplingMode, DWM's own filter (0 nearest / 1 smooth = Lanczos, the former High // resolution cursor toggle, #227). It applies live (#369) and turning Smooth on asks first (#441). { key: 'txSamplingMode', type: 'select', label: 'Transform scaling', tag: 'Experimental', keywords: ['scaling', 'upscaling', 'filter', 'sharp', 'crisp', 'smooth', 'blurry', 'pixelated', 'hidpi', 'sampling', 'quality', 'nearest', 'lanczos', 'bitmap smoothing', 'high resolution cursor', 'mpo', 'overlay', 'cursor', 'pointer', 'resolution', '4k', 'antialiasing'], - desc: 'Smooth also sharpens the pointer at high zoom', options: ['0', '1'], optionLabels: { '0': 'Nearest', '1': 'Smooth' }, def: '1', + desc: 'Smooth also sharpens the pointer at high zoom', options: ['0', '1'], optionLabels: { '0': 'Nearest', '1': 'Smooth' }, def: '0', showIf: { key: 'model', ne: 'render' } }, // Render: renderScaling (issue #456), the engine's scaling filter: nearest (default, #508), xBR // or NVIDIA Image Scaling, picked in the 2026-10-12 upscaler A/B. Applies on the config reload. diff --git a/ui/tests/scaling-notice.spec.js b/ui/tests/scaling-notice.spec.js index 21a4589..8addb75 100644 --- a/ui/tests/scaling-notice.spec.js +++ b/ui/tests/scaling-notice.spec.js @@ -6,7 +6,7 @@ test.beforeEach(async ({ page }) => { await page.addInitScript(() => { window.__skipSplash = true; window.__msgs = []; - window.__live = { maxLevel: '12', model: 'transform', txSamplingMode: '0', uiTheme: 'dark' }; + window.__live = { maxLevel: '12', model: 'transform', uiTheme: 'dark' }; window.__saved = { ...window.__live }; const listeners = new Set(); const send = (data) => listeners.forEach((fn) => fn({ data })); diff --git a/ui/tests/session.spec.js b/ui/tests/session.spec.js index 8b172d0..96992f3 100644 --- a/ui/tests/session.spec.js +++ b/ui/tests/session.spec.js @@ -284,7 +284,6 @@ test('a Wind with both engines up switches engine live: no Restart Wind button', }); test('Smooth transform scaling applies live: no MPO registry write, no restart prompt (#369)', async ({ page }) => { - await page.addInitScript(() => { window.__live.txSamplingMode = '0'; window.__saved.txSamplingMode = '0'; }); await page.goto('/'); await go(page, 'zoom'); const sel = page.locator('[data-key="txSamplingMode"] select'); diff --git a/ui/tests/settings.spec.js b/ui/tests/settings.spec.js index 1c267ab..484e189 100644 --- a/ui/tests/settings.spec.js +++ b/ui/tests/settings.spec.js @@ -214,7 +214,6 @@ test('View: the edge margin shows only when the pointer is kept within the edges }); test('Smooth transform scaling is tagged Experimental and keeps the release glide (#439)', async ({ page }) => { - await page.addInitScript(() => { window.__cfgExtra = { txSamplingMode: '0' }; }); await page.goto('/'); await go(page, 'zoom'); await expect(key(page, 'txSamplingMode').locator('.tag')).toHaveText('Experimental');