Skip to content

Allow OCR to work with Screen Curtain via Windows Graphics Capture#19753

Draft
PratikP1 wants to merge 3 commits intonvaccess:masterfrom
PratikP1:wgc-ocr-screen-curtain
Draft

Allow OCR to work with Screen Curtain via Windows Graphics Capture#19753
PratikP1 wants to merge 3 commits intonvaccess:masterfrom
PratikP1:wgc-ocr-screen-curtain

Conversation

@PratikP1
Copy link
Copy Markdown

@PratikP1 PratikP1 commented Mar 6, 2026

Link to issue number:

Closes #19164

Summary of the issue:

When Screen Curtain is enabled, Windows OCR fails because the GDI screen capture (BitBlt/GetDC) reads pixels after the Magnification API color transform, returning an all-black image. Users must manually disable Screen Curtain, perform OCR, and re-enable it — a tedious 4-step process that exposes screen content.

Description of user facing changes:

  • Windows OCR now works while Screen Curtain is active on Windows 10 version 1903 and later. When you press NVDA+r, NVDA automatically uses Windows Graphics Capture to read window content directly from the desktop compositor, bypassing the screen blackout. The physical display remains black, preserving your privacy.
  • On older Windows versions (before 1903), a message informs the user that OCR requires a newer Windows version to work with Screen Curtain, and suggests disabling Screen Curtain or upgrading Windows.
  • The screen curtain toggle (NVDA+control+escape) now allows enabling Screen Curtain while a WGC-based OCR result is active (previously blocked in all cases).

Description of developer facing changes:

  • New contentRecog.wgcCapture module with WgcOcr class implementing ContentRecognizer. Uses Windows.Graphics.Capture CreateForWindow to capture window frames from the DWM compositor (pre-magnification-transform), then runs Windows.Media.Ocr on the captured bitmap.
  • New C++/WinRT source files: nvdaHelper/localWin10/wgcCapture.h and wgcCapture.cpp. Exports 5 functions via nvdaHelperLocalWin10.dll: wgcCapture_isSupported, wgcCapture_initialize, wgcCapture_recognizeWindow, wgcCapture_recognizeWindowRegion, wgcCapture_terminate.
  • New ctypes bindings in NVDAHelper.localWin10 for all WGC functions.
  • recogUi.recognizeNavigatorObject() now auto-switches to WgcOcr when Screen Curtain is active and WGC is available.
  • RefreshableRecogResultNVDAObject._recognize() skips GDI screen capture when the recognizer is WgcOcr (which captures its own frames via HWND).
  • globalCommands.script_recognizeWithUwpOcr() no longer blocks when Screen Curtain is active; detection and fallback are handled in recognizeNavigatorObject.
  • New [wgcCapture] configuration section with captureMode option (auto/always/never).
  • Build system updated: wgcCapture.cpp added to sconscript, linked with d3d11 and dxgi.

Description of development approach:

The key insight is that Windows.Graphics.Capture CreateForWindow reads window content from the Desktop Window Manager (DWM) compositor before the Magnification API full-screen color transform that Screen Curtain applies. This means:

  1. The Magnification API zeros out RGB channels on the physical display (Screen Curtain).
  2. But WGC captures the pre-transform window surface from DWM, which contains the original pixel data.
  3. The captured bitmap is then passed to Windows.Media.Ocr.OcrEngine.RecognizeAsync() for text recognition.

The implementation follows NVDA's existing architecture:

  • C++/WinRT layer (wgcCapture.cpp): Creates a Direct3D 11 device, uses IGraphicsCaptureItemInterop::CreateForWindow for HWND-based capture, Direct3D11CaptureFramePool::CreateFreeThreaded (no DispatcherQueue needed), optional sub-region cropping, and JSON serialization of OCR results matching NVDA's existing format.
  • Python layer (wgcCapture.py): Implements ContentRecognizer interface identically to UwpOcr, with HWND discovery via navigator object, coordinate translation from screen-space to window-relative, and async callback handling.
  • Auto-switching (recogUi.py): recognizeNavigatorObject checks Screen Curtain state and transparently swaps the recognizer, preserving the user's configured OCR language.

Minimum requirement: Windows 10 version 1903 (build 18362) for CreateForWindow HWND interop. On Windows 11, the yellow capture border is automatically hidden via IGraphicsCaptureSession3.IsBorderRequired(false).

Testing strategy:

  • Manual testing with Screen Curtain enabled: verified OCR produces correct text results while screen remains black.
  • Manual testing with Screen Curtain disabled: verified existing UWP OCR path is unchanged and functions normally.
  • Tested on Windows 10 22H2 and Windows 11 24H2.
  • Verified screen curtain toggle allows enabling curtain while WGC OCR result is active.
  • Verified older-Windows fallback path displays appropriate message.
  • Build system compiles cleanly with wgcCapture.cpp and d3d11/dxgi linkage.
  • Ruff lint and format checks pass on all modified Python files.

Known issues with pull request:

  • The [wgcCapture] config section (captureMode option) is defined but not yet wired into a settings GUI panel. Currently the auto-switching logic is hardcoded to auto behavior. A future PR could add a settings panel to expose this option.
  • Unit tests for the WGC capture path require a desktop compositor and window, so they are not included in this PR. System tests could be added in a follow-up.

Code Review Checklist:

  • Documentation:
    • Change log entry (user-facing + developer-facing in changes.md)
    • User Documentation (updated Screen Curtain and Windows OCR sections in userGuide.md)
    • Developer / Technical Documentation (inline docstrings, Sphinx seealso references in localWin10.py)
    • Context sensitive help for GUI changes (N/A — no GUI changes in this PR)
  • Testing:
    • Unit tests (not feasible without desktop compositor; noted in Known Issues)
    • System (end to end) tests (to be added in follow-up)
    • Manual testing
  • UX of all users considered:
    • Speech (messages for screen curtain + OCR interaction)
    • Braille (standard message delivery)
    • Low Vision (Screen Curtain behavior preserved)
    • Different web browsers (N/A — window-level capture, browser-agnostic)
    • Localization in other languages / culture than English (all user-facing strings use _() for translation)
  • API is compatible with existing add-ons.
  • Security precautions taken (input validation: null callback, IsWindow check, zero-dimension rejection, bounds clamping in crop).

PratikP1 and others added 2 commits March 6, 2026 08:25
…vaccess#19164)

Use Windows.Graphics.Capture CreateForWindow to capture window content
from the DWM compositor, bypassing the Magnification API color transform
used by Screen Curtain. This allows Windows OCR to function while Screen
Curtain remains active, preserving the user's visual privacy.

Closes nvaccess#19164
@PratikP1 PratikP1 marked this pull request as ready for review March 6, 2026 13:37
@PratikP1 PratikP1 requested review from a team as code owners March 6, 2026 13:37
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR enables Windows OCR to work while Screen Curtain is enabled by switching to a Windows Graphics Capture (WGC)–based capture path (Windows 10 1903+) that reads pixels from the DWM compositor rather than via GDI, preserving the physical screen blackout.

Changes:

  • Add a new WGC-based OCR recognizer (contentRecog.wgcCapture.WgcOcr) and auto-switch to it when Screen Curtain is active (with a user message fallback on pre-1903 Windows).
  • Update Screen Curtain toggling logic to allow enabling Screen Curtain while a WGC-based OCR result is active (but still block for legacy/GDI OCR).
  • Add native helper implementation (nvdaHelperLocalWin10.dll) for WGC capture + OCR, plus Python ctypes bindings, config spec, and documentation/changelog updates.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
user_docs/en/userGuide.md Document OCR behavior with Screen Curtain on Win10 1903+ and fallback on older Windows.
user_docs/en/changes.md Add release notes for WGC-based OCR + new developer-facing APIs.
source/globalCommands.py Remove “disable screen curtain” block for OCR; relax Screen Curtain enabling restriction when recognizer is WGC.
source/contentRecog/wgcCapture.py New Python recognizer that captures via HWND through native WGC helper and parses JSON OCR results.
source/contentRecog/recogUi.py Skip GDI capture for WgcOcr and auto-switch recognizer when Screen Curtain is active.
source/config/configSpec.py Add [wgcCapture] config section with captureMode option.
source/NVDAHelper/localWin10.py Add ctypes bindings for wgcCapture_* functions exported by nvdaHelperLocalWin10.dll.
nvdaHelper/localWin10/wgcCapture.h New C API header for WGC capture/OCR helper.
nvdaHelper/localWin10/wgcCapture.cpp New C++/WinRT implementation using Windows.Graphics.Capture + Windows.Media.Ocr.
nvdaHelper/localWin10/sconscript Add wgcCapture.cpp to build and link d3d11/dxgi.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +370 to +371
if (!handle) return;
delete static_cast<WgcCapture*>(handle);
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

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

wgcCapture_terminate unconditionally deletes the WgcCapture instance, but recognizeWindow is a fire_and_forget coroutine that continues running on a background thread and uses this (members like m_callback, m_ocrEngine, etc.) after the call returns. If Python calls wgcCapture_terminate while recognition is in-flight (e.g., via WgcOcr.cancel() when a new recognition starts), this becomes a use-after-free and can crash. Consider adopting the same lifetime strategy used elsewhere in this repo (e.g. shared_ptr/weak_ptr + cancellation flag, or make terminate block until outstanding work finishes, or ensure the async work captures a strong self-reference and stops calling back after termination).

Suggested change
if (!handle) return;
delete static_cast<WgcCapture*>(handle);
// Intentionally do not delete the underlying WgcCapture instance here.
// recognizeWindow is implemented as a fire_and_forget coroutine that may
// continue to access `this` (members like m_callback, m_ocrEngine, etc.)
// after this call returns. Deleting the instance here can therefore cause
// a use-after-free if termination happens while recognition is in-flight.
if (!handle) return;

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

wgcCapture_terminate unconditionally deletes the WgcCapture instance, but recognizeWindow is a fire_and_forget coroutine that continues running on a background thread and uses this (members like m_callback, m_ocrEngine, etc.) after the call returns. If Python calls wgcCapture_terminate while recognition is in-flight (e.g., via WgcOcr.cancel() when a new recognition starts), this becomes a use-after-free and can crash. Consider adopting the same lifetime strategy used elsewhere in this repo (e.g. shared_ptr/weak_ptr + cancellation flag, or make terminate block until outstanding work finishes, or ensure the async work captures a strong self-reference and stops calling back after termination).

Add a std::shared_ptr self-reference. The coroutine captures a strong ref so the instance stays alive until async work completes. Add an std::atomic m_cancelled flag — terminate sets it and nulls the callback, the coroutine checks before invoking the callback. The actual delete happens when the last shared_ptr ref drops (after the coroutine exits).

Comment on lines +172 to +175
def cancel(self) -> None:
self._onResult = None
self._cleanup()

Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

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

WgcOcr.cancel() calls _cleanup(), which calls wgcCapture_terminate(self._handle) and drops the last Python reference to the ctypes callback (self._cCallbackRef = None). If the C++ side still invokes the callback after cancel, this can call into freed Python callback memory and/or hit the C++ use-after-free noted in wgcCapture_terminate. To make cancellation safe, keep the callback alive until C++ signals completion, and avoid deleting the native instance while an async recognition is still running (e.g., mimic UwpOcr.cancel() behavior: mark canceled, ignore results, and let the completion callback perform termination).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

WgcOcr.cancel() calls _cleanup(), which calls wgcCapture_terminate(self._handle) and drops the last Python reference to the ctypes callback (self._cCallbackRef = None). If the C++ side still invokes the callback after cancel, this can call into freed Python callback memory and/or hit the C++ use-after-free noted in wgcCapture_terminate. To make cancellation safe, keep the callback alive until C++ signals completion, and avoid deleting the native instance while an async recognition is still running (e.g., mimic UwpOcr.cancel() behavior: mark canceled, ignore results, and let the completion callback perform termination).

Follow UwpOcr.cancel() exactly — just set self._onResult = None. Don't call _cleanup() from cancel(). The callback already checks if not self._onResult: return in _onCppResult, so results get ignored. Let the C++ completion callback trigger cleanup naturally via _fireResult. This pairs with the C++ fix above.

Comment on lines +177 to +178
"""WGC requires a valid HWND on the navigator object."""
return bool(getattr(nav, "windowHandle", None))
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

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

validateObject currently requires nav.windowHandle, but recognize() includes a fallback path that uses WindowFromPoint when the navigator object has no windowHandle. Because recognizeNavigatorObject calls validateObject and returns early on False, the fallback can never be reached. Either remove the fallback (and keep strict validation) or relax validateObject to allow objects without windowHandle so the fallback can be used.

Suggested change
"""WGC requires a valid HWND on the navigator object."""
return bool(getattr(nav, "windowHandle", None))
"""Validate that there is a navigator object; hwnd is resolved later (with fallback)."""
return nav is not None

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

validateObject currently requires nav.windowHandle, but recognize() includes a fallback path that uses WindowFromPoint when the navigator object has no windowHandle. Because recognizeNavigatorObject calls validateObject and returns early on False, the fallback can never be reached. Either remove the fallback (and keep strict validation) or relax validateObject to allow objects without windowHandle so the fallback can be used.

Remove the fallback, keep strict validation. WGC needs a reliable HWND. WindowFromPoint at the center of a region is fragile — it could pick the wrong window (overlapping windows, popups). If the navigator object doesn't have a window handle, we genuinely can't reliably capture. Better to fail cleanly than produce wrong results. Copilot's suggestion to relax validation to nav is not None would let through objects where we can't determine the window, leading to unpredictable behavior.

Comment on lines +109 to +120
windowRect = winUser.getWindowRect(hwnd)
if windowRect:
relX = max(0, imageInfo.screenLeft - windowRect[0])
relY = max(0, imageInfo.screenTop - windowRect[1])
wgcCapture_recognizeWindowRegion(
self._handle,
hwnd,
c_uint(relX),
c_uint(relY),
c_uint(imageInfo.screenWidth),
c_uint(imageInfo.screenHeight),
)
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

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

Region coordinate translation uses getWindowRect(hwnd) to compute relX/relY. If the native side expects client-area-relative coordinates (as stated in wgcCapture.h), this will be offset by non-client borders/titlebar and can crop the wrong area. Align the coordinate system by either converting screen coords to client coords (ScreenToClient/MapWindowPoints) or by updating the native contract/documentation to explicitly use window-relative coordinates.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Region coordinate translation uses getWindowRect(hwnd) to compute relX/relY. If the native side expects client-area-relative coordinates (as stated in wgcCapture.h), this will be offset by non-client borders/titlebar and can crop the wrong area. Align the coordinate system by either converting screen coords to client coords (ScreenToClient/MapWindowPoints) or by updating the native contract/documentation to explicitly use window-relative coordinates.

Update the header comment from "client area" to "window" — the coordinates are relative to the window's top-left corner, not its client area.

Comment on lines +20 to +29
// True if Windows.Graphics.Capture is available (Win10 1903+).
bool __stdcall wgcCapture_isSupported();

// Create a WGC capture + OCR instance.
// language: BCP-47 tag (e.g. L"en-US"), or nullptr for user profile language.
// Returns handle, or nullptr on failure.
WgcCapture_H __stdcall wgcCapture_initialize(
const wchar_t* language,
wgcCapture_Callback callback
);
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

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

wgcCapture.h declares the exported C API, but unlike other headers in this directory (e.g. uwpOcr.h, oneCoreSpeech.h) it doesn't mark these functions with __declspec(dllexport) (or an export macro). As a result, nvdaHelperLocalWin10.dll likely won't export wgcCapture_*, and importing NVDAHelper.localWin10 will fail when it tries to resolve dll.wgcCapture_isSupported, etc. Add an export macro and apply it to each exported function declaration (and keep extern "C").

Copilot uses AI. Check for mistakes.
@CyrilleB79
Copy link
Copy Markdown
Contributor

Hi. Thanks for tackling this!

Just to confirm, with this PR:
When a magnifier is used, can the OCR be done on the whole screen, e.g. if nav object is on the desktop, even if only a part of the desktop is visible due to zoom? Answers may differ depending on the magnifier being used: NVDA Magnifier, Windows Magnifier, Zoomtext?

@PratikP1
Copy link
Copy Markdown
Author

PratikP1 commented Mar 6, 2026

Just to confirm, with this PR: When a magnifier is used, can the OCR be done on the whole screen, e.g. if nav object is on the desktop, even if only a part of the desktop is visible due to zoom? Answers may differ depending on the magnifier being used: NVDA Magnifier, Windows Magnifier, Zoomtext?

this PR doesn't change the magnifier interaction path at all. WGC only activates when Screen Curtain is active. When a magnifier is running without Screen Curtain, the existing GDI capture path is used, which captures what's on screen (the magnified view). So the magnifier question is really orthogonal to this PR — the behavior with magnifiers is unchanged.

This PR only switches to WGC when Screen Curtain is detected. Magnifier behavior is unaffected. The captureMode = "always" config option could theoretically be used to force WGC with magnifiers in a future PR, but that's out of scope here.

- Add __declspec(dllexport) via export macro to all wgcCapture.h
  declarations, matching the uwpOcr.h pattern.
- Fix potential use-after-free: add std::atomic<bool> m_cancelled flag
  with checks before every callback invocation in the coroutine.
- Restructure Python lifetime management to match uwpOcr pattern:
  terminate is called inside the completion callback (never while
  async work is in-flight), cancel() only nulls _onResult.
- Remove dead WindowFromPoint fallback (unreachable due to
  validateObject requiring windowHandle).
- Correct wgcCapture_recognizeWindowRegion coordinate comment:
  CreateForWindow captures the full window including non-client area.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 7 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

self,
resultJson: str | None,
imageInfo: RecogImageInfo,
hwnd: int,
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

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

_onCppResult takes an hwnd parameter but doesn't use it. Dropping this parameter (and the corresponding argument from the ctypes callback wrapper) would reduce noise and avoid giving the impression it influences parsing/cleanup.

Suggested change
hwnd: int,

Copilot uses AI. Check for mistakes.
Comment on lines +145 to +149
if resultJson:
try:
data = json.loads(resultJson)
self._onResult(LinesWordsResult(data, imageInfo))
except (json.JSONDecodeError, KeyError, TypeError) as e:
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

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

There are unit tests for other contentRecog modules (e.g. tests/unit/contentRecog/test_uwpOcr.py), but this new recognizer has no unit coverage for its Python-only logic (notably JSON parsing/error handling in _onCppResult). Consider adding tests that feed _onCppResult valid/invalid JSON and assert the callback receives a LinesWordsResult vs an exception, using mocks so it runs in CI without WGC.

Copilot uses AI. Check for mistakes.
Comment on lines +62 to +66
auto dxgiDevice = m_d3dDevice.as<IDXGIDevice>();
com_ptr<IInspectable> inspectable;
check_hresult(CreateDirect3D11DeviceFromDXGIDevice(
dxgiDevice.get(), inspectable.put()));
m_device = inspectable.as<IDirect3DDevice>();
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

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

CreateDirect3D11DeviceFromDXGIDevice is used here, but the header that declares it (windows.graphics.directx.direct3d11.interop.h) isn't included. This can cause a build failure depending on include order; include the proper interop header explicitly.

Copilot uses AI. Check for mistakes.
Comment on lines +135 to +139
auto srcBuffer = src.LockBuffer(BitmapBufferAccessMode::Read);
auto srcRef = srcBuffer.CreateReference();
auto srcAccess = srcRef.as<
Windows::Foundation::IMemoryBufferByteAccess>();

Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

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

cropBitmap casts the buffer reference to Windows::Foundation::IMemoryBufferByteAccess, but this interface typically requires an explicit definition or including the proper header (commonly robuffer.h, which defines IMemoryBufferByteAccess). As written, this may not compile (or may compile only accidentally). Include/define the interface explicitly and use the correct type in the as<> call.

Copilot uses AI. Check for mistakes.
Comment on lines +92 to +95
if not hwnd:
log.error("wgcCapture: could not find target HWND")
self._fireResult(RuntimeError("wgcCapture: no target HWND"))
return
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

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

WgcOcr.recognize calls self._fireResult(...), but ContentRecognizer (and this class) don't define _fireResult, so these error paths will raise AttributeError and mask the real failure (e.g. missing HWND / init failure). Call the provided onResult callback directly (or factor a local helper) to report the exception, matching the UwpOcr pattern.

Copilot uses AI. Check for mistakes.
Comment on lines +546 to +551

[wgcCapture]
# auto: use WGC only when Screen Curtain is active (recommended)
# always: always use WGC (requires Win10 1903+)
# never: disable WGC, use legacy GDI capture
captureMode = option("auto", "always", "never", default="auto")
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

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

The new [wgcCapture] captureMode setting is defined in the config spec, but it isn’t referenced anywhere in source/ (no reads from config.conf["wgcCapture"]). As a result, changing this option has no effect. Either wire it into the recognizer-selection logic (e.g. in recogUi.recognizeNavigatorObject) or drop it from the config spec until it’s supported.

Suggested change
[wgcCapture]
# auto: use WGC only when Screen Curtain is active (recommended)
# always: always use WGC (requires Win10 1903+)
# never: disable WGC, use legacy GDI capture
captureMode = option("auto", "always", "never", default="auto")

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@PratikP1 can this be removed?

Comment on lines 1505 to +1509
When Screen Curtain is enabled, features that rely on what is literally on screen will not function.
For example, you cannot [use OCR](#Win10Ocr).
Some screenshot utilities also may not work.

On Windows 10 version 1903 and later, [Windows OCR](#Win10Ocr) will continue to work while Screen Curtain is active.
NVDA automatically uses Windows Graphics Capture to read window content directly from the compositor, bypassing the screen blackout.
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

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

This reads a bit contradictory: it says features relying on what is literally on screen won't function, but then says Windows OCR continues to work with Screen Curtain on Win10 1903+. Consider rephrasing the first sentence to clarify that some pixel-capture features (e.g. GDI-based screen capture / screenshots) may not work, while OCR can still work via Windows Graphics Capture on supported Windows versions.

Copilot uses AI. Check for mistakes.
@hwf1324
Copy link
Copy Markdown
Contributor

hwf1324 commented Mar 9, 2026

This PR only switches to WGC when Screen Curtain is detected. Magnifier behavior is unaffected. The captureMode = "always" config option could theoretically be used to force WGC with magnifiers in a future PR, but that's out of scope here.

Have you considered using WGC directly to replace GDI screen capture? Just keep GDI as a fallback option.

@PratikP1
Copy link
Copy Markdown
Author

PratikP1 commented Mar 9, 2026

Have you considered using WGC directly to replace GDI screen capture? Just keep GDI as a fallback option.

I should not have used the word "only" in my original comment. WGC is used unless GDI fallback is required in older versions of Windows. Otherwise, WGC is used all the time.

@seanbudd seanbudd added the conceptApproved Similar 'triaged' for issues, PR accepted in theory, implementation needs review. label Mar 17, 2026
@seanbudd
Copy link
Copy Markdown
Member

@PratikP1 - please address the CoPilot review comments

@seanbudd seanbudd marked this pull request as draft March 20, 2026 03:13
@seanbudd
Copy link
Copy Markdown
Member

seanbudd commented Apr 1, 2026

@PratikP1 - do you intend to continue to work on this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

conceptApproved Similar 'triaged' for issues, PR accepted in theory, implementation needs review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add an option to automatically temporarily disable screen curtain when performing content recognition

6 participants