Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/SHELL-PANEL-CURSOR-FINDINGS.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ transform: it drifts off the view centre by speed x tick x level.
which marshals every tick write onto the system-wide mouse input thread. Field: hitches on zoom,
worst when zooming with the picker open (the public write, 3-9 ms, landed on that thread too).
- **Shipped: freeze.** While a panel is open the pointer is pinned with a 1px `ClipCursor`; the hand's
motion arrives as ballistics-cooked raw input (the Inspect machinery, `cookActive`), and the
motion arrives as ballistics-cooked raw input (the Inspect machinery), and the
transform model moves the clip (which moves the pointer) right after writing the view, on the tick
thread. Owner-verified: no wobble, no flicker, normal hitch-free zoom.

Expand Down
2 changes: 1 addition & 1 deletion docs/architecture/01-overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ build. See [11](11-build-test-release.md).
| Tray | `tray_host.*`, `tray_ipc.h`, `tray_items.*`, `tray_status.h`, `tray_app/` |
| Settings host | `config_ui/` (`main.cpp`, `ini_edit.*`, `mpo.h`, `wind_watchdog.h`, `webview_recover.h`) |
| Logging and diagnostics | `logging.*`, `test_telemetry.h`, `hitch_record.*`, `tick_span.h` |
| Installer support | `installer_state.h`, `webview2_probe.h`, `version.h` |
| Installer support | `installer_state.h` (ParseVersion), `webview2_probe.h`, `version.h` |

Other top-level folders: `ui/` (Svelte settings app and Playwright tests), `tests/` (doctest),
`tools/` (deploy, release and measurement scripts, [12](12-instrumentation.md)), `installer/`
Expand Down
5 changes: 2 additions & 3 deletions docs/architecture/02-tick-loop.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ All state that feeds the view is read and written on the tick thread, in one pas
across threads makes the view and the cursor sample different instants, which shows as a visible
beat (the wobble class in [../NATIVE-MAGNIFIER-STOMP.md](../NATIVE-MAGNIFIER-STOMP.md)).

`RunTick` never sleeps or waits; the caller paces it (see [Pacing](#pacing)). That is also why it
is safe to call from the `WM_TIMER` branch in `WndProc` during a modal loop.
`RunTick` never sleeps or waits; the caller paces it (see [Pacing](#pacing)).

## The phases of a tick

Expand Down Expand Up @@ -106,7 +105,7 @@ to 1x. `StripUiOnlyKeys` (`src/config.cpp`) drops `uiTheme`, `uiPalette`, `showA
(`t.lastCoreIni`). An identical fingerprint skips the reload. The fingerprint is seeded at startup;
an empty one would make the first Settings write of a session reload.

A real reload re-binds the hook's buttons and swallowed keys (`g_input.setButtons`/`setKeys`),
A real reload re-binds the hook's buttons and swallowed keys (`g_input.setButtonBinds`/`setKeys`),
re-registers the hotkeys, invalidates the foreground cache, and rebuilds `ZoomController` and `CursorMapper` with the mapper's centre kept.
Engine-shaped keys (`model`) need a restart: they decide which models exist.

Expand Down
2 changes: 1 addition & 1 deletion docs/architecture/06-input.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Bound inputs are eaten so they never also fire in the focused app.
- **Balanced down/up.** A DOWN of a bound input is swallowed and recorded (`g_swallowedDown`,
`g_kbSwallowedDown`); an UP is swallowed only if its DOWN was. Swallowing an UP whose DOWN the
system saw leaves the input held system-wide (the stuck side-button bug, issue #113).
- **No stranded keys.** Records are cleared on every remap (`setButtons`, `setKeys`), and teardown
- **No stranded keys.** Records are cleared on every remap (`setButtonBinds`, `setKeys`), and teardown
runs `ReleaseSwallowedButtons`/`ReleaseSwallowedKeys`, which synthesize the missing UP.
- **Decide once per press.** A key bind is swallowed only when a bind on that key has all its
modifiers held (`keyBindMatches`). A VK-only test once ate a plain F1 system-wide for a Ctrl+F1
Expand Down
6 changes: 3 additions & 3 deletions docs/architecture/07-cursor.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,9 @@ Inspect and the hide-cursor hotkey (`cursorVisibility=never`) hide DWM's pointer
session; the Inspect crosshair is then a window of Wind's.

- **`CursorBlanker`** (`src/cursor_blanker.*`) swaps the 14 system cursors for transparent ones and
keeps the originals. It first reloads the user's scheme, so a previously killed Wind's blanks are
never captured as originals. `MagShowSystemCursor(FALSE)` covers app-custom cursors. The swaps
run in order on the `Wind cursor swaps` worker thread (#363): the restore is a full
restores them by reloading the user's scheme (no cached originals). The constructor reloads it
first, so a previously killed Wind's blanks never outlive it. `MagShowSystemCursor(FALSE)` covers
app-custom cursors. The swaps run in order on the `Wind cursor swaps` worker thread (#363): the restore is a full
`SPI_SETCURSORS` scheme reload (8 ms median, up to 90 ms under load) and the blank 14
`SetSystemCursor` calls, which froze the 1x landing frame on the tick thread. The zoom-out repaint
nudge rides on the worker after the restore. `restoreSync()` waits (shutdown); the crash filter
Expand Down
8 changes: 0 additions & 8 deletions src/config_ui/webview_recover.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,4 @@ inline WvRecovery DecideWvRecovery(int kind, WvRecoverBudget& b, unsigned long l
return r;
}

// The page's unsaved edits travel as a JSON object of string values; only something shaped like one
// is ever echoed back into the page.
inline bool LooksLikeJsonObject(const char* s, unsigned long long len) {
unsigned long long a = 0, z = len;
while (a < z && (s[a] == ' ' || s[a] == '\n' || s[a] == '\r' || s[a] == '\t')) ++a;
while (z > a && (s[z - 1] == ' ' || s[z - 1] == '\n' || s[z - 1] == '\r' || s[z - 1] == '\t')) --z;
return z - a >= 2 && s[a] == '{' && s[z - 1] == '}';
}
} // namespace wind
8 changes: 1 addition & 7 deletions src/cursor_blanker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,8 @@ static void RestoreAll() {

CursorBlanker::CursorBlanker() : st_(std::make_shared<State>()) {
// If a previous Wind was hard-killed while cursors were blanked, the desktop still has blank
// shared cursors; reload the user's scheme FIRST or the blanks get captured as "originals".
// shared cursors; reload the user's scheme so they do not outlive the crash.
RestoreAll();
for (UINT id : kStandardIds) {
HCURSOR shared = LoadCursorW(nullptr, MAKEINTRESOURCEW(id));
if (!shared) continue;
HCURSOR copy = CopyCursor(shared);
if (copy) originals_[shared] = copy;
}
worker_ = std::thread([st = st_] { run(st); });
}

Expand Down
2 changes: 0 additions & 2 deletions src/cursor_blanker.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
#include <memory>
#include <mutex>
#include <thread>
#include <unordered_map>
namespace wind {
// Swaps the system cursor set for blanks while the real pointer is hidden (Inspect, the hide-cursor
// hotkey), and back.
Expand Down Expand Up @@ -41,7 +40,6 @@ class CursorBlanker {
void post(std::function<void()> op);
bool runSync(std::function<void()> op);
static void run(std::shared_ptr<State> st);
std::unordered_map<HCURSOR, HCURSOR> originals_;
bool blanked_ = false;
std::shared_ptr<State> st_;
std::thread worker_;
Expand Down
1 change: 0 additions & 1 deletion src/cursor_sprite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,6 @@ void CursorSprite::keepOnTop() {
// synchronous DWM z-order transaction that hitches a fullscreen game (issue #148; same fix
// as the render overlay's calm-topmost). The per-tick displaced() walk reclaims immediately.
if (displaced()) {
lastTopmostMs_ = GetTickCount64();
SetWindowPos(hwnd_, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
}
}
Expand Down
1 change: 0 additions & 1 deletion src/cursor_sprite.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,5 @@ class CursorSprite {
int scale_ = 1;
bool visible_ = false;
bool crosshairMode_ = false; // window currently holds the crosshair pixels
unsigned long long lastTopmostMs_ = 0; // last HWND_TOPMOST re-assert (throttled)
};
}
6 changes: 0 additions & 6 deletions src/gain_learner.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,6 @@ class GainLearner {
return gain_[lo] + (gain_[hi] - gain_[lo]) * (t < 0.0 ? 0.0 : (t > 1.0 ? 1.0 : t));
}

bool warmedUp() const {
int have = 0;
for (int i = 0; i < kBins; ++i) if (n_[i] > 0) ++have;
return have >= 2;
}

void reset() { for (int i = 0; i < kBins; ++i) { gain_[i] = 1.0; n_[i] = 0; } }

// Text round-trip, so a restart starts from the last learned curve instead of raw passthrough
Expand Down
11 changes: 1 addition & 10 deletions src/input_router.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ static bool g_hookOk = false; // result of SetWindowsHookExW, publi
// Only an UP whose DOWN we swallowed may be swallowed too, so the system's down/up view stays
// balanced and a button can never be left believed-held. Reset on remap so a stale flag from a
// previous binding can't cause a later UP to be wrongly swallowed. ATOMIC: touched by three
// contexts - the hook thread (MouseProc), the tick thread (setButtons on hot-reload), and the
// contexts - the hook thread (MouseProc), the tick thread (setButtonBinds on hot-reload), and the
// teardown caller (ReleaseSwallowedButtons via stop()) - so plain bools would be a data race.
static wind::SwallowLedger<6> g_swallowedDown; // index = button id 1..5 (#285: 3/4/5 = L/R/M)
// Which direction a pressed bound button is holding (1 in, 2 out, 0 none) and that bind's modifiers,
Expand Down Expand Up @@ -143,15 +143,6 @@ void InputRouter::setButtonState(int buttonId, bool down) {
}
PublishButtonHeld(state_);
}
bool InputRouter::isZoomButton(int xbuttonId) const {
return xbuttonId == inButtonId_.load(std::memory_order_relaxed)
|| xbuttonId == inButtonId2_.load(std::memory_order_relaxed)
|| xbuttonId == outButtonId_.load(std::memory_order_relaxed)
|| xbuttonId == outButtonId2_.load(std::memory_order_relaxed);
}
void InputRouter::setButtons(int inButtonId, int inButtonId2, int outButtonId, int outButtonId2) {
setButtonBinds(inButtonId, 0, inButtonId2, 0, outButtonId, 0, outButtonId2, 0);
}
void InputRouter::setButtonBinds(int inButtonId, int inMods, int inButtonId2, int inMods2,
int outButtonId, int outMods, int outButtonId2, int outMods2) {
inButtonMods_.store(inMods, std::memory_order_relaxed);
Expand Down
9 changes: 3 additions & 6 deletions src/input_router.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@ class InputRouter {
// Map an XBUTTON id (1 = XBUTTON1, 2 = XBUTTON2) to the in/out held state, using the
// configured zoom buttons. Shared by the WH_MOUSE_LL hook and main's WM_INPUT path.
void setButtonState(int xbuttonId, bool down);
// Whether the id is one of the configured zoom buttons (used to decide swallowing).
bool isZoomButton(int xbuttonId) const;
// Whether the hook should swallow the configured zoom buttons (set in start()).
bool swallowEnabled() const { return swallow_; }
// --- Keyboard binds (WH_KEYBOARD_LL hook) -------------------------------------------------
Expand Down Expand Up @@ -148,11 +146,10 @@ class InputRouter {
// (Raw Input still delivers the transition even though the hook swallows the legacy message, so
// both writing would race/double-count). WM_INPUT button writes are only the WIND_NOHOOK fallback.
bool hookActive() const { return hookActive_.load(std::memory_order_relaxed); }
// Live-rebind the configured zoom buttons (called from the tick thread on hot-reload).
// Atomic so the hook thread's reads in setButtonState/isZoomButton stay race-free, and the
// Live-rebind the configured zoom buttons (called from the tick thread on hot-reload), with a
// modifier mask per slot; button ids 3/4/5 = left/right/middle click (#285).
// Atomic so the hook thread's reads in setButtonState stay race-free, and the
// held flags are cleared so a stale press of the previous button does not stick.
void setButtons(int inButtonId, int inButtonId2, int outButtonId, int outButtonId2);
// #285: the same with a modifier mask per slot, and button ids 3/4/5 = left/right/middle click.
void setButtonBinds(int inButtonId, int inMods, int inButtonId2, int inMods2,
int outButtonId, int outMods, int outButtonId2, int outMods2);
// The slot a button press belongs to, given the modifiers held (hook thread). False = not a bind.
Expand Down
25 changes: 1 addition & 24 deletions src/installer_state.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
#pragma once
// Pure decision logic shared between the installer's NSIS script and the test suite.
// NSIS reimplements these rules in its own dialect; these definitions are what the rules
// are checked against, so a change here is a change the installer must follow.
// Version parsing used by the WebView2 runtime probe.
// NO <windows.h>: this header is compiled into the desktop-free test binary.
#include <string>

Expand All @@ -14,8 +12,6 @@ struct Version {
bool valid = false;
};

enum class InstallState { Fresh, Upgrade, Reinstall, Downgrade };

// "1.2.3" or "1.2.3.4" (the build field is read and discarded: ARP writes three parts,
// VERSIONINFO writes four, and they must compare equal). Anything else is invalid.
inline Version ParseVersion(const std::string& s) {
Expand Down Expand Up @@ -46,23 +42,4 @@ inline Version ParseVersion(const std::string& s) {
return v;
}

inline int CompareVersion(const Version& a, const Version& b) {
if (a.major != b.major) return a.major < b.major ? -1 : 1;
if (a.minor != b.minor) return a.minor < b.minor ? -1 : 1;
if (a.patch != b.patch) return a.patch < b.patch ? -1 : 1;
return 0;
}

// `found` is whatever DisplayVersion the ARP key held, which is "" on a clean machine and
// can be junk left by a half-removed install. Either way there is nothing to upgrade from.
inline InstallState ClassifyInstall(const std::string& found, const std::string& ours) {
const Version f = ParseVersion(found);
const Version o = ParseVersion(ours);
if (!f.valid || !o.valid) return InstallState::Fresh;
const int c = CompareVersion(f, o);
if (c < 0) return InstallState::Upgrade;
if (c == 0) return InstallState::Reinstall;
return InstallState::Downgrade;
}

} // namespace wind
1 change: 0 additions & 1 deletion src/java_bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ class JavaBridge {
bool ensure(HWND javaWindow, DWORD wakeTid, UINT wakeMsg, bool log);
// The caret of the focused Java text component, screen px. False when there is none.
bool caret(HWND javaWindow, RECT& out);
bool loaded() const { return mod_ != nullptr; }
// The last successful read: which bridge call answered and the caret index (trackLog).
const char* lastSrc() const { return lastSrc_; }
int lastIndex() const { return lastIndex_; }
Expand Down
16 changes: 0 additions & 16 deletions src/keybind_rules.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,20 +92,4 @@ inline BindVerdict CheckClickBind(int button, int mods) {
return BindVerdict::Ok;
}

inline const char* BindVerdictName(BindVerdict v) {
switch (v) {
case BindVerdict::Ok: return "ok";
case BindVerdict::NeverBindable: return "never";
case BindVerdict::ModifierAsKey: return "modifier";
case BindVerdict::NotAlone: return "notalone";
case BindVerdict::ShiftTypes: return "shifttypes";
case BindVerdict::AltGrTypes: return "altgr";
case BindVerdict::SystemReserved: return "system";
case BindVerdict::WindowsReserved: return "windows";
case BindVerdict::NeedsModifier: return "needsmod";
case BindVerdict::CtrlAlone: return "ctrlalone";
case BindVerdict::ShiftAlone: return "shiftalone";
}
return "?";
}
} // namespace wind
Loading
Loading