Add ZoomIt panoramic screenshot functionality (#46506)

<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
This adds several ZoomIt features:

- Panorama / scrolling screenshots. The image reconstruction happens
based on visual cues and accuracy depends on scroll speed during the
capture.
- Text extraction when snipping.
- Break timer improvements (the break timer is now a screen saver,
offering the possibility to lock the computer).
- Functionality for standalone clip trimming is present but not exposed
in the XAML UI.


<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] Closes: #xxx
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [x] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

The build is successful both with PowerToys and as a standalone
Sysinternals executable. We ensured that the features behave as expected
and that no regressions are introduced.

---------

Co-authored-by: Mark Russinovich <markruss@ntdev.microsoft.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: markrussinovich <markrussinovich@users.noreply.github.com>
Co-authored-by: MarioHewardt <marioh@microsoft.com>
This commit is contained in:
Alex Mihaiuc
2026-03-26 13:21:43 +01:00
committed by GitHub
parent c33053b26b
commit c83dd972a0
31 changed files with 21465 additions and 139 deletions

View File

@@ -1,9 +1,23 @@
accelscroll
acq
adr
Adr
APPLYTOSUBMENUS
AUDCLNT
axisdefer
axisflip
axisstart
bitmaps
BREAKSCR
BUFFERFLAGS
Cands
capturepath
centiseconds
CLASSW
coeffs
coprime
CREATEDIBSECTION
crossfades
Ctl
CTLCOLOR
CTLCOLORBTN
@@ -11,53 +25,163 @@ CTLCOLORDLG
CTLCOLOREDIT
CTLCOLORLISTBOX
CTrim
ddy
DFCS
dlg
dlu
DONTCARE
downsample
DRAWITEM
DRAWITEMSTRUCT
droppedband
Droppedband
dsum
dupburst
dupsegments
DWLP
EDITCONTROL
ENABLEHOOK
expectedlock
fastscroll
FDE
GETCHANNELRECT
GETCHECK
GETSCREENSAVEACTIVE
GETSCREENSAVETIMEOUT
GETTHUMBRECT
GIFs
hcfdark
hcfwhitespace
HTBOTTOMRIGHT
HTHEME
htol
ICONINFORMATION
ICONWARNING
Inj
jumprecover
KSDATAFORMAT
latestcapture
ldx
LEFTNOWORDWRAP
legitjumps
letterbox
lld
llu
llums
logfont
lookback
lround
lte
luma
Luma
manualdrop
maskcache
maxstep
MENUINFO
mic
middledrop
middledrop
MMRESULT
momentumreversal
mrate
mrt
narrowstrip
ncapture
ncm
nduplicates
niterations
nmonitor
NONCLIENTMETRICS
nonvle
nredraw
nstop
nsubpixel
ntorn
nvw
osc
OWNERDRAW
PBGRA
periodictrap
pfdc
playhead
pointerreuse
pwfx
Qpc
quantums
RCZOOMITSCR
realcapture
REFKNOWNFOLDERID
reposted
SCREENSAVE
SCRNSAVE
SCRNSAVECONFIGURE
scrnsavw
Scrnsavw
scrollramp
SCROLLSIZEGRIP
selftest
SETBARCOLOR
SETBKCOLOR
SETDEFID
SETRECT
SETSCREENSAVETIMEOUT
SHAREMODE
SHAREVIOLATION
shortlist
slowthenfast
smallstart
SNIPOCR
ssi
startuprecovery
stf
stopafter
STREAMFLAGS
submix
sxx
sxy
syy
tallportal
tci
tcsicmp
TEXTMETRIC
tinystep
tme
toolbars
TRACKMOUSEEVENT
Unadvise
vaddq
vaddvq
vandq
vcgeq
vdup
vld
vle
Vle
VLE
vminq
vmlal
vmull
vqaddq
vshrn
vsntprintf
vsnwprintf
vsync
WASAPI
WAVEFORMATEX
WAVEFORMATEXTENSIBLE
wfopen
wideportal
wil
WMU
wrapjump
wtol
WTSSESSION
WTSUn
XEnd
XStart
XStep
YInternal
ZMBS
zncc
Zncc
ZNCC

View File

@@ -427,7 +427,7 @@
</Project>
</Folder>
<Folder Name="/modules/FileLocksmith/">
<Project Path="src/modules/FileLocksmith/FileLocksmithCLI/FileLocksmithCLI.vcxproj" Id="49D456D3-F485-45AF-8875-45B44F193DDC" />
<Project Path="src/modules/FileLocksmith/FileLocksmithCLI/FileLocksmithCLI.vcxproj" Id="49d456d3-f485-45af-8875-45b44f193ddc" />
<Project Path="src/modules/FileLocksmith/FileLocksmithContextMenu/FileLocksmithContextMenu.vcxproj" Id="799a50d8-de89-4ed1-8ff8-ad5a9ed8c0ca" />
<Project Path="src/modules/FileLocksmith/FileLocksmithExt/FileLocksmithExt.vcxproj" Id="57175ec7-92a5-4c1e-8244-e3fbca2a81de" />
<Project Path="src/modules/FileLocksmith/FileLocksmithLib/FileLocksmithLib.vcxproj" Id="9d52fd25-ef90-4f9a-a015-91efc5daf54f" />
@@ -438,7 +438,7 @@
</Project>
</Folder>
<Folder Name="/modules/FileLocksmith/Tests/">
<Project Path="src/modules/FileLocksmith/FileLocksmithCLI/tests/FileLocksmithCLIUnitTests.vcxproj" Id="A1B2C3D4-E5F6-7890-1234-567890ABCDEF" />
<Project Path="src/modules/FileLocksmith/FileLocksmithCLI/tests/FileLocksmithCLIUnitTests.vcxproj" Id="a1b2c3d4-e5f6-7890-1234-567890abcdef" />
</Folder>
<Folder Name="/modules/Hosts/">
<Project Path="src/modules/Hosts/Hosts/Hosts.csproj">
@@ -464,13 +464,13 @@
</Folder>
<Folder Name="/modules/imageresizer/">
<Project Path="src/modules/imageresizer/dll/ImageResizerExt.vcxproj" Id="0b43679e-edfa-4da0-ad30-f4628b308b1b" />
<Project Path="src/modules/imageresizer/ImageResizerContextMenu/ImageResizerContextMenu.vcxproj" Id="93b72a06-c8bd-484f-a6f7-c9f280b150bf" />
<Project Path="src/modules/imageresizer/ImageResizerLib/ImageResizerLib.vcxproj" Id="18b3db45-4ffe-4d01-97d6-5223feee1853" />
<Project Path="src/modules/imageresizer/ui/ImageResizerUI.csproj">
<Project Path="src/modules/imageresizer/ImageResizerCLI/ImageResizerCLI.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src/modules/imageresizer/ImageResizerCLI/ImageResizerCLI.csproj">
<Project Path="src/modules/imageresizer/ImageResizerContextMenu/ImageResizerContextMenu.vcxproj" Id="93b72a06-c8bd-484f-a6f7-c9f280b150bf" />
<Project Path="src/modules/imageresizer/ImageResizerLib/ImageResizerLib.vcxproj" Id="18b3db45-4ffe-4d01-97d6-5223feee1853" />
<Project Path="src/modules/imageresizer/ui/ImageResizerUI.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
@@ -1027,7 +1027,10 @@
<File Path="src/modules/Workspaces/workspaces-common/WindowUtils.h" />
</Folder>
<Folder Name="/modules/ZoomIt/">
<Project Path="src/modules/ZoomIt/ZoomIt/ZoomIt.vcxproj" Id="0a84f764-3a88-44cd-aa96-41bdbd48627b" />
<Project Path="src/modules/ZoomIt/ZoomIt/ZoomIt.vcxproj" Id="0a84f764-3a88-44cd-aa96-41bdbd48627b">
<BuildDependency Project="src/modules/ZoomIt/ZoomItBreak/ZoomItBreak.vcxproj" />
</Project>
<Project Path="src/modules/ZoomIt/ZoomItBreak/ZoomItBreak.vcxproj" Id="94ba3051-c8d7-454a-9d46-1a7c78e228a3" />
<Project Path="src/modules/ZoomIt/ZoomItModuleInterface/ZoomItModuleInterface.vcxproj" Id="e4585179-2ac1-4d5f-a3ff-cfc5392f694c" />
<Project Path="src/modules/ZoomIt/ZoomItSettingsInterop/ZoomItSettingsInterop.vcxproj" Id="ca7d8106-30b9-4aec-9d05-b69b31b8c461" />
</Folder>

View File

@@ -151,6 +151,7 @@ namespace CommonSharedConstants
const wchar_t ZOOMIT_BREAK_EVENT[] = L"Local\\PowerToysZoomIt-BreakEvent-17f2e63c-4c56-41dd-90a0-2d12f9f50c6b";
const wchar_t ZOOMIT_LIVEZOOM_EVENT[] = L"Local\\PowerToysZoomIt-LiveZoomEvent-390bf0c7-616f-47dc-bafe-a2d228add20d";
const wchar_t ZOOMIT_SNIP_EVENT[] = L"Local\\PowerToysZoomIt-SnipEvent-2fd9c211-436d-4f17-a902-2528aaae3e30";
const wchar_t ZOOMIT_SNIPOCR_EVENT[] = L"Local\\PowerToysZoomIt-SnipOcrEvent-a7c3b1d2-9e4f-4a6b-8d5c-1f2e3a4b5c6d";
const wchar_t ZOOMIT_RECORD_EVENT[] = L"Local\\PowerToysZoomIt-RecordEvent-74539344-eaad-4711-8e83-23946e424512";
// Path to the events used by PowerDisplay

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,42 @@
//============================================================================
//
// PanoramaCapture.h
//
// Panorama (scrolling) screen capture and stitching.
//
// Copyright (C) Mark Russinovich
// Sysinternals - www.sysinternals.com
//
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//============================================================================
#pragma once
#include <windows.h>
#include <vector>
// Globals shared with the main ZoomIt module.
extern bool g_PanoramaCaptureActive;
extern bool g_PanoramaStopRequested;
extern bool g_PanoramaDebugMode;
// Run the panorama capture flow: select a region, capture frames while
// scrolling, stitch them together, and copy the result to the clipboard.
bool RunPanoramaCaptureToClipboard( HWND hWnd );
// Run the panorama capture flow and save the result to a file via a
// Save As dialog instead of copying to the clipboard.
bool RunPanoramaCaptureToFile( HWND hWnd );
// Run a synthetic, non-interactive self-test for panorama frame stitching.
// Returns true when stitching output matches expected dimensions/content.
#ifdef _DEBUG
bool RunPanoramaStitchSelfTest();
// Re-stitch frames from a specific debug dump directory.
bool RunPanoramaStitchDumpDirectory( const wchar_t* path );
// Re-stitch accepted panorama frames from the latest debug dump session and
// save output into that same session directory.
bool RunPanoramaStitchLatestDebugDump();
#endif

View File

@@ -11,6 +11,23 @@
#include "Utility.h"
#include "WindowsVersions.h"
static void SelectRectangleDebugLog( const wchar_t* format, ... )
{
#if _DEBUG
wchar_t message[1024]{};
va_list args;
#pragma warning( push )
#pragma warning( disable : 26492 )
va_start( args, format );
#pragma warning( pop )
vswprintf_s( message, format, args );
va_end( args );
OutputDebugStringW( message );
#else
UNREFERENCED_PARAMETER( format );
#endif
}
//----------------------------------------------------------------------------
//
// SelectRectangle::Start
@@ -18,6 +35,12 @@
//----------------------------------------------------------------------------
bool SelectRectangle::Start( HWND ownerWindow, bool fullMonitor )
{
m_stopping = false;
SelectRectangleDebugLog( L"[SelectRectangle] Start owner=%p fullMonitor=%d minSize=%d alpha=%u\n",
ownerWindow,
fullMonitor ? 1 : 0,
MinSize(),
Alpha() );
WNDCLASSW windowClass{};
windowClass.lpfnWndProc = []( HWND window, UINT message, WPARAM wordParam, LPARAM longParam ) -> LRESULT
{
@@ -46,10 +69,16 @@ bool SelectRectangle::Start( HWND ownerWindow, bool fullMonitor )
m_cancel = false;
auto rect = GetMonitorRectFromCursor();
SelectRectangleDebugLog( L"[SelectRectangle] Monitor rect=(%ld,%ld)-(%ld,%ld)\n",
rect.left,
rect.top,
rect.right,
rect.bottom );
m_window = wil::unique_hwnd( CreateWindowExW( WS_EX_LAYERED | WS_EX_TOOLWINDOW | WS_EX_TOPMOST, m_className, nullptr, WS_POPUP,
rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, ownerWindow,
nullptr, nullptr, this ) );
THROW_LAST_ERROR_IF_NULL( m_window.get() );
SelectRectangleDebugLog( L"[SelectRectangle] Window created hwnd=%p\n", m_window.get() );
if( fullMonitor )
{
@@ -58,7 +87,11 @@ bool SelectRectangle::Start( HWND ownerWindow, bool fullMonitor )
}
else
{
SetLayeredWindowAttributes( m_window.get(), 0, Alpha(), LWA_ALPHA );
const BOOL layered = SetLayeredWindowAttributes( m_window.get(), 0, Alpha(), LWA_ALPHA );
SelectRectangleDebugLog( L"[SelectRectangle] SetLayeredWindowAttributes(alpha=%u) success=%d err=%lu\n",
Alpha(),
layered ? 1 : 0,
layered ? 0 : GetLastError() );
}
ShowWindow( m_window.get(), SW_SHOW );
@@ -69,6 +102,7 @@ bool SelectRectangle::Start( HWND ownerWindow, bool fullMonitor )
GetClipCursor( &m_oldClipRect );
ClipCursor( &rect );
m_setClip = true;
SelectRectangleDebugLog( L"[SelectRectangle] Cursor clipped to monitor bounds\n" );
}
MSG message;
@@ -78,13 +112,20 @@ bool SelectRectangle::Start( HWND ownerWindow, bool fullMonitor )
DispatchMessageW( &message );
if( m_cancel )
{
SelectRectangleDebugLog( L"[SelectRectangle] Start cancelled via Stop()\n" );
return false;
}
if( m_selected )
{
SelectRectangleDebugLog( L"[SelectRectangle] Selection finalized rect=(%ld,%ld)-(%ld,%ld)\n",
m_selectedRect.left,
m_selectedRect.top,
m_selectedRect.right,
m_selectedRect.bottom );
break;
}
}
SelectRectangleDebugLog( L"[SelectRectangle] Start complete selected=%d cancel=%d\n", m_selected ? 1 : 0, m_cancel ? 1 : 0 );
return true;
}
@@ -95,15 +136,38 @@ bool SelectRectangle::Start( HWND ownerWindow, bool fullMonitor )
//----------------------------------------------------------------------------
void SelectRectangle::Stop()
{
if( m_stopping )
{
SelectRectangleDebugLog( L"[SelectRectangle] Stop ignored due to reentrancy\n" );
return;
}
m_stopping = true;
SelectRectangleDebugLog( L"[SelectRectangle] Stop hwnd=%p selected=%d cancel=%d clip=%d rect=(%ld,%ld)-(%ld,%ld)\n",
m_window.get(),
m_selected ? 1 : 0,
m_cancel ? 1 : 0,
m_setClip ? 1 : 0,
m_selectedRect.left,
m_selectedRect.top,
m_selectedRect.right,
m_selectedRect.bottom );
if( m_setClip )
{
ClipCursor( &m_oldClipRect );
m_setClip = false;
}
m_window.reset();
HWND window = m_window.release();
if( window != nullptr && IsWindow( window ) )
{
DestroyWindow( window );
}
m_selected = false;
m_selectedRect = {};
m_cancel = true;
m_stopping = false;
}
//----------------------------------------------------------------------------
@@ -114,11 +178,20 @@ void SelectRectangle::Stop()
void SelectRectangle::ShowSelected()
{
m_selected = true;
SelectRectangleDebugLog( L"[SelectRectangle] ShowSelected rect=(%ld,%ld)-(%ld,%ld) dpi=%u\n",
m_selectedRect.left,
m_selectedRect.top,
m_selectedRect.right,
m_selectedRect.bottom,
m_dpi );
// Set the alpha to match the Windows graphics capture API yellow border
// and set the window to be transparent and disabled, so it will be skipped
// for hit testing and as a candidate for the next foreground window.
SetLayeredWindowAttributes( m_window.get(), 0, 191, LWA_ALPHA );
const BOOL layered = SetLayeredWindowAttributes( m_window.get(), 0, 191, LWA_ALPHA );
SelectRectangleDebugLog( L"[SelectRectangle] ShowSelected SetLayeredWindowAttributes(alpha=191) success=%d err=%lu\n",
layered ? 1 : 0,
layered ? 0 : GetLastError() );
SetWindowLong( m_window.get(), GWL_EXSTYLE, GetWindowLong( m_window.get(), GWL_EXSTYLE ) | WS_EX_TRANSPARENT );
EnableWindow( m_window.get(), FALSE );
@@ -144,6 +217,12 @@ void SelectRectangle::ShowSelected()
point.x += windowRect.left;
point.y += windowRect.top;
MoveWindow( m_window.get(), point.x, point.y, rect.right, rect.bottom, true );
SelectRectangleDebugLog( L"[SelectRectangle] Border window moved to (%ld,%ld) size=%ldx%ld borderWidth=%d\n",
point.x,
point.y,
rect.right,
rect.bottom,
width );
// Use a region to keep everything but the border transparent.
wil::unique_hrgn region{CreateRectRgnIndirect( &rect )};
@@ -151,6 +230,11 @@ void SelectRectangle::ShowSelected()
wil::unique_hrgn insideRegion{CreateRectRgnIndirect( &rect )};
CombineRgn( region.get(), region.get(), insideRegion.get(), RGN_XOR );
SetWindowRgn( m_window.get(), region.release(), true );
SelectRectangleDebugLog( L"[SelectRectangle] Border window region applied\n" );
// Force immediate paint so the yellow border is visible instead of a
// transient black frame from the class background brush.
RedrawWindow( m_window.get(), nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW | RDW_FRAME );
}
//----------------------------------------------------------------------------
@@ -162,6 +246,7 @@ void SelectRectangle::UpdateOwner( HWND window )
{
if( m_window != nullptr )
{
SelectRectangleDebugLog( L"[SelectRectangle] UpdateOwner hwnd=%p newOwner=%p\n", m_window.get(), window );
SetWindowLongPtr( m_window.get(), GWLP_HWNDPARENT, reinterpret_cast<LONG_PTR>(window) );
SetWindowPos( m_window.get(), HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE );
}
@@ -179,10 +264,24 @@ LRESULT SelectRectangle::WindowProc( HWND window, UINT message, WPARAM wordParam
case WM_CREATE:
m_dpi = GetDpiForWindowHelper( window );
SetWindowDisplayAffinity( window, WDA_EXCLUDEFROMCAPTURE );
SelectRectangleDebugLog( L"[SelectRectangle] WM_CREATE hwnd=%p dpi=%u\n", window, m_dpi );
return 0;
case WM_DESTROY:
Stop();
SelectRectangleDebugLog( L"[SelectRectangle] WM_DESTROY hwnd=%p\n", window );
if( m_window.get() == window )
{
m_window.release();
}
if( m_setClip )
{
ClipCursor( &m_oldClipRect );
m_setClip = false;
}
m_selected = false;
m_selectedRect = {};
m_cancel = true;
m_stopping = false;
return 0;
case WM_LBUTTONDOWN:
@@ -190,6 +289,7 @@ LRESULT SelectRectangle::WindowProc( HWND window, UINT message, WPARAM wordParam
SetCapture( window );
m_startPoint = { GET_X_LPARAM( longParam ), GET_Y_LPARAM( longParam ) };
SelectRectangleDebugLog( L"[SelectRectangle] WM_LBUTTONDOWN startPoint=(%ld,%ld)\n", m_startPoint.x, m_startPoint.y );
[[fallthrough]];
}
case WM_MOUSEMOVE:
@@ -199,6 +299,11 @@ LRESULT SelectRectangle::WindowProc( HWND window, UINT message, WPARAM wordParam
GetClientRect( window, &rect );
POINT point{ GET_X_LPARAM( longParam ), GET_Y_LPARAM( longParam ) };
m_selectedRect = ForceRectInBounds( RectFromPointsMinSize( m_startPoint, point, MinSize() ), rect );
SelectRectangleDebugLog( L"[SelectRectangle] Drag rect=(%ld,%ld)-(%ld,%ld)\n",
m_selectedRect.left,
m_selectedRect.top,
m_selectedRect.right,
m_selectedRect.bottom );
// Use a region to carve out the selected rectangle.
wil::unique_hrgn region{CreateRectRgnIndirect( &m_selectedRect )};
@@ -211,6 +316,7 @@ LRESULT SelectRectangle::WindowProc( HWND window, UINT message, WPARAM wordParam
case WM_KEYDOWN:
if( wordParam == VK_ESCAPE )
{
SelectRectangleDebugLog( L"[SelectRectangle] WM_KEYDOWN Escape pressed\n" );
Stop();
}
return 0;
@@ -218,12 +324,18 @@ LRESULT SelectRectangle::WindowProc( HWND window, UINT message, WPARAM wordParam
case WM_KILLFOCUS:
if( !m_selected )
{
SelectRectangleDebugLog( L"[SelectRectangle] WM_KILLFOCUS before selection complete\n" );
Stop();
}
return 0;
case WM_LBUTTONUP:
{
SelectRectangleDebugLog( L"[SelectRectangle] WM_LBUTTONUP selectedRect=(%ld,%ld)-(%ld,%ld)\n",
m_selectedRect.left,
m_selectedRect.top,
m_selectedRect.right,
m_selectedRect.bottom );
if( m_setClip )
{
ClipCursor( &m_oldClipRect );
@@ -249,6 +361,11 @@ LRESULT SelectRectangle::WindowProc( HWND window, UINT message, WPARAM wordParam
RECT rect;
GetClientRect( window, &rect );
SelectRectangleDebugLog( L"[SelectRectangle] WM_PAINT selected border rect=(%ld,%ld)-(%ld,%ld)\n",
rect.left,
rect.top,
rect.right,
rect.bottom );
// Draw a border matching the Windows graphics capture API border.
// The outer frame is yellow and two logical pixels wide, while the

View File

@@ -20,10 +20,14 @@ public:
void MinSize( int minSize ) { m_minSize = minSize; }
int MinSize() const { return m_minSize; }
RECT SelectedRect() const { return m_selectedRect; }
bool IsActive() const { return m_window != nullptr; }
bool Start( HWND ownerWindow = nullptr, bool fullMonitor = false );
void Stop();
void UpdateOwner( HWND window );
void Hide() { if( m_window ) ShowWindow( m_window.get(), SW_HIDE ); }
void Show() { if( m_window ) ShowWindow( m_window.get(), SW_SHOWNA ); }
void SetExcludeFromCapture( bool exclude ) { if( m_window ) SetWindowDisplayAffinity( m_window.get(), exclude ? WDA_EXCLUDEFROMCAPTURE : WDA_NONE ); }
private:
BYTE m_alpha = 176;
@@ -36,6 +40,7 @@ private:
RECT m_oldClipRect{};
bool m_selected{ false };
bool m_setClip{ false };
bool m_stopping{ false };
POINT m_startPoint{};
wil::unique_hwnd m_window;

View File

@@ -406,7 +406,10 @@ static bool LoadGifFrames(const std::wstring& gifPath, VideoRecordingSession::Tr
const auto& lastFrame = pData->gifFrames.back();
pData->videoDuration = winrt::TimeSpan{ lastFrame.start.count() + lastFrame.duration.count() };
pData->trimEnd = pData->videoDuration;
if( pData->trimEnd.count() <= 0 )
{
pData->trimEnd = pData->videoDuration;
}
pData->gifFramesLoaded = true;
pData->gifLastFrameIndex = 0;
@@ -721,13 +724,9 @@ namespace
SetDlgItemText(hDlg, IDC_TRIM_DURATION_LABEL, durationText.c_str());
}
// Enable OK when trimming is active (even if unchanged since dialog opened),
// or when the user changed the selection (including reverting to full length).
const bool trimChanged = (pData->trimStart.count() != pData->originalTrimStart.count()) ||
(pData->trimEnd.count() != pData->originalTrimEnd.count());
const bool trimIsActive = (pData->trimStart.count() > 0) ||
(pData->videoDuration.count() > 0 && pData->trimEnd.count() < pData->videoDuration.count());
EnableWindow(GetDlgItem(hDlg, IDOK), trimChanged || trimIsActive);
// Always enable OK so users can close the dialog after previewing
// without being forced to use Cancel.
EnableWindow(GetDlgItem(hDlg, IDOK), TRUE);
}
RECT GetTimelineTrackRect(const RECT& clientRect, UINT dpi)
@@ -1345,7 +1344,10 @@ public:
auto trimResult = VideoRecordingSession::ShowTrimDialog(hParent, m_videoPath, *m_pTrimStart, *m_pTrimEnd);
if (trimResult == IDOK)
{
*m_pShouldTrim = true;
// Trim values are only written back when the user actually
// changed the selection, so a non-zero trimEnd means a
// real trim is requested.
*m_pShouldTrim = (m_pTrimEnd->count() > 0);
}
else if( trimResult == IDCANCEL )
{
@@ -1502,12 +1504,13 @@ INT_PTR VideoRecordingSession::ShowTrimDialog(
HWND hParent,
const std::wstring& videoPath,
winrt::TimeSpan& trimStart,
winrt::TimeSpan& trimEnd)
winrt::TimeSpan& trimEnd,
bool standaloneMode)
{
std::promise<INT_PTR> resultPromise;
auto resultFuture = resultPromise.get_future();
std::thread staThread([hParent, videoPath, &trimStart, &trimEnd, promise = std::move(resultPromise)]() mutable
std::thread staThread([hParent, videoPath, &trimStart, &trimEnd, standaloneMode, promise = std::move(resultPromise)]() mutable
{
bool coInitialized = false;
try
@@ -1525,7 +1528,7 @@ INT_PTR VideoRecordingSession::ShowTrimDialog(
try
{
INT_PTR dlgResult = ShowTrimDialogInternal(hParent, videoPath, trimStart, trimEnd);
INT_PTR dlgResult = ShowTrimDialogInternal(hParent, videoPath, trimStart, trimEnd, standaloneMode);
promise.set_value(dlgResult);
}
catch (const winrt::hresult_error& e)
@@ -1584,7 +1587,8 @@ INT_PTR VideoRecordingSession::ShowTrimDialogInternal(
HWND hParent,
const std::wstring& videoPath,
winrt::TimeSpan& trimStart,
winrt::TimeSpan& trimEnd)
winrt::TimeSpan& trimEnd,
bool standaloneMode)
{
TrimDialogData data;
data.videoPath = videoPath;
@@ -1592,6 +1596,7 @@ INT_PTR VideoRecordingSession::ShowTrimDialogInternal(
data.trimStart = trimStart;
data.trimEnd = trimEnd;
data.isGif = IsGifPath(videoPath);
data.standaloneMode = standaloneMode;
if (data.isGif)
{
@@ -1786,8 +1791,17 @@ INT_PTR VideoRecordingSession::ShowTrimDialogInternal(
if (result == IDOK)
{
trimStart = data.trimStart;
trimEnd = data.trimEnd;
// Only write back trim values when the user actually changed the
// selection. This lets the caller distinguish "confirmed without
// trimming" (preview-only) from a real trim operation.
const bool selectionChanged =
(data.trimStart.count() != data.originalTrimStart.count()) ||
(data.trimEnd.count() != data.originalTrimEnd.count());
if (selectionChanged)
{
trimStart = data.trimStart;
trimEnd = data.trimEnd;
}
}
return result;
@@ -3890,6 +3904,12 @@ INT_PTR CALLBACK VideoRecordingSession::TrimDialogProc(HWND hDlg, UINT message,
// Make OK the default button
SendMessage(hDlg, DM_SETDEFID, IDOK, 0);
// In standalone mode, change OK button text to "Save As"
if (pData->standaloneMode)
{
SetDlgItemText(hDlg, IDOK, L"Save As");
}
// Subclass the dialog to handle resize grip hit testing
SetWindowSubclass(hDlg, TrimDialogSubclassProc, 0, reinterpret_cast<DWORD_PTR>(pData));
@@ -5029,6 +5049,115 @@ INT_PTR CALLBACK VideoRecordingSession::TrimDialogProc(HWND hDlg, UINT message,
case IDOK:
pData = reinterpret_cast<TrimDialogData*>(GetWindowLongPtr(hDlg, DWLP_USER));
StopPlayback(hDlg, pData);
if (pData->standaloneMode)
{
// In standalone mode, "Save As" shows a save dialog and performs the trim
auto saveDialog = wil::CoCreateInstance<::IFileSaveDialog>(CLSID_FileSaveDialog);
FILEOPENDIALOGOPTIONS options;
if (SUCCEEDED(saveDialog->GetOptions(&options)))
saveDialog->SetOptions(options | FOS_FORCEFILESYSTEM);
wil::com_ptr<::IShellItem> videosItem;
if (SUCCEEDED(SHGetKnownFolderItem(FOLDERID_Videos, KF_FLAG_DEFAULT, nullptr,
IID_IShellItem, (void**)videosItem.put())))
saveDialog->SetDefaultFolder(videosItem.get());
// Derive suggested filename from source
std::wstring suggestedName;
{
auto pos = pData->videoPath.find_last_of(L"\\/");
suggestedName = (pos != std::wstring::npos) ? pData->videoPath.substr(pos + 1) : pData->videoPath;
auto dot = suggestedName.find_last_of(L'.');
if (dot != std::wstring::npos)
suggestedName.insert(dot, L"_trimmed");
else
suggestedName += L"_trimmed";
}
if (pData->isGif)
{
saveDialog->SetDefaultExtension(L".gif");
COMDLG_FILTERSPEC fileTypes[] = { { L"GIF Animation", L"*.gif" } };
saveDialog->SetFileTypes(_countof(fileTypes), fileTypes);
}
else
{
saveDialog->SetDefaultExtension(L".mp4");
COMDLG_FILTERSPEC fileTypes[] = { { L"MP4 Video", L"*.mp4" } };
saveDialog->SetFileTypes(_countof(fileTypes), fileTypes);
}
saveDialog->SetFileName(suggestedName.c_str());
saveDialog->SetTitle(L"ZoomIt: Save Trimmed Video As...");
HRESULT hr = saveDialog->Show(hDlg);
if (FAILED(hr))
{
// User cancelled save dialog — return to trim editor
return TRUE;
}
wil::com_ptr<::IShellItem> resultItem;
THROW_IF_FAILED(saveDialog->GetResult(resultItem.put()));
wil::unique_cotaskmem_string savePath;
THROW_IF_FAILED(resultItem->GetDisplayName(SIGDN_FILESYSPATH, savePath.put()));
// Capture what we need before closing the dialog
std::wstring videoPath = pData->videoPath;
bool isGif = pData->isGif;
auto trimStart = pData->trimStart;
auto trimEnd = pData->trimEnd;
std::wstring savePathStr(savePath.get());
// Close the trim dialog immediately
EndDialog(hDlg, IDOK);
// Perform the trim after the dialog is closed
try
{
auto trimOp = isGif
? TrimGifAsync(videoPath, trimStart, trimEnd)
: TrimVideoAsync(videoPath, trimStart, trimEnd);
// Pump messages while waiting for async operation
while (trimOp.Status() == winrt::AsyncStatus::Started)
{
MSG msg;
while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Sleep(10);
}
auto trimmedPath = std::wstring(trimOp.GetResults());
if (trimmedPath.empty())
{
MessageBox(nullptr, L"Failed to trim video.", L"Error", MB_OK | MB_ICONERROR);
return TRUE;
}
// Copy trimmed file to the user-chosen save location
if (!CopyFile(trimmedPath.c_str(), savePathStr.c_str(), FALSE))
{
MessageBox(nullptr, L"Failed to save the trimmed file.", L"Error", MB_OK | MB_ICONERROR);
DeleteFile(trimmedPath.c_str());
return TRUE;
}
// Clean up temp file
DeleteFile(trimmedPath.c_str());
}
catch (...)
{
MessageBox(nullptr, L"Failed to trim video.", L"Error", MB_OK | MB_ICONERROR);
}
return TRUE;
}
// Trim times are already set by mouse dragging
EndDialog(hDlg, IDOK);
return TRUE;

View File

@@ -132,6 +132,7 @@ public:
bool isDragging{ false };
int lastPlayheadX{ -1 }; // Track last playhead pixel position for efficient invalidation
MMRESULT mmTimerId{ 0 }; // Multimedia timer for smooth MP4 playback
bool standaloneMode{ false }; // When true, OK becomes "Save As" and handles file saving directly
// Helper to convert time to pixel position
int TimeToPixel(winrt::Windows::Foundation::TimeSpan time, int timelineWidth) const
@@ -162,7 +163,8 @@ public:
HWND hParent,
const std::wstring& videoPath,
winrt::Windows::Foundation::TimeSpan& trimStart,
winrt::Windows::Foundation::TimeSpan& trimEnd);
winrt::Windows::Foundation::TimeSpan& trimEnd,
bool standaloneMode = false);
private:
static INT_PTR CALLBACK TrimDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
@@ -179,7 +181,8 @@ private:
HWND hParent,
const std::wstring& videoPath,
winrt::Windows::Foundation::TimeSpan& trimStart,
winrt::Windows::Foundation::TimeSpan& trimEnd);
winrt::Windows::Foundation::TimeSpan& trimEnd,
bool standaloneMode = false);
private:
VideoRecordingSession(

View File

@@ -113,26 +113,26 @@ END
// Dialog
//
OPTIONS DIALOGEX 0, 0, 299, 325
OPTIONS DIALOGEX 0, 0, 299, 331
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CLIPSIBLINGS | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_CONTROLPARENT
CAPTION "ZoomIt - Sysinternals: www.sysinternals.com"
FONT 8, "MS Shell Dlg", 0, 0, 0x0
BEGIN
DEFPUSHBUTTON "OK",IDOK,186,306,50,14
PUSHBUTTON "Cancel",IDCANCEL,243,306,50,14
DEFPUSHBUTTON "OK",IDOK,184,308,50,14
PUSHBUTTON "Cancel",IDCANCEL,241,308,50,14
LTEXT "ZoomIt v10.1",IDC_VERSION,42,7,73,10
LTEXT "Copyright \251 2006-2026 Mark Russinovich",IDC_COPYRIGHT,42,17,251,8
CONTROL "<a HREF=""https://www.sysinternals.com"">Sysinternals - www.sysinternals.com</a>",IDC_LINK,
"SysLink",WS_TABSTOP,42,26,150,9
ICON "APPICON",IDC_STATIC,12,9,20,20
CONTROL "Show tray icon",IDC_SHOW_TRAY_ICON,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,295,105,10
CONTROL "",IDC_TAB,"SysTabControl32",TCS_MULTILINE | WS_TABSTOP,8,46,285,247
CONTROL "Run ZoomIt when Windows starts",IDC_AUTOSTART,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,309,122,10
CONTROL "Show tray icon",IDC_SHOW_TRAY_ICON,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,302,105,10
CONTROL "",IDC_TAB,"SysTabControl32",TCS_MULTILINE | WS_TABSTOP,8,45,285,255
CONTROL "Run ZoomIt when Windows starts",IDC_AUTOSTART,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,316,122,10
END
ADVANCED_BREAK DIALOGEX 0, 0, 209, 225
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
ADVANCED_BREAK DIALOGEX 0, 0, 209, 223
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Advanced Break Options"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
@@ -158,8 +158,8 @@ BEGIN
EDITTEXT IDC_BACKGROUND_FILE,62,164,125,12,ES_AUTOHSCROLL | ES_READONLY
PUSHBUTTON "&...",IDC_BACKGROUND_BROWSE,188,164,13,11
CONTROL "Scale to screen:",IDC_CHECK_BACKGROUND_STRETCH,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,58,180,67,10,WS_EX_RIGHT
DEFPUSHBUTTON "OK",IDOK,97,199,50,14
PUSHBUTTON "Cancel",IDCANCEL,150,199,50,14
DEFPUSHBUTTON "OK",IDOK,97,202,50,14
PUSHBUTTON "Cancel",IDCANCEL,150,202,50,14
LTEXT "Alarm Sound File:",IDC_STATIC_SOUND_FILE,61,26,56,8
LTEXT "Timer Opacity:",IDC_STATIC,8,59,48,8
LTEXT "Timer Position:",IDC_STATIC,8,77,48,8
@@ -215,21 +215,23 @@ BEGIN
GROUPBOX "Sample",IDC_TEXT_FONT,8,61,99,28
END
BREAK DIALOGEX 0, 0, 260, 123
STYLE DS_SETFONT | DS_CONTROL | WS_CHILD | WS_SYSMENU
BREAK DIALOGEX 0, 0, 260, 159
STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "",IDC_BREAK_HOTKEY,"msctls_hotkey32",WS_BORDER | WS_TABSTOP,52,67,80,12
EDITTEXT IDC_TIMER,52,86,31,13,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER
CONTROL "",IDC_SPIN_TIMER,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,66,86,11,12
LTEXT "minutes",IDC_STATIC,88,88,25,8
PUSHBUTTON "&Advanced",IDC_ADVANCED_BREAK,192,102,41,14
LTEXT "Enter timer mode by using the ZoomIt tray icon's Break menu item. Increase and decrease time with the arrow keys. If you Alt-Tab away from the timer window, reactivate it by left-clicking on the ZoomIt tray icon. Exit timer mode with Escape. ",IDC_STATIC,7,7,230,33
LTEXT "Start Timer:",IDC_STATIC,7,70,39,8
LTEXT "Timer:",IDC_STATIC,7,88,20,8
LTEXT "Change the break timer color using the same keys that the drawing color. The break timer font is the same as text font.",IDC_STATIC,7,45,230,20
CONTROL "",IDC_BREAK_HOTKEY,"msctls_hotkey32",WS_BORDER | WS_TABSTOP,52,74,80,12
EDITTEXT IDC_TIMER,52,93,31,13,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER
CONTROL "",IDC_SPIN_TIMER,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,66,93,11,12
LTEXT "minutes",IDC_STATIC,88,95,25,8
PUSHBUTTON "&Advanced",IDC_ADVANCED_BREAK,213,140,41,14
LTEXT "Enter timer mode by using the ZoomIt tray icon's Break menu item. Increase and decrease time with the arrow keys. If you Alt-Tab away from the timer window, reactivate it by left-clicking on the ZoomIt tray icon. Exit timer mode with Escape. ",IDC_STATIC,7,7,242,33
LTEXT "Start Timer:",IDC_STATIC,7,77,39,8
LTEXT "Timer:",IDC_STATIC,7,95,20,8
LTEXT "Change the break timer color using the same keys that the drawing color, including background color. The break timer font is the same as text font.",IDC_STATIC,7,45,241,26
CONTROL "Show Time Elapsed After Expiration:",IDC_CHECK_SHOW_EXPIRED,
"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,8,104,132,10
"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,7,111,130,10
CONTROL "Lock Workstation During Break:",IDC_CHECK_LOCK_WORKSTATION,
"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,7,126,113,10
END
1543 DIALOGEX 100, 50, 216, 131
@@ -249,19 +251,19 @@ BEGIN
CTEXT "AaBbYyZz",1092,16,88,127,31,SS_NOPREFIX | NOT WS_VISIBLE
END
LIVEZOOM DIALOGEX 0, 0, 260, 134
STYLE DS_SETFONT | DS_CONTROL | WS_CHILD | WS_SYSMENU
LIVEZOOM DIALOGEX 0, 0, 317, 136
STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "",IDC_LIVE_HOTKEY,"msctls_hotkey32",WS_BORDER | WS_TABSTOP,69,108,80,12
LTEXT "LiveZoom mode is supported on Windows 7 and higher where window updates show while zoomed. ",IDC_STATIC,7,7,230,18
LTEXT "LiveZoom mode is supported on Windows 7 and higher where window updates show while zoomed. ",IDC_STATIC,7,7,255,18
LTEXT "LiveZoom Toggle:",IDC_STATIC,7,110,62,8
LTEXT "To enter and exit LiveZoom, enter the hotkey specified below.",IDC_STATIC,7,94,230,13
LTEXT "Note that in LiveZoom you must use Ctrl+Up and Ctrl+Down to control the zoom level. To enter drawing mode, use the standard zoom-without-draw hotkey and then escape to go back to LiveZoom.",IDC_STATIC,7,30,230,27
LTEXT "Use LiveDraw to draw and annotate the live desktop. To activate LiveDraw, enter the hotkey with the Shift key in the opposite mode. You can remove LiveDraw annotations by activating LiveDraw and enter the escape key",IDC_STATIC,7,62,230,32
LTEXT "Use LiveDraw to draw and annotate the live desktop. To activate LiveDraw, enter the hotkey with the Shift key in the opposite mode. You can remove LiveDraw annotations by activating LiveDraw and enter the escape key",IDC_STATIC,7,62,249,32
LTEXT "Note that in LiveZoom you must use Ctrl+Up and Ctrl+Down to control the zoom level. To enter drawing mode, use the standard zoom-without-draw hotkey and then escape to go back to LiveZoom.",IDC_STATIC,7,30,255,27
END
RECORD DIALOGEX 0, 0, 260, 181
RECORD DIALOGEX 0, 0, 263, 224
STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
@@ -282,19 +284,33 @@ BEGIN
CONTROL "Mono",IDC_MIC_MONO_MIX,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,98,161,30,10
COMBOBOX IDC_MICROPHONE,81,176,152,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Microphone:",IDC_MICROPHONE_LABEL,32,178,47,8
PUSHBUTTON "&Trim",IDC_TRIM_FILE,207,209,53,14
END
SNIP DIALOGEX 0, 0, 260, 68
STYLE DS_SETFONT | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_SYSMENU
SNIP DIALOGEX 0, 0, 260, 80
STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_SYSMENU
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "",IDC_SNIP_HOTKEY,"msctls_hotkey32",WS_BORDER | WS_TABSTOP,55,32,80,12
LTEXT "Copy a region of the screen to the clipboard or enter the hotkey with the Shift key in the opposite mode to save it to a file.",IDC_STATIC,7,7,230,19
LTEXT "Snip Toggle:",IDC_STATIC,7,33,45,8
LTEXT "Copy a region of the screen to the clipboard or enter the hotkey with the Shift key in the opposite mode to save it to a file. ",IDC_STATIC,7,7,230,19
CONTROL "",IDC_SNIP_HOTKEY,"msctls_hotkey32",WS_BORDER | WS_TABSTOP,67,32,80,12
LTEXT "Copy text from the selected region to the clipboard:",IDC_STATIC,7,50,230,10
LTEXT "Text Toggle:",IDC_STATIC,7,65,55,8
CONTROL "",IDC_SNIP_OCR_HOTKEY,"msctls_hotkey32",WS_BORDER | WS_TABSTOP,67,63,80,12
END
PANORAMA DIALOGEX 0, 0, 260, 105
STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_SYSMENU
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "Capture a scrolling panorama of a selected screen region. Select the area, then scroll the content. Move slowly and consistently, and do not rewind to previously covered areas. Press the hotkey again or with Shift to save to a file.",IDC_STATIC,7,7,245,33
LTEXT "Panorama Toggle:",IDC_STATIC,7,74,63,8
CONTROL "",IDC_SNIP_PANORAMA_HOTKEY,"msctls_hotkey32",WS_BORDER | WS_TABSTOP,73,72,80,12
LTEXT "For the best results, scroll slowly and at a constant rate, do not include stationary content (like scrollbars) in the capture area, and avoid content that is changing (e.g., animations or videos). ",IDC_STATIC,7,41,245,30
END
DEMOTYPE DIALOGEX 0, 0, 260, 249
STYLE DS_SETFONT | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_SYSMENU
STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_SYSMENU
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "",IDC_DEMOTYPE_HOTKEY,"msctls_hotkey32",WS_BORDER | WS_TABSTOP,74,154,80,12
@@ -308,11 +324,11 @@ BEGIN
LTEXT "Fast",IDC_DEMOTYPE_STATIC2,186,213,17,8
EDITTEXT IDC_DEMOTYPE_FILE,44,137,167,12,ES_AUTOHSCROLL | ES_READONLY
LTEXT "Input file:",IDC_STATIC,7,139,32,8
LTEXT "When you reach the end of the file, ZoomIt will reload the file and start at the beginning. Enter the hotkey with the Shift key in the opposite mode to step back to the last [end].",IDC_STATIC,7,108,230,24
LTEXT "DemoType has ZoomIt type text specified in the input file when you enter the DemoType toggle. Simply separate snippets with the [end] keyword, or you can insert text from the clipboard if it is prefixed with the [start].",IDC_STATIC,7,7,230,24
LTEXT "When you reach the end of the file, ZoomIt will reload the file and start at the beginning. Enter the hotkey with the Shift key in the opposite mode to step back to the last [end].",IDC_STATIC,7,108,249,24
LTEXT "DemoType has ZoomIt type text specified in the input file when you enter the DemoType toggle. Simply separate snippets with the [end] keyword, or you can insert text from the clipboard if it is prefixed with the [start].",IDC_STATIC,7,7,247,24
LTEXT " - Insert pauses with the [pause:n] keyword where 'n' is seconds. ",IDC_STATIC,19,34,218,11
LTEXT "You can have ZoomIt send text automatically, or select the option to drive input with typing. ZoomIt will block keyboard input while sending output.",IDC_STATIC,7,68,230,16
LTEXT "When driving input, hit the space bar to unblock keyboard input at the end of a snippet. In auto mode, control will be returned upon completion.",IDC_STATIC,7,88,230,16
LTEXT "You can have ZoomIt send text automatically, or select the option to drive input with typing. ZoomIt will block keyboard input while sending output.",IDC_STATIC,7,68,245,16
LTEXT "When driving input, hit the space bar to unblock keyboard input at the end of a snippet. In auto mode, control will be returned upon completion.",IDC_STATIC,7,88,243,16
LTEXT "- Send text via the clipboard with [paste] and [/paste]. ",IDC_STATIC,23,45,210,8
LTEXT "- Send keystrokes with [enter], [up], [down], [left], and [right].",IDC_STATIC,23,56,210,8
END
@@ -349,13 +365,13 @@ BEGIN
"OPTIONS", DIALOG
BEGIN
RIGHTMARGIN, 293
BOTTOMMARGIN, 320
BOTTOMMARGIN, 326
END
"ADVANCED_BREAK", DIALOG
BEGIN
RIGHTMARGIN, 207
BOTTOMMARGIN, 215
BOTTOMMARGIN, 214
END
"ZOOM", DIALOG
@@ -383,7 +399,7 @@ BEGIN
BEGIN
LEFTMARGIN, 7
TOPMARGIN, 7
BOTTOMMARGIN, 116
BOTTOMMARGIN, 154
END
1543, DIALOG
@@ -395,22 +411,27 @@ BEGIN
"LIVEZOOM", DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 181
TOPMARGIN, 7
BOTTOMMARGIN, 127
BOTTOMMARGIN, 89
END
"RECORD", DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 260
TOPMARGIN, 7
BOTTOMMARGIN, 164
BOTTOMMARGIN, 223
END
"SNIP", DIALOG
BEGIN
LEFTMARGIN, 7
TOPMARGIN, 7
BOTTOMMARGIN, 61
END
"PANORAMA", DIALOG
BEGIN
END
"DEMOTYPE", DIALOG
@@ -496,6 +517,16 @@ BEGIN
0
END
ADVANCED_BREAK AFX_DIALOG_LAYOUT
BEGIN
0
END
PANORAMA AFX_DIALOG_LAYOUT
BEGIN
0
END
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////

View File

@@ -68,8 +68,8 @@
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<DisableSpecificWarnings>4100;4091;4245</DisableSpecificWarnings>
<AdditionalIncludeDirectories>..\..\..\;$(MSBuildThisFileDirectory)..\..\..\common\sysinternals;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>
<DisableSpecificWarnings>26451;4100;4091;4245</DisableSpecificWarnings>
<AdditionalIncludeDirectories>..\..\..\;$(MSBuildThisFileDirectory)..\..\..\common\sysinternals;..\ZoomItBreak;$(MSBuildThisFileDirectory);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PrecompiledHeader>Create</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<LanguageStandard>stdcpplatest</LanguageStandard>
@@ -90,7 +90,7 @@
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)..\..\..\common\version;$(MSBuildThisFileDirectory)PowerToys;$(InterPlatformDir)</AdditionalIncludeDirectories>
</ResourceCompile>
<Link>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;Wtsapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<RandomizedBaseAddress>true</RandomizedBaseAddress>
@@ -109,10 +109,10 @@
<ResourceCompile>
<PreprocessorDefinitions>NDEBUG;_M_X64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0409</Culture>
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)..\..\..\common\version;$(MSBuildThisFileDirectory)PowerToys;</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)..\..\..\common\version;$(MSBuildThisFileDirectory)PowerToys;$(MSBuildThisFileDirectory)..\ZoomItBreak\$(Platform)\$(Configuration)\</AdditionalIncludeDirectories>
</ResourceCompile>
<Link>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;Wtsapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<RandomizedBaseAddress>true</RandomizedBaseAddress>
@@ -132,10 +132,10 @@
<ResourceCompile>
<PreprocessorDefinitions>NDEBUG;_M_ARM64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0409</Culture>
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)..\..\..\common\version;$(MSBuildThisFileDirectory)PowerToys;</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)..\..\..\common\version;$(MSBuildThisFileDirectory)PowerToys;$(MSBuildThisFileDirectory)..\ZoomItBreak\$(Platform)\$(Configuration)\</AdditionalIncludeDirectories>
</ResourceCompile>
<Link>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;Wtsapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<FixedBaseAddress>
@@ -156,7 +156,7 @@
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)..\..\..\common\version;$(MSBuildThisFileDirectory)PowerToys;$(InterPlatformDir)</AdditionalIncludeDirectories>
</ResourceCompile>
<Link>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;Wtsapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
@@ -174,10 +174,10 @@
<ResourceCompile>
<PreprocessorDefinitions>_DEBUG;_M_X64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0409</Culture>
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)..\..\..\common\version;$(MSBuildThisFileDirectory)PowerToys;</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)..\..\..\common\version;$(MSBuildThisFileDirectory)PowerToys;$(MSBuildThisFileDirectory)..\ZoomItBreak\$(Platform)\$(Configuration)\</AdditionalIncludeDirectories>
</ResourceCompile>
<Link>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;Wtsapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<UACUIAccess>true</UACUIAccess>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
@@ -196,10 +196,10 @@
<ResourceCompile>
<PreprocessorDefinitions>_DEBUG;_M_ARM64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0409</Culture>
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)..\..\..\common\version;$(MSBuildThisFileDirectory)PowerToys;</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)..\..\..\common\version;$(MSBuildThisFileDirectory)PowerToys;$(MSBuildThisFileDirectory)..\ZoomItBreak\$(Platform)\$(Configuration)\</AdditionalIncludeDirectories>
</ResourceCompile>
<Link>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;Wtsapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<UACUIAccess>true</UACUIAccess>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
@@ -208,6 +208,14 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\ZoomItBreak\BreakTimer.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="AudioSampleGenerator.cpp">
<MultiProcessorCompilation Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</MultiProcessorCompilation>
<MultiProcessorCompilation Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</MultiProcessorCompilation>
@@ -249,6 +257,14 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="GifRecordingSession.cpp" />
<ClCompile Include="PanoramaCapture.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Use</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">Use</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Use</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Use</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">Use</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Use</PrecompiledHeader>
</ClCompile>
<ClCompile Include="pch.cpp" />
<ClCompile Include="SelectRectangle.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Use</PrecompiledHeader>
@@ -300,11 +316,13 @@
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\ZoomItBreak\BreakTimer.h" />
<ClInclude Include="AudioSampleGenerator.h" />
<ClInclude Include="LoopbackCapture.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\..\common\sysinternals\Eula\Eula.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)..\ZoomItModuleInterface\Trace.h" />
<ClInclude Include="GifRecordingSession.h" />
<ClInclude Include="PanoramaCapture.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="Registry.h" />
<ClInclude Include="resource.h" />
@@ -378,4 +396,4 @@
<Import Project="..\..\..\..\packages\robmikh.common.0.0.23-beta\build\native\robmikh.common.targets" Condition="Exists('..\..\..\..\packages\robmikh.common.0.0.23-beta\build\native\robmikh.common.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
</Project>
</Project>

View File

@@ -60,6 +60,12 @@
<ClCompile Include="GifRecordingSession.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="PanoramaCapture.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\ZoomItBreak\BreakTimer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Registry.h">
@@ -107,6 +113,12 @@
<ClInclude Include="GifRecordingSession.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="PanoramaCapture.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\ZoomItBreak\BreakTimer.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Image Include="appicon.ico">

View File

@@ -17,6 +17,8 @@ DWORD g_BreakToggleKey = ((HOTKEYF_CONTROL) << 8)| '3';
DWORD g_DemoTypeToggleKey = ((HOTKEYF_CONTROL) << 8) | '7';
DWORD g_RecordToggleKey = ((HOTKEYF_CONTROL) << 8) | '5';
DWORD g_SnipToggleKey = ((HOTKEYF_CONTROL) << 8) | '6';
DWORD g_SnipPanoramaToggleKey = ((HOTKEYF_CONTROL) << 8) | '8';
DWORD g_SnipOcrToggleKey = ((HOTKEYF_CONTROL | HOTKEYF_ALT) << 8) | '6';
DWORD g_ShowExpiredTime = 1;
DWORD g_SliderZoomLevel = 3;
@@ -24,6 +26,7 @@ BOOLEAN g_AnimateZoom = TRUE;
BOOLEAN g_SmoothImage = TRUE;
DWORD g_PenColor = COLOR_RED;
DWORD g_BreakPenColor = COLOR_RED;
DWORD g_BreakBackgroundColor = 0;
DWORD g_RootPenWidth = PEN_WIDTH;
int g_FontScale = 10;
DWORD g_BreakTimeout = 10;
@@ -40,6 +43,7 @@ BOOLEAN g_ShowTrayIcon = TRUE;
BOOLEAN g_SnapToGrid = TRUE;
BOOLEAN g_TelescopeZoomOut = TRUE;
BOOLEAN g_BreakOnSecondary = FALSE;
BOOLEAN g_BreakLockWorkstation = FALSE;
LOGFONT g_LogFont;
BOOLEAN g_DemoTypeUserDriven = false;
TCHAR g_DemoTypeFile[MAX_PATH] = {0};
@@ -66,10 +70,13 @@ REG_SETTING RegSettings[] = {
{ L"DrawToggleKey", SETTING_TYPE_DWORD, 0, &g_DrawToggleKey, static_cast<DOUBLE>(g_DrawToggleKey) },
{ L"RecordToggleKey", SETTING_TYPE_DWORD, 0, &g_RecordToggleKey, static_cast<DOUBLE>(g_RecordToggleKey) },
{ L"SnipToggleKey", SETTING_TYPE_DWORD, 0, &g_SnipToggleKey, static_cast<DOUBLE>(g_SnipToggleKey) },
{ L"SnipPanoramaToggleKey", SETTING_TYPE_DWORD, 0, &g_SnipPanoramaToggleKey, static_cast<DOUBLE>(g_SnipPanoramaToggleKey) },
{ L"SnipOcrToggleKey", SETTING_TYPE_DWORD, 0, &g_SnipOcrToggleKey, static_cast<DOUBLE>(g_SnipOcrToggleKey) },
{ L"PenColor", SETTING_TYPE_DWORD, 0, &g_PenColor, static_cast<DOUBLE>(g_PenColor) },
{ L"PenWidth", SETTING_TYPE_DWORD, 0, &g_RootPenWidth, static_cast<DOUBLE>(g_RootPenWidth) },
{ L"OptionsShown", SETTING_TYPE_BOOLEAN, 0, &g_OptionsShown, static_cast<DOUBLE>(g_OptionsShown) },
{ L"BreakPenColor", SETTING_TYPE_DWORD, 0, &g_BreakPenColor, static_cast<DOUBLE>(g_BreakPenColor) },
{ L"BreakBackgroundColor", SETTING_TYPE_DWORD, 0, &g_BreakBackgroundColor, static_cast<DOUBLE>(g_BreakBackgroundColor) },
{ L"BreakTimerKey", SETTING_TYPE_DWORD, 0, &g_BreakToggleKey, static_cast<DOUBLE>(g_BreakToggleKey) },
{ L"DemoTypeToggleKey", SETTING_TYPE_DWORD, 0, &g_DemoTypeToggleKey, static_cast<DOUBLE>(g_DemoTypeToggleKey) },
{ L"DemoTypeFile", SETTING_TYPE_STRING, sizeof( g_DemoTypeFile ), g_DemoTypeFile, static_cast<DOUBLE>(0) },
@@ -85,6 +92,7 @@ REG_SETTING RegSettings[] = {
{ L"BreakTimerPosition", SETTING_TYPE_DWORD, 0, &g_BreakTimerPosition, static_cast<DOUBLE>(g_BreakTimerPosition) },
{ L"BreakShowDesktop", SETTING_TYPE_BOOLEAN, 0, &g_BreakShowDesktop, static_cast<DOUBLE>(g_BreakShowDesktop) },
{ L"BreakOnSecondary", SETTING_TYPE_BOOLEAN, 0, &g_BreakOnSecondary,static_cast<DOUBLE>(g_BreakOnSecondary) },
{ L"BreakLockWorkstation", SETTING_TYPE_BOOLEAN, 0, &g_BreakLockWorkstation, static_cast<DOUBLE>(g_BreakLockWorkstation) },
{ L"FontScale", SETTING_TYPE_DWORD, 0, &g_FontScale, static_cast<DOUBLE>(g_FontScale) },
{ L"ShowExpiredTime", SETTING_TYPE_BOOLEAN, 0, &g_ShowExpiredTime, static_cast<DOUBLE>(g_ShowExpiredTime) },
{ L"ShowTrayIcon", SETTING_TYPE_BOOLEAN, 0, &g_ShowTrayIcon, static_cast<DOUBLE>(g_ShowTrayIcon) },

File diff suppressed because it is too large Load Diff

View File

@@ -15,4 +15,14 @@ RCZOOMIT64 BINRES MOVEABLE PURE RCZOOMIT_x64_path
#endif
// Embed the break timer screensaver for the current platform.
// The .scr is built by the ZoomItBreak project into the shared output directory.
#ifdef _M_IX86
RCZOOMITSCR BINRES MOVEABLE PURE "ZoomItBreak.scr"
#elif defined(_M_X64)
RCZOOMITSCR BINRES MOVEABLE PURE "ZoomItBreak64.scr"
#elif defined(_M_ARM64)
RCZOOMITSCR BINRES MOVEABLE PURE "ZoomItBreak64a.scr"
#endif
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "ZoomIt.exe.manifest"

View File

@@ -53,6 +53,9 @@
#include <winrt/Windows.Storage.Pickers.h>
#include <winrt/Windows.Storage.FileProperties.h>
#include <winrt/Windows.Devices.Enumeration.h>
#include <winrt/Windows.Media.Ocr.h>
#include <Windows.Graphics.Imaging.Interop.h>
#include <filesystem>

View File

@@ -78,6 +78,8 @@
#define IDC_RECORD_FRAME_RATE2 1059
#define IDC_RECORD_SCALING 1059
#define IDC_SNIP_HOTKEY 1060
#define IDC_SNIP_OCR_HOTKEY 1112
#define IDC_SNIP_PANORAMA_HOTKEY 1114
#define IDC_CAPTURE_AUDIO 1061
#define IDC_MICROPHONE 1062
#define IDC_PEN_CONTROL 1063
@@ -111,12 +113,15 @@
#define IDC_SMOOTH_IMAGE 1107
#define IDC_CAPTURE_SYSTEM_AUDIO 1108
#define IDC_MICROPHONE_LABEL 1109
#define IDC_MIC_MONO_MIX 1110
#define IDC_TRIM_FILE 1110
#define IDC_MIC_MONO_MIX 1111
#define IDC_CHECK_LOCK_WORKSTATION 1112
#define IDC_SAVE 40002
#define IDC_COPY 40004
#define IDC_RECORD 40006
#define IDC_RECORD_HOTKEY 40007
#define IDC_COPY_CROP 40008
#define IDC_COPY_OCR 40014
#define IDC_SAVE_CROP 40009
#define IDC_DEMOTYPE_HOTKEY 40011
@@ -125,8 +130,8 @@
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 120
#define _APS_NEXT_COMMAND_VALUE 40013
#define _APS_NEXT_CONTROL_VALUE 1099
#define _APS_NEXT_COMMAND_VALUE 40015
#define _APS_NEXT_CONTROL_VALUE 1113
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

View File

@@ -0,0 +1,520 @@
//============================================================================
//
// BreakTimer.cpp
//
// Shared break timer rendering module used by both ZoomIt and the
// ZoomItBreak screensaver (.scr).
//
// Copyright (C) Mark Russinovich
// Sysinternals - www.sysinternals.com
//
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//============================================================================
// When built inside ZoomIt (with PCH), pch.h is included automatically.
// When built for the screensaver project, we include the headers we need.
#ifndef __ZOOMIT_SCREENSAVER__
#include "pch.h"
#endif
#include "BreakTimer.h"
#include <stdio.h>
#pragma comment(lib, "gdiplus.lib")
#pragma comment(lib, "Msimg32.lib")
#pragma comment(lib, "Winmm.lib")
//----------------------------------------------------------------------------
//
// BreakTimer_UpdateMonitorInfo
//
// Determine monitor geometry for the given screen point.
//
//----------------------------------------------------------------------------
void BreakTimer_UpdateMonitorInfo( POINT point, MONITORINFO* monInfo )
{
HMONITOR hMon = MonitorFromPoint( point, MONITOR_DEFAULTTONEAREST );
if( hMon != nullptr )
{
monInfo->cbSize = sizeof *monInfo;
GetMonitorInfo( hMon, monInfo );
}
else
{
*monInfo = {};
HDC hdcScreen = CreateDC( L"DISPLAY", nullptr, nullptr, nullptr );
if( hdcScreen != nullptr )
{
monInfo->rcMonitor.right = GetDeviceCaps( hdcScreen, HORZRES );
monInfo->rcMonitor.bottom = GetDeviceCaps( hdcScreen, VERTRES );
DeleteDC( hdcScreen );
}
}
}
//----------------------------------------------------------------------------
//
// BreakTimer_LoadImageFile
//
// Use GDI+ to load an image file and return an HBITMAP.
//
//----------------------------------------------------------------------------
HBITMAP BreakTimer_LoadImageFile( PTCHAR Filename )
{
HBITMAP hBmp;
Gdiplus::Bitmap* bitmap = Gdiplus::Bitmap::FromFile( Filename );
if( bitmap == nullptr || bitmap->GetHBITMAP( NULL, &hBmp ) != Gdiplus::Ok )
{
delete bitmap;
return NULL;
}
delete bitmap;
return hBmp;
}
//----------------------------------------------------------------------------
//
// BreakTimer_CreateFadedDesktopBackground
//
// Creates a snapshot of the desktop that is faded and alpha-blended
// with black.
//
//----------------------------------------------------------------------------
HBITMAP BreakTimer_CreateFadedDesktopBackground( HDC hdc, LPRECT rcScreen, LPRECT rcCrop )
{
int width = rcScreen->right - rcScreen->left;
int height = rcScreen->bottom - rcScreen->top;
HDC hdcScreen = hdc;
HDC hdcMem = CreateCompatibleDC( hdcScreen );
HBITMAP hBitmap = CreateCompatibleBitmap( hdcScreen, width, height );
HBITMAP hOld = static_cast<HBITMAP>( SelectObject( hdcMem, hBitmap ) );
HBRUSH hBrush = CreateSolidBrush( RGB( 0, 0, 0 ) );
// Start with black background.
FillRect( hdcMem, rcScreen, hBrush );
if( rcCrop != NULL && rcCrop->left != -1 )
{
// Copy screen contents that are not cropped.
BitBlt( hdcMem, rcCrop->left, rcCrop->top,
rcCrop->right - rcCrop->left,
rcCrop->bottom - rcCrop->top,
hdcScreen, rcCrop->left, rcCrop->top, SRCCOPY );
}
// Blend screen contents into the black background.
BLENDFUNCTION blend = { 0 };
blend.BlendOp = AC_SRC_OVER;
blend.BlendFlags = 0;
blend.SourceConstantAlpha = 0x4F;
blend.AlphaFormat = 0;
AlphaBlend( hdcMem, 0, 0, width, height,
hdcScreen, rcScreen->left, rcScreen->top,
width, height, blend );
SelectObject( hdcMem, hOld );
DeleteDC( hdcMem );
DeleteObject( hBrush );
return hBitmap;
}
//----------------------------------------------------------------------------
//
// BreakTimer_Init
//
// Create fonts, backing bitmap, and optionally load background.
// Returns TRUE on success.
//
//----------------------------------------------------------------------------
BOOLEAN BreakTimer_Init(
HWND hWnd,
BreakTimerState* state,
const BreakTimerSettings* settings,
int timeoutSeconds,
HBITMAP hExistingBackground,
HDC hExistingBackgroundDC )
{
state->active = TRUE;
state->timeoutSeconds = timeoutSeconds;
// Get screen DC.
state->hdcScreen = CreateDC( L"DISPLAY", static_cast<PTCHAR>( NULL ),
static_cast<PTCHAR>( NULL ),
static_cast<CONST DEVMODE*>( NULL ) );
if( !state->hdcScreen )
return FALSE;
// Determine monitor.
POINT cursorPos;
GetCursorPos( &cursorPos );
BreakTimer_UpdateMonitorInfo( cursorPos, &state->monInfo );
state->width = state->monInfo.rcMonitor.right - state->monInfo.rcMonitor.left;
state->height = state->monInfo.rcMonitor.bottom - state->monInfo.rcMonitor.top;
// Manage background bitmap.
if( hExistingBackground )
{
// Caller supplied a pre-captured background (e.g. from command line).
state->hBackgroundBmp = hExistingBackground;
state->hDcBackgroundFile = hExistingBackgroundDC;
}
else if( settings->showBackgroundFile && !settings->showDesktop )
{
// Load image file.
state->hBackgroundBmp = BreakTimer_LoadImageFile(
const_cast<PTCHAR>( settings->backgroundFile ) );
if( !state->hBackgroundBmp )
return FALSE;
state->hDcBackgroundFile = CreateCompatibleDC( state->hdcScreen );
SelectObject( state->hDcBackgroundFile, state->hBackgroundBmp );
}
else if( settings->showBackgroundFile && settings->showDesktop )
{
// Faded desktop screenshot.
HDC hDcDesktop = GetDC( NULL );
state->hBackgroundBmp = BreakTimer_CreateFadedDesktopBackground(
hDcDesktop, &state->monInfo.rcMonitor, NULL );
ReleaseDC( NULL, hDcDesktop );
state->hDcBackgroundFile = CreateCompatibleDC( state->hdcScreen );
SelectObject( state->hDcBackgroundFile, state->hBackgroundBmp );
}
else
{
state->hBackgroundBmp = NULL;
state->hDcBackgroundFile = NULL;
}
// Create fonts.
LOGFONT lf = settings->logFont;
lf.lfHeight = state->height / 5;
state->hTimerFont = CreateFontIndirect( &lf );
lf.lfHeight = state->height / 8;
state->hNegativeTimerFont = CreateFontIndirect( &lf );
// Create backing bitmap for double buffering.
state->hdcScreenCompat = CreateCompatibleDC( state->hdcScreen );
state->bmp.bmBitsPixel = static_cast<BYTE>( GetDeviceCaps( state->hdcScreen, BITSPIXEL ) );
state->bmp.bmPlanes = static_cast<BYTE>( GetDeviceCaps( state->hdcScreen, PLANES ) );
state->bmp.bmWidth = state->width;
state->bmp.bmHeight = state->height;
state->bmp.bmWidthBytes = ( ( state->bmp.bmWidth + 15 ) & ~15 ) / 8;
state->hbmpCompat = CreateBitmap( state->bmp.bmWidth, state->bmp.bmHeight,
state->bmp.bmPlanes, state->bmp.bmBitsPixel, static_cast<CONST VOID*>( NULL ) );
SelectObject( state->hdcScreenCompat, state->hbmpCompat );
SetTextColor( state->hdcScreenCompat, settings->penColor );
SetBkMode( state->hdcScreenCompat, TRANSPARENT );
SelectObject( state->hdcScreenCompat, state->hTimerFont );
return TRUE;
}
//----------------------------------------------------------------------------
//
// BreakTimer_Tick
//
// Decrement counter, invalidate window, play sound at zero.
//
//----------------------------------------------------------------------------
void BreakTimer_Tick(
HWND hWnd,
BreakTimerState* state,
const BreakTimerSettings* settings )
{
state->timeoutSeconds -= 1;
InvalidateRect( hWnd, NULL, FALSE );
if( state->timeoutSeconds == 0 && settings->playSound )
{
PlaySound( settings->soundFile, NULL, SND_FILENAME | SND_ASYNC );
}
}
//----------------------------------------------------------------------------
//
// BreakTimer_Paint
//
// Render the break timer into the back buffer and blit to the paint DC.
//
//----------------------------------------------------------------------------
void BreakTimer_Paint(
HDC hdc,
BreakTimerState* state,
const BreakTimerSettings* settings )
{
RECT rc, rc1;
TCHAR timerText[16];
TCHAR negativeTimerText[16];
// Fill background (white by default, black if backgroundColor == 1).
rc.top = rc.left = 0;
rc.bottom = state->height;
rc.right = state->width;
if( settings->backgroundColor )
{
HBRUSH hBrush = CreateSolidBrush( RGB( 0, 0, 0 ) );
FillRect( state->hdcScreenCompat, &rc, hBrush );
DeleteObject( hBrush );
}
else
{
FillRect( state->hdcScreenCompat, &rc, GetSysColorBrush( COLOR_WINDOW ) );
}
// Draw background bitmap if present.
if( state->hBackgroundBmp )
{
BITMAP local_bmp;
GetObject( state->hBackgroundBmp, sizeof( local_bmp ), &local_bmp );
SetStretchBltMode( state->hdcScreenCompat,
settings->smoothImage ? HALFTONE : COLORONCOLOR );
if( settings->backgroundStretch )
{
StretchBlt( state->hdcScreenCompat, 0, 0, state->width, state->height,
state->hDcBackgroundFile, 0, 0,
local_bmp.bmWidth, local_bmp.bmHeight, SRCCOPY | CAPTUREBLT );
}
else
{
BitBlt( state->hdcScreenCompat,
state->width / 2 - local_bmp.bmWidth / 2,
state->height / 2 - local_bmp.bmHeight / 2,
local_bmp.bmWidth, local_bmp.bmHeight,
state->hDcBackgroundFile, 0, 0, SRCCOPY | CAPTUREBLT );
}
}
// Format timer text.
if( state->timeoutSeconds > 0 )
{
_stprintf( timerText, L"% 2d:%02d",
state->timeoutSeconds / 60, state->timeoutSeconds % 60 );
}
else
{
_tcscpy( timerText, L"0:00" );
}
// Measure timer text.
rc.left = rc.top = 0;
DrawText( state->hdcScreenCompat, timerText, -1, &rc,
DT_NOCLIP | DT_LEFT | DT_NOPREFIX | DT_CALCRECT );
// Measure expired text if needed.
rc1.left = rc1.right = rc1.bottom = rc1.top = 0;
if( settings->showExpiredTime && state->timeoutSeconds < 0 )
{
_stprintf( negativeTimerText, L"(-% 2d:%02d)",
-state->timeoutSeconds / 60, -state->timeoutSeconds % 60 );
HFONT prevFont = static_cast<HFONT>(
SelectObject( state->hdcScreenCompat, state->hNegativeTimerFont ) );
DrawText( state->hdcScreenCompat, negativeTimerText, -1, &rc1,
DT_NOCLIP | DT_LEFT | DT_NOPREFIX | DT_CALCRECT );
SelectObject( state->hdcScreenCompat, prevFont );
}
// Position vertically.
switch( settings->timerPosition )
{
case 0: case 1: case 2:
rc.top = 50;
break;
case 3: case 4: case 5:
rc.top = ( state->height - ( rc.bottom - rc.top ) ) / 2;
break;
case 6: case 7: case 8:
rc.top = state->height - rc.bottom - 50 - rc1.bottom;
break;
}
// Position horizontally.
switch( settings->timerPosition )
{
case 0: case 3: case 6:
rc.left = 50;
break;
case 1: case 4: case 7:
rc.left = ( state->width - ( rc.right - rc.left ) ) / 2;
break;
case 2: case 5: case 8:
rc.left = state->width - rc.right - 50;
break;
}
rc.bottom += rc.top;
rc.right += rc.left;
// Draw timer text.
DrawText( state->hdcScreenCompat, timerText, -1, &rc,
DT_NOCLIP | DT_LEFT | DT_NOPREFIX );
// Draw expired text below the timer.
if( settings->showExpiredTime && state->timeoutSeconds < 0 )
{
rc1.top = rc.bottom + 10;
rc1.left = rc.left + ( ( rc.right - rc.left ) - ( rc1.right - rc1.left ) ) / 2;
HFONT prevFont = static_cast<HFONT>(
SelectObject( state->hdcScreenCompat, state->hNegativeTimerFont ) );
DrawText( state->hdcScreenCompat, negativeTimerText, -1, &rc1,
DT_NOCLIP | DT_LEFT | DT_NOPREFIX );
SelectObject( state->hdcScreenCompat, prevFont );
}
// Copy to screen.
BitBlt( hdc, 0, 0, state->width, state->height,
state->hdcScreenCompat, 0, 0, SRCCOPY | CAPTUREBLT );
}
//----------------------------------------------------------------------------
//
// BreakTimer_Cleanup
//
// Free the GDI resources used by the break timer.
//
//----------------------------------------------------------------------------
void BreakTimer_Cleanup(
BreakTimerState* state,
BOOLEAN freeBackground )
{
if( freeBackground && state->hBackgroundBmp )
{
DeleteObject( state->hBackgroundBmp );
DeleteDC( state->hDcBackgroundFile );
state->hBackgroundBmp = NULL;
state->hDcBackgroundFile = NULL;
}
if( state->hTimerFont )
{
DeleteObject( state->hTimerFont );
state->hTimerFont = NULL;
}
if( state->hNegativeTimerFont )
{
DeleteObject( state->hNegativeTimerFont );
state->hNegativeTimerFont = NULL;
}
if( state->hdcScreen )
{
DeleteDC( state->hdcScreen );
state->hdcScreen = NULL;
}
if( state->hdcScreenCompat )
{
DeleteDC( state->hdcScreenCompat );
state->hdcScreenCompat = NULL;
}
if( state->hbmpCompat )
{
DeleteObject( state->hbmpCompat );
state->hbmpCompat = NULL;
}
state->active = FALSE;
}
//----------------------------------------------------------------------------
//
// BreakTimer_AdjustTime
//
// Round to the nearest minute boundary and adjust by deltaMinutes.
// Resets the 1-second timer on the window.
//
//----------------------------------------------------------------------------
void BreakTimer_AdjustTime(
HWND hWnd,
BreakTimerState* state,
int deltaMinutes )
{
int breakTimeout = state->timeoutSeconds;
if( deltaMinutes > 0 )
{
if( breakTimeout < 0 ) breakTimeout = 0;
if( breakTimeout % 60 )
{
breakTimeout += ( 60 - breakTimeout % 60 );
deltaMinutes--;
}
breakTimeout += deltaMinutes * 60;
}
else
{
int absDelta = -deltaMinutes;
if( breakTimeout % 60 )
{
breakTimeout -= breakTimeout % 60;
absDelta--;
}
breakTimeout -= absDelta * 60;
}
if( breakTimeout < 0 ) breakTimeout = 0;
state->timeoutSeconds = breakTimeout;
KillTimer( hWnd, 0 );
SetTimer( hWnd, 0, 1000, NULL );
InvalidateRect( hWnd, NULL, TRUE );
}
//----------------------------------------------------------------------------
//
// BreakScrConfig_GetPath
//
// Build the full path to the config file in %TEMP%.
//
//----------------------------------------------------------------------------
static void BreakScrConfig_GetPath( TCHAR* path, size_t cch )
{
GetTempPath( static_cast<DWORD>( cch ), path );
_tcscat( path, BREAKSCR_CONFIG_FILE );
}
//----------------------------------------------------------------------------
//
// BreakScrConfig_Write
//
//----------------------------------------------------------------------------
BOOLEAN BreakScrConfig_Write( const BreakScrConfig* config )
{
TCHAR path[MAX_PATH];
BreakScrConfig_GetPath( path, MAX_PATH );
HANDLE hFile = CreateFile( path, GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
if( hFile == INVALID_HANDLE_VALUE )
return FALSE;
DWORD written;
BOOL ok = WriteFile( hFile, config, sizeof( *config ), &written, NULL );
CloseHandle( hFile );
return ok && written == sizeof( *config );
}
//----------------------------------------------------------------------------
//
// BreakScrConfig_Read
//
//----------------------------------------------------------------------------
BOOLEAN BreakScrConfig_Read( BreakScrConfig* config )
{
TCHAR path[MAX_PATH];
BreakScrConfig_GetPath( path, MAX_PATH );
HANDLE hFile = CreateFile( path, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
if( hFile == INVALID_HANDLE_VALUE )
return FALSE;
DWORD bytesRead;
BOOL ok = ReadFile( hFile, config, sizeof( *config ), &bytesRead, NULL );
CloseHandle( hFile );
if( !ok || bytesRead != sizeof( *config ) )
return FALSE;
if( config->magic != BREAKSCR_CONFIG_MAGIC )
return FALSE;
return TRUE;
}

View File

@@ -0,0 +1,141 @@
//============================================================================
//
// BreakTimer.h
//
// Shared break timer rendering module used by both ZoomIt and the
// ZoomItBreak screensaver (.scr).
//
// Copyright (C) Mark Russinovich
// Sysinternals - www.sysinternals.com
//
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//============================================================================
#pragma once
#include <windows.h>
#include <tchar.h>
#define GDIPVER 0x0110
#include <gdiplus.h>
//----------------------------------------------------------------------------
// BreakTimerSettings — read-only configuration, populated from globals
// or from command-line arguments in the screensaver.
//----------------------------------------------------------------------------
struct BreakTimerSettings
{
DWORD penColor;
DWORD backgroundColor; // 0 = white, 1 = black
DWORD timerPosition; // 08 (3×3 grid)
DWORD opacity; // 0100
DWORD showExpiredTime; // 0 or 1
BOOLEAN smoothImage;
BOOLEAN backgroundStretch;
BOOLEAN playSound;
TCHAR soundFile[MAX_PATH];
BOOLEAN showDesktop;
BOOLEAN showBackgroundFile;
TCHAR backgroundFile[MAX_PATH];
LOGFONT logFont;
};
//----------------------------------------------------------------------------
// BreakTimerState — runtime state for an active break timer.
//----------------------------------------------------------------------------
struct BreakTimerState
{
BOOLEAN active;
int timeoutSeconds; // counts down; goes negative if expired
HFONT hTimerFont;
HFONT hNegativeTimerFont;
HBITMAP hBackgroundBmp;
HDC hDcBackgroundFile;
HDC hdcScreen;
HDC hdcScreenCompat;
HBITMAP hbmpCompat;
BITMAP bmp;
int width;
int height;
MONITORINFO monInfo;
};
//----------------------------------------------------------------------------
// Shared utility functions
//----------------------------------------------------------------------------
// Determine monitor geometry for the given screen point.
void BreakTimer_UpdateMonitorInfo( POINT point, MONITORINFO* monInfo );
// Load an image file via GDI+; returns an HBITMAP or NULL on failure.
HBITMAP BreakTimer_LoadImageFile( PTCHAR Filename );
// Capture a faded (alpha-blended with black) screenshot of the desktop.
HBITMAP BreakTimer_CreateFadedDesktopBackground( HDC hdc, LPRECT rcScreen, LPRECT rcCrop );
//----------------------------------------------------------------------------
// Break timer lifecycle
//----------------------------------------------------------------------------
// Create fonts, backing bitmap, and load background.
// The caller is responsible for creating/showing the window itself.
// |timeoutSeconds| is already in seconds (e.g. g_BreakTimeout * 60 + 1).
BOOLEAN BreakTimer_Init(
HWND hWnd,
BreakTimerState* state,
const BreakTimerSettings* settings,
int timeoutSeconds,
HBITMAP hExistingBackground, // optional pre-captured background
HDC hExistingBackgroundDC // optional DC for above
);
// Called every second; decrements the counter and invalidates the window.
void BreakTimer_Tick(
HWND hWnd,
BreakTimerState* state,
const BreakTimerSettings* settings
);
// Render the timer into hdcScreenCompat then BitBlt to hdc (from BeginPaint).
void BreakTimer_Paint(
HDC hdc,
BreakTimerState* state,
const BreakTimerSettings* settings
);
// Free fonts, DCs, bitmaps. If |freeBackground| is false the background
// bitmap/DC are left for the caller to manage (e.g. shallow destroy).
void BreakTimer_Cleanup(
BreakTimerState* state,
BOOLEAN freeBackground
);
// Adjust the remaining time by |deltaMinutes| (positive = add time).
// Resets the 1-second timer on hWnd.
void BreakTimer_AdjustTime(
HWND hWnd,
BreakTimerState* state,
int deltaMinutes
);
//----------------------------------------------------------------------------
// BreakScrConfig — binary blob written to a temp file by ZoomIt and
// read by the screensaver on startup. This avoids command-line arg
// issues since Windows launches screensavers with only /s.
//----------------------------------------------------------------------------
#define BREAKSCR_CONFIG_MAGIC 0x5A4D4253 // 'ZMBS'
#define BREAKSCR_CONFIG_FILE L"ZoomItBreakConfig.dat"
struct BreakScrConfig
{
DWORD magic; // must be BREAKSCR_CONFIG_MAGIC
int timeoutSeconds;
BOOL resumed; // set TRUE by screensaver on first launch
BreakTimerSettings settings;
TCHAR screenshotPath[MAX_PATH];
};
// Write config to %TEMP%\BREAKSCR_CONFIG_FILE.
BOOLEAN BreakScrConfig_Write( const BreakScrConfig* config );
// Read config from %TEMP%\BREAKSCR_CONFIG_FILE.
BOOLEAN BreakScrConfig_Read( BreakScrConfig* config );

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"
xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<asmv3:application>
<asmv3:windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2,PerMonitor</dpiAwareness>
</asmv3:windowsSettings>
</asmv3:application>
</assembly>

View File

@@ -0,0 +1,27 @@
//============================================================================
//
// ZoomItBreak.rc
//
// Minimal resources required by Scrnsavw.lib.
//
//============================================================================
#include <windows.h>
#include <scrnsave.h>
// Embed DPI-awareness manifest so the screensaver sees native resolution.
// This ensures the pre-captured desktop screenshot (saved at physical pixels
// by the DPI-aware ZoomIt process) matches the screensaver window dimensions.
1 RT_MANIFEST "ZoomItBreak.manifest"
// IDS_DESCRIPTION is used by scrnsavw.lib as the window class name.
STRINGTABLE
BEGIN
IDS_DESCRIPTION, "ZoomIt Break Timer"
END
// Stub configuration dialog - never shown (ScreenSaverConfigureDialog returns FALSE).
DLG_SCRNSAVECONFIGURE DIALOG 0, 0, 200, 60
STYLE WS_DLGFRAME | WS_POPUP | WS_VISIBLE | DS_MODALFRAME | WS_CAPTION
CAPTION "ZoomIt Break Timer"
BEGIN
END

View File

@@ -0,0 +1,230 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|ARM64">
<Configuration>Debug</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM64">
<Configuration>Release</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>18.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{94ba3051-c8d7-454a-9d46-1a7c78e228a3}</ProjectGuid>
<RootNamespace>ZoomItBreak</RootNamespace>
<WindowsTargetPlatformVersion>10.0.26100.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<!--
Output .scr instead of .exe. A screensaver is a renamed executable.
Use a separate intermediate dir to avoid colliding with ZoomIt.
-->
<PropertyGroup>
<TargetExt>.scr</TargetExt>
<GenerateManifest>false</GenerateManifest>
<IntDir>$(Platform)\$(Configuration)\ZoomItBreak\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<OutDir>$(MsBuildProjectDirectory)\$(Platform)\$(Configuration)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<OutDir>$(MsBuildProjectDirectory)\$(Platform)\$(Configuration)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<OutDir>$(MsBuildProjectDirectory)\$(Platform)\$(Configuration)\</OutDir>
<TargetName>$(ProjectName)64</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<OutDir>$(MsBuildProjectDirectory)\$(Platform)\$(Configuration)\</OutDir>
<TargetName>$(ProjectName)64a</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<OutDir>$(MsBuildProjectDirectory)\$(Platform)\$(Configuration)\</OutDir>
<TargetName>$(ProjectName)64</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<OutDir>$(MsBuildProjectDirectory)\$(Platform)\$(Configuration)\</OutDir>
<TargetName>$(ProjectName)64a</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v145</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v145</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v145</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v145</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v145</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v145</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup>
<ClCompile>
<DisableSpecificWarnings>4100;4091;4245</DisableSpecificWarnings>
<AdditionalIncludeDirectories>..\..\..\;$(MSBuildThisFileDirectory)..\..\..\common\sysinternals;..\ZoomIt;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<LanguageStandard>stdcpplatest</LanguageStandard>
<LanguageStandard_C>stdc17</LanguageStandard_C>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PreprocessorDefinitions>__ZOOMIT_SCREENSAVER__;_UNICODE;UNICODE;WINVER=0x0602;_DEBUG;_WIN32_WINNT=0x602;_WIN32_WINDOWS=0x600;WIN32;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PreprocessorDefinitions>__ZOOMIT_SCREENSAVER__;_UNICODE;UNICODE;WINVER=0x602;NDEBUG;_WIN32_WINNT=0x602;_WIN32_WINDOWS=0x501;WIN32;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PreprocessorDefinitions>__ZOOMIT_SCREENSAVER__;_UNICODE;UNICODE;WINVER=0x0602;_DEBUG;_WIN32_WINNT=0x602;_WIN32_WINDOWS=0x600;WIN32;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<ClCompile>
<PreprocessorDefinitions>__ZOOMIT_SCREENSAVER__;_UNICODE;UNICODE;WINVER=0x0602;_DEBUG;_WIN32_WINNT=0x602;_WIN32_WINDOWS=0x600;WIN32;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PreprocessorDefinitions>__ZOOMIT_SCREENSAVER__;_UNICODE;UNICODE;WINVER=0x602;NDEBUG;_WIN32_WINNT=0x602;_WIN32_WINDOWS=0x501;WIN32;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<ClCompile>
<PreprocessorDefinitions>__ZOOMIT_SCREENSAVER__;_UNICODE;UNICODE;WINVER=0x602;NDEBUG;_WIN32_WINNT=0x602;_WIN32_WINDOWS=0x501;WIN32;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="BreakTimer.cpp" />
<ClCompile Include="ZoomItBreakScr.cpp" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="ZoomItBreak.rc" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="BreakTimer.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
</Target>
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
</Project>

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="BreakTimer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ZoomItBreakScr.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="BreakTimer.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="ZoomItBreak.rc">
<Filter>Resource Files</Filter>
</ResourceCompile>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,283 @@
//============================================================================
//
// ZoomItBreakScr.cpp
//
// ZoomIt break timer screensaver (.scr). When launched by Winlogon on the
// Screen-saver desktop with password protection, the user must authenticate
// to dismiss it. The break timer countdown and rendering are provided by
// the shared BreakTimer module.
//
// Copyright (C) Mark Russinovich
// Sysinternals - www.sysinternals.com
//
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//============================================================================
#include <windows.h>
#include <windowsx.h>
#include <tchar.h>
#include <stdio.h>
#include <stdlib.h>
#include <scrnsave.h>
#define GDIPVER 0x0110
#include <gdiplus.h>
#include "BreakTimer.h"
static void DbgPrint( LPCTSTR fmt, ... )
{
TCHAR buf[512];
va_list ap;
#pragma warning( push )
#pragma warning( disable : 26492 )
va_start( ap, fmt );
#pragma warning( pop )
_vsntprintf( buf, _countof(buf), fmt, ap );
va_end( ap );
buf[_countof(buf)-1] = 0;
OutputDebugString( buf );
}
#pragma comment(lib, "scrnsavw.lib")
#pragma comment(lib, "comctl32.lib")
#pragma comment(lib, "gdiplus.lib")
#pragma comment(lib, "Msimg32.lib")
#pragma comment(lib, "Winmm.lib")
//----------------------------------------------------------------------------
// Globals
//----------------------------------------------------------------------------
static BreakTimerSettings g_Settings;
static BreakTimerState g_State;
static ULONG_PTR g_GdiplusToken;
static TCHAR g_ScreenshotPath[MAX_PATH] = { 0 };
static int g_LastSavedTimeout = 0; // For state persistence
//----------------------------------------------------------------------------
// Load settings from the binary config file written by ZoomIt,
// falling back to hard-coded defaults if the file is missing.
//----------------------------------------------------------------------------
static void LoadSettings( void )
{
BreakScrConfig config;
if( BreakScrConfig_Read( &config ) )
{
g_Settings = config.settings;
g_State.timeoutSeconds = config.timeoutSeconds;
_tcscpy( g_ScreenshotPath, config.screenshotPath );
DbgPrint( L"[BreakScr] Config loaded: timeout=%d, bgFile=%d, showDesktop=%d, screenshot=%s\n",
config.timeoutSeconds, config.settings.showBackgroundFile,
config.settings.showDesktop, config.screenshotPath );
return;
}
DbgPrint( L"[BreakScr] Config file not found, using fallback defaults\n" );
// Fallback defaults (for testing the .scr directly).
memset( &g_Settings, 0, sizeof( g_Settings ) );
g_Settings.penColor = RGB( 255, 0, 0 );
g_Settings.timerPosition = 4;
g_Settings.opacity = 100;
g_Settings.showExpiredTime = 1;
g_Settings.smoothImage = TRUE;
g_Settings.backgroundStretch = FALSE;
g_Settings.showDesktop = TRUE;
g_Settings.showBackgroundFile = FALSE;
g_State.timeoutSeconds = 600;
NONCLIENTMETRICS ncm = { sizeof( ncm ) };
SystemParametersInfo( SPI_GETNONCLIENTMETRICS, sizeof( ncm ), &ncm, 0 );
g_Settings.logFont = ncm.lfMessageFont;
}
//----------------------------------------------------------------------------
//
// ScreenSaverProc
//
// Main window procedure for the screensaver, called by Scrnsavw.lib.
//
//----------------------------------------------------------------------------
LRESULT WINAPI ScreenSaverProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch( msg )
{
case WM_CREATE:
{
DbgPrint( L"[BreakScr] WM_CREATE: hwnd=%p\n", hWnd );
// Initialize GDI+.
Gdiplus::GdiplusStartupInput startupIn;
Gdiplus::GdiplusStartup( &g_GdiplusToken, &startupIn, NULL );
LoadSettings();
// Check if a previous screensaver instance already ran (resumed == TRUE).
// On first launch, ZoomIt sets resumed = FALSE, so we skip the deduction.
BreakScrConfig resumeConfig;
if( BreakScrConfig_Read( &resumeConfig ) && resumeConfig.resumed )
{
// Subtract the screensaver idle timeout to compensate for
// the time the screensaver wasn't running on the lock screen.
UINT scrTimeout = 0;
SystemParametersInfo( SPI_GETSCREENSAVETIMEOUT, 0, &scrTimeout, 0 );
g_State.timeoutSeconds -= static_cast<int>( scrTimeout );
if( g_State.timeoutSeconds < 0 && !g_Settings.showExpiredTime )
g_State.timeoutSeconds = 0;
DbgPrint( L"[BreakScr] Resumption: subtracted %u sec idle, timeout=%d\n",
scrTimeout, g_State.timeoutSeconds );
}
// Mark as resumed so subsequent screensaver launches know to deduct idle time.
{
BreakScrConfig markConfig;
if( BreakScrConfig_Read( &markConfig ) )
{
markConfig.resumed = TRUE;
BreakScrConfig_Write( &markConfig );
}
}
// Load pre-captured screenshot if provided.
HBITMAP hBgBmp = NULL;
HDC hBgDC = NULL;
if( g_ScreenshotPath[0] )
{
hBgBmp = BreakTimer_LoadImageFile( g_ScreenshotPath );
DbgPrint( L"[BreakScr] LoadImageFile(%s) => %p\n", g_ScreenshotPath, hBgBmp );
if( hBgBmp )
{
HDC hdcScreen = CreateDC( L"DISPLAY", NULL, NULL, NULL );
hBgDC = CreateCompatibleDC( hdcScreen );
SelectObject( hBgDC, hBgBmp );
DeleteDC( hdcScreen );
}
}
int timeout = g_State.timeoutSeconds;
memset( &g_State, 0, sizeof( g_State ) );
DbgPrint( L"[BreakScr] Calling BreakTimer_Init, timeout=%d\n", timeout );
if( !BreakTimer_Init( hWnd, &g_State, &g_Settings, timeout, hBgBmp, hBgDC ) )
{
DbgPrint( L"[BreakScr] BreakTimer_Init FAILED\n" );
PostMessage( hWnd, WM_CLOSE, 0, 0 );
return 0;
}
DbgPrint( L"[BreakScr] BreakTimer_Init OK, active=%d\n", g_State.active );
// Prevent the monitor from blanking due to power management.
SetThreadExecutionState( ES_CONTINUOUS | ES_DISPLAY_REQUIRED | ES_SYSTEM_REQUIRED );
// Kick off the first tick and start the 1-second timer.
SendMessage( hWnd, WM_TIMER, 1, 0 );
SetTimer( hWnd, 1, 1000, NULL );
return 0;
}
case WM_TIMER:
if( wParam == 1 )
{
BreakTimer_Tick( hWnd, &g_State, &g_Settings );
// Periodically save state (every 5 seconds) for resumption after
// credential provider timeout. This allows the screensaver to continue
// from where it left off if a student triggers the login screen but
// doesn't authenticate.
if( g_State.timeoutSeconds != g_LastSavedTimeout &&
g_State.timeoutSeconds % 5 == 0 )
{
BreakScrConfig config;
if( BreakScrConfig_Read( &config ) )
{
config.timeoutSeconds = g_State.timeoutSeconds;
if( BreakScrConfig_Write( &config ) )
{
g_LastSavedTimeout = g_State.timeoutSeconds;
DbgPrint( L"[BreakScr] Saved state: %d seconds remaining\n",
g_State.timeoutSeconds );
}
}
}
}
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint( hWnd, &ps );
if( g_State.active )
{
BreakTimer_Paint( hdc, &g_State, &g_Settings );
}
EndPaint( hWnd, &ps );
return 0;
}
case WM_DESTROY:
DbgPrint( L"[BreakScr] WM_DESTROY\n" );
SetThreadExecutionState( ES_CONTINUOUS ); // Restore default power behavior
KillTimer( hWnd, 1 );
BreakTimer_Cleanup( &g_State, TRUE );
Gdiplus::GdiplusShutdown( g_GdiplusToken );
return 0;
//------------------------------------------------------------------
// Prevent DefScreenSaverProc from auto-closing on user input.
// The screensaver must stay up until the break timer expires or
// the user authenticates via Ctrl+Alt+Del. DefScreenSaverProc
// would close the window on mouse movement, clicks, keyboard,
// or deactivation.
//------------------------------------------------------------------
case WM_MOUSEMOVE:
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_KEYDOWN:
if( wParam == 'W' || wParam == 'K' )
{
g_Settings.backgroundColor = ( wParam == 'K' ) ? 1 : 0;
InvalidateRect( hWnd, NULL, FALSE );
}
return 0;
case WM_KEYUP:
case WM_SYSKEYDOWN:
return 0;
case WM_ACTIVATE:
case WM_ACTIVATEAPP:
// Don't close on deactivation (e.g. LockWorkStation switches desktop).
return 0;
case WM_SYSCOMMAND:
// Block SC_CLOSE from Alt+F4 etc.
if( ( wParam & 0xFFF0 ) == SC_CLOSE )
return 0;
break;
}
return DefScreenSaverProc( hWnd, msg, wParam, lParam );
}
//----------------------------------------------------------------------------
//
// ScreenSaverConfigureDialog
//
// No configuration — ZoomIt handles all settings.
//
//----------------------------------------------------------------------------
BOOL WINAPI ScreenSaverConfigureDialog( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam )
{
return FALSE;
}
//----------------------------------------------------------------------------
//
// RegisterDialogClasses
//
// Nothing to register.
//
//----------------------------------------------------------------------------
BOOL WINAPI RegisterDialogClasses( HANDLE hInst )
{
return TRUE;
}

View File

@@ -91,3 +91,12 @@ void Trace::ZoomItActivateSnip() noexcept
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}
void Trace::ZoomItActivateSnipOcr() noexcept
{
TraceLoggingWriteWrapper(
g_hProvider,
"ZoomIt_ActivateSnipOcr",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}

View File

@@ -14,4 +14,5 @@ public:
static void ZoomItActivateDemoType() noexcept;
static void ZoomItActivateRecord() noexcept;
static void ZoomItActivateSnip() noexcept;
static void ZoomItActivateSnipOcr() noexcept;
};

View File

@@ -70,6 +70,8 @@ namespace winrt::PowerToys::ZoomItSettingsInterop::implementation
{ L"DrawToggleKey", SPECIAL_SEMANTICS_SHORTCUT },
{ L"RecordToggleKey", SPECIAL_SEMANTICS_SHORTCUT },
{ L"SnipToggleKey", SPECIAL_SEMANTICS_SHORTCUT },
{ L"SnipOcrToggleKey", SPECIAL_SEMANTICS_SHORTCUT },
{ L"SnipPanoramaToggleKey", SPECIAL_SEMANTICS_SHORTCUT },
{ L"BreakTimerKey", SPECIAL_SEMANTICS_SHORTCUT },
{ L"DemoTypeToggleKey", SPECIAL_SEMANTICS_SHORTCUT },
{ L"PenColor", SPECIAL_SEMANTICS_COLOR },

View File

@@ -28,6 +28,12 @@ namespace Microsoft.PowerToys.Settings.UI.Library
[CmdConfigureIgnore]
public static HotkeySettings DefaultSnipToggleKey => new HotkeySettings(false, true, false, false, '6'); // Ctrl+6
[CmdConfigureIgnore]
public static HotkeySettings DefaultSnipOcrToggleKey => new HotkeySettings(false, true, true, false, '6'); // Ctrl+Alt+6
[CmdConfigureIgnore]
public static HotkeySettings DefaultSnipPanoramaToggleKey => new HotkeySettings(false, true, false, false, '8'); // Ctrl+8
[CmdConfigureIgnore]
public static HotkeySettings DefaultBreakTimerKey => new HotkeySettings(false, true, false, false, '3'); // Ctrl+3
@@ -44,6 +50,10 @@ namespace Microsoft.PowerToys.Settings.UI.Library
public KeyboardKeysProperty SnipToggleKey { get; set; }
public KeyboardKeysProperty SnipOcrToggleKey { get; set; }
public KeyboardKeysProperty SnipPanoramaToggleKey { get; set; }
public KeyboardKeysProperty BreakTimerKey { get; set; }
public StringProperty Font { get; set; }
@@ -96,5 +106,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
public BoolProperty MicMonoMix { get; set; }
public StringProperty MicrophoneDeviceId { get; set; }
public BoolProperty BreakLockWorkstation { get; set; }
}
}

View File

@@ -240,6 +240,9 @@
Visibility="{x:Bind ViewModel.BreakShowBackgroundFile, Mode=OneWay}">
<CheckBox x:Uid="ZoomIt_Break_BackgroundStretch" IsChecked="{x:Bind ViewModel.BreakBackgroundStretch, Mode=TwoWay}" />
</tkcontrols:SettingsCard>
<tkcontrols:SettingsCard Name="ZoomItBreakLockWorkstation" ContentAlignment="Left">
<CheckBox x:Uid="ZoomIt_Break_LockWorkstation" IsChecked="{x:Bind ViewModel.BreakLockWorkstation, Mode=TwoWay}" />
</tkcontrols:SettingsCard>
<tkcontrols:SettingsCard>
<tkcontrols:SettingsCard.Description>
<tkcontrols:MarkdownTextBlock x:Uid="ZoomIt_BreakFAQ" Config="{StaticResource DescriptionTextMarkdownConfig}" />
@@ -325,6 +328,22 @@
</tkcontrols:SettingsCard>
</tkcontrols:SettingsExpander.Items>
</tkcontrols:SettingsExpander>
<tkcontrols:SettingsExpander
Name="ZoomItSnipOcrShortcut"
x:Uid="ZoomIt_SnipOcr_Shortcut"
HeaderIcon="{ui:FontIcon Glyph=&#xF7ED;}"
IsExpanded="True">
<controls:ShortcutControl MinWidth="{StaticResource SettingActionControlMinWidth}" HotkeySettings="{x:Bind ViewModel.SnipOcrToggleKey, Mode=TwoWay}" />
</tkcontrols:SettingsExpander>
</controls:SettingsGroup>
<controls:SettingsGroup x:Uid="ZoomIt_PanoramaGroup" IsEnabled="{x:Bind ViewModel.IsEnabled, Mode=OneWay}">
<tkcontrols:SettingsExpander
Name="ZoomItPanoramaShortcut"
x:Uid="ZoomIt_Panorama_Shortcut"
HeaderIcon="{ui:FontIcon Glyph=&#xE7C5;}"
IsExpanded="True">
<controls:ShortcutControl MinWidth="{StaticResource SettingActionControlMinWidth}" HotkeySettings="{x:Bind ViewModel.SnipPanoramaToggleKey, Mode=TwoWay}" />
</tkcontrols:SettingsExpander>
</controls:SettingsGroup>
</StackPanel>
</controls:SettingsPageControl.ModuleContent>

View File

@@ -4813,6 +4813,9 @@ The break timer font matches the text font.</value>
<data name="ZoomIt_Record_Microphones_Default_Name" xml:space="preserve">
<value>Default</value>
</data>
<data name="ZoomIt_Break_LockWorkstation.Content" xml:space="preserve">
<value>Lock workstation during break</value>
</data>
<data name="ZoomIt_SnipGroup.Header" xml:space="preserve">
<value>Snip</value>
</data>
@@ -4825,6 +4828,24 @@ The break timer font matches the text font.</value>
<data name="ZoomIt_Snip_Shortcut_Save" xml:space="preserve">
<value>Press **{0}** to save the snip to a file instead of the clipboard.</value>
</data>
<data name="ZoomIt_SnipOcr_Shortcut.Header" xml:space="preserve">
<value>Snip OCR activation</value>
</data>
<data name="ZoomIt_SnipOcr_Shortcut.Description" xml:space="preserve">
<value>Copy text from the selected region to the clipboard.</value>
</data>
<data name="ZoomIt_PanoramaGroup.Header" xml:space="preserve">
<value>Panorama</value>
</data>
<data name="ZoomIt_PanoramaGroup.Description" xml:space="preserve">
<value>Capture a scrolling panorama of a selected screen region.</value>
</data>
<data name="ZoomIt_Panorama_Shortcut.Header" xml:space="preserve">
<value>Panorama activation</value>
</data>
<data name="ZoomIt_Panorama_Shortcut.Description" xml:space="preserve">
<value>Select the area, then scroll the content. Move slowly and consistently, and do not rewind to previously covered areas. Press the hotkey again or with Shift to save to a file.</value>
</data>
<data name="Oobe_ZoomIt.Description" xml:space="preserve">
<value>ZoomIt is a screen zoom, annotation, and recording tool for technical presentations and demos. You can also use ZoomIt to snip screenshots to the clipboard or to a file.</value>
<comment>{Locked="ZoomIt"}</comment>

View File

@@ -367,6 +367,34 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
}
}
public HotkeySettings SnipOcrToggleKey
{
get => _zoomItSettings.Properties.SnipOcrToggleKey.Value;
set
{
if (_zoomItSettings.Properties.SnipOcrToggleKey.Value != value)
{
_zoomItSettings.Properties.SnipOcrToggleKey.Value = value ?? ZoomItProperties.DefaultSnipOcrToggleKey;
OnPropertyChanged(nameof(SnipOcrToggleKey));
NotifySettingsChanged();
}
}
}
public HotkeySettings SnipPanoramaToggleKey
{
get => _zoomItSettings.Properties.SnipPanoramaToggleKey.Value;
set
{
if (_zoomItSettings.Properties.SnipPanoramaToggleKey.Value != value)
{
_zoomItSettings.Properties.SnipPanoramaToggleKey.Value = value ?? ZoomItProperties.DefaultSnipPanoramaToggleKey;
OnPropertyChanged(nameof(SnipPanoramaToggleKey));
NotifySettingsChanged();
}
}
}
public HotkeySettings BreakTimerKey
{
get => _zoomItSettings.Properties.BreakTimerKey.Value;
@@ -783,6 +811,20 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
}
}
public bool BreakLockWorkstation
{
get => _zoomItSettings.Properties.BreakLockWorkstation.Value;
set
{
if (_zoomItSettings.Properties.BreakLockWorkstation.Value != value)
{
_zoomItSettings.Properties.BreakLockWorkstation.Value = value;
OnPropertyChanged(nameof(BreakLockWorkstation));
NotifySettingsChanged();
}
}
}
public double RecordScaling
{
get