Skip to content

refactor: Migrate the powershell cursor script into native cursor sampler#621

Merged
siddharthvaddem merged 3 commits into
siddharthvaddem:mainfrom
LucaFontanot:refactor-cursor
May 21, 2026
Merged

refactor: Migrate the powershell cursor script into native cursor sampler#621
siddharthvaddem merged 3 commits into
siddharthvaddem:mainfrom
LucaFontanot:refactor-cursor

Conversation

@LucaFontanot
Copy link
Copy Markdown
Contributor

@LucaFontanot LucaFontanot commented May 19, 2026

Description

Removed the powershell script that handles the cursor and implemented a native version with the same functionality and output patterns.

Motivation

About the issue about antivirus flags, it is necessary to find a different approach, because the cursors "recording" method via the powershell is often used by spyware.

Type of Change

  • New Feature
  • Bug Fix
  • Refactor / Code Cleanup
  • Documentation Update
  • Other (please specify)

Related Issue(s)

#620

Screenshots / Video

This update does not include any visual change.
The cursor editor works like before.

Checklist

  • I have performed a self-review of my code.
  • I have added any necessary screenshots or videos.
  • I have linked related issue(s) and updated the changelog if applicable.

Summary by CodeRabbit

  • Refactor
    • Replaced temporary script-based cursor sampling with a native Windows helper for improved performance and reliability.
  • New Features
    • Native cursor sampler emits periodic cursor state samples, optional window bounds, cursor images, and cursor-type classification, plus explicit readiness/error signals.
  • Chores
    • Windows build and packaging updated to include and distribute the new cursor sampling helper.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 19, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5f940742-e131-41e3-9d9e-232ae42fc3e3

📥 Commits

Reviewing files that changed from the base of the PR and between 7826cc4 and cfe6b9e.

📒 Files selected for processing (1)
  • electron/native/wgc-capture/src/cursor-sampler.cpp

📝 Walkthrough

Walkthrough

Swaps Windows cursor sampling from dynamically-generated PowerShell scripts to a compiled C++ executable. The bridge now resolves and spawns cursor-sampler.exe directly; the new binary handles mouse hook installation, cursor state polling, PNG asset rendering, and JSON event streaming natively. Old script cleanup code is removed and replaced with direct exe launching.

Changes

PowerShell to C++ Cursor Sampler Migration

Layer / File(s) Summary
Bridge exe launcher migration
electron/native-bridge/cursor/recording/windowsNativeRecordingSession.ts
WindowsNativeRecordingSession resolves cursor-sampler.exe from multiple candidate paths (env var, app install, asar-adjusted) and spawns it directly with interval and optional window handle. Removed PowerShell script generation, temporary file handling (helperScriptPath, cleanupHelperScript), and cleanup on process exit/error/failure.
Cursor sampler hook, utilities, and type detection
electron/native/wgc-capture/src/cursor-sampler.cpp (lines 1–172)
Global WH_MOUSE_LL hook state and atomic button counters; LowLevelMouseProc callback for event capture. Utility functions for JSON output, string escaping, base64 encoding. PNG encoder CLSID discovery and standard cursor type lookup from cached LoadCursor results. Custom cursor classification heuristic analyzes non-transparent pixels and bounding box to detect hand gestures ("closed-hand", "open-hand").
PNG asset rendering and encoding
electron/native/wgc-capture/src/cursor-sampler.cpp (lines 183–308)
buildAssetJson extracts cursor hotspot and dimensions, duplicates cursor handle, renders to 32-bpp transparent top-down DIB via DrawIconEx (preserves per-pixel alpha), converts to PNG through GDI+ Bitmap and in-memory stream, base64-encodes to data URL, and returns asset JSON with optional cursorType.
Sampling loop and main entrypoint
electron/native/wgc-capture/src/cursor-sampler.cpp (lines 313–479)
runSamplingLoop resets button counters, queries cursor state, derives button press/release from atomic counters and GetAsyncKeyState, optionally fetches target window bounds, emits sample JSON to stdout (with asset JSON only on cursor handle change), exits when stdout fails. main parses args, initializes GDI+, installs hook, starts background thread, runs message pump, cleans up on quit.
Build configuration and distribution
electron/native/wgc-capture/CMakeLists.txt, scripts/build-windows-wgc-helper.mjs
CMakeLists adds cursor-sampler executable target with Windows definitions (NOMINMAX, _WIN32_WINNT=0x0A00), compile options (/EHsc /W4 /utf-8), and GDI+/gdi32 linkage. Build script validates exe exists post-build and copies it to bin/win32-x64 distribution directory alongside wgc-capture.exe.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes


Possibly related PRs

  • siddharthvaddem/openscreen#217: earlier changes that began replacing the PowerShell-based sampler flow with a native executable and modified related bridge pieces.

Suggested reviewers

  • siddharthvaddem
  • FabLrc

Poem

⚙️ powerShell got benched at midnight,
c++ pulls a hook and samples light,
pngs get wrapped in base64 song,
stdout hums the whole night long,
kinda cursed, kinda tidy — lowkey right.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 53.85% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: migrating PowerShell cursor script to native C++ implementation.
Description check ✅ Passed Description covers all key sections: clear purpose, solid motivation (antivirus concerns), correct type selection, linked related issue, and checklist completion.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 49ee3ac0db

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
electron/native/wgc-capture/CMakeLists.txt (1)

53-67: 💤 Low value

nit: missing WIN32_LEAN_AND_MEAN for consistency

wgc-capture defines WIN32_LEAN_AND_MEAN but cursor-sampler doesn't. not a big deal for a small target, but it does slightly reduce include bloat and keeps the two targets consistent.

also lowkey nice to avoid accidentally pulling in winsock1 vs winsock2 issues if networking ever gets added.

🧹 optional diff
 target_compile_definitions(cursor-sampler PRIVATE
   NOMINMAX
+  WIN32_LEAN_AND_MEAN
   _WIN32_WINNT=0x0A00
 )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@electron/native/wgc-capture/CMakeLists.txt` around lines 53 - 67, Add
WIN32_LEAN_AND_MEAN to the cursor-sampler target's compile definitions to match
wgc-capture and reduce Windows header bloat: update the
target_compile_definitions call for the cursor-sampler executable (symbol:
cursor-sampler) to include WIN32_LEAN_AND_MEAN alongside NOMINMAX and
_WIN32_WINNT=0x0A00 so the preprocessor define is applied consistently.
electron/native-bridge/cursor/recording/windowsNativeRecordingSession.ts (1)

18-30: 💤 Low value

heads up: arm64 windows won't find the helper in bin dir

the build script only produces win32-x64 artifacts, so archTag = 'win32-arm64' will look for a path that doesn't exist. users would need to set OPENSCREEN_CURSOR_SAMPLER_EXE manually or rely on the build dir fallback.

probably fine if arm64 isn't officially supported (or x64 emulation handles it), but might be worth a comment noting the limitation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@electron/native-bridge/cursor/recording/windowsNativeRecordingSession.ts`
around lines 18 - 30, getCursorSamplerCandidates currently sets archTag to
"win32-arm64" for arm64 Windows but the build only produces "win32-x64"
artifacts; update the function to handle this by falling back to the x64 bin
path when the arm64 artifact is missing (e.g., set archTag to "win32-x64" for
arm64 or check the resolved path exists and try the x64 variant), and add a
brief comment explaining the limitation; reference getCursorSamplerCandidates,
the archTag variable, and OPENSCREEN_CURSOR_SAMPLER_EXE when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@electron/native/wgc-capture/src/cursor-sampler.cpp`:
- Around line 475-477: sampler.detach() races with teardown
(UnhookWindowsHookEx(g_mouseHook) and Gdiplus::GdiplusShutdown), so replace the
detach with an explicit stop signalling and join: add/use a thread-safe stop
flag or a sampler.request_stop() method, signal it before teardown, then call
sampler.join() (or the thread's join) to wait for the sampler loop to exit; only
after the join return should you call UnhookWindowsHookEx(g_mouseHook) and
Gdiplus::GdiplusShutdown(gdipToken) to ensure no code is running while
hooks/GDI+ are torn down.
- Line 416: The code uses std::max to compute intervalMs (const int intervalMs =
std::max(1, std::atoi(argv[1]));) but the file lacks an explicit `#include`
<algorithm>, which can break compilation (especially with NOMINMAX); add an
`#include` <algorithm> to the includes at the top of cursor-sampler.cpp so
std::max is defined and unambiguous.

---

Nitpick comments:
In `@electron/native-bridge/cursor/recording/windowsNativeRecordingSession.ts`:
- Around line 18-30: getCursorSamplerCandidates currently sets archTag to
"win32-arm64" for arm64 Windows but the build only produces "win32-x64"
artifacts; update the function to handle this by falling back to the x64 bin
path when the arm64 artifact is missing (e.g., set archTag to "win32-x64" for
arm64 or check the resolved path exists and try the x64 variant), and add a
brief comment explaining the limitation; reference getCursorSamplerCandidates,
the archTag variable, and OPENSCREEN_CURSOR_SAMPLER_EXE when making the change.

In `@electron/native/wgc-capture/CMakeLists.txt`:
- Around line 53-67: Add WIN32_LEAN_AND_MEAN to the cursor-sampler target's
compile definitions to match wgc-capture and reduce Windows header bloat: update
the target_compile_definitions call for the cursor-sampler executable (symbol:
cursor-sampler) to include WIN32_LEAN_AND_MEAN alongside NOMINMAX and
_WIN32_WINNT=0x0A00 so the preprocessor define is applied consistently.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d7343f8c-0613-407b-837f-3640feeeee3f

📥 Commits

Reviewing files that changed from the base of the PR and between 00fbd95 and 49ee3ac.

📒 Files selected for processing (5)
  • electron/native-bridge/cursor/recording/windowsNativeRecordingSession.script.ts
  • electron/native-bridge/cursor/recording/windowsNativeRecordingSession.ts
  • electron/native/wgc-capture/CMakeLists.txt
  • electron/native/wgc-capture/src/cursor-sampler.cpp
  • scripts/build-windows-wgc-helper.mjs
💤 Files with no reviewable changes (1)
  • electron/native-bridge/cursor/recording/windowsNativeRecordingSession.script.ts

Comment thread electron/native/wgc-capture/src/cursor-sampler.cpp
Comment thread electron/native/wgc-capture/src/cursor-sampler.cpp Outdated
@siddharthvaddem siddharthvaddem merged commit 9f7f498 into siddharthvaddem:main May 21, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants