ZoomIt: Add DemoMirror to mirror a monitor, region, or window to a second display - #49472
Open
markrussinovich wants to merge 14 commits into
Open
ZoomIt: Add DemoMirror to mirror a monitor, region, or window to a second display#49472markrussinovich wants to merge 14 commits into
markrussinovich wants to merge 14 commits into
Conversation
…cond monitor Mirrors a selected screen region (Ctrl+9) or the window under the cursor (Ctrl+Alt+9), including the mouse pointer, onto a second monitor on top of a slide show, so a presenter can demo an app on their laptop while the audience watches it on the presentation monitor without leaving PowerPoint presenter view. Ctrl+Up/Ctrl+Down zoom the mirrored image around the mouse pointer. Implementation: Windows.Graphics.Capture frames (cursor capture enabled) are cached to a texture and the visible sub-rectangle is copied into a flip-model swapchain on a topmost, no-activate, click-through, capture-excluded window; the swapchain source size drives the zoom via DXGI linear-filtered stretch. The target monitor prefers the one showing a PowerPoint slide show, falling back to the first non-source monitor. Mirroring stops automatically when the mirrored window closes. Co-Authored-By: Claude Mythos 5 <[email protected]>
When the mirrored window resizes, recreate the capture frame pool and swapchain at the new content size and re-fit the mirror window to the new aspect ratio on the target monitor. Previously both were fixed at the window's initial size, clipping content when the window grew and distorting it when the aspect ratio changed. Co-Authored-By: Claude Mythos 5 <[email protected]>
Scale-to-fit already handled sources larger than the target display, but the mirror window was sized exactly to the image, so the presentation showed around it. Add a full-monitor black backdrop window behind the mirror so the letterbox areas above/below (or beside) the image are black, kept directly below the mirror by the topmost timer and excluded from capture. Co-Authored-By: Claude Mythos 5 <[email protected]>
…oring Window capture pulls frames from the mirrored window's own surface, so ZoomIt's zoom, draw, and live-zoom overlays - separate top-level windows - never appeared in a window mirror. When one of those modes activates, the mirror now temporarily switches to capturing the monitor under the cursor (where the annotation UI appears) and switches back to occlusion-proof window capture when the mode exits. Cursor capture is disabled during live zoom, which already renders a magnified cursor, to avoid doubling it. The mirror re-fits to the monitor's aspect ratio during the override and back to the window's afterward. Co-Authored-By: Claude Mythos 5 <[email protected]>
Add a track-window mode (on by default, toggle on the DemoMirror options tab): window mirroring captures the monitor and crops to the tracked window rectangle every frame instead of capturing the window surface. Zoom, draw, and live-zoom annotations then show in place without the mirror jumping to the full monitor, and the crop follows the window as it moves and resizes. The trade-off is that overlapping windows become visible in the mirror; unchecking the option restores occlusion-proof window-surface capture with the monitor-switch annotation behavior. Co-Authored-By: Claude Mythos 5 <[email protected]>
… fit Window mirrors previously scaled to fill the target monitor, so every source window resize rescaled the whole image. Show mirrored windows at 1:1 pixel size, centered on the black backdrop, and only scale down when the window is larger than the target monitor. Regions still fill the monitor since enlarging a selected area is the point of region mirroring. Co-Authored-By: Claude Mythos 5 <[email protected]>
Window mirroring had no border showing what was mirrored; add a green frame that follows the source window as it moves and resizes, drawn just outside the window rect with the same width and translucency as the record border and excluded from capture. The region-mirror selection border is now the same bright green, distinguishing mirror borders from the yellow record/panorama borders (SelectRectangle gains a BorderColor setter). Co-Authored-By: Claude Mythos 5 <[email protected]>
…border The yellow border seen while mirroring was the Windows 11 system capture border: IsBorderRequired(false) is ignored until the process requests borderless capture access via GraphicsCaptureAccess::RequestAccessAsync, which is granted without a prompt for desktop apps. Request it on the render thread (the call blocks, so it needs an MTA thread) and re-apply the border setting to the live session. Also make both mirror borders fully opaque so the green reads bright instead of blending with what is underneath. Co-Authored-By: Claude Mythos 5 <[email protected]>
…ture border Two fixes from testing: - Window mirrors showed a margin of desktop around the window because GetWindowRect includes the invisible resize/shadow frame. Use the DWM extended frame bounds (visible edges) for the mirror crop, the green border, and cursor mapping. - The system capture border could still appear because IsBorderRequired was set after StartCapture, before the borderless-capture grant existed. Acquire the grant in Start on a short-lived MTA thread before creating the session, and let CaptureFrameWait apply IsBorderRequired(false) before StartCapture via a new constructor parameter. Co-Authored-By: Claude Mythos 5 <[email protected]>
SelectRectangle::Start resets the border color to yellow on every call (to recover from the recording-active orange), silently discarding the green set for mirror borders beforehand. Track the configured color and have Start reset to it instead of hardcoded yellow; the record and panorama instances never configure a color and keep the yellow default. Verified with an automated region-mirror run capturing OutputDebugString: the border now paints 0x00FF00 and borderless capture access is granted. Co-Authored-By: Claude Mythos 5 <[email protected]>
…om zoom Rework the mirror hotkeys to match recording: the base hotkey (Ctrl+9) mirrors the entire monitor under the cursor with a full-monitor green border, Shift selects a region, and Alt mirrors the window under the cursor. Remove the mirror-specific Ctrl+Up/Ctrl+Down zoom and mouse-follow pan; standard ZoomIt zoom and draw modes show in the mirror on all monitor-capture paths, so the custom controls were redundant and conflicted with LiveZoom hotkeys. Monitor captures (screen, region, window tracking) now also disable cursor capture while live zoom renders its magnified cursor. Co-Authored-By: Claude Mythos 5 <[email protected]>
Add MirrorToggleKey to the settings interop shortcut map so it round-trips between the registry HOTKEYF encoding and PowerToys hotkey JSON, declare MirrorToggleKey/MirrorTrackWindow in ZoomItProperties, and add a DemoMirror group to the ZoomIt settings page with the activation shortcut (showing the derived Shift=region and Alt=window variants like Record does) and the track-window checkbox. MirrorTrackWindow flows through the existing generic boolean handling in the interop. Co-Authored-By: Claude Mythos 5 <[email protected]>
Put the explanation in static text above the checkbox and use a short right-side checkbox label matching the style of the other option tabs. Co-Authored-By: Claude Mythos 5 <[email protected]>
…static zoom Remove WDA_EXCLUDEFROMCAPTURE from the mirror and backdrop windows so Teams and other WGC/DDA screen shares of the target monitor show the mirrored content. The annotation monitor-override now pins to the source monitor so a capture-visible mirror can never capture itself. Static zoom and draw raise a full-screen topmost window that buried the green source border; the mirror's topmost timer now reclaims the border (the mirror-owned window border, or the SelectRectangle border passed via the new sourceBorderWindow parameter), skipped during live zoom whose own reclaim timer would fight it. Co-Authored-By: Claude Mythos 5 <[email protected]>
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary of the Pull Request
Adds DemoMirror to ZoomIt: a new hotkey family (default Ctrl+9) that live-mirrors content onto a second monitor — designed for presenters who want to demo an app on their laptop screen while the audience sees it on the presentation display (e.g., on top of a PowerPoint slideshow), without leaving Presenter View.
The mirror includes the mouse cursor, targets the monitor running a PowerPoint slideshow when one is found (otherwise the first non-source monitor), letterboxes on a black backdrop, shows a bright green border around the mirrored source, and works with ZoomIt's existing zoom and draw/annotation modes. The mirrored content is visible in Teams/meeting screen shares.
PR Checklist
Detailed Description of the Pull Request / Additional comments
Capture & rendering: Uses Windows.Graphics.Capture through the existing
CaptureFrameWaithelper (cursor capture enabled, system capture border suppressed). A newMirrorWindowclass hosts a topmost, no-activate, click-through window on the target monitor with an HWND-bound flip-model swapchain; a render thread caches frames so static content keeps rendering. A black backdrop window letterboxes the mirrored content.Window mode: Windows mirror at native size (scaled down only if larger than the target monitor), crop to the DWM extended frame bounds (
DWMWA_EXTENDED_FRAME_BOUNDS) to exclude invisible resize borders, adapt to source-window resizes by recreating the frame pool and swapchain, and auto-stop when the mirrored window closes. A new Track window setting (default on) captures the monitor cropped to the tracked window each frame so annotations show in place and the crop follows moves/resizes; when off, window-surface capture is used instead.Annotations: Window capture can't see ZoomIt's own overlay windows, so while zoom/draw/LiveZoom is active the render thread switches capture to the monitor under the cursor and back on exit, letting annotations appear in the mirror.
Screen-share compatibility: The mirror and backdrop windows are visible to display capture so remote meeting attendees see the mirrored content; self-capture feedback loops are avoided by substituting the source monitor when the annotation-override capture would resolve to the target monitor. The green border stays above ZoomIt's own fullscreen-topmost windows via the mirror's topmost-reassert timer.
Win11 capture border:
IsBorderRequired(false)is silently ignored until the process callsGraphicsCaptureAccess::RequestAccessAsync(Borderless); this is now requested (auto-granted for desktop apps) before session creation.Settings: New DemoMirror tab in ZoomIt's options dialog (hotkey + track-window checkbox), persisted in registry settings, and exposed in the PowerToys Settings UI (
ZoomItProperties,ZoomItViewModel,ZoomItPage.xaml) with localizable strings inResources.resw.Validation Steps Performed
Manually tested on a two-monitor setup (including a Surface ARM device with an external monitor):
🤖 Generated with Claude Code