//============================================================================
//
// Zoomit
// Copyright (C) Mark Russinovich
// Sysinternals - www.sysinternals.com
//
// Screen zoom and annotation tool.
//
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//============================================================================
#include "pch.h"

#include "zoomit.h"
#include "Utility.h"
#include "WindowsVersions.h"
#include "ZoomItSettings.h"
#include "GifRecordingSession.h"
#include "BreakTimer.h"
#include "PanoramaCapture.h"
#include <wtsapi32.h>
#include <tlhelp32.h>
#include <limits>
#include <vector>

#ifdef __ZOOMIT_POWERTOYS__
#include <common/interop/shared_constants.h>
#include <common/utils/ProcessWaiter.h>
#include <common/utils/process_path.h>

#include "../ZoomItModuleInterface/trace.h"
#include <common/Telemetry/EtwTrace/EtwTrace.h>
#include <common/logger/logger.h>
#include <common/utils/logger_helper.h>
#include <common/utils/winapi_error.h>
#include <common/utils/gpo.h>
#include <array>
#include <vector>
#endif // __ZOOMIT_POWERTOYS__

#ifdef __ZOOMIT_POWERTOYS__
enum class ZoomItCommand
{
    Zoom,
    Draw,
    Break,
    LiveZoom,
    Snip,
    SnipOcr,
    Record,
};
#endif // __ZOOMIT_POWERTOYS__

namespace winrt
{
    using namespace Windows::Foundation;
    using namespace Windows::Graphics;
    using namespace Windows::Graphics::Capture;
    using namespace Windows::Graphics::Imaging;
    using namespace Windows::Storage;
    using namespace Windows::UI::Composition;
    using namespace Windows::Storage::Pickers;
    using namespace Windows::System;
    using namespace Windows::Devices::Enumeration;
    using namespace Windows::Media::Ocr;
}

namespace util
{
    using namespace robmikh::common::uwp;
    using namespace robmikh::common::desktop;
}

// This workaround keeps live zoom enabled after zooming out at level 1 (not zoomed) and disables
// live zoom when recording is stopped
#define WINDOWS_CURSOR_RECORDING_WORKAROUND 1

HINSTANCE		g_hInstance;

COLORREF	g_CustomColors[16];

#define ZOOM_HOTKEY				0
#define DRAW_HOTKEY				1
#define BREAK_HOTKEY			2
#define LIVE_HOTKEY				3
#define LIVE_DRAW_HOTKEY		    4
#define RECORD_HOTKEY		    5
#define RECORD_CROP_HOTKEY	    6
#define RECORD_WINDOW_HOTKEY	    7
#define SNIP_HOTKEY			    8
#define SNIP_SAVE_HOTKEY		    9
#define DEMOTYPE_HOTKEY		    10
#define DEMOTYPE_RESET_HOTKEY    11
#define RECORD_GIF_HOTKEY        12
#define RECORD_GIF_WINDOW_HOTKEY 13
#define SAVE_IMAGE_HOTKEY        14
#define SAVE_CROP_HOTKEY         15
#define COPY_IMAGE_HOTKEY        16
#define COPY_CROP_HOTKEY         17
#define SNIP_OCR_HOTKEY          18
#define SNIP_PANORAMA_HOTKEY     19
#define SNIP_PANORAMA_SAVE_HOTKEY 20

#define ZOOM_PAGE	  0
#define LIVE_PAGE	  1

// Screensaver activation constant
#ifndef SC_SCREENSAVE
#define SC_SCREENSAVE 0xF140
#endif
#define DRAW_PAGE	  2
#define TYPE_PAGE	  3
#define DEMOTYPE_PAGE 4
#define BREAK_PAGE	  5
#define RECORD_PAGE	  6
#define SNIP_PAGE	  7
#define PANORAMA_PAGE 8

OPTION_TABS g_OptionsTabs[] = {
    { _T("Zoom"), NULL },
    { _T("LiveZoom"), NULL },
    { _T("Draw"), NULL },
    { _T("Type"), NULL },
    { _T("DemoType"), NULL },
    { _T("Break"), NULL },
    { _T("Record"), NULL },
    { _T("Snip"), NULL },
    { _T("Panorama"), NULL }
};

static const TCHAR* g_RecordingFormats[] = {
    _T("GIF"),
    _T("MP4")
};

float g_ZoomLevels[] = {
    1.25,
    1.50,
    1.75,
    2.00,
    3.00,
    4.00
};

DWORD g_FramerateOptions[] = {
    15,
    24,
    30,
    60
};

//
// For typing mode
//
typedef enum {
    TypeModeOff = 0,
    TypeModeLeftJustify,
    TypeModeRightJustify
} TypeModeState;

const DWORD CURSOR_ARM_LENGTH = 4;

const float NORMAL_BLUR_RADIUS = 20;
const float STRONG_BLUR_RADIUS = 40;

DWORD	g_ToggleMod;
DWORD	g_LiveZoomToggleMod;
DWORD	g_DrawToggleMod;
DWORD	g_BreakToggleMod;
DWORD	g_DemoTypeToggleMod;
DWORD	g_RecordToggleMod;
DWORD   g_SnipToggleMod;
DWORD   g_SnipPanoramaToggleMod;
DWORD   g_SnipOcrToggleMod;

BOOLEAN	g_ZoomOnLiveZoom = FALSE;
DWORD	g_PenWidth = PEN_WIDTH;
float   g_BlurRadius = NORMAL_BLUR_RADIUS;
HWND	hWndOptions = NULL;
BOOLEAN	g_DrawPointer = FALSE;
BOOLEAN g_PenDown = FALSE;
BOOLEAN g_PenInverted = FALSE;
DWORD	g_OsVersion;
HWND	g_hWndLiveZoom = NULL;
HWND    g_hWndLiveZoomMag = NULL;
HWND	g_hWndMain;
int		g_AlphaBlend = 0x80;
BOOL	g_fullScreenWorkaround = FALSE;
bool	g_bSaveInProgress = false;
bool    g_PanoramaCaptureActive = false;
bool    g_PanoramaStopRequested = false;
bool    g_PanoramaDebugMode = false;
std::wstring	g_TextBuffer;
// This is useful in the context of right-justified text only.
std::list<std::wstring> g_TextBufferPreviousLines;
#if WINDOWS_CURSOR_RECORDING_WORKAROUND
bool	g_LiveZoomLevelOne = false;
#endif

// True if ZoomIt was started by PowerToys instead of standalone.
BOOLEAN g_StartedByPowerToys = FALSE;
BOOLEAN g_running = TRUE;

// Screen recording globals
#define DEFAULT_RECORDING_FILE		L"Recording.mp4"
#define DEFAULT_GIF_RECORDING_FILE	L"Recording.gif"
#define DEFAULT_SCREENSHOT_FILE		L"ZoomIt.png"

BOOL	g_RecordToggle = FALSE;
BOOL	g_RecordCropping = FALSE;
SelectRectangle g_SelectRectangle;
std::wstring	g_RecordingSaveLocation;
std::wstring	g_ScreenshotSaveLocation;
winrt::IDirect3DDevice	g_RecordDevice{ nullptr };
std::shared_ptr<VideoRecordingSession> g_RecordingSession = nullptr;
std::shared_ptr<GifRecordingSession> g_GifRecordingSession = nullptr;
type_pGetMonitorInfo		pGetMonitorInfo;
type_MonitorFromPoint		pMonitorFromPoint;
type_pSHAutoComplete		pSHAutoComplete;
type_pSetLayeredWindowAttributes	pSetLayeredWindowAttributes;
type_pSetProcessDPIAware	pSetProcessDPIAware;
type_pMagSetWindowSource	pMagSetWindowSource;
type_pMagSetWindowTransform pMagSetWindowTransform;
type_pMagSetFullscreenTransform pMagSetFullscreenTransform;
type_pMagSetInputTransform	pMagSetInputTransform;
type_pMagShowSystemCursor	pMagShowSystemCursor;
type_pMagSetWindowFilterList pMagSetWindowFilterList;
type_MagSetFullscreenUseBitmapSmoothing pMagSetFullscreenUseBitmapSmoothing;
type_pMagSetLensUseBitmapSmoothing pMagSetLensUseBitmapSmoothing;
type_pMagInitialize			pMagInitialize;
type_pDwmIsCompositionEnabled	pDwmIsCompositionEnabled;
type_pGetPointerType		pGetPointerType;
type_pGetPointerPenInfo pGetPointerPenInfo;
type_pSystemParametersInfoForDpi pSystemParametersInfoForDpi;
type_pGetDpiForWindow		pGetDpiForWindow;

type_pSHQueryUserNotificationState	pSHQueryUserNotificationState;

type_pCreateDirect3D11DeviceFromDXGIDevice		pCreateDirect3D11DeviceFromDXGIDevice;
type_pCreateDirect3D11SurfaceFromDXGISurface	pCreateDirect3D11SurfaceFromDXGISurface;
type_pD3D11CreateDevice 						pD3D11CreateDevice;

ClassRegistry	reg( _T("Software\\Sysinternals\\") APPNAME );

ComputerGraphicsInit	g_GraphicsInit;


// Event handler to set icon and extended style on dialog creation
class OpenSaveDialogEvents : public IFileDialogEvents
{
public:
    OpenSaveDialogEvents(bool showOnTaskbar = true) : m_refCount(1), m_initialized(false), m_showOnTaskbar(showOnTaskbar) {}

    // IUnknown
    IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv)
    {
        static const QITAB qit[] = {
            QITABENT(OpenSaveDialogEvents, IFileDialogEvents),
            { 0 },
        };
        return QISearch(this, qit, riid, ppv);
    }
    IFACEMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&m_refCount); }
    IFACEMETHODIMP_(ULONG) Release()
    {
        ULONG count = InterlockedDecrement(&m_refCount);
        if (count == 0) delete this;
        return count;
    }

    // IFileDialogEvents
    IFACEMETHODIMP OnFileOk(IFileDialog*) { return S_OK; }
    IFACEMETHODIMP OnFolderChange(IFileDialog* pfd)
    {
        if (!m_initialized)
        {
            m_initialized = true;
            wil::com_ptr<IOleWindow> pWindow;
            if (SUCCEEDED(pfd->QueryInterface(IID_PPV_ARGS(&pWindow))))
            {
                HWND hwndDialog = nullptr;
                if (SUCCEEDED(pWindow->GetWindow(&hwndDialog)) && hwndDialog)
                {
                    if (m_showOnTaskbar)
                    {
                        // Set WS_EX_APPWINDOW extended style
                        LONG_PTR exStyle = GetWindowLongPtr(hwndDialog, GWL_EXSTYLE);
                        SetWindowLongPtr(hwndDialog, GWL_EXSTYLE, exStyle | WS_EX_APPWINDOW);
                    }

                    // Set the dialog icon
                    HICON hIcon = LoadIcon(g_hInstance, L"APPICON");
                    if (hIcon)
                    {
                        SendMessage(hwndDialog, WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(hIcon));
                        SendMessage(hwndDialog, WM_SETICON, ICON_SMALL, reinterpret_cast<LPARAM>(hIcon));
                    }
                }
            }
        }
        return S_OK;
    }
    IFACEMETHODIMP OnFolderChanging(IFileDialog*, IShellItem*) { return S_OK; }
    IFACEMETHODIMP OnSelectionChange(IFileDialog*) { return S_OK; }
    IFACEMETHODIMP OnShareViolation(IFileDialog*, IShellItem*, FDE_SHAREVIOLATION_RESPONSE*) { return S_OK; }
    IFACEMETHODIMP OnTypeChange(IFileDialog*) { return S_OK; }
    IFACEMETHODIMP OnOverwrite(IFileDialog*, IShellItem*, FDE_OVERWRITE_RESPONSE*) { return S_OK; }

private:
    LONG m_refCount;
    bool m_initialized;
    bool m_showOnTaskbar;
};


//----------------------------------------------------------------------------
//
// Saves specified filePath to clipboard.
//
//----------------------------------------------------------------------------
bool SaveToClipboard( const WCHAR* filePath, HWND hwnd )
{
    if( filePath == NULL || hwnd == NULL || wcslen( filePath ) == 0 )
    {
        return false;
    }

    size_t size = sizeof(DROPFILES) + sizeof(WCHAR) * ( _tcslen( filePath ) + 1 ) + sizeof(WCHAR);

    HDROP hDrop   = static_cast<HDROP>(GlobalAlloc( GHND, size ));
    if (hDrop == NULL)
    {
        return false;
    }

    DROPFILES* dFiles = static_cast<DROPFILES*>(GlobalLock( hDrop ));
    if (dFiles == NULL)
    {
        GlobalFree( hDrop );
        return false;
    }

    dFiles->pFiles = sizeof(DROPFILES);
    dFiles->fWide = TRUE;

    wcscpy( reinterpret_cast<WCHAR*>(& dFiles[1]), filePath);
    GlobalUnlock( hDrop );

    if( OpenClipboard( hwnd ) )
    {
        EmptyClipboard();
        SetClipboardData( CF_HDROP, hDrop );
        CloseClipboard();
    }

    GlobalFree( hDrop );

    return true;
}

//----------------------------------------------------------------------
//
// OutputDebug
//
//----------------------------------------------------------------------
void OutputDebug(const TCHAR* format, ...)
{
#if _DEBUG
    TCHAR	msg[1024];
    va_list	va;

#ifdef _MSC_VER
// For some reason, ARM64 Debug builds causes an analyzer error on va_start: "error C26492: Don't use const_cast to cast away const or volatile (type.3)."
#pragma warning(push)
#pragma warning(disable : 26492)
#endif
    va_start(va, format);
#ifdef _MSC_VER
#pragma warning(pop)
#endif
    _vstprintf_s(msg, format, va);
    va_end(va);

    OutputDebugString(msg);
#endif
}

const wchar_t* HotkeyIdToString( WPARAM hotkeyId )
{
    switch( hotkeyId )
    {
    case ZOOM_HOTKEY: return L"ZOOM_HOTKEY";
    case DRAW_HOTKEY: return L"DRAW_HOTKEY";
    case BREAK_HOTKEY: return L"BREAK_HOTKEY";
    case LIVE_HOTKEY: return L"LIVE_HOTKEY";
    case LIVE_DRAW_HOTKEY: return L"LIVE_DRAW_HOTKEY";
    case RECORD_HOTKEY: return L"RECORD_HOTKEY";
    case RECORD_CROP_HOTKEY: return L"RECORD_CROP_HOTKEY";
    case RECORD_WINDOW_HOTKEY: return L"RECORD_WINDOW_HOTKEY";
    case SNIP_HOTKEY: return L"SNIP_HOTKEY";
    case SNIP_SAVE_HOTKEY: return L"SNIP_SAVE_HOTKEY";
    case DEMOTYPE_HOTKEY: return L"DEMOTYPE_HOTKEY";
    case DEMOTYPE_RESET_HOTKEY: return L"DEMOTYPE_RESET_HOTKEY";
    case RECORD_GIF_HOTKEY: return L"RECORD_GIF_HOTKEY";
    case RECORD_GIF_WINDOW_HOTKEY: return L"RECORD_GIF_WINDOW_HOTKEY";
    case SAVE_IMAGE_HOTKEY: return L"SAVE_IMAGE_HOTKEY";
    case SAVE_CROP_HOTKEY: return L"SAVE_CROP_HOTKEY";
    case COPY_IMAGE_HOTKEY: return L"COPY_IMAGE_HOTKEY";
    case COPY_CROP_HOTKEY: return L"COPY_CROP_HOTKEY";
    case SNIP_OCR_HOTKEY: return L"SNIP_OCR_HOTKEY";
    case SNIP_PANORAMA_HOTKEY: return L"SNIP_PANORAMA_HOTKEY";
    case SNIP_PANORAMA_SAVE_HOTKEY: return L"SNIP_PANORAMA_SAVE_HOTKEY";
    default: return L"UNKNOWN_HOTKEY";
    }
}

static void LogHotkeyRegistrationResult( const wchar_t* phase, HWND hWnd, int hotkeyId, UINT modifiers, UINT key, BOOL success )
{
#if _DEBUG
    const DWORD lastError = success ? 0 : GetLastError();
    OutputDebug( L"[Hotkey/%s] hwnd=%p id=%d(%s) mods=0x%X key=0x%X success=%d err=%lu\n",
                 phase,
                 hWnd,
                 hotkeyId,
                 HotkeyIdToString( hotkeyId ),
                 modifiers,
                 key,
                 success ? 1 : 0,
                 lastError );
#else
    UNREFERENCED_PARAMETER( phase );
    UNREFERENCED_PARAMETER( hWnd );
    UNREFERENCED_PARAMETER( hotkeyId );
    UNREFERENCED_PARAMETER( modifiers );
    UNREFERENCED_PARAMETER( key );
    UNREFERENCED_PARAMETER( success );
#endif
}

static void LogPanoramaState( const wchar_t* phase, WPARAM hotkeyId = static_cast<WPARAM>(-1) )
{
#if _DEBUG
    const auto mainWindowStyle = ( g_hWndMain != nullptr ) ? static_cast<unsigned long long>( GetWindowLongPtr( g_hWndMain, GWL_EXSTYLE ) ) : 0ULL;
    OutputDebug( L"[Panorama/State] %s hotkey=%ld(%s) active=%d stop=%d recordCropping=%d selectActive=%d mainExStyle=0x%llX\n",
                 phase,
                 static_cast<long>( hotkeyId ),
                 HotkeyIdToString( hotkeyId ),
                 g_PanoramaCaptureActive ? 1 : 0,
                 g_PanoramaStopRequested ? 1 : 0,
                 g_RecordCropping ? 1 : 0,
                 g_SelectRectangle.IsActive() ? 1 : 0,
                 mainWindowStyle );
#else
    UNREFERENCED_PARAMETER( phase );
    UNREFERENCED_PARAMETER( hotkeyId );
#endif
}

//----------------------------------------------------------------------------
//
// InitializeFonts
//
// Return a bold equivalent of either a DPI aware font face for GUI text or
// just the stock object for DEFAULT_GUI_FONT.
//
//----------------------------------------------------------------------------
void InitializeFonts( HWND hwnd, HFONT *bold )
{
    LOGFONT logFont;
    bool haveLogFont = false;

    if( *bold )
    {
        DeleteObject( *bold );
        *bold = nullptr;
    }

    if( pSystemParametersInfoForDpi && pGetDpiForWindow )
    {
        NONCLIENTMETRICSW metrics{};
        metrics.cbSize = sizeof( metrics );

        if( pSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, sizeof( metrics ), &metrics, 0, pGetDpiForWindow( hwnd ) ) )
        {
            CopyMemory( &logFont, &metrics.lfMessageFont, sizeof( logFont ) );
            haveLogFont = true;
        }
    }

    if( !haveLogFont )
    {
        auto normal = static_cast<HFONT>(GetStockObject( DEFAULT_GUI_FONT ));
        GetObject( normal, sizeof( logFont ), &logFont );
        haveLogFont = true; // for correctness
    }

    logFont.lfWeight = FW_BOLD;
    *bold = CreateFontIndirect( &logFont );
}

//----------------------------------------------------------------------------
//
// EnsureForeground
//
//----------------------------------------------------------------------------
void EnsureForeground()
{
    if( !IsWindowVisible( g_hWndMain ) )
        SetForegroundWindow( g_hWndMain );
}

//----------------------------------------------------------------------------
//
// RestoreForeground
//
//----------------------------------------------------------------------------
void RestoreForeground()
{
    // If the main window is not visible, move foreground to the next window.
    if( !IsWindowVisible( g_hWndMain ) ) {

        // Activate the next window by showing and hiding the main window.
        MoveWindow( g_hWndMain, 0, 0, 0, 0, FALSE );
        ShowWindow( g_hWndMain, SW_SHOWNA );
        ShowWindow( g_hWndMain, SW_HIDE );

		OutputDebug(L"RESTORE FOREGROUND\n");
    }
}

//----------------------------------------------------------------------------
//
// ErrorDialog
//
//----------------------------------------------------------------------------
VOID ErrorDialog( HWND hParent, PCTSTR message, DWORD _Error )
{
    LPTSTR	lpMsgBuf;
    TCHAR	errmsg[1024];

    FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
                    NULL, _Error,
                    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                    reinterpret_cast<LPTSTR>(&lpMsgBuf), 0, NULL );
    _stprintf( errmsg, L"%s: %s", message, lpMsgBuf );
#ifdef __ZOOMIT_POWERTOYS__
    if( g_StartedByPowerToys )
    {
        Logger::error( errmsg );
    }
#endif // __ZOOMIT_POWERTOYS__
    MessageBox( hParent, errmsg, APPNAME, MB_OK|MB_ICONERROR);
}

//----------------------------------------------------------------------------
//
// ErrorDialogString
//
//----------------------------------------------------------------------------
VOID ErrorDialogString( HWND hParent, PCTSTR Message, const wchar_t *_Error )
{
    TCHAR	errmsg[1024];

    _stprintf_s( errmsg, _countof( errmsg ), L"%s: %s", Message, _Error );
    if( hParent == g_hWndMain )
    {
        EnsureForeground();
    }
#ifdef __ZOOMIT_POWERTOYS__
    if( g_StartedByPowerToys )
    {
        Logger::error( errmsg );
    }
#endif // __ZOOMIT_POWERTOYS__
    MessageBox(hParent, errmsg, APPNAME, MB_OK | MB_ICONERROR);
    if( hParent == g_hWndMain )
    {
        RestoreForeground();
    }
}


//--------------------------------------------------------------------
//
// SetAutostartFilePath
//
// Sets the file path for later autostart config.
//
//--------------------------------------------------------------------
void SetAutostartFilePath()
{
    HKEY hZoomit;
    DWORD error;
    TCHAR imageFile[MAX_PATH] = { 0 };

    error = RegCreateKeyEx( HKEY_CURRENT_USER, _T( "Software\\Sysinternals\\Zoomit" ), 0,
        0, 0, KEY_SET_VALUE, NULL, &hZoomit, NULL );
    if( error == ERROR_SUCCESS ) {

        GetModuleFileName( NULL, imageFile + 1, _countof( imageFile ) - 2 );
        imageFile[0] = '"';
        *(_tcschr( imageFile, 0 )) = '"';
        error = RegSetValueEx( hZoomit, L"FilePath", 0, REG_SZ, (BYTE *) imageFile,
            static_cast<DWORD>(_tcslen( imageFile ) + 1)* sizeof( TCHAR ));
        RegCloseKey( hZoomit );
    }
}

//--------------------------------------------------------------------
//
// ConfigureAutostart
//
// Enables or disables Zoomit autostart for the current image file.
//
//--------------------------------------------------------------------
bool ConfigureAutostart( HWND hParent, bool Enable )
{
    HKEY hRunKey, hZoomit;
    DWORD error, length, type;
    TCHAR imageFile[MAX_PATH];

    error = RegOpenKeyEx( HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Run",
        0, KEY_SET_VALUE, &hRunKey );
    if( error == ERROR_SUCCESS ) {

        if( Enable ) {

            error = RegOpenKeyEx( HKEY_CURRENT_USER, _T("Software\\Sysinternals\\Zoomit"), 0,
                        KEY_QUERY_VALUE, &hZoomit );
            if( error == ERROR_SUCCESS ) {

                length = sizeof(imageFile);
#ifdef _WIN64
                // Unconditionally reset filepath in case this was already set by 32 bit version
                SetAutostartFilePath();
#endif
                error = RegQueryValueEx( hZoomit, _T( "Filepath" ), 0, &type, (BYTE *) imageFile, &length );
                RegCloseKey( hZoomit );
                if( error == ERROR_SUCCESS ) {

                    error = RegSetValueEx( hRunKey, APPNAME, 0, REG_SZ, (BYTE *) imageFile,
                        static_cast<DWORD>(_tcslen(imageFile)+1) * sizeof(TCHAR));
                }
            }
        } else {

            error = RegDeleteValue( hRunKey, APPNAME );
            if( error == ERROR_FILE_NOT_FOUND ) error = ERROR_SUCCESS;
        }
        RegCloseKey( hRunKey );
    }
    if( error != ERROR_SUCCESS ) {

        ErrorDialog( hParent, L"Error configuring auto start", error );
    }
    return error == ERROR_SUCCESS;
}


//--------------------------------------------------------------------
//
// IsAutostartConfigured
//
// Is this version of zoomit configured to autostart.
//
//--------------------------------------------------------------------
bool IsAutostartConfigured()
{
    HKEY	hRunKey;
    TCHAR	imageFile[MAX_PATH];
    DWORD	error, imageFileLength, type;

    error = RegOpenKeyEx( HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Run",
        0, KEY_QUERY_VALUE, &hRunKey );
    if( error == ERROR_SUCCESS ) {

        imageFileLength = sizeof(imageFile);
        error = RegQueryValueEx( hRunKey, _T("Zoomit"), 0, &type, (BYTE *) imageFile, &imageFileLength );
        RegCloseKey( hRunKey );
    }
    return error == ERROR_SUCCESS;
}


#ifndef _WIN64

//--------------------------------------------------------------------
//
// RunningOnWin64
//
// Returns true if this is the 32-bit version of the executable
// and we're on 64-bit Windows.
//
//--------------------------------------------------------------------
typedef BOOL (__stdcall *P_IS_WOW64PROCESS)(
            HANDLE hProcess,
            PBOOL Wow64Process
            );
BOOL
RunningOnWin64(
    VOID
    )
{
    P_IS_WOW64PROCESS		pIsWow64Process;
    BOOL				isWow64 = FALSE;

    pIsWow64Process = (P_IS_WOW64PROCESS) GetProcAddress(GetModuleHandle(_T("kernel32.dll")),
                            "IsWow64Process");
    if( pIsWow64Process ) {

        pIsWow64Process( GetCurrentProcess(), &isWow64 );
    }
    return isWow64;
}


//--------------------------------------------------------------------
//
// ExtractImageResource
//
// Extracts the specified file that is located in a resource for
// this executable.
//
//--------------------------------------------------------------------
BOOLEAN ExtractImageResource( PTCHAR ResourceName, PTCHAR TargetFile )
{
    HRSRC		hResource;
    HGLOBAL		hImageResource;
    DWORD		dwImageSize;
    LPVOID		lpvImage;
    FILE		*hFile;

    // Locate the resource
    hResource = FindResource( NULL, ResourceName, _T("BINRES") );
    if( !hResource )
        return FALSE;

    hImageResource	= LoadResource( NULL, hResource );
    dwImageSize		= SizeofResource( NULL, hResource );
    lpvImage		= LockResource( hImageResource );

    // Now copy it out
    _tfopen_s( &hFile, TargetFile, _T("wb") );
    if( hFile == NULL ) return FALSE;

    fwrite( lpvImage, 1, dwImageSize, hFile );
    fclose( hFile );
    return TRUE;
}



//--------------------------------------------------------------------
//
// Run64bitVersion
//
// Returns true if this is the 32-bit version of the executable
// and we're on 64-bit Windows.
//
//--------------------------------------------------------------------
DWORD
Run64bitVersion(
    void
    )
{
    TCHAR		szPath[MAX_PATH];
    TCHAR		originalPath[MAX_PATH];
    TCHAR		tmpPath[MAX_PATH];
    SHELLEXECUTEINFO	info = { 0 };

    if ( GetModuleFileName( NULL, szPath, sizeof(szPath)/sizeof(TCHAR)) == 0 ) {

        return -1;
    }
    _tcscpy_s( originalPath, _countof(originalPath), szPath );

    *_tcsrchr( originalPath, '.') = 0;
    _tcscat_s( originalPath, _countof(szPath), _T("64.exe"));

    //
    // Extract the 64-bit version
    //
    ExpandEnvironmentStrings( L"%TEMP%", tmpPath, sizeof tmpPath / sizeof ( TCHAR));
    _tcscat_s( tmpPath, _countof(tmpPath), _tcsrchr( originalPath, '\\'));
    _tcscpy_s( szPath, _countof(szPath), tmpPath );
    if( !ExtractImageResource( _T("RCZOOMIT64"), szPath )) {

        if( GetFileAttributes( szPath ) == INVALID_FILE_ATTRIBUTES ) {

            ErrorDialog( NULL,_T("Error launching 64-bit version"), GetLastError());
            return -1;
        }
    }

    info.cbSize = sizeof(info);
    info.fMask = SEE_MASK_NOASYNC | SEE_MASK_NOCLOSEPROCESS;
    info.lpFile = szPath;
    info.lpParameters = GetCommandLine();
    info.nShow = SW_SHOWNORMAL;
    if( !ShellExecuteEx( &info ) ) {

        ErrorDialog( NULL,_T("Error launching 64-bit version"), GetLastError());
        DeleteFile( szPath );
        return -1;
    }
    WaitForSingleObject( info.hProcess, INFINITE );

    DWORD result;
    GetExitCodeProcess( info.hProcess, &result );
    CloseHandle( info.hProcess );
    DeleteFile( szPath );
    return result;
}
#endif


//----------------------------------------------------------------------------
//
// IsPresentationMode
//
//----------------------------------------------------------------------------
BOOLEAN IsPresentationMode()
{
    QUERY_USER_NOTIFICATION_STATE pUserState;

    pSHQueryUserNotificationState( &pUserState );
    return pUserState == QUNS_PRESENTATION_MODE;
}

//----------------------------------------------------------------------------
//
// EnableDisableSecondaryDisplay
//
// Creates a second display on the secondary monitor for displaying the
// break timer.
//
//----------------------------------------------------------------------------
LONG EnableDisableSecondaryDisplay( HWND hWnd, BOOLEAN Enable,
                                    PDEVMODE OriginalDevMode )
{
    LONG		result;
    DEVMODE		devMode{};

    if( Enable ) {

        //
        // Prepare the position of Display 2 to be right to the right of Display 1
        //
        devMode.dmSize = sizeof(devMode);
        devMode.dmDriverExtra = 0;
        EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &devMode);
        *OriginalDevMode = devMode;

        //
        // Enable display 2 in the registry
        //
        devMode.dmPosition.x = devMode.dmPelsWidth;
        devMode.dmFields = DM_POSITION |
                            DM_DISPLAYORIENTATION |
                            DM_BITSPERPEL |
                            DM_PELSWIDTH |
                            DM_PELSHEIGHT |
                            DM_DISPLAYFLAGS |
                            DM_DISPLAYFREQUENCY;
        result = ChangeDisplaySettingsEx( L"\\\\.\\DISPLAY2",
                                          &devMode,
                                          NULL,
                                          CDS_NORESET | CDS_UPDATEREGISTRY,
                                          NULL);

    } else {

        OriginalDevMode->dmFields = DM_POSITION |
                            DM_DISPLAYORIENTATION |
                            DM_BITSPERPEL |
                            DM_PELSWIDTH |
                            DM_PELSHEIGHT |
                            DM_DISPLAYFLAGS |
                            DM_DISPLAYFREQUENCY;
        result = ChangeDisplaySettingsEx( L"\\\\.\\DISPLAY2",
                                          OriginalDevMode,
                                          NULL,
                                          CDS_NORESET | CDS_UPDATEREGISTRY,
                                          NULL);
    }

    //
    // Update the hardware
    //
    if( result == DISP_CHANGE_SUCCESSFUL ) {

        if( !ChangeDisplaySettingsEx(NULL, NULL, NULL, 0, NULL)) {

            result = GetLastError();
        }

        //
        // If enabling, move zoomit to the second monitor
        //
        if( Enable && result == DISP_CHANGE_SUCCESSFUL ) {

            SetWindowPos(FindWindowW(L"ZoomitClass", NULL),
                     NULL,
                     devMode.dmPosition.x,
                     0,
                     0,
                     0,
                     SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
            SetCursorPos( devMode.dmPosition.x+1, devMode.dmPosition.y+1 );
        }
    }
    return result;
}

//----------------------------------------------------------------------------
//
// GetLineBounds
//
// Gets the rectangle bounding a line, taking into account pen width
//
//----------------------------------------------------------------------------
Gdiplus::Rect GetLineBounds( POINT p1, POINT p2, int penWidth )
{
    Gdiplus::Rect rect( min(p1.x, p2.x), min(p1.y, p2.y),
                        abs(p1.x - p2.x), abs( p1.y - p2.y));
    rect.Inflate( penWidth, penWidth );
    return rect;
}

//----------------------------------------------------------------------------
//
// InvalidateGdiplusRect
//
// Invalidate portion of window specified by Gdiplus::Rect
//
//----------------------------------------------------------------------------
void InvalidateGdiplusRect(HWND hWnd, Gdiplus::Rect BoundsRect)
{
    RECT lineBoundsGdi;
    lineBoundsGdi.left = BoundsRect.X;
    lineBoundsGdi.top = BoundsRect.Y;
    lineBoundsGdi.right = BoundsRect.X + BoundsRect.Width;
    lineBoundsGdi.bottom = BoundsRect.Y + BoundsRect.Height;
    InvalidateRect(hWnd, &lineBoundsGdi, FALSE);
}



//----------------------------------------------------------------------------
//
// CreateGdiplusBitmap
//
// Creates a gdiplus bitmap of the specified region of the HDC.
//
//----------------------------------------------------------------------------
Gdiplus::Bitmap *CreateGdiplusBitmap( HDC hDc, int x, int y, int Width, int Height )
{
    HBITMAP hBitmap = CreateCompatibleBitmap(hDc, Width, Height);

    // Create a device context for the new bitmap
    HDC hdcNewBitmap = CreateCompatibleDC(hDc);
    SelectObject(hdcNewBitmap, hBitmap);

    // Copy from the oldest undo bitmap to the new bitmap using the lineBounds as the source rectangle
    BitBlt(hdcNewBitmap, 0, 0, Width, Height, hDc, x, y, SRCCOPY);
    Gdiplus::Bitmap *blurBitmap = new Gdiplus::Bitmap(hBitmap, NULL);
    DeleteDC(hdcNewBitmap);
    DeleteObject(hBitmap);
    return blurBitmap;
}


//----------------------------------------------------------------------------
//
// CreateBitmapMemoryDIB
//
// Creates a memory DC and DIB for the specified region of the screen.
//
//----------------------------------------------------------------------------
BYTE* CreateBitmapMemoryDIB(HDC hdcScreenCompat, HDC hBitmapDc, Gdiplus::Rect* lineBounds,
    HDC* hdcMem, HBITMAP* hDIBOrig, HBITMAP* hPreviousBitmap)
{
    // Create a memory DIB for the relevant region of the original bitmap
    BITMAPINFO bmiOrig = { 0 };
    bmiOrig.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bmiOrig.bmiHeader.biWidth = lineBounds->Width;
    bmiOrig.bmiHeader.biHeight = -lineBounds->Height;  // Top-down DIB
    bmiOrig.bmiHeader.biPlanes = 1;
    bmiOrig.bmiHeader.biBitCount = 32;  // 32 bits per pixel
    bmiOrig.bmiHeader.biCompression = BI_RGB;

    VOID* pDIBBitsOrig;
    *hDIBOrig = CreateDIBSection(hdcScreenCompat, &bmiOrig, DIB_RGB_COLORS, &pDIBBitsOrig, NULL, 0);

    if( *hDIBOrig == NULL ) {

        OutputDebug(L"NULL DIB: %d\n", GetLastError());
        OutputDebug(L"lineBounds: %d %d %d %d\n", lineBounds->X, lineBounds->Y, lineBounds->Width, lineBounds->Height);
        return NULL;
    }

    *hdcMem = CreateCompatibleDC(hdcScreenCompat);
    *hPreviousBitmap = static_cast<HBITMAP>(SelectObject(*hdcMem, *hDIBOrig));

    // Copy the relevant part of hdcScreenCompat to the DIB
    BitBlt(*hdcMem, 0, 0, lineBounds->Width, lineBounds->Height, hBitmapDc, lineBounds->X, lineBounds->Y, SRCCOPY);

    // Pointer to the DIB bits
    return static_cast<BYTE*>(pDIBBitsOrig);
}

//----------------------------------------------------------------------------
//
// LockGdiPlusBitmap
//
// Locks the Gdi+ bitmap so that we can access its pixels in memory.
//
//----------------------------------------------------------------------------
#ifdef _MSC_VER
    // Analyzers want us to use a scoped object instead of new. But given all the operations done in Bitmaps it seems better to leave it as a heap object.
    #pragma warning(push)
    #pragma warning(disable : 26402)
#endif

Gdiplus::BitmapData* LockGdiPlusBitmap(Gdiplus::Bitmap* Bitmap)
{
    Gdiplus::BitmapData *lineData = new Gdiplus::BitmapData();
    Bitmap->GetPixelFormat();
    Gdiplus::Rect lineBitmapBounds(0, 0, Bitmap->GetWidth(), Bitmap->GetHeight());
    Bitmap->LockBits(&lineBitmapBounds, Gdiplus::ImageLockModeRead,
        Bitmap->GetPixelFormat(), lineData);
    return lineData;
}
#ifdef _MSC_VER
    #pragma warning(pop)
#endif


//----------------------------------------------------------------------------
//
// BlurScreen
//
// Blur the portion of the screen by copying a blurred bitmap with the
// specified shape.
//
//----------------------------------------------------------------------------
void BlurScreen(HDC hdcScreenCompat, Gdiplus::Rect* lineBounds,
                    Gdiplus::Bitmap *BlurBitmap, BYTE* pPixels)
{
    HDC hdcDIB;
    HBITMAP hDibOrigBitmap, hDibBitmap;
    BYTE* pDestPixels = CreateBitmapMemoryDIB(hdcScreenCompat, hdcScreenCompat, lineBounds,
                                &hdcDIB, &hDibBitmap, &hDibOrigBitmap);

    // Iterate through the pixels
    for (int y = 0; y < lineBounds->Height; ++y) {
        for (int x = 0; x < lineBounds->Width; ++x) {
            int index = (y * lineBounds->Width * 4) + (x * 4);  // Assuming 4 bytes per pixel
            // BYTE b = pPixels[index + 0];  // Blue channel
            // BYTE g = pPixels[index + 1];  // Green channel
            // BYTE r = pPixels[index + 2];  // Red channel
            BYTE a = pPixels[index + 3];  // Alpha channel

            // Check if this is a drawn pixel
            if (a != 0) {
                // get the blur pixel
                Gdiplus::Color pixel;
                BlurBitmap->GetPixel(x, y, &pixel);

                COLORREF newPixel = pixel.GetValue() & 0xFFFFFF;
                pDestPixels[index + 0] = GetRValue(newPixel);
                pDestPixels[index + 1] = GetGValue(newPixel);
                pDestPixels[index + 2] = GetBValue(newPixel);
            }
        }
    }

    // Copy the updated DIB back to hdcScreenCompat
    BitBlt(hdcScreenCompat, lineBounds->X, lineBounds->Y, lineBounds->Width, lineBounds->Height, hdcDIB, 0, 0, SRCCOPY);

    // Clean up
    SelectObject(hdcDIB, hDibOrigBitmap);
    DeleteObject(hDibBitmap);
    DeleteDC(hdcDIB);
}



//----------------------------------------------------------------------------
//
// BitmapBlur
//
// Blurs the bitmap.
//
//----------------------------------------------------------------------------
void BitmapBlur(Gdiplus::Bitmap* hBitmap)
{
    // Git bitmap size
    Gdiplus::Size bitmapSize;
    bitmapSize.Width = hBitmap->GetWidth();
    bitmapSize.Height = hBitmap->GetHeight();

    // Blur the new bitmap
    Gdiplus::Blur blurObject;
    Gdiplus::BlurParams blurParams;
    blurParams.radius = g_BlurRadius;
    blurParams.expandEdge = FALSE;
    blurObject.SetParameters(&blurParams);

    // Apply blur to image
    RECT linesRect;
    linesRect.left = 0;
    linesRect.top = 0;
    linesRect.right = bitmapSize.Width;
    linesRect.bottom = bitmapSize.Height;
    hBitmap->ApplyEffect(&blurObject, &linesRect);
}


//----------------------------------------------------------------------------
//
// DrawBlurredShape
//
// Blur a shaped region of the screen.
//
//----------------------------------------------------------------------------
void DrawBlurredShape( DWORD Shape, Gdiplus::Pen *pen, HDC hdcScreenCompat, Gdiplus::Graphics *dstGraphics,
                    int x1, int y1, int x2, int y2)
{
    // Create a new bitmap that's the size of the area covered by the line + 2 * g_PenWidth
    Gdiplus::Rect lineBounds( min( x1, x2 ), min( y1, y2 ), abs( x2 - x1 ), abs( y2 - y1 ) );

    // Expand for line drawing
    if (Shape == DRAW_LINE)
        lineBounds.Inflate( static_cast<int>(g_PenWidth / 2), static_cast<int>(g_PenWidth / 2) );

    Gdiplus::Bitmap* lineBitmap = new Gdiplus::Bitmap(lineBounds.Width, lineBounds.Height, PixelFormat32bppARGB);
    Gdiplus::Graphics lineGraphics(lineBitmap);
    static const auto blackBrush = Gdiplus::SolidBrush(Gdiplus::Color::Black);
    switch (Shape) {
    case DRAW_RECTANGLE:
        lineGraphics.FillRectangle(&blackBrush, 0, 0, lineBounds.Width, lineBounds.Height);
        break;
    case DRAW_ELLIPSE:
        lineGraphics.FillEllipse(&blackBrush, 0, 0, lineBounds.Width, lineBounds.Height);
        break;
    case DRAW_LINE:
        OutputDebug(L"BLUR_LINE: %d %d\n", lineBounds.Width, lineBounds.Height);
        lineGraphics.DrawLine( pen, x1 - lineBounds.X, y1 - lineBounds.Y, x2 - lineBounds.X, y2 - lineBounds.Y );
        break;
    }

    Gdiplus::BitmapData* lineData = LockGdiPlusBitmap(lineBitmap);
    BYTE* pPixels = static_cast<BYTE*>(lineData->Scan0);

    // Create a GDI bitmap that's the size of the lineBounds rectangle
    Gdiplus::Bitmap* blurBitmap = CreateGdiplusBitmap(hdcScreenCompat,
        lineBounds.X, lineBounds.Y, lineBounds.Width, lineBounds.Height);

    // Blur it
    BitmapBlur(blurBitmap);
    BlurScreen(hdcScreenCompat, &lineBounds, blurBitmap, pPixels);

    // Unlock the bits
    lineBitmap->UnlockBits(lineData);
    delete lineBitmap;
    delete blurBitmap;
}

//----------------------------------------------------------------------------
//
// CreateDrawingBitmap
//
// Create a bitmap to draw on.
//
//----------------------------------------------------------------------------
Gdiplus::Bitmap* CreateDrawingBitmap(Gdiplus::Rect lineBounds )
{
    Gdiplus::Bitmap* lineBitmap = new Gdiplus::Bitmap(lineBounds.Width, lineBounds.Height, PixelFormat32bppARGB);
    Gdiplus::Graphics lineGraphics(lineBitmap);
    return lineBitmap;
}


//----------------------------------------------------------------------------
//
// DrawBitmapLine
//
// Creates a bitmap and draws a line on it.
//
//----------------------------------------------------------------------------
Gdiplus::Bitmap* DrawBitmapLine(Gdiplus::Rect lineBounds, POINT p1, POINT p2, Gdiplus::Pen *pen)
{
    Gdiplus::Bitmap* lineBitmap = new Gdiplus::Bitmap(lineBounds.Width, lineBounds.Height, PixelFormat32bppARGB);
    Gdiplus::Graphics lineGraphics(lineBitmap);

    // Draw the line on the temporary bitmap
    lineGraphics.DrawLine(pen, static_cast<INT>(p1.x - lineBounds.X), static_cast<INT>(p1.y - lineBounds.Y),
        static_cast<INT>(p2.x - lineBounds.X), static_cast<INT>(p2.y - lineBounds.Y));

    return lineBitmap;
}


//----------------------------------------------------------------------------
//
// ColorFromColorRef
//
// Returns a color object from the colorRef that includes the alpha channel
//
//----------------------------------------------------------------------------
Gdiplus::Color ColorFromColorRef(DWORD colorRef) {
    BYTE a = (colorRef >> 24) & 0xFF;  // Extract the alpha channel value
    BYTE b = (colorRef >> 16) & 0xFF;  // Extract the red channel value
    BYTE g = (colorRef >> 8) & 0xFF;   // Extract the green channel value
    BYTE r = colorRef & 0xFF;          // Extract the blue channel value
    OutputDebug( L"ColorFromColorRef: %d %d %d %d\n", a, r, g, b );
    return Gdiplus::Color(a, r, g, b);
}

//----------------------------------------------------------------------------
//
// AdjustHighlighterColor
//
// Lighten the color.
//
//----------------------------------------------------------------------------
void AdjustHighlighterColor(BYTE* red, BYTE* green, BYTE* blue) {

    // Adjust the color to be more visible
    *red = min( 0xFF, *red ? *red + 0x40 : *red + 0x80 );
    *green = min( 0xFF, *green ? *green + 0x40 : *green + 0x80);
    *blue = min( 0xFF, *blue ? *blue + 0x40 : *blue + 0x80);
}

//----------------------------------------------------------------------------
//
// BlendColors
//
// Blends two colors together using the alpha channel of the second color.
// The highlighter is the second color.
//
//----------------------------------------------------------------------------
COLORREF BlendColors(COLORREF color1, const Gdiplus::Color& color2) {

    BYTE redResult, greenResult, blueResult;

    // Extract the channels from the COLORREF
    BYTE red1 = GetRValue(color1);
    BYTE green1 = GetGValue(color1);
    BYTE blue1 = GetBValue(color1);

    // Get the channels and alpha from the Gdiplus::Color
    BYTE blue2 = color2.GetRed();
    BYTE green2 = color2.GetGreen();
    BYTE red2 = color2.GetBlue();
    float alpha2 = color2.GetAlpha() / 255.0f;  // Normalize to [0, 1]
    //alpha2 /= 2; // Use half the alpha for higher contrast

    // Don't blend grey's as much
    // int minValue = min(red1, min(green1, blue1));
    // int maxValue = max(red1, max(green1, blue1));
    if(TRUE) { // red1 > 0x10 && red1 < 0xC0 && (maxValue - minValue < 0x40)) {

        // This does a standard bright highlight
        alpha2 = 0;
        AdjustHighlighterColor( &red2, &green2, &blue2 );
        redResult	= red2 & red1;
        greenResult = green2 & green1;
        blueResult	= blue2 & blue1;
    }
    else {

        // Blend each channel
        redResult = static_cast<BYTE>(red2 * alpha2 + red1 * (1 - alpha2));
        greenResult = static_cast<BYTE>(green2 * alpha2 + green1 * (1 - alpha2));
        blueResult = static_cast<BYTE>(blue2 * alpha2 + blue1 * (1 - alpha2));
    }
    // Combine the result channels back into a COLORREF
    return RGB(redResult, greenResult, blueResult);
}



//----------------------------------------------------------------------------
//
// DrawHighlightedShape
//
// Draws the shape with the highlighter color.
//
//----------------------------------------------------------------------------
void DrawHighlightedShape( DWORD Shape, HDC hdcScreenCompat, Gdiplus::Brush *pBrush,
                        Gdiplus::Pen *pPen, int x1, int y1, int x2, int y2)
{
    // Create a new bitmap that's the size of the area covered by the line + 2 * g_PenWidth
    Gdiplus::Rect lineBounds(min(x1, x2), min(y1, y2), abs(x2 - x1), abs(y2 - y1));

    OutputDebug(L"DrawHighlightedShape\n");

    // Expand for line drawing
    if (Shape == DRAW_LINE)
        lineBounds.Inflate(static_cast<int>(g_PenWidth / 2), static_cast<int>(g_PenWidth / 2));

    Gdiplus::Bitmap* lineBitmap = CreateDrawingBitmap(lineBounds);
    Gdiplus::Graphics lineGraphics(lineBitmap);
    switch (Shape) {
    case DRAW_RECTANGLE:
        lineGraphics.FillRectangle(pBrush, 0, 0, lineBounds.Width, lineBounds.Height);
        break;
    case DRAW_ELLIPSE:
        lineGraphics.FillEllipse( pBrush, 0, 0, lineBounds.Width, lineBounds.Height);
        break;
    case DRAW_LINE:
        lineGraphics.DrawLine(pPen, x1 - lineBounds.X, y1 - lineBounds.Y, x2 - lineBounds.X, y2 - lineBounds.Y);
        break;
    }

    Gdiplus::BitmapData* lineData = LockGdiPlusBitmap(lineBitmap);
    BYTE* pPixels = static_cast<BYTE*>(lineData->Scan0);

    // Create a DIB section for efficient pixel manipulation
    BITMAPINFO bmi = { 0 };
    bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bmi.bmiHeader.biWidth = lineBounds.Width;
    bmi.bmiHeader.biHeight = -lineBounds.Height;  // Top-down DIB
    bmi.bmiHeader.biPlanes = 1;
    bmi.bmiHeader.biBitCount = 32;  // 32 bits per pixel
    bmi.bmiHeader.biCompression = BI_RGB;

    VOID* pDIBBits;
    HBITMAP hDIB = CreateDIBSection(hdcScreenCompat, &bmi, DIB_RGB_COLORS, &pDIBBits, NULL, 0);

    HDC hdcDIB = CreateCompatibleDC(hdcScreenCompat);
    SelectObject(hdcDIB, hDIB);

    // Copy the relevant part of hdcScreenCompat to the DIB
    BitBlt(hdcDIB, 0, 0, lineBounds.Width, lineBounds.Height, hdcScreenCompat, lineBounds.X, lineBounds.Y, SRCCOPY);

    // Pointer to the DIB bits
    BYTE* pDestPixels = static_cast<BYTE*>(pDIBBits);

    // Pointer to screen bits
    HDC hdcDIBOrig;
    HBITMAP hDibOrigBitmap, hDibBitmap;
    BYTE* pDestPixels2 = CreateBitmapMemoryDIB(hdcScreenCompat, hdcScreenCompat, &lineBounds,
        &hdcDIBOrig, &hDibBitmap, &hDibOrigBitmap);

    for (int y = 0; y < lineBounds.Height; ++y) {
        for (int x = 0; x < lineBounds.Width; ++x) {
            int index = (y * lineBounds.Width * 4) + (x * 4);  // Assuming 4 bytes per pixel
            // BYTE b = pPixels[index + 0];  // Blue channel
            // BYTE g = pPixels[index + 1];  // Green channel
            // BYTE r = pPixels[index + 2];  // Red channel
            BYTE a = pPixels[index + 3];  // Alpha channel

            // Check if this is a drawn pixel
            if (a != 0) {
                // Assuming pDestPixels is a valid pointer to the destination bitmap's pixel data
                BYTE destB = pDestPixels2[index + 0];  // Blue channel
                BYTE destG = pDestPixels2[index + 1];  // Green channel
                BYTE destR = pDestPixels2[index + 2];  // Red channel

                // Create a COLORREF value from the destination pixel data
                COLORREF currentPixel = RGB(destR, destG, destB);
                // Blend the colors
                COLORREF newPixel = BlendColors(currentPixel, g_PenColor);
                // Update the destination pixel data with the new color
                pDestPixels[index + 0] = GetBValue(newPixel);
                pDestPixels[index + 1] = GetGValue(newPixel);
                pDestPixels[index + 2] = GetRValue(newPixel);
            }
        }
    }

    // Copy the updated DIB back to hdcScreenCompat
    BitBlt(hdcScreenCompat, lineBounds.X, lineBounds.Y, lineBounds.Width, lineBounds.Height, hdcDIB, 0, 0, SRCCOPY);

    // Clean up
    DeleteObject(hDIB);
    DeleteDC(hdcDIB);

    SelectObject(hdcDIBOrig, hDibOrigBitmap);
    DeleteObject(hDibBitmap);
    DeleteDC(hdcDIBOrig);

    // Invalidate the updated rectangle
    //InvalidateGdiplusRect(hWnd, lineBounds);
}

//----------------------------------------------------------------------------
//
// CreateFadedDesktopBackground
//
// Creates a snapshot of the desktop that's faded and alpha blended with
// black.
//
//----------------------------------------------------------------------------
HBITMAP CreateFadedDesktopBackground( HDC hdc, LPRECT rcScreen, LPRECT rcCrop )
{
    // create bitmap
    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 it
    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);
    ReleaseDC( NULL, hdcScreen );

    return hBitmap;
}

//----------------------------------------------------------------------------
//
// AdjustToMoveBoundary
//
// Shifts to accomodate move boundary.
//
//----------------------------------------------------------------------------
void AdjustToMoveBoundary( float zoomLevel, int *coordinate, int cursor, int size, int max )
{
    int diff = static_cast<int> (static_cast<float>(size)/ static_cast<float>(LIVEZOOM_MOVE_REGIONS));
    if( cursor - *coordinate < diff )
        *coordinate = max( 0, cursor - diff );
    else if( (*coordinate + size) - cursor < diff )
        *coordinate = min( cursor + diff - size, max - size );
}

//----------------------------------------------------------------------------
//
// GetZoomedTopLeftCoordinates
//
// Gets the left top coordinate of the zoomed area of the screen
//
//----------------------------------------------------------------------------
void GetZoomedTopLeftCoordinates( float zoomLevel, POINT *cursorPos, int *x, int width, int *y, int height )
{
    // smoother and more natural zoom in
    float scaledWidth = width/zoomLevel;
    float scaledHeight = height/zoomLevel;
    *x = max( 0, min( (int) (width - scaledWidth), (int) (cursorPos->x - (int) (((float) cursorPos->x/ (float) width)*scaledWidth))));
    AdjustToMoveBoundary( zoomLevel, x, cursorPos->x, static_cast<int>(scaledWidth), width );
    *y = max( 0, min( (int) (height - scaledHeight), (int) (cursorPos->y - (int) (((float) cursorPos->y/ (float) height)*scaledHeight))));
    AdjustToMoveBoundary( zoomLevel, y, cursorPos->y, static_cast<int>(scaledHeight), height );
}


//----------------------------------------------------------------------------
//
// ScaleImage
//
// Use gdi+ for anti-aliased bitmap stretching.
//
//----------------------------------------------------------------------------
void ScaleImage( HDC hdcDst, float xDst, float yDst, float wDst, float hDst,
                 HBITMAP bmSrc, float xSrc, float ySrc, float wSrc, float hSrc )
{
    Gdiplus::Graphics	dstGraphics( hdcDst );
    {
        Gdiplus::Bitmap		srcBitmap( bmSrc, NULL );

        // Use high quality interpolation when smooth image is enabled
        if (g_SmoothImage) {
            dstGraphics.SetInterpolationMode( Gdiplus::InterpolationModeHighQuality );
        } else {
            dstGraphics.SetInterpolationMode( Gdiplus::InterpolationModeLowQuality );
        }
        dstGraphics.SetPixelOffsetMode( Gdiplus::PixelOffsetModeHalf );

        dstGraphics.DrawImage( &srcBitmap, Gdiplus::RectF(xDst,yDst,wDst,hDst), xSrc, ySrc, wSrc, hSrc, Gdiplus::UnitPixel );
    }
}


//----------------------------------------------------------------------------
//
// GetEncoderClsid
//
//----------------------------------------------------------------------------
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
   UINT  num = 0;          // number of image encoders
   UINT  size = 0;         // size of the image encoder array in bytes
using namespace Gdiplus;

   ImageCodecInfo* pImageCodecInfo = NULL;

   GetImageEncodersSize(&num, &size);
   if(size == 0)
      return -1;  // Failure

   pImageCodecInfo = static_cast<ImageCodecInfo*>(malloc(size));
   if(pImageCodecInfo == NULL)
      return -1;  // Failure

   GetImageEncoders(num, size, pImageCodecInfo);

   for(UINT j = 0; j < num; ++j)
   {
      if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
      {
         *pClsid = pImageCodecInfo[j].Clsid;
         free(pImageCodecInfo);
         return j;  // Success
      }
   }

   free(pImageCodecInfo);
   return -1;  // Failure
}

//----------------------------------------------------------------------
//
// ConvertToUnicode
//
//----------------------------------------------------------------------
void
ConvertToUnicode(
    PCHAR aString,
    PWCHAR  wString,
    DWORD wStringLength
    )
{
    size_t	len;

    len = MultiByteToWideChar( CP_ACP, 0, aString, static_cast<int>(strlen(aString)),
                wString, wStringLength );
    wString[len] = 0;
}


//----------------------------------------------------------------------------
//
// LoadImageFile
//
// Use gdi+ to load an image.
//
//----------------------------------------------------------------------------
HBITMAP LoadImageFile( PTCHAR Filename )
{
    HBITMAP		hBmp;

    Gdiplus::Bitmap		*bitmap;

    bitmap = Gdiplus::Bitmap::FromFile(Filename);
    if( bitmap->GetHBITMAP( NULL, &hBmp )) {

        return NULL;
    }
    delete bitmap;
    return hBmp;
}


//----------------------------------------------------------------------------
//
// SavePng
//
// Use gdi+ to save a PNG.
//
//----------------------------------------------------------------------------
DWORD SavePng( LPCTSTR Filename, HBITMAP hBitmap )
{
    Gdiplus::Bitmap		bitmap( hBitmap, NULL );
    CLSID pngClsid;
    GetEncoderClsid(L"image/png", &pngClsid);
    if( bitmap.Save( Filename, &pngClsid, NULL )) {

        return GetLastError();
    }
    return ERROR_SUCCESS;
}


//----------------------------------------------------------------------------
//
// OcrFromHBITMAP
//
// Perform OCR on an HBITMAP and return the recognized text.
// Uses WIC to convert HBITMAP to IWICBitmap, then ISoftwareBitmapNativeFactory
// for zero-copy conversion to SoftwareBitmap, then Windows.Media.Ocr.
//
//----------------------------------------------------------------------------
std::wstring OcrFromHBITMAP( HBITMAP hBitmap )
{
    try
    {
        // The main thread is STA (CoInitialize), but RecognizeAsync().get()
        // asserts if called on an STA thread. Run the entire OCR pipeline
        // on a background MTA thread.
        auto future = std::async( std::launch::async, [hBitmap]() -> std::wstring
        {
            // Initialize COM as MTA on this worker thread
            CoInitializeEx( nullptr, COINIT_MULTITHREADED );
            auto comCleanup = wil::scope_exit( [] { CoUninitialize(); } );

            // Create WIC factory
            wil::com_ptr<IWICImagingFactory> wicFactory;
            THROW_IF_FAILED( CoCreateInstance(
                CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER,
                IID_PPV_ARGS( wicFactory.put() ) ) );

            // Create IWICBitmap from HBITMAP
            wil::com_ptr<IWICBitmap> wicBitmap;
            THROW_IF_FAILED( wicFactory->CreateBitmapFromHBITMAP(
                hBitmap, nullptr, WICBitmapIgnoreAlpha, wicBitmap.put() ) );

            // Convert to SoftwareBitmap via ISoftwareBitmapNativeFactory (zero-copy)
            auto softwareBitmap = winrt::capture<winrt::SoftwareBitmap>(
                winrt::create_instance<ISoftwareBitmapNativeFactory>( CLSID_SoftwareBitmapNativeFactory ),
                &ISoftwareBitmapNativeFactory::CreateFromWICBitmap,
                wicBitmap.get(),
                false );

            // OcrEngine requires Gray8 or Bgra8 pixel format
            if( softwareBitmap.BitmapPixelFormat() != winrt::BitmapPixelFormat::Bgra8 ||
                softwareBitmap.BitmapAlphaMode() != winrt::BitmapAlphaMode::Premultiplied )
            {
                softwareBitmap = winrt::SoftwareBitmap::Convert(
                    softwareBitmap,
                    winrt::BitmapPixelFormat::Bgra8,
                    winrt::BitmapAlphaMode::Premultiplied );
            }

            // Scale down if image exceeds OCR engine's max dimension (2600px)
            const uint32_t maxDim = winrt::OcrEngine::MaxImageDimension();
            if( softwareBitmap.PixelWidth() > static_cast<int32_t>( maxDim ) ||
                softwareBitmap.PixelHeight() > static_cast<int32_t>( maxDim ) )
            {
                // Calculate scale factor to fit within max dimensions
                float scale = min(
                    static_cast<float>( maxDim ) / softwareBitmap.PixelWidth(),
                    static_cast<float>( maxDim ) / softwareBitmap.PixelHeight() );

                int32_t newWidth = static_cast<int32_t>( softwareBitmap.PixelWidth() * scale );
                int32_t newHeight = static_cast<int32_t>( softwareBitmap.PixelHeight() * scale );

                // Use WIC scaler for high-quality downscale
                wil::com_ptr<IWICBitmapScaler> scaler;
                THROW_IF_FAILED( wicFactory->CreateBitmapScaler( scaler.put() ) );
                THROW_IF_FAILED( scaler->Initialize(
                    wicBitmap.get(), newWidth, newHeight, WICBitmapInterpolationModeHighQualityCubic ) );

                // Create new WICBitmap from scaled result
                wil::com_ptr<IWICBitmap> scaledBitmap;
                THROW_IF_FAILED( wicFactory->CreateBitmapFromSource(
                    scaler.get(), WICBitmapCacheOnDemand, scaledBitmap.put() ) );

                softwareBitmap = winrt::capture<winrt::SoftwareBitmap>(
                    winrt::create_instance<ISoftwareBitmapNativeFactory>( CLSID_SoftwareBitmapNativeFactory ),
                    &ISoftwareBitmapNativeFactory::CreateFromWICBitmap,
                    scaledBitmap.get(),
                    false );

                if( softwareBitmap.BitmapPixelFormat() != winrt::BitmapPixelFormat::Bgra8 ||
                    softwareBitmap.BitmapAlphaMode() != winrt::BitmapAlphaMode::Premultiplied )
                {
                    softwareBitmap = winrt::SoftwareBitmap::Convert(
                        softwareBitmap,
                        winrt::BitmapPixelFormat::Bgra8,
                        winrt::BitmapAlphaMode::Premultiplied );
                }
            }

            // Create OCR engine from user profile languages
            winrt::OcrEngine engine = winrt::OcrEngine::TryCreateFromUserProfileLanguages();
            if( !engine )
            {
                return std::wstring{};
            }

            // Run OCR — safe to call .get() on an MTA thread
            winrt::OcrResult result = engine.RecognizeAsync( softwareBitmap ).get();
            return std::wstring( result.Text() );
        } );

        return future.get();
    }
    catch( ... )
    {
        OutputDebug( L"OcrFromHBITMAP failed\n" );
        return {};
    }
}


//----------------------------------------------------------------------------
//
// EnableDisableTrayIcon
//
//----------------------------------------------------------------------------
void EnableDisableTrayIcon( HWND hWnd, BOOLEAN Enable )
{
    NOTIFYICONDATA tNotifyIconData;

    memset( &tNotifyIconData, 0, sizeof(tNotifyIconData));
    tNotifyIconData.cbSize = sizeof(NOTIFYICONDATA);
    tNotifyIconData.hWnd = hWnd;
    tNotifyIconData.uID = 1;
    tNotifyIconData.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
    tNotifyIconData.uCallbackMessage = WM_USER_TRAY_ACTIVATE;
    tNotifyIconData.hIcon = LoadIcon( g_hInstance, L"APPICON" );
    lstrcpyn(tNotifyIconData.szTip, APPNAME, sizeof(APPNAME));
    Shell_NotifyIcon(Enable ? NIM_ADD : NIM_DELETE, &tNotifyIconData);
}

//----------------------------------------------------------------------------
//
// EnableDisableOpacity
//
//----------------------------------------------------------------------------
void EnableDisableOpacity( HWND hWnd, BOOLEAN Enable )
{
    DWORD	exStyle;

    if( pSetLayeredWindowAttributes && g_BreakOpacity != 100 ) {

        if( Enable ) {

            exStyle = GetWindowLong(hWnd, GWL_EXSTYLE);
            SetWindowLong(hWnd, GWL_EXSTYLE, (exStyle | WS_EX_LAYERED));

            pSetLayeredWindowAttributes(hWnd, 0, static_cast<BYTE> ((255 * g_BreakOpacity) / 100), LWA_ALPHA);
            RedrawWindow(hWnd, 0, 0, RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN);

        } else {

            exStyle = GetWindowLong(hWnd, GWL_EXSTYLE);
            SetWindowLong(hWnd, GWL_EXSTYLE, (exStyle & ~WS_EX_LAYERED));
        }
    }
}

//----------------------------------------------------------------------------
//
// EnableDisableScreenSaver
//
//----------------------------------------------------------------------------
void EnableDisableScreenSaver( BOOLEAN Enable )
{
    SystemParametersInfo(SPI_SETSCREENSAVEACTIVE,Enable,0,0);
    SystemParametersInfo(SPI_SETPOWEROFFACTIVE,Enable,0,0);
    SystemParametersInfo(SPI_SETLOWPOWERACTIVE,Enable,0,0);
}


//----------------------------------------------------------------------------
//
// SaveBitmapToFile
//
// Save an HBITMAP to a BMP file using GDI+.
//
//----------------------------------------------------------------------------
static BOOLEAN SaveBitmapToFile( HBITMAP hBitmap, PTCHAR filePath )
{
    Gdiplus::Bitmap bitmap( hBitmap, NULL );
    if( bitmap.GetLastStatus() != Gdiplus::Ok )
        return FALSE;

    // Find the BMP encoder CLSID.
    UINT num = 0, size = 0;
    Gdiplus::GetImageEncodersSize( &num, &size );
    if( size == 0 ) return FALSE;

    Gdiplus::ImageCodecInfo* pInfo = static_cast<Gdiplus::ImageCodecInfo*>( malloc( size ) );
    if( !pInfo ) return FALSE;

    Gdiplus::GetImageEncoders( num, size, pInfo );
    CLSID clsid = {};
    BOOLEAN found = FALSE;
    for( UINT i = 0; i < num; i++ )
    {
        if( wcscmp( pInfo[i].MimeType, L"image/bmp" ) == 0 )
        {
            clsid = pInfo[i].Clsid;
            found = TRUE;
            break;
        }
    }
    free( pInfo );
    if( !found ) return FALSE;

    return bitmap.Save( filePath, &clsid, NULL ) == Gdiplus::Ok;
}


//----------------------------------------------------------------------------
//
// ExtractResourceToFile
//
// Extracts a BINRES resource to a file on disk.
//
//----------------------------------------------------------------------------
static BOOLEAN ExtractResourceToFile( LPCTSTR resName, LPCTSTR resType, LPCTSTR destPath )
{
    HRSRC hRes = FindResource( NULL, resName, resType );
    if( !hRes ) return FALSE;

    HGLOBAL hData = LoadResource( NULL, hRes );
    if( !hData ) return FALSE;

    LPVOID pData = LockResource( hData );
    DWORD  size  = SizeofResource( NULL, hRes );
    if( !pData || size == 0 ) return FALSE;

    HANDLE hFile = CreateFile( destPath, GENERIC_WRITE, 0, NULL,
                               CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
    if( hFile == INVALID_HANDLE_VALUE ) return FALSE;

    DWORD written;
    BOOL ok = WriteFile( hFile, pData, size, &written, NULL );
    CloseHandle( hFile );

    return ok && written == size;
}


//----------------------------------------------------------------------------
//
// Screensaver settings save / restore for crash recovery.
//
// Before swapping in our .scr, save the user's current screensaver
// settings.  On startup and WM_ENDSESSION, check for orphaned settings
// and restore them.
//
//----------------------------------------------------------------------------
#define SCREENSAVER_BACKUP_KEY  L"Software\\Sysinternals\\ZoomIt\\SavedScreenSaver"

struct ScreenSaverSnapshot
{
    TCHAR scrPath[MAX_PATH] = {};
    TCHAR screenSaveActive[8] = {};
    TCHAR secure[8] = {};
    TCHAR timeoutText[16] = {};
    DWORD timeoutSeconds = 0;
    BOOLEAN hasScrPath = FALSE;
    BOOLEAN hasScreenSaveActive = FALSE;
    BOOLEAN hasSecure = FALSE;
    BOOLEAN hasTimeoutText = FALSE;
    BOOLEAN screenSaveActiveRuntime = TRUE;
    BOOLEAN valid = FALSE;
};

// Per-run snapshot captured immediately before we swap in ZoomIt's .scr.
// This avoids relying only on registry backup timing for unlock restore.
static ScreenSaverSnapshot g_PreBreakScreenSaverSnapshot;

static BOOLEAN CaptureCurrentScreenSaverSettings( ScreenSaverSnapshot* snapshot )
{
    if( snapshot == nullptr )
        return FALSE;

    ScreenSaverSnapshot localSnapshot;

    DWORD cbPath = sizeof( localSnapshot.scrPath );
    localSnapshot.hasScrPath =
        ( RegGetValue( HKEY_CURRENT_USER, L"Control Panel\\Desktop", L"SCRNSAVE.EXE",
                       RRF_RT_REG_SZ, NULL, localSnapshot.scrPath, &cbPath ) == ERROR_SUCCESS );

    DWORD cbScreenSaveActive = sizeof( localSnapshot.screenSaveActive );
    localSnapshot.hasScreenSaveActive =
        ( RegGetValue( HKEY_CURRENT_USER, L"Control Panel\\Desktop", L"ScreenSaveActive",
                       RRF_RT_REG_SZ, NULL, localSnapshot.screenSaveActive, &cbScreenSaveActive ) == ERROR_SUCCESS );

    DWORD cbSecure = sizeof( localSnapshot.secure );
    localSnapshot.hasSecure =
        ( RegGetValue( HKEY_CURRENT_USER, L"Control Panel\\Desktop", L"ScreenSaverIsSecure",
                       RRF_RT_REG_SZ, NULL, localSnapshot.secure, &cbSecure ) == ERROR_SUCCESS );

    DWORD cbTimeoutText = sizeof( localSnapshot.timeoutText );
    localSnapshot.hasTimeoutText =
        ( RegGetValue( HKEY_CURRENT_USER, L"Control Panel\\Desktop", L"ScreenSaveTimeOut",
                       RRF_RT_REG_SZ, NULL, localSnapshot.timeoutText, &cbTimeoutText ) == ERROR_SUCCESS );

    UINT timeout = 0;
    SystemParametersInfo( SPI_GETSCREENSAVETIMEOUT, 0, &timeout, 0 );
    localSnapshot.timeoutSeconds = timeout;

    BOOL screenSaveActive = TRUE;
    SystemParametersInfo( SPI_GETSCREENSAVEACTIVE, 0, &screenSaveActive, 0 );
    localSnapshot.screenSaveActiveRuntime = screenSaveActive ? TRUE : FALSE;

    if( localSnapshot.hasTimeoutText )
    {
        const int parsed = _tstoi( localSnapshot.timeoutText );
        if( parsed > 0 )
        {
            localSnapshot.timeoutSeconds = static_cast<DWORD>( parsed );
        }
    }

    localSnapshot.valid = TRUE;

    *snapshot = localSnapshot;
    return TRUE;
}

static void ApplyScreenSaverSnapshot( const ScreenSaverSnapshot* snapshot )
{
    if( snapshot == nullptr || !snapshot->valid )
        return;

    if( snapshot->hasScrPath )
    {
        RegSetKeyValue( HKEY_CURRENT_USER, L"Control Panel\\Desktop",
                        L"SCRNSAVE.EXE", REG_SZ, snapshot->scrPath,
                        static_cast<DWORD>( ( _tcslen( snapshot->scrPath ) + 1 ) * sizeof( TCHAR ) ) );
    }
    else
    {
        RegDeleteKeyValue( HKEY_CURRENT_USER, L"Control Panel\\Desktop", L"SCRNSAVE.EXE" );
    }

    if( snapshot->hasScreenSaveActive )
    {
        RegSetKeyValue( HKEY_CURRENT_USER, L"Control Panel\\Desktop",
                        L"ScreenSaveActive", REG_SZ, snapshot->screenSaveActive,
                        static_cast<DWORD>( ( _tcslen( snapshot->screenSaveActive ) + 1 ) * sizeof( TCHAR ) ) );
    }
    else
    {
        RegDeleteKeyValue( HKEY_CURRENT_USER, L"Control Panel\\Desktop", L"ScreenSaveActive" );
    }
    SystemParametersInfo( SPI_SETSCREENSAVEACTIVE, snapshot->screenSaveActiveRuntime ? TRUE : FALSE, 0, SPIF_SENDCHANGE );

    if( snapshot->hasSecure )
    {
        RegSetKeyValue( HKEY_CURRENT_USER, L"Control Panel\\Desktop",
                        L"ScreenSaverIsSecure", REG_SZ, snapshot->secure,
                        static_cast<DWORD>( ( _tcslen( snapshot->secure ) + 1 ) * sizeof( TCHAR ) ) );
    }
    else
    {
        RegDeleteKeyValue( HKEY_CURRENT_USER, L"Control Panel\\Desktop", L"ScreenSaverIsSecure" );
    }

    if( snapshot->hasTimeoutText )
    {
        RegSetKeyValue( HKEY_CURRENT_USER, L"Control Panel\\Desktop",
                        L"ScreenSaveTimeOut", REG_SZ, snapshot->timeoutText,
                        static_cast<DWORD>( ( _tcslen( snapshot->timeoutText ) + 1 ) * sizeof( TCHAR ) ) );
    }
    else
    {
        RegDeleteKeyValue( HKEY_CURRENT_USER, L"Control Panel\\Desktop", L"ScreenSaveTimeOut" );
    }

    SystemParametersInfo( SPI_SETSCREENSAVETIMEOUT, snapshot->timeoutSeconds, 0, SPIF_SENDCHANGE );
}

static void SaveScreenSaverSettings( void )
{
    CaptureCurrentScreenSaverSettings( &g_PreBreakScreenSaverSnapshot );

    HKEY hKey;
    if( RegCreateKeyEx( HKEY_CURRENT_USER, SCREENSAVER_BACKUP_KEY, 0, NULL,
                        0, KEY_SET_VALUE, NULL, &hKey, NULL ) == ERROR_SUCCESS )
    {
        const ScreenSaverSnapshot& snap = g_PreBreakScreenSaverSnapshot;

        DWORD has = snap.hasScrPath;
        RegSetValueEx( hKey, L"HasSCRNSAVE.EXE", 0, REG_DWORD,
                       reinterpret_cast<const BYTE*>( &has ), sizeof( DWORD ) );
        RegSetValueEx( hKey, L"SCRNSAVE.EXE", 0, REG_SZ,
                       reinterpret_cast<const BYTE*>( snap.scrPath ),
                       static_cast<DWORD>( ( _tcslen( snap.scrPath ) + 1 ) * sizeof( TCHAR ) ) );

        has = snap.hasScreenSaveActive;
        RegSetValueEx( hKey, L"HasScreenSaveActive", 0, REG_DWORD,
                       reinterpret_cast<const BYTE*>( &has ), sizeof( DWORD ) );
        RegSetValueEx( hKey, L"ScreenSaveActive", 0, REG_SZ,
                       reinterpret_cast<const BYTE*>( snap.screenSaveActive ),
                       static_cast<DWORD>( ( _tcslen( snap.screenSaveActive ) + 1 ) * sizeof( TCHAR ) ) );

        has = snap.hasSecure;
        RegSetValueEx( hKey, L"HasScreenSaverIsSecure", 0, REG_DWORD,
                       reinterpret_cast<const BYTE*>( &has ), sizeof( DWORD ) );
        RegSetValueEx( hKey, L"ScreenSaverIsSecure", 0, REG_SZ,
                       reinterpret_cast<const BYTE*>( snap.secure ),
                       static_cast<DWORD>( ( _tcslen( snap.secure ) + 1 ) * sizeof( TCHAR ) ) );

        has = snap.hasTimeoutText;
        RegSetValueEx( hKey, L"HasScreenSaveTimeOut", 0, REG_DWORD,
                       reinterpret_cast<const BYTE*>( &has ), sizeof( DWORD ) );
        RegSetValueEx( hKey, L"ScreenSaveTimeOutText", 0, REG_SZ,
                       reinterpret_cast<const BYTE*>( snap.timeoutText ),
                       static_cast<DWORD>( ( _tcslen( snap.timeoutText ) + 1 ) * sizeof( TCHAR ) ) );

        RegSetValueEx( hKey, L"ScreenSaveTimeOut", 0, REG_DWORD,
                       reinterpret_cast<const BYTE*>( &snap.timeoutSeconds ), sizeof( DWORD ) );

        DWORD runtimeActive = snap.screenSaveActiveRuntime;
        RegSetValueEx( hKey, L"ScreenSaveActiveRuntime", 0, REG_DWORD,
                       reinterpret_cast<const BYTE*>( &runtimeActive ), sizeof( DWORD ) );

        RegCloseKey( hKey );
    }
}

static void RestoreScreenSaverSettings( void )
{
    bool restored = false;

    // Preferred restore path: exact values captured in-memory before this run
    // applied ZoomIt's screensaver settings.
    if( g_PreBreakScreenSaverSnapshot.valid )
    {
        ApplyScreenSaverSnapshot( &g_PreBreakScreenSaverSnapshot );
        g_PreBreakScreenSaverSnapshot.valid = FALSE;
        restored = true;
        RegDeleteKey( HKEY_CURRENT_USER, SCREENSAVER_BACKUP_KEY );
    }
    else
    {
        // Crash-recovery fallback path.
        HKEY hKey;
        if( RegOpenKeyEx( HKEY_CURRENT_USER, SCREENSAVER_BACKUP_KEY, 0,
                          KEY_QUERY_VALUE, &hKey ) == ERROR_SUCCESS )
        {
            ScreenSaverSnapshot backupSnapshot;

            DWORD has = 1;
            DWORD cbHas = sizeof( has );
            if( RegQueryValueEx( hKey, L"HasSCRNSAVE.EXE", NULL, NULL,
                                 reinterpret_cast<BYTE*>( &has ), &cbHas ) == ERROR_SUCCESS )
            {
                backupSnapshot.hasScrPath = has ? TRUE : FALSE;
            }
            else
            {
                backupSnapshot.hasScrPath = TRUE;
            }

            DWORD cbPath = sizeof( backupSnapshot.scrPath );
            if( RegQueryValueEx( hKey, L"SCRNSAVE.EXE", NULL, NULL,
                                 reinterpret_cast<BYTE*>( backupSnapshot.scrPath ), &cbPath ) != ERROR_SUCCESS )
            {
                backupSnapshot.scrPath[0] = 0;
            }

            has = 1;
            cbHas = sizeof( has );
            if( RegQueryValueEx( hKey, L"HasScreenSaveActive", NULL, NULL,
                                 reinterpret_cast<BYTE*>( &has ), &cbHas ) == ERROR_SUCCESS )
            {
                backupSnapshot.hasScreenSaveActive = has ? TRUE : FALSE;
            }
            else
            {
                backupSnapshot.hasScreenSaveActive = TRUE;
            }

            DWORD cbScreenSaveActive = sizeof( backupSnapshot.screenSaveActive );
            if( RegQueryValueEx( hKey, L"ScreenSaveActive", NULL, NULL,
                                 reinterpret_cast<BYTE*>( backupSnapshot.screenSaveActive ), &cbScreenSaveActive ) != ERROR_SUCCESS )
            {
                backupSnapshot.screenSaveActive[0] = 0;
            }

            has = 1;
            cbHas = sizeof( has );
            if( RegQueryValueEx( hKey, L"HasScreenSaverIsSecure", NULL, NULL,
                                 reinterpret_cast<BYTE*>( &has ), &cbHas ) == ERROR_SUCCESS )
            {
                backupSnapshot.hasSecure = has ? TRUE : FALSE;
            }
            else
            {
                backupSnapshot.hasSecure = TRUE;
            }

            DWORD cbSecure = sizeof( backupSnapshot.secure );
            if( RegQueryValueEx( hKey, L"ScreenSaverIsSecure", NULL, NULL,
                                 reinterpret_cast<BYTE*>( backupSnapshot.secure ), &cbSecure ) != ERROR_SUCCESS )
            {
                backupSnapshot.secure[0] = 0;
            }

            has = 1;
            cbHas = sizeof( has );
            if( RegQueryValueEx( hKey, L"HasScreenSaveTimeOut", NULL, NULL,
                                 reinterpret_cast<BYTE*>( &has ), &cbHas ) == ERROR_SUCCESS )
            {
                backupSnapshot.hasTimeoutText = has ? TRUE : FALSE;
            }
            else
            {
                backupSnapshot.hasTimeoutText = TRUE;
            }

            DWORD cbTimeoutText = sizeof( backupSnapshot.timeoutText );
            if( RegQueryValueEx( hKey, L"ScreenSaveTimeOutText", NULL, NULL,
                                 reinterpret_cast<BYTE*>( backupSnapshot.timeoutText ), &cbTimeoutText ) != ERROR_SUCCESS )
            {
                backupSnapshot.timeoutText[0] = 0;
            }

            DWORD dwTimeout = 0;
            DWORD cbTimeout = sizeof( dwTimeout );
            if( RegQueryValueEx( hKey, L"ScreenSaveTimeOut", NULL, NULL,
                                 reinterpret_cast<BYTE*>( &dwTimeout ), &cbTimeout ) == ERROR_SUCCESS )
            {
                backupSnapshot.timeoutSeconds = dwTimeout;
            }
            else
            {
                UINT timeout = 0;
                SystemParametersInfo( SPI_GETSCREENSAVETIMEOUT, 0, &timeout, 0 );
                backupSnapshot.timeoutSeconds = timeout;
            }

            DWORD runtimeActive = TRUE;
            DWORD cbRuntimeActive = sizeof( runtimeActive );
            if( RegQueryValueEx( hKey, L"ScreenSaveActiveRuntime", NULL, NULL,
                                 reinterpret_cast<BYTE*>( &runtimeActive ), &cbRuntimeActive ) == ERROR_SUCCESS )
            {
                backupSnapshot.screenSaveActiveRuntime = runtimeActive ? TRUE : FALSE;
            }
            else
            {
                BOOL active = TRUE;
                SystemParametersInfo( SPI_GETSCREENSAVEACTIVE, 0, &active, 0 );
                backupSnapshot.screenSaveActiveRuntime = active ? TRUE : FALSE;
            }

            backupSnapshot.valid = TRUE;
            ApplyScreenSaverSnapshot( &backupSnapshot );
            restored = true;

            RegCloseKey( hKey );
            RegDeleteKey( HKEY_CURRENT_USER, SCREENSAVER_BACKUP_KEY );
        }
    }

    if( restored )
    {
        // Broadcast settings change so Windows picks up all registry-only
        // values (SCRNSAVE.EXE, ScreenSaverIsSecure) and refreshed timeout.
        SendNotifyMessage( HWND_BROADCAST, WM_SETTINGCHANGE, 0,
                           reinterpret_cast<LPARAM>( L"Control Panel\\Desktop" ) );
        SendNotifyMessage( HWND_BROADCAST, WM_SETTINGCHANGE, SPI_SETSCREENSAVEACTIVE, 0 );
    }
}

static BOOLEAN HasOrphanedScreenSaverSettings( void )
{
    HKEY hKey;
    if( RegOpenKeyEx( HKEY_CURRENT_USER, SCREENSAVER_BACKUP_KEY, 0,
                      KEY_QUERY_VALUE, &hKey ) == ERROR_SUCCESS )
    {
        RegCloseKey( hKey );
        return TRUE;
    }
    return FALSE;
}

static BOOLEAN IsBreakScreenSaverRunning( void )
{
    HANDLE hSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
    if( hSnap == INVALID_HANDLE_VALUE )
    {
        return FALSE;
    }

    PROCESSENTRY32 pe = { sizeof( pe ) };
    BOOLEAN running = FALSE;
    if( Process32First( hSnap, &pe ) )
    {
        do
        {
            if( _tcsicmp( pe.szExeFile, L"ZoomItBreak.scr" ) == 0 )
            {
                running = TRUE;
                break;
            }
        }
        while( Process32Next( hSnap, &pe ) );
    }

    CloseHandle( hSnap );
    return running;
}


//----------------------------------------------------------------------------
//
// ActivateBreakScreenSaver
//
// Activates the break timer as a secure screensaver using the registry-swap
// approach:
// 1. Extracts the embedded .scr to %TEMP%
// 2. Writes config file with break timer settings
// 3. Saves user's current screensaver settings
// 4. Configures system to use our .scr with password protection
// 5. Triggers screensaver via SC_SCREENSAVE
//
// The screensaver runs on the ScreenSaver Desktop and displays the countdown
// timer.  With ScreenSaverIsSecure=1, students must authenticate to dismiss it.
// The user's original settings are restored on session unlock (WTS_SESSION_UNLOCK)
// or on ZoomIt shutdown (crash recovery).
//
//----------------------------------------------------------------------------
static BOOLEAN ActivateBreakScreenSaver( HWND hWnd, int breakTimeoutSeconds )
{
    TCHAR tempDir[MAX_PATH];
    GetTempPath( MAX_PATH, tempDir );

    {
        TCHAR dbg[256];
        _stprintf( dbg, L"[ZoomIt] ActivateBreakScreenSaver: timeout=%d\n", breakTimeoutSeconds );
        OutputDebugString( dbg );
    }

    //
    // 1. Extract the embedded .scr to %TEMP%.
    //    Kill any previous instance first — the file may be locked by a
    //    still-running screensaver process (ERROR_SHARING_VIOLATION).
    //
    TCHAR scrPath[MAX_PATH];
    _stprintf( scrPath, L"%sZoomItBreak.scr", tempDir );

    // Try extracting; if the file is locked, find and kill the old process.
    if( !ExtractResourceToFile( L"RCZOOMITSCR", L"BINRES", scrPath ) )
    {
        DWORD err = GetLastError();
        if( err == ERROR_SHARING_VIOLATION )
        {
            OutputDebugString( L"[ZoomIt] .scr file locked, killing previous instance\n" );

            // Find and terminate any running ZoomItBreak.scr process.
            HANDLE hSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
            if( hSnap != INVALID_HANDLE_VALUE )
            {
                PROCESSENTRY32 pe = { sizeof( pe ) };
                if( Process32First( hSnap, &pe ) )
                {
                    do
                    {
                        if( _tcsicmp( pe.szExeFile, L"ZoomItBreak.scr" ) == 0 )
                        {
                            HANDLE hProc = OpenProcess( PROCESS_TERMINATE, FALSE, pe.th32ProcessID );
                            if( hProc )
                            {
                                TerminateProcess( hProc, 0 );
                                WaitForSingleObject( hProc, 2000 );
                                CloseHandle( hProc );
                            }
                        }
                    }
                    while( Process32Next( hSnap, &pe ) );
                }
                CloseHandle( hSnap );
            }
            Sleep( 200 );  // Brief wait for file handle release.

            // Retry extraction.
            if( !ExtractResourceToFile( L"RCZOOMITSCR", L"BINRES", scrPath ) )
            {
                TCHAR dbg[256];
                _stprintf( dbg, L"[ZoomIt] ExtractResourceToFile FAILED (retry), err=%lu\n", GetLastError() );
                OutputDebugString( dbg );
                return FALSE;
            }
        }
        else
        {
            TCHAR dbg[256];
            _stprintf( dbg, L"[ZoomIt] ExtractResourceToFile FAILED, err=%lu\n", err );
            OutputDebugString( dbg );
            return FALSE;
        }
    }
    //
    // 2. Capture desktop screenshot if the user wants "show desktop" background.
    //
    TCHAR screenshotPath[MAX_PATH] = {};
    if( g_BreakShowBackgroundFile && g_BreakShowDesktop )
    {
        _stprintf( screenshotPath, L"%sZoomItBreakBg.bmp", tempDir );
        HDC hdcDesktop = GetDC( NULL );
        MONITORINFO monInfo = { sizeof( monInfo ) };
        POINT pt;
        GetCursorPos( &pt );
        HMONITOR hMon = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
        GetMonitorInfo( hMon, &monInfo );
        HBITMAP hBmp = CreateFadedDesktopBackground( hdcDesktop, &monInfo.rcMonitor, NULL );
        ReleaseDC( NULL, hdcDesktop );
        if( hBmp )
        {
            SaveBitmapToFile( hBmp, screenshotPath );
            DeleteObject( hBmp );
        }
    }

    //
    // 3. Write the config file for the screensaver to read.
    //
    BreakScrConfig config = {};
    config.magic = BREAKSCR_CONFIG_MAGIC;
    config.timeoutSeconds = breakTimeoutSeconds;
    config.settings.penColor         = g_BreakPenColor;
    config.settings.backgroundColor  = g_BreakBackgroundColor;
    config.settings.timerPosition    = g_BreakTimerPosition;
    config.settings.opacity          = g_BreakOpacity;
    config.settings.showExpiredTime  = g_ShowExpiredTime;
    config.settings.smoothImage      = g_SmoothImage;
    config.settings.backgroundStretch = g_BreakBackgroundStretch;
    config.settings.playSound        = g_BreakPlaySoundFile;
    _tcscpy( config.settings.soundFile, g_BreakSoundFile );
    config.settings.showDesktop      = g_BreakShowDesktop;
    config.settings.showBackgroundFile = g_BreakShowBackgroundFile;
    _tcscpy( config.settings.backgroundFile, g_BreakBackgroundFile );
    config.settings.logFont          = g_LogFont;
    _tcscpy( config.screenshotPath, screenshotPath );

    // If a desktop screenshot was captured, tell the screensaver to load it
    // as its background file.
    if( screenshotPath[0] )
    {
        config.settings.showBackgroundFile = TRUE;
        config.settings.showDesktop = FALSE;
        _tcscpy( config.settings.backgroundFile, screenshotPath );
    }

    if( !BreakScrConfig_Write( &config ) )
    {
        OutputDebugString( L"[ZoomIt] BreakScrConfig_Write FAILED\n" );
        return FALSE;
    }

    //
    // 4. Save current screensaver settings for later restoration.
    //    Always take a fresh snapshot immediately before swapping in
    //    ZoomIt's .scr so unlock cleanup can reliably restore the user's
    //    pre-break values (path, active flag, secure flag, timeout).
    //
    // If a stale backup exists from an interrupted prior run, restore it
    // first so we don't snapshot already-overridden ZoomIt values.
    if( HasOrphanedScreenSaverSettings() )
    {
        RestoreScreenSaverSettings();
    }
    SaveScreenSaverSettings();

    //
    // 4a. Ensure screensaver is fully reset before reconfiguring.
    //     Disable screensaver, wait briefly, then reconfigure.  This ensures
    //     Windows processes the state change and allows subsequent activations.
    //
    SystemParametersInfo( SPI_SETSCREENSAVEACTIVE, FALSE, 0, SPIF_SENDCHANGE );
    Sleep( 200 );  // Allow Windows to process the disable

    //
    // 5. Configure system to use our screensaver.
    //

    // Set our .scr as the system screensaver
    if( RegSetKeyValue( HKEY_CURRENT_USER, L"Control Panel\\Desktop",
                        L"SCRNSAVE.EXE", REG_SZ, scrPath,
                        static_cast<DWORD>( ( _tcslen( scrPath ) + 1 ) * sizeof( TCHAR ) ) ) != ERROR_SUCCESS )
    {
        OutputDebugString( L"[ZoomIt] Failed to set SCRNSAVE.EXE\n" );
        RestoreScreenSaverSettings();
        return FALSE;
    }

    // Set screensaver timeout to 15 seconds.
    // The initial activation uses SC_SCREENSAVE for immediate launch.
    // This timeout controls how quickly the screensaver REACTIVATES on the lock
    // screen after a student triggers the credential provider but doesn't
    // authenticate and walks away.  1 second may be below Windows' enforced
    // minimum, so we use 15 seconds as a practical value.
    // ScreenSaveTimeOut is stored as REG_SZ in Control Panel\Desktop.
    RegSetKeyValue( HKEY_CURRENT_USER, L"Control Panel\\Desktop",
                    L"ScreenSaveTimeOut", REG_SZ, L"15",
                    static_cast<DWORD>( 3 * sizeof( TCHAR ) ) );
    SystemParametersInfo( SPI_SETSCREENSAVETIMEOUT, 15, 0, SPIF_SENDCHANGE );

    // Force password protection for classroom security
    TCHAR secure[] = L"1";
    if( RegSetKeyValue( HKEY_CURRENT_USER, L"Control Panel\\Desktop",
                        L"ScreenSaverIsSecure", REG_SZ, secure,
                        static_cast<DWORD>( ( _tcslen( secure ) + 1 ) * sizeof( TCHAR ) ) ) != ERROR_SUCCESS )
    {
        OutputDebugString( L"[ZoomIt] Failed to set ScreenSaverIsSecure\n" );
        RestoreScreenSaverSettings();
        return FALSE;
    }

    // Activate screensaver (must be enabled for SC_SCREENSAVE to work).
    // Set both the registry value and runtime state — Winlogon reads the
    // registry directly and ignores the SPI runtime flag.
    RegSetKeyValue( HKEY_CURRENT_USER, L"Control Panel\\Desktop",
                    L"ScreenSaveActive", REG_SZ, L"1",
                    static_cast<DWORD>( 2 * sizeof( TCHAR ) ) );
    SystemParametersInfo( SPI_SETSCREENSAVEACTIVE, TRUE, 0, SPIF_SENDCHANGE );

    // Broadcast and give Windows time to process all registry changes before
    // triggering the screensaver.  If we trigger too quickly, Windows may not
    // see ScreenSaverIsSecure=1 and the screensaver won't require authentication.
    SendNotifyMessage( HWND_BROADCAST, WM_SETTINGCHANGE, SPI_SETSCREENSAVEACTIVE,
                      reinterpret_cast<LPARAM>( L"Policy" ) );
    Sleep( 500 );

    //
    // 6. Trigger the screensaver via system mechanism.
    //    This causes Windows/Winlogon to launch our .scr on the ScreenSaver Desktop.
    //
    //    IMPORTANT: Use DefWindowProc on our own window rather than
    //    SendMessage(HWND_BROADCAST).  Broadcasting WM_SYSCOMMAND is synchronous
    //    and blocks our message loop for the entire screensaver session, making
    //    the desktop unresponsive.  It also delivers SC_SCREENSAVE to ZoomIt's
    //    own window, triggering unwanted LiveZoom activation.
    //
    DefWindowProc( hWnd, WM_SYSCOMMAND, SC_SCREENSAVE, 0 );

    // Defensive cleanup path: if session-change notifications are delayed or
    // missed, poll until the break .scr exits and then restore original values.
    // This ensures ScreenSaveTimeOut/SCRNSAVE.EXE don't remain overridden.
    SetTimer( hWnd, 4, 2000, NULL );

    return TRUE;
}

//----------------------------------------------------------------------------
//
// EnableDisableStickyKeys
//
//----------------------------------------------------------------------------
void EnableDisableStickyKeys( BOOLEAN Enable )
{
    static STICKYKEYS	prevStickyKeyValue = {0};
    STICKYKEYS			newStickyKeyValue = {0};

    // Need to do this on Vista tablet to stop sticky key popup when you
    // hold down the shift key and draw with the pen.
    if( Enable ) {

        if( prevStickyKeyValue.cbSize == sizeof(STICKYKEYS)) {

            SystemParametersInfo(SPI_SETSTICKYKEYS,
                    sizeof(STICKYKEYS), &prevStickyKeyValue, SPIF_SENDCHANGE);
        }

    } else {

        prevStickyKeyValue.cbSize = sizeof(STICKYKEYS);
        if (SystemParametersInfo(SPI_GETSTICKYKEYS, sizeof(STICKYKEYS),
                &prevStickyKeyValue, 0)) {

            newStickyKeyValue.cbSize = sizeof(STICKYKEYS);
            newStickyKeyValue.dwFlags = 0;
            if( !SystemParametersInfo(SPI_SETSTICKYKEYS,
                sizeof(STICKYKEYS), &newStickyKeyValue, SPIF_SENDCHANGE)) {

                // DWORD error = GetLastError();

            }
        }
    }
}


//----------------------------------------------------------------------------
//
// GetKeyMod
//
//----------------------------------------------------------------------------
constexpr DWORD GetKeyMod( DWORD Key )
{
    DWORD	 keyMod = 0;
    if( (Key >> 8) & HOTKEYF_ALT ) keyMod |= MOD_ALT;
    if( (Key >> 8) & HOTKEYF_CONTROL) keyMod |= MOD_CONTROL;
    if( (Key >> 8) & HOTKEYF_SHIFT) keyMod |= MOD_SHIFT;
    if( (Key >> 8) & HOTKEYF_EXT) keyMod |= MOD_WIN;
    return keyMod;
}


//----------------------------------------------------------------------------
//
// AdvancedBreakProc
//
//----------------------------------------------------------------------------
INT_PTR CALLBACK AdvancedBreakProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )
{
    TCHAR	opacity[10];
    static	TCHAR newSoundFile[MAX_PATH];
    static	TCHAR newBackgroundFile[MAX_PATH];
    TCHAR	filePath[MAX_PATH], initDir[MAX_PATH];
    DWORD	i;
    static UINT currentDpi = DPI_BASELINE;

    switch ( message )  {
    case WM_INITDIALOG:
        // Set the dialog icon
        {
            HICON hIcon = LoadIcon( g_hInstance, L"APPICON" );
            if( hIcon )
            {
                SendMessage( hDlg, WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(hIcon) );
                SendMessage( hDlg, WM_SETICON, ICON_SMALL, reinterpret_cast<LPARAM>(hIcon) );
            }
        }
        if( pSHAutoComplete ) {
            pSHAutoComplete( GetDlgItem( hDlg, IDC_SOUND_FILE), SHACF_FILESYSTEM );
            pSHAutoComplete( GetDlgItem( hDlg, IDC_BACKGROUND_FILE), SHACF_FILESYSTEM );
        }
        CheckDlgButton( hDlg, IDC_CHECK_BACKGROUND_FILE,
            g_BreakShowBackgroundFile ? BST_CHECKED: BST_UNCHECKED );
        CheckDlgButton( hDlg, IDC_CHECK_SOUND_FILE,
            g_BreakPlaySoundFile ? BST_CHECKED: BST_UNCHECKED );
        CheckDlgButton( hDlg, IDC_CHECK_SHOW_EXPIRED,
            g_ShowExpiredTime ? BST_CHECKED : BST_UNCHECKED );
        CheckDlgButton( hDlg, IDC_CHECK_BACKGROUND_STRETCH,
            g_BreakBackgroundStretch ? BST_CHECKED : BST_UNCHECKED );
#if 0
        CheckDlgButton( hDlg, IDC_CHECK_SECONDARYDISPLAY,
            g_BreakOnSecondary ? BST_CHECKED : BST_UNCHECKED );
#endif
        if( pSetLayeredWindowAttributes == NULL ) {

            EnableWindow( GetDlgItem( hDlg, IDC_OPACITY ), FALSE );
        }

        // sound file
        if( !g_BreakPlaySoundFile ) {

            EnableWindow( GetDlgItem( hDlg, IDC_STATIC_SOUND_FILE ), FALSE );
            EnableWindow( GetDlgItem( hDlg, IDC_SOUND_FILE ), FALSE );
            EnableWindow( GetDlgItem( hDlg, IDC_SOUND_BROWSE ), FALSE );
        }
        _tcscpy( newSoundFile, g_BreakSoundFile );
        _tcscpy( filePath, g_BreakSoundFile );
        if( _tcsrchr( filePath, '\\' )) _tcscpy( filePath, _tcsrchr( g_BreakSoundFile, '\\' )+1);
        if( _tcsrchr( filePath, '.' )) *_tcsrchr( filePath, '.' ) = 0;
        SetDlgItemText( hDlg, IDC_SOUND_FILE, filePath );

        // background file
        if( !g_BreakShowBackgroundFile ) {

            EnableWindow( GetDlgItem( hDlg, IDC_STATIC_DESKTOP_BACKGROUND ), FALSE );
            EnableWindow( GetDlgItem( hDlg, IDC_STATIC_DESKTOP_BACKGROUND ), FALSE );
            EnableWindow( GetDlgItem( hDlg, IDC_STATIC_BACKGROUND_FILE ), FALSE );
            EnableWindow( GetDlgItem( hDlg, IDC_BACKGROUND_FILE ), FALSE );
            EnableWindow( GetDlgItem( hDlg, IDC_BACKGROUND_BROWSE ), FALSE );
            EnableWindow( GetDlgItem( hDlg, IDC_CHECK_BACKGROUND_STRETCH ), FALSE );
        }
        CheckDlgButton( hDlg,
            g_BreakShowDesktop ? IDC_STATIC_DESKTOP_BACKGROUND : IDC_STATIC_BACKGROUND_FILE, BST_CHECKED );
        _tcscpy( newBackgroundFile, g_BreakBackgroundFile );
        SetDlgItemText( hDlg, IDC_BACKGROUND_FILE, g_BreakBackgroundFile );

        CheckDlgButton( hDlg, IDC_TIMER_POS1 + g_BreakTimerPosition, BST_CHECKED );

        for( i = 10; i <= 100; i += 10) {

            _stprintf( opacity, L"%d%%", i );
            SendMessage( GetDlgItem( hDlg, IDC_OPACITY ), CB_ADDSTRING, 0,
                    reinterpret_cast<LPARAM>(opacity));
        }
        SendMessage( GetDlgItem( hDlg, IDC_OPACITY ), CB_SETCURSEL,
                g_BreakOpacity / 10 - 1, 0 );

        // Apply DPI scaling to the dialog
        currentDpi = GetDpiForWindowHelper( hDlg );
        if( currentDpi != DPI_BASELINE )
        {
            ScaleDialogForDpi( hDlg, currentDpi, DPI_BASELINE );
        }

        // Apply dark mode
        ApplyDarkModeToDialog( hDlg );
        return TRUE;

    case WM_DPICHANGED:
        HandleDialogDpiChange( hDlg, wParam, lParam, currentDpi );
        return TRUE;

    case WM_ERASEBKGND:
        if (IsDarkModeEnabled())
        {
            HDC hdc = reinterpret_cast<HDC>(wParam);
            RECT rc;
            GetClientRect(hDlg, &rc);
            FillRect(hdc, &rc, GetDarkModeBrush());
            return TRUE;
        }
        break;

    case WM_CTLCOLORDLG:
    case WM_CTLCOLORSTATIC:
    case WM_CTLCOLORBTN:
    case WM_CTLCOLOREDIT:
    case WM_CTLCOLORLISTBOX:
    {
        HDC hdc = reinterpret_cast<HDC>(wParam);
        HWND hCtrl = reinterpret_cast<HWND>(lParam);
        HBRUSH hBrush = HandleDarkModeCtlColor(hdc, hCtrl, message);
        if (hBrush)
        {
            return reinterpret_cast<INT_PTR>(hBrush);
        }
        break;
    }

    case WM_COMMAND:
        switch ( HIWORD( wParam )) {
        case BN_CLICKED:
            if( LOWORD( wParam ) == IDC_CHECK_SOUND_FILE ) {

                EnableWindow( GetDlgItem( hDlg, IDC_STATIC_SOUND_FILE ),
                        IsDlgButtonChecked( hDlg, IDC_CHECK_SOUND_FILE) == BST_CHECKED );
                EnableWindow( GetDlgItem( hDlg, IDC_SOUND_FILE ),
                        IsDlgButtonChecked( hDlg, IDC_CHECK_SOUND_FILE) == BST_CHECKED );
                EnableWindow( GetDlgItem( hDlg, IDC_SOUND_BROWSE ),
                        IsDlgButtonChecked( hDlg, IDC_CHECK_SOUND_FILE) == BST_CHECKED );
            }
            if( LOWORD( wParam ) == IDC_CHECK_BACKGROUND_FILE ) {

                EnableWindow( GetDlgItem( hDlg, IDC_CHECK_BACKGROUND_STRETCH ),
                        IsDlgButtonChecked( hDlg, IDC_CHECK_BACKGROUND_FILE) == BST_CHECKED );
                EnableWindow( GetDlgItem( hDlg, IDC_STATIC_DESKTOP_BACKGROUND ),
                        IsDlgButtonChecked( hDlg, IDC_CHECK_BACKGROUND_FILE) == BST_CHECKED );
                EnableWindow( GetDlgItem( hDlg, IDC_STATIC_BACKGROUND_FILE ),
                        IsDlgButtonChecked( hDlg, IDC_CHECK_BACKGROUND_FILE) == BST_CHECKED );
                EnableWindow( GetDlgItem( hDlg, IDC_BACKGROUND_FILE ),
                        IsDlgButtonChecked( hDlg, IDC_CHECK_BACKGROUND_FILE) == BST_CHECKED );
                EnableWindow( GetDlgItem( hDlg, IDC_BACKGROUND_BROWSE ),
                        IsDlgButtonChecked( hDlg, IDC_CHECK_BACKGROUND_FILE) == BST_CHECKED );
            }
            break;
        }
        switch ( LOWORD( wParam )) {
        case IDC_SOUND_BROWSE:
        {
            auto openDialog = wil::CoCreateInstance<IFileOpenDialog>( CLSID_FileOpenDialog );

            FILEOPENDIALOGOPTIONS options;
            if( SUCCEEDED( openDialog->GetOptions( &options ) ) )
                openDialog->SetOptions( options | FOS_FORCEFILESYSTEM );

            COMDLG_FILTERSPEC fileTypes[] = {
                { L"Sounds", L"*.wav" },
                { L"All Files", L"*.*" }
            };
            openDialog->SetFileTypes( _countof( fileTypes ), fileTypes );
            openDialog->SetFileTypeIndex( 1 );
            openDialog->SetDefaultExtension( L"wav" );
            openDialog->SetTitle( L"ZoomIt: Specify Sound File..." );

            // Set initial folder
            GetDlgItemText( hDlg, IDC_SOUND_FILE, filePath, _countof( filePath ) );
            if( _tcsrchr( filePath, '\\' ) )
            {
                _tcscpy( initDir, filePath );
                *( _tcsrchr( initDir, '\\' ) + 1 ) = 0;
            }
            else
            {
                _tcscpy( filePath, L"%WINDIR%\\Media" );
                ExpandEnvironmentStrings( filePath, initDir, _countof( initDir ) );
            }
            wil::com_ptr<IShellItem> folderItem;
            if( SUCCEEDED( SHCreateItemFromParsingName( initDir, nullptr, IID_PPV_ARGS( &folderItem ) ) ) )
            {
                openDialog->SetFolder( folderItem.get() );
            }

            OpenSaveDialogEvents* pEvents = new OpenSaveDialogEvents(false);
            DWORD dwCookie = 0;
            openDialog->Advise( pEvents, &dwCookie );

            if( SUCCEEDED( openDialog->Show( hDlg ) ) )
            {
                wil::com_ptr<IShellItem> resultItem;
                if( SUCCEEDED( openDialog->GetResult( &resultItem ) ) )
                {
                    wil::unique_cotaskmem_string pathStr;
                    if( SUCCEEDED( resultItem->GetDisplayName( SIGDN_FILESYSPATH, &pathStr ) ) )
                    {
                        _tcscpy( newSoundFile, pathStr.get() );
                        _tcscpy( filePath, pathStr.get() );
                        if( _tcsrchr( filePath, '\\' ) ) _tcscpy( filePath, _tcsrchr( filePath, '\\' ) + 1 );
                        if( _tcsrchr( filePath, '.' ) ) *_tcsrchr( filePath, '.' ) = 0;
                        SetDlgItemText( hDlg, IDC_SOUND_FILE, filePath );
                    }
                }
            }

            openDialog->Unadvise( dwCookie );
            pEvents->Release();
            break;
        }

        case IDC_BACKGROUND_BROWSE:
        {
            auto openDialog = wil::CoCreateInstance<IFileOpenDialog>( CLSID_FileOpenDialog );

            FILEOPENDIALOGOPTIONS options;
            if( SUCCEEDED( openDialog->GetOptions( &options ) ) )
                openDialog->SetOptions( options | FOS_FORCEFILESYSTEM );

            COMDLG_FILTERSPEC fileTypes[] = {
                { L"Bitmap Files (*.bmp;*.dib)", L"*.bmp;*.dib" },
                { L"PNG (*.png)", L"*.png" },
                { L"JPEG (*.jpg;*.jpeg;*.jpe;*.jfif)", L"*.jpg;*.jpeg;*.jpe;*.jfif" },
                { L"GIF (*.gif)", L"*.gif" },
                { L"All Picture Files", L"*.bmp;*.dib;*.png;*.jpg;*.jpeg;*.jpe;*.jfif;*.gif" },
                { L"All Files", L"*.*" }
            };
            openDialog->SetFileTypes( _countof( fileTypes ), fileTypes );
            openDialog->SetFileTypeIndex( 5 ); // Default to "All Picture Files"
            openDialog->SetTitle( L"ZoomIt: Specify Background File..." );

            // Set initial folder
            GetDlgItemText( hDlg, IDC_BACKGROUND_FILE, filePath, _countof( filePath ) );
            if( _tcsrchr( filePath, '\\' ) )
            {
                _tcscpy( initDir, filePath );
                *( _tcsrchr( initDir, '\\' ) + 1 ) = 0;
            }
            else
            {
                _tcscpy( filePath, L"%USERPROFILE%\\Pictures" );
                ExpandEnvironmentStrings( filePath, initDir, _countof( initDir ) );
            }
            wil::com_ptr<IShellItem> folderItem;
            if( SUCCEEDED( SHCreateItemFromParsingName( initDir, nullptr, IID_PPV_ARGS( &folderItem ) ) ) )
            {
                openDialog->SetFolder( folderItem.get() );
            }

            OpenSaveDialogEvents* pEvents = new OpenSaveDialogEvents(false);
            DWORD dwCookie = 0;
            openDialog->Advise( pEvents, &dwCookie );

            if( SUCCEEDED( openDialog->Show( hDlg ) ) )
            {
                wil::com_ptr<IShellItem> resultItem;
                if( SUCCEEDED( openDialog->GetResult( &resultItem ) ) )
                {
                    wil::unique_cotaskmem_string pathStr;
                    if( SUCCEEDED( resultItem->GetDisplayName( SIGDN_FILESYSPATH, &pathStr ) ) )
                    {
                        _tcscpy( newBackgroundFile, pathStr.get() );
                        SetDlgItemText( hDlg, IDC_BACKGROUND_FILE, pathStr.get() );
                    }
                }
            }

            openDialog->Unadvise( dwCookie );
            pEvents->Release();
            break;
        }

        case IDOK:

            // sound file has to be valid
            g_BreakPlaySoundFile = IsDlgButtonChecked( hDlg, IDC_CHECK_SOUND_FILE ) == BST_CHECKED;
            g_BreakShowBackgroundFile = IsDlgButtonChecked( hDlg, IDC_CHECK_BACKGROUND_FILE ) == BST_CHECKED;
            g_BreakBackgroundStretch = IsDlgButtonChecked( hDlg, IDC_CHECK_BACKGROUND_STRETCH ) == BST_CHECKED;
#if 0
            g_BreakOnSecondary = IsDlgButtonChecked( hDlg, IDC_CHECK_SECONDARYDISPLAY ) == BST_CHECKED;
#endif
            if( g_BreakPlaySoundFile && GetFileAttributes( newSoundFile ) == -1 ) {

                MessageBox( hDlg, L"The specified sound file is inaccessible",
                        L"Advanced Break Options Error", MB_ICONERROR );
                break;
            }
            _tcscpy( g_BreakSoundFile, newSoundFile );

            // Background file
            g_BreakShowDesktop = IsDlgButtonChecked( hDlg, IDC_STATIC_DESKTOP_BACKGROUND ) == BST_CHECKED;

            if( !g_BreakShowDesktop && g_BreakShowBackgroundFile && GetFileAttributes( newBackgroundFile ) == -1 ) {

                MessageBox( hDlg, L"The specified background file is inaccessible",
                        L"Advanced Break Options Error", MB_ICONERROR );
                break;
            }
            _tcscpy( g_BreakBackgroundFile, newBackgroundFile );

            for( i = 0; i < 10; i++ ) {

                if( IsDlgButtonChecked( hDlg, IDC_TIMER_POS1+i) == BST_CHECKED ) {

                    g_BreakTimerPosition = i;
                    break;
                }
            }
            GetDlgItemText( hDlg, IDC_OPACITY, opacity, sizeof(opacity)/sizeof(opacity[0]));
            _stscanf( opacity, L"%d%%", &g_BreakOpacity );
            reg.WriteRegSettings( RegSettings );
            EndDialog(hDlg, 0);
            break;

        case IDCANCEL:
            EndDialog( hDlg, 0 );
            return TRUE;
        }
        break;

    default:
        break;
    }
    return FALSE;
}


//----------------------------------------------------------------------------
//
// OptionsTabProc
//
//----------------------------------------------------------------------------

static UINT_PTR CALLBACK ChooseFontHookProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_INITDIALOG:
        // Set the dialog icon
        {
            HICON hIcon = LoadIcon( g_hInstance, L"APPICON" );
            if( hIcon )
            {
                SendMessage( hDlg, WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(hIcon) );
                SendMessage( hDlg, WM_SETICON, ICON_SMALL, reinterpret_cast<LPARAM>(hIcon) );
            }
        }
        // Basic (incomplete) dark mode attempt: theme the main common dialog window.
        ApplyDarkModeToDialog(hDlg);
        return 0;

    case WM_CTLCOLORDLG:
    case WM_CTLCOLORSTATIC:
    case WM_CTLCOLORBTN:
    case WM_CTLCOLOREDIT:
    case WM_CTLCOLORLISTBOX:
    {
        HDC hdc = reinterpret_cast<HDC>(wParam);
        HWND hCtrl = reinterpret_cast<HWND>(lParam);
        HBRUSH hBrush = HandleDarkModeCtlColor(hdc, hCtrl, message);
        if (hBrush)
        {
            return reinterpret_cast<UINT_PTR>(hBrush);
        }
        break;
    }
    }

    return 0;
}

INT_PTR CALLBACK OptionsTabProc( HWND hDlg, UINT message,
                                WPARAM wParam, LPARAM lParam )
{
    HDC			hDC;
    LOGFONT		lf;
    CHOOSEFONT	chooseFont;
    HFONT		hFont;
    PAINTSTRUCT	ps;
    HWND		hTextPreview;
    HDC			hDc;
    RECT		previewRc;

    switch ( message )  {
    case WM_INITDIALOG:
        return TRUE;

    case WM_ERASEBKGND:
        if (IsDarkModeEnabled())
        {
            HDC hdc = reinterpret_cast<HDC>(wParam);
            RECT rc;
            GetClientRect(hDlg, &rc);
            FillRect(hdc, &rc, GetDarkModeBrush());
            return TRUE;
        }
        break;

    case WM_CTLCOLORDLG:
    case WM_CTLCOLORSTATIC:
    case WM_CTLCOLORBTN:
    case WM_CTLCOLOREDIT:
    case WM_CTLCOLORLISTBOX:
    {
        HDC hdc = reinterpret_cast<HDC>(wParam);
        HWND hCtrl = reinterpret_cast<HWND>(lParam);
        HBRUSH hBrush = HandleDarkModeCtlColor(hdc, hCtrl, message);
        if (hBrush)
        {
            return reinterpret_cast<INT_PTR>(hBrush);
        }
        break;
    }

    case WM_COMMAND:
        // Handle combo box selection changes
        if (HIWORD(wParam) == CBN_SELCHANGE) {
            if (LOWORD(wParam) == IDC_RECORD_SCALING) {

                int format = static_cast<int>(SendMessage(GetDlgItem(hDlg, IDC_RECORD_FORMAT), CB_GETCURSEL, 0, 0));
                int scale = static_cast<int>(SendMessage(GetDlgItem(hDlg, IDC_RECORD_SCALING), CB_GETCURSEL, 0, 0));
                if(format == 0)
                {
                    g_RecordScalingGIF = static_cast<BYTE>((scale + 1) * 10);
                }
                else
                {
                    g_RecordScalingMP4 = static_cast<BYTE>((scale + 1) * 10);
                }
            }
            else if (LOWORD(wParam) == IDC_RECORD_FORMAT) {
                // Get the currently selected format
                int selection = static_cast<int>(SendMessage(GetDlgItem(hDlg, IDC_RECORD_FORMAT),
                                                            CB_GETCURSEL, 0, 0));

                // Get the selected text to check if it's GIF
                TCHAR selectedText[32] = {0};
                SendMessage(GetDlgItem(hDlg, IDC_RECORD_FORMAT),
                           CB_GETLBTEXT, selection, reinterpret_cast<LPARAM>(selectedText));

                // Check if GIF is selected by comparing the text
                bool isGifSelected = (wcscmp(selectedText, L"GIF") == 0);

                // If GIF is selected, set the scaling to the g_RecordScalingGIF value
                if (isGifSelected) {
                    g_RecordScaling = g_RecordScalingGIF;

                } else {

                    g_RecordScaling = g_RecordScalingMP4;
                }

                for (int i = 0; i < 10; i++) {
                    int scalingValue = (i + 1) * 10;
                    if (scalingValue == static_cast<int>(g_RecordScaling)) {
                        SendMessage(GetDlgItem(hDlg, IDC_RECORD_SCALING),
                                    CB_SETCURSEL, i, 0);
                        break;
                    }
                }

                // Enable/disable audio controls based on selection (GIF has no audio)
                EnableWindow(GetDlgItem(hDlg, IDC_CAPTURE_SYSTEM_AUDIO), !isGifSelected);
                EnableWindow(GetDlgItem(hDlg, IDC_CAPTURE_AUDIO), !isGifSelected);
                EnableWindow(GetDlgItem(hDlg, IDC_MICROPHONE_LABEL), !isGifSelected);
                EnableWindow(GetDlgItem(hDlg, IDC_MICROPHONE), !isGifSelected);
            }
        }

        switch ( LOWORD( wParam )) {
        case IDC_ADVANCED_BREAK:
            DialogBox( g_hInstance, L"ADVANCED_BREAK", hDlg, AdvancedBreakProc );
            break;
        case IDC_FONT:
            hDC = GetDC (hDlg );
            lf = g_LogFont;
            lf.lfHeight = -21;
            chooseFont.hDC = CreateCompatibleDC (hDC);
            ReleaseDC (hDlg, hDC);
            chooseFont.lStructSize = sizeof (CHOOSEFONT);
            chooseFont.hwndOwner = hDlg;
            chooseFont.lpLogFont = &lf;
            chooseFont.Flags     = CF_SCREENFONTS|CF_ENABLETEMPLATE|CF_ENABLEHOOK|
                        CF_INITTOLOGFONTSTRUCT|CF_LIMITSIZE;
            chooseFont.rgbColors = RGB (0, 0, 0);
            chooseFont.lCustData = 0;
            chooseFont.nSizeMin  = 16;
            chooseFont.nSizeMax  = 16;
            chooseFont.hInstance = g_hInstance;
            chooseFont.lpszStyle = static_cast<LPTSTR>(NULL);
            chooseFont.nFontType = SCREEN_FONTTYPE;
            chooseFont.lpfnHook  = ChooseFontHookProc;
            chooseFont.lpTemplateName = static_cast<LPTSTR>(MAKEINTRESOURCE (FORMATDLGORD31));
            if( ChooseFont( &chooseFont ) ) {
                g_LogFont = lf;
                InvalidateRect( hDlg, NULL, TRUE );
            }
            break;
        case IDC_DEMOTYPE_BROWSE:
        {
            auto openDialog = wil::CoCreateInstance<IFileOpenDialog>( CLSID_FileOpenDialog );

            FILEOPENDIALOGOPTIONS options;
            if( SUCCEEDED( openDialog->GetOptions( &options ) ) )
                openDialog->SetOptions( options | FOS_FORCEFILESYSTEM );

            COMDLG_FILTERSPEC fileTypes[] = {
                { L"All Files", L"*.*" }
            };
            openDialog->SetFileTypes( _countof( fileTypes ), fileTypes );
            openDialog->SetFileTypeIndex( 1 );
            openDialog->SetTitle( L"ZoomIt: Specify DemoType File..." );

            OpenSaveDialogEvents* pEvents = new OpenSaveDialogEvents(false);
            DWORD dwCookie = 0;
            openDialog->Advise( pEvents, &dwCookie );

            if( SUCCEEDED( openDialog->Show( hDlg ) ) )
            {
                wil::com_ptr<IShellItem> resultItem;
                if( SUCCEEDED( openDialog->GetResult( &resultItem ) ) )
                {
                    wil::unique_cotaskmem_string pathStr;
                    if( SUCCEEDED( resultItem->GetDisplayName( SIGDN_FILESYSPATH, &pathStr ) ) )
                    {
                        if( GetFileAttributes( pathStr.get() ) == INVALID_FILE_ATTRIBUTES )
                        {
                            MessageBox( hDlg, L"The specified file is inaccessible", APPNAME, MB_ICONERROR );
                        }
                        else
                        {
                            SetDlgItemText( g_OptionsTabs[DEMOTYPE_PAGE].hPage, IDC_DEMOTYPE_FILE, pathStr.get() );
                            _tcscpy( g_DemoTypeFile, pathStr.get() );
                        }
                    }
                }
            }

            openDialog->Unadvise( dwCookie );
            pEvents->Release();
            break;
        }
        case IDC_TRIM_FILE:
        {
            // Open a video file for trimming
            auto openDialog = wil::CoCreateInstance<IFileOpenDialog>( CLSID_FileOpenDialog );

            FILEOPENDIALOGOPTIONS options;
            if( SUCCEEDED( openDialog->GetOptions( &options ) ) )
                openDialog->SetOptions( options | FOS_FORCEFILESYSTEM );

            COMDLG_FILTERSPEC fileTypes[] = {
                { L"Video Files (*.mp4;*.gif)", L"*.mp4;*.gif" },
                { L"MP4 Video (*.mp4)", L"*.mp4" },
                { L"GIF Animation (*.gif)", L"*.gif" }
            };
            openDialog->SetFileTypes( _countof( fileTypes ), fileTypes );
            openDialog->SetFileTypeIndex( 1 );
            openDialog->SetTitle( L"ZoomIt: Open Video for Trimming..." );

            wil::com_ptr<IShellItem> videosFolder;
            if( SUCCEEDED( SHGetKnownFolderItem( FOLDERID_Videos, KF_FLAG_DEFAULT, nullptr, IID_PPV_ARGS( &videosFolder ) ) ) )
                openDialog->SetDefaultFolder( videosFolder.get() );

            OpenSaveDialogEvents* pEvents = new OpenSaveDialogEvents(false);
            DWORD dwCookie = 0;
            openDialog->Advise( pEvents, &dwCookie );

            if( SUCCEEDED( openDialog->Show( hDlg ) ) )
            {
                wil::com_ptr<IShellItem> resultItem;
                if( SUCCEEDED( openDialog->GetResult( &resultItem ) ) )
                {
                    wil::unique_cotaskmem_string pathStr;
                    if( SUCCEEDED( resultItem->GetDisplayName( SIGDN_FILESYSPATH, &pathStr ) ) )
                    {
                        std::wstring videoPath( pathStr.get() );
                        winrt::Windows::Foundation::TimeSpan trimStart{ 0 };
                        winrt::Windows::Foundation::TimeSpan trimEnd{ 0 };
                        VideoRecordingSession::ShowTrimDialog(
                            GetParent( hDlg ), videoPath, trimStart, trimEnd, true );
                    }
                }
            }

            openDialog->Unadvise( dwCookie );
            pEvents->Release();
            break;
        }
        }
        break;

    case WM_PAINT:
        if( (hTextPreview = GetDlgItem( hDlg, IDC_TEXT_FONT )) != 0 ) {

            // 16-pt preview
            LOGFONT _lf = g_LogFont;
            _lf.lfHeight = -21;
            hFont = CreateFontIndirect( &_lf);
            hDc = BeginPaint(hDlg, &ps);
            SelectObject( hDc, hFont );

            GetWindowRect( hTextPreview, &previewRc );
            MapWindowPoints( NULL, hDlg, reinterpret_cast<LPPOINT>(&previewRc), 2);

            previewRc.top += 6;

            // Set text color based on dark mode
            if (IsDarkModeEnabled())
            {
                SetTextColor(hDc, DarkMode::TextColor);
                SetBkMode(hDc, TRANSPARENT);
            }

            DrawText( hDc, L"AaBbYyZz", static_cast<int>(_tcslen(L"AaBbYyZz")), &previewRc,
                DT_CENTER|DT_VCENTER|DT_SINGLELINE );

            EndPaint( hDlg, &ps );
            DeleteObject( hFont );
        }
        break;
    default:
        break;
    }
    return FALSE;
}


//----------------------------------------------------------------------------
//
// RepositionTabPages
//
// Reposition tab pages to fit current tab control size (call after DPI change)
//
//----------------------------------------------------------------------------
VOID RepositionTabPages( HWND hTabCtrl )
{
    RECT rc, pageRc;
    GetWindowRect( hTabCtrl, &rc );
    TabCtrl_AdjustRect( hTabCtrl, FALSE, &rc );

    // Inset the display area to leave room for border in dark mode
    if (IsDarkModeEnabled())
    {
        rc.left += 2;
        rc.top += 2;
        rc.right -= 2;
        rc.bottom -= 2;
    }

    // Get the parent dialog to convert coordinates correctly
    HWND hParent = GetParent( hTabCtrl );

    for( int i = 0; i < sizeof( g_OptionsTabs )/sizeof(g_OptionsTabs[0]); i++ ) {
        if( g_OptionsTabs[i].hPage ) {
            pageRc = rc;
            // Convert screen coords to parent dialog client coords
            MapWindowPoints( NULL, hParent, reinterpret_cast<LPPOINT>(&pageRc), 2);

            SetWindowPos( g_OptionsTabs[i].hPage,
                 HWND_TOP,
                 pageRc.left, pageRc.top,
                 pageRc.right - pageRc.left, pageRc.bottom - pageRc.top,
                 SWP_NOACTIVATE | SWP_NOZORDER );
        }
    }
}

//----------------------------------------------------------------------------
//
// OptionsAddTabs
//
//----------------------------------------------------------------------------
VOID OptionsAddTabs( HWND hOptionsDlg, HWND hTabCtrl )
{
    int		i;
    TCITEM	tcItem;
    RECT	rc, pageRc;

    GetWindowRect( hTabCtrl, &rc );
    for( i = 0; i < sizeof( g_OptionsTabs )/sizeof(g_OptionsTabs[0]); i++ ) {

        tcItem.mask = TCIF_TEXT;
        tcItem.pszText = g_OptionsTabs[i].TabTitle;
        TabCtrl_InsertItem( hTabCtrl, i, &tcItem );
        g_OptionsTabs[i].hPage = CreateDialog( g_hInstance, g_OptionsTabs[i].TabTitle,
                    hOptionsDlg, OptionsTabProc );
    }

    TabCtrl_AdjustRect( hTabCtrl, FALSE, &rc );

    // Inset the display area to leave room for border in dark mode
    // Need 2 pixels so tab pages don't cover the 1-pixel border
    if (IsDarkModeEnabled())
    {
        rc.left += 2;
        rc.top += 2;
        rc.right -= 2;
        rc.bottom -= 2;
    }

    for( i = 0; i < sizeof( g_OptionsTabs )/sizeof(g_OptionsTabs[0]); i++ ) {

        pageRc = rc;
        // Convert screen coords to parent dialog client coords.
        MapWindowPoints( NULL, hOptionsDlg, reinterpret_cast<LPPOINT>(&pageRc), 2);

        SetWindowPos( g_OptionsTabs[i].hPage,
             HWND_TOP,
             pageRc.left, pageRc.top,
             pageRc.right - pageRc.left, pageRc.bottom - pageRc.top,
             SWP_NOACTIVATE | SWP_HIDEWINDOW );

        if( pEnableThemeDialogTexture ) {
            if( IsDarkModeEnabled() ) {
                // Disable theme dialog texture in dark mode - it interferes with dark backgrounds
                pEnableThemeDialogTexture( g_OptionsTabs[i].hPage, ETDT_DISABLE );
            } else {
                // Enable tab texturing in light mode
                pEnableThemeDialogTexture( g_OptionsTabs[i].hPage, ETDT_ENABLETAB );
            }
        }
    }

    // Show the initial page once positioned to reduce visible churn.
    if( g_OptionsTabs[0].hPage )
    {
        ShowWindow( g_OptionsTabs[0].hPage, SW_SHOW );
    }
}

//----------------------------------------------------------------------------
//
// UnregisterAllHotkeys
//
//----------------------------------------------------------------------------
void UnregisterAllHotkeys( HWND hWnd )
{
    auto unregisterHotkey = [hWnd]( int id )
    {
        const BOOL unregistered = UnregisterHotKey( hWnd, id );
        LogHotkeyRegistrationResult( L"unregister", hWnd, id, 0, 0, unregistered );
    };

    unregisterHotkey( ZOOM_HOTKEY );
    unregisterHotkey( LIVE_HOTKEY );
    unregisterHotkey( LIVE_DRAW_HOTKEY );
    unregisterHotkey( DRAW_HOTKEY );
    unregisterHotkey( BREAK_HOTKEY );
    unregisterHotkey( RECORD_HOTKEY );
    unregisterHotkey( RECORD_CROP_HOTKEY );
    unregisterHotkey( RECORD_WINDOW_HOTKEY );
    unregisterHotkey( SNIP_HOTKEY );
    unregisterHotkey( SNIP_SAVE_HOTKEY );
    unregisterHotkey( SNIP_PANORAMA_HOTKEY );
    unregisterHotkey( SNIP_PANORAMA_SAVE_HOTKEY );
    unregisterHotkey( SNIP_OCR_HOTKEY );
    unregisterHotkey( DEMOTYPE_HOTKEY );
    unregisterHotkey( DEMOTYPE_RESET_HOTKEY );
    unregisterHotkey( RECORD_GIF_HOTKEY );
    unregisterHotkey( RECORD_GIF_WINDOW_HOTKEY );
    unregisterHotkey( SAVE_IMAGE_HOTKEY );
    unregisterHotkey( SAVE_CROP_HOTKEY );
    unregisterHotkey( COPY_IMAGE_HOTKEY );
    unregisterHotkey( COPY_CROP_HOTKEY );
}

//----------------------------------------------------------------------------
//
// RegisterAllHotkeys
//
//----------------------------------------------------------------------------
void RegisterAllHotkeys(HWND hWnd)
{
    auto registerHotkey = [hWnd]( int id, UINT modifiers, UINT key )
    {
        const BOOL registered = RegisterHotKey( hWnd, id, modifiers, key );
        LogHotkeyRegistrationResult( L"register", hWnd, id, modifiers, key, registered );
    };

    if (g_ToggleKey) 			registerHotkey( ZOOM_HOTKEY, g_ToggleMod, g_ToggleKey & 0xFF );
    if (g_LiveZoomToggleKey) {
        registerHotkey( LIVE_HOTKEY, g_LiveZoomToggleMod, g_LiveZoomToggleKey & 0xFF );
        registerHotkey( LIVE_DRAW_HOTKEY, ( g_LiveZoomToggleMod ^ MOD_SHIFT ), g_LiveZoomToggleKey & 0xFF );
    }
    if (g_DrawToggleKey) 		registerHotkey( DRAW_HOTKEY, g_DrawToggleMod, g_DrawToggleKey & 0xFF );
    if (g_BreakToggleKey) 		registerHotkey( BREAK_HOTKEY, g_BreakToggleMod, g_BreakToggleKey & 0xFF );
    if (g_DemoTypeToggleKey) {
        registerHotkey( DEMOTYPE_HOTKEY, g_DemoTypeToggleMod, g_DemoTypeToggleKey & 0xFF );
        registerHotkey( DEMOTYPE_RESET_HOTKEY, ( g_DemoTypeToggleMod ^ MOD_SHIFT ), g_DemoTypeToggleKey & 0xFF );
    }
    if (g_SnipToggleKey) {
        registerHotkey( SNIP_HOTKEY, g_SnipToggleMod, g_SnipToggleKey & 0xFF );
        registerHotkey( SNIP_SAVE_HOTKEY, ( g_SnipToggleMod ^ MOD_SHIFT ), g_SnipToggleKey & 0xFF );
    }
    if( g_SnipPanoramaToggleKey &&
        (g_SnipPanoramaToggleKey != g_SnipToggleKey || g_SnipPanoramaToggleMod != g_SnipToggleMod) ) {
        registerHotkey( SNIP_PANORAMA_HOTKEY, g_SnipPanoramaToggleMod | MOD_NOREPEAT, g_SnipPanoramaToggleKey & 0xFF );
        registerHotkey( SNIP_PANORAMA_SAVE_HOTKEY, ( g_SnipPanoramaToggleMod ^ MOD_SHIFT ) | MOD_NOREPEAT, g_SnipPanoramaToggleKey & 0xFF );
    }
    if (g_SnipOcrToggleKey) {
        registerHotkey( SNIP_OCR_HOTKEY, g_SnipOcrToggleMod, g_SnipOcrToggleKey & 0xFF );
    }
    if (g_RecordToggleKey) {
        registerHotkey( RECORD_HOTKEY, g_RecordToggleMod | MOD_NOREPEAT, g_RecordToggleKey & 0xFF );
        registerHotkey( RECORD_CROP_HOTKEY, ( g_RecordToggleMod ^ MOD_SHIFT ) | MOD_NOREPEAT, g_RecordToggleKey & 0xFF );
        registerHotkey( RECORD_WINDOW_HOTKEY, ( g_RecordToggleMod ^ MOD_ALT ) | MOD_NOREPEAT, g_RecordToggleKey & 0xFF );
    }

    // Note: COPY_IMAGE_HOTKEY, COPY_CROP_HOTKEY (Ctrl+C, Ctrl+Shift+C) and
    // SAVE_IMAGE_HOTKEY, SAVE_CROP_HOTKEY (Ctrl+S, Ctrl+Shift+S) are registered
    // only during static zoom mode to avoid blocking system-wide Ctrl+C/Ctrl+S
}



//----------------------------------------------------------------------------
//
// UpdateDrawTabHeaderFont
//
//----------------------------------------------------------------------------
void UpdateDrawTabHeaderFont()
{
    static HFONT	headerFont = nullptr;
    TCHAR 			text[64];

    constexpr int headers[] = { IDC_PEN_CONTROL, IDC_COLORS, IDC_HIGHLIGHT_AND_BLUR, IDC_SHAPES, IDC_SCREEN };

    HWND hPage = g_OptionsTabs[DRAW_PAGE].hPage;
    if( !hPage )
    {
        return;
    }

    // Get the font from an actual body text control that has been DPI-scaled.
    // This ensures headers use the exact same font as body text, just bold.
    // Find the first static text child control (ID -1) to get the scaled body text font.
    HFONT hBaseFont = nullptr;
    HWND hChild = GetWindow( hPage, GW_CHILD );
    while( hChild != nullptr )
    {
        if( GetDlgCtrlID( hChild ) == -1 )  // IDC_STATIC is -1
        {
            hBaseFont = reinterpret_cast<HFONT>(SendMessage( hChild, WM_GETFONT, 0, 0 ));
            if( hBaseFont )
            {
                break;
            }
        }
        hChild = GetWindow( hChild, GW_HWNDNEXT );
    }

    if( !hBaseFont )
    {
        hBaseFont = static_cast<HFONT>(GetStockObject( DEFAULT_GUI_FONT ));
    }

    LOGFONT lf{};
    if( !GetObject( hBaseFont, sizeof( LOGFONT ), &lf ) )
    {
        GetObject( GetStockObject( DEFAULT_GUI_FONT ), sizeof( LOGFONT ), &lf );
    }
    lf.lfWeight = FW_BOLD;

    HFONT newHeaderFont = CreateFontIndirect( &lf );
    if( !newHeaderFont )
    {
        return;
    }

    // Swap fonts safely: apply the new font to all header controls first, then delete the old.
    HFONT oldHeaderFont = headerFont;
    headerFont = newHeaderFont;

    for( int i = 0; i < _countof( headers ); i++ )
    {
        // Change the header font to bold
        HWND hHeader = GetDlgItem( hPage, headers[i] );
        if( !hHeader )
        {
            continue;
        }

        // StaticTextSubclassProc already supports a per-control font override via this property.
        // Setting it here makes Draw tab headers resilient if something later overwrites WM_SETFONT.
        SetPropW( hHeader, L"ZoomIt.HeaderFont", headerFont );

        SendMessage( hHeader, WM_SETFONT, reinterpret_cast<WPARAM>(headerFont), TRUE );

        // Resize the control to fit the text
        GetWindowText( hHeader, text, sizeof( text ) / sizeof( text[0] ) );
        RECT rc;
        GetWindowRect( hHeader, &rc );
        MapWindowPoints( NULL, g_OptionsTabs[DRAW_PAGE].hPage, reinterpret_cast<LPPOINT>(&rc), 2 );
        HDC hDC = GetDC( hHeader );
        SelectFont( hDC, headerFont );
        DrawText( hDC, text, static_cast<int>(_tcslen( text )), &rc, DT_CALCRECT | DT_SINGLELINE | DT_LEFT | DT_VCENTER );
        ReleaseDC( hHeader, hDC );
        SetWindowPos( hHeader, nullptr, 0, 0, rc.right - rc.left + ScaleForDpi( 4, GetDpiForWindowHelper( hHeader ) ), rc.bottom - rc.top, SWP_NOMOVE | SWP_NOZORDER );
        InvalidateRect( hHeader, nullptr, TRUE );
    }

    if( oldHeaderFont )
    {
        DeleteObject( oldHeaderFont );
    }
}

//----------------------------------------------------------------------------
//
// CheckboxSubclassProc
//
// Subclass procedure for checkbox and radio button controls to handle dark mode colors
//
//----------------------------------------------------------------------------
LRESULT CALLBACK CheckboxSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
    switch (uMsg)
    {
    case WM_PAINT:
        if (IsDarkModeEnabled())
        {
            TCHAR dbgText[256] = { 0 };
            GetWindowText(hWnd, dbgText, _countof(dbgText));
            bool dbgEnabled = IsWindowEnabled(hWnd);
            LONG dbgStyle = GetWindowLong(hWnd, GWL_STYLE);
            LONG dbgType = dbgStyle & BS_TYPEMASK;
            bool dbgIsRadio = (dbgType == BS_RADIOBUTTON || dbgType == BS_AUTORADIOBUTTON);
            OutputDebugStringW((std::wstring(L"[Checkbox] WM_PAINT: ") + dbgText +
                L" enabled=" + (dbgEnabled ? L"1" : L"0") +
                L" isRadio=" + (dbgIsRadio ? L"1" : L"0") + L"\n").c_str());

            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);

            RECT rc;
            GetClientRect(hWnd, &rc);

            // Fill background
            FillRect(hdc, &rc, GetDarkModeBrush());

            // Get button state and style
            LRESULT state = SendMessage(hWnd, BM_GETCHECK, 0, 0);
            bool isChecked = (state == BST_CHECKED);
            bool isEnabled = IsWindowEnabled(hWnd);

            // Check if this is a radio button
            LONG style = GetWindowLong(hWnd, GWL_STYLE);
            LONG buttonType = style & BS_TYPEMASK;
            bool isRadioButton = (buttonType == BS_RADIOBUTTON || buttonType == BS_AUTORADIOBUTTON);

            // Check if checkbox should be on the right (BS_LEFTTEXT or WS_EX_RIGHT)
            bool checkOnRight = (style & BS_LEFTTEXT) != 0;
            LONG exStyle = GetWindowLong(hWnd, GWL_EXSTYLE);
            if (exStyle & WS_EX_RIGHT)
                checkOnRight = true;

            // Get DPI for scaling
            UINT dpi = GetDpiForWindowHelper(hWnd);
            int checkSize = ScaleForDpi(13, dpi);
            int margin = ScaleForDpi(2, dpi);

            // Calculate checkbox/radio position
            RECT rcCheck;
            if (checkOnRight)
            {
                rcCheck.right = rc.right - margin;
                rcCheck.left = rcCheck.right - checkSize;
            }
            else
            {
                rcCheck.left = rc.left + margin;
                rcCheck.right = rcCheck.left + checkSize;
            }
            rcCheck.top = rc.top + (rc.bottom - rc.top - checkSize) / 2;
            rcCheck.bottom = rcCheck.top + checkSize;

            // Choose colors based on enabled state
            COLORREF borderColor = isEnabled ? DarkMode::BorderColor : RGB(60, 60, 60);
            COLORREF fillColor = isChecked ? (isEnabled ? DarkMode::AccentColor : RGB(80, 80, 85)) : DarkMode::SurfaceColor;
            COLORREF textColor = isEnabled ? DarkMode::TextColor : RGB(100, 100, 100);

            if (isRadioButton)
            {
                // Draw radio button (circle)
                HPEN hPen = CreatePen(PS_SOLID, 1, borderColor);
                HPEN hOldPen = static_cast<HPEN>(SelectObject(hdc, hPen));
                HBRUSH hFillBrush = CreateSolidBrush(isChecked ? fillColor : DarkMode::SurfaceColor);
                HBRUSH hOldBrush = static_cast<HBRUSH>(SelectObject(hdc, hFillBrush));
                Ellipse(hdc, rcCheck.left, rcCheck.top, rcCheck.right, rcCheck.bottom);
                SelectObject(hdc, hOldBrush);
                SelectObject(hdc, hOldPen);
                DeleteObject(hPen);
                DeleteObject(hFillBrush);

                // Draw inner circle if checked
                if (isChecked)
                {
                    int innerMargin = ScaleForDpi(3, dpi);
                    HBRUSH hInnerBrush = CreateSolidBrush(isEnabled ? RGB(255, 255, 255) : RGB(140, 140, 140));
                    HBRUSH hOldInnerBrush = static_cast<HBRUSH>(SelectObject(hdc, hInnerBrush));
                    HPEN hNullPen = static_cast<HPEN>(SelectObject(hdc, GetStockObject(NULL_PEN)));
                    Ellipse(hdc, rcCheck.left + innerMargin, rcCheck.top + innerMargin,
                            rcCheck.right - innerMargin, rcCheck.bottom - innerMargin);
                    SelectObject(hdc, hNullPen);
                    SelectObject(hdc, hOldInnerBrush);
                    DeleteObject(hInnerBrush);
                }
            }
            else
            {
                // Draw checkbox (rectangle)
                HPEN hPen = CreatePen(PS_SOLID, 1, borderColor);
                HPEN hOldPen = static_cast<HPEN>(SelectObject(hdc, hPen));
                HBRUSH hFillBrush = CreateSolidBrush(fillColor);
                HBRUSH hOldBrush = static_cast<HBRUSH>(SelectObject(hdc, hFillBrush));
                Rectangle(hdc, rcCheck.left, rcCheck.top, rcCheck.right, rcCheck.bottom);
                SelectObject(hdc, hOldBrush);
                SelectObject(hdc, hOldPen);
                DeleteObject(hPen);
                DeleteObject(hFillBrush);

                // Draw checkmark if checked
                if (isChecked)
                {
                    COLORREF checkColor = isEnabled ? RGB(255, 255, 255) : RGB(140, 140, 140);
                    HPEN hCheckPen = CreatePen(PS_SOLID, ScaleForDpi(2, dpi), checkColor);
                    HPEN hOldCheckPen = static_cast<HPEN>(SelectObject(hdc, hCheckPen));

                    // Draw checkmark
                    int x = rcCheck.left + ScaleForDpi(3, dpi);
                    int y = rcCheck.top + ScaleForDpi(6, dpi);
                    MoveToEx(hdc, x, y, nullptr);
                    LineTo(hdc, x + ScaleForDpi(2, dpi), y + ScaleForDpi(3, dpi));
                    LineTo(hdc, x + ScaleForDpi(7, dpi), y - ScaleForDpi(3, dpi));

                    SelectObject(hdc, hOldCheckPen);
                    DeleteObject(hCheckPen);
                }
            }

            // Draw text
            TCHAR text[256] = { 0 };
            GetWindowText(hWnd, text, _countof(text));

            SetBkMode(hdc, TRANSPARENT);
            SetTextColor(hdc, textColor);
            HFONT hFont = reinterpret_cast<HFONT>(SendMessage(hWnd, WM_GETFONT, 0, 0));
            HFONT hOldFont = nullptr;
            if (hFont)
            {
                hOldFont = static_cast<HFONT>(SelectObject(hdc, hFont));
            }

            RECT rcText = rc;
            UINT textFormat = DT_VCENTER | DT_SINGLELINE;
            if (checkOnRight)
            {
                rcText.right = rcCheck.left - ScaleForDpi(4, dpi);
                textFormat |= DT_RIGHT;
            }
            else
            {
                rcText.left = rcCheck.right + ScaleForDpi(4, dpi);
                textFormat |= DT_LEFT;
            }
            DrawText(hdc, text, -1, &rcText, textFormat);

            if (hOldFont)
            {
                SelectObject(hdc, hOldFont);
            }

            EndPaint(hWnd, &ps);
            return 0;
        }
        break;

    case WM_ENABLE:
        if (IsDarkModeEnabled())
        {
            // Let base window proc handle enable state change, but avoid any subclass chain
            // that might trigger themed drawing
            LRESULT result = DefWindowProc(hWnd, uMsg, wParam, lParam);
            // Force immediate repaint with our custom painting
            InvalidateRect(hWnd, nullptr, TRUE);
            UpdateWindow(hWnd);
            return result;
        }
        break;

    case WM_NCDESTROY:
        RemoveWindowSubclass(hWnd, CheckboxSubclassProc, uIdSubclass);
        break;
    }
    return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}

//----------------------------------------------------------------------------
//
// HotkeyControlSubclassProc
//
// Subclass procedure for hotkey controls to handle dark mode colors
//
//----------------------------------------------------------------------------
LRESULT CALLBACK HotkeyControlSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
    switch (uMsg)
    {
    case WM_PAINT:
        if (IsDarkModeEnabled())
        {
            // Get the hotkey from the control using HKM_GETHOTKEY
            LRESULT hk = SendMessage(hWnd, HKM_GETHOTKEY, 0, 0);
            WORD hotkey = LOWORD(hk);
            BYTE vk = LOBYTE(hotkey);
            BYTE mods = HIBYTE(hotkey);

            // Build the hotkey text
            std::wstring text;
            if (vk != 0)
            {
                if (mods & HOTKEYF_CONTROL)
                    text += L"Ctrl+";
                if (mods & HOTKEYF_SHIFT)
                    text += L"Shift+";
                if (mods & HOTKEYF_ALT)
                    text += L"Alt+";

                // Get key name using virtual key code
                UINT scanCode = MapVirtualKeyW(vk, MAPVK_VK_TO_VSC);
                if (scanCode != 0)
                {
                    TCHAR keyName[64] = { 0 };
                    LONG lParamKey = (scanCode << 16);
                    // Set extended key bit for certain keys
                    if ((vk >= VK_PRIOR && vk <= VK_DELETE) ||
                        (vk >= VK_LWIN && vk <= VK_APPS) ||
                        vk == VK_DIVIDE || vk == VK_NUMLOCK)
                    {
                        lParamKey |= (1 << 24);
                    }
                    if (GetKeyNameTextW(lParamKey, keyName, _countof(keyName)) > 0)
                    {
                        text += keyName;
                    }
                    else
                    {
                        // Fallback: use the virtual key character for printable keys
                        if (vk >= '0' && vk <= '9')
                        {
                            text += static_cast<wchar_t>(vk);
                        }
                        else if (vk >= 'A' && vk <= 'Z')
                        {
                            text += static_cast<wchar_t>(vk);
                        }
                        else if (vk >= VK_F1 && vk <= VK_F24)
                        {
                            text += L"F";
                            text += std::to_wstring(vk - VK_F1 + 1);
                        }
                    }
                }
                else
                {
                    // No scan code, try direct character representation
                    if (vk >= '0' && vk <= '9')
                    {
                        text += static_cast<wchar_t>(vk);
                    }
                    else if (vk >= 'A' && vk <= 'Z')
                    {
                        text += static_cast<wchar_t>(vk);
                    }
                    else if (vk >= VK_F1 && vk <= VK_F24)
                    {
                        text += L"F";
                        text += std::to_wstring(vk - VK_F1 + 1);
                    }
                }
            }

            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);

            RECT rc;
            GetClientRect(hWnd, &rc);

            // Fill background with dark surface color
            FillRect(hdc, &rc, GetDarkModeSurfaceBrush());

            // No border in dark mode - just the filled background

            // Draw text if we have any
            if (!text.empty())
            {
                SetBkMode(hdc, TRANSPARENT);
                SetTextColor(hdc, DarkMode::TextColor);
                HFONT hFont = reinterpret_cast<HFONT>(SendMessage(hWnd, WM_GETFONT, 0, 0));
                HFONT hOldFont = nullptr;
                if (hFont)
                {
                    hOldFont = static_cast<HFONT>(SelectObject(hdc, hFont));
                }
                RECT rcText = rc;
                rcText.left += 4;
                rcText.right -= 4;
                DrawTextW(hdc, text.c_str(), -1, &rcText, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
                if (hOldFont)
                {
                    SelectObject(hdc, hOldFont);
                }
            }

            EndPaint(hWnd, &ps);
            return 0;
        }
        break;

    case WM_NCPAINT:
        if (IsDarkModeEnabled())
        {
            // Fill the non-client area with background color to hide the border
            HDC hdc = GetWindowDC(hWnd);
            if (hdc)
            {
                RECT rc;
                GetWindowRect(hWnd, &rc);
                int width = rc.right - rc.left;
                int height = rc.bottom - rc.top;
                rc.left = 0;
                rc.top = 0;
                rc.right = width;
                rc.bottom = height;

                // Get NC border size
                RECT rcClient;
                GetClientRect(hWnd, &rcClient);
                MapWindowPoints(hWnd, nullptr, reinterpret_cast<LPPOINT>(&rcClient), 2);

                RECT rcWindow;
                GetWindowRect(hWnd, &rcWindow);

                int borderLeft = rcClient.left - rcWindow.left;
                int borderTop = rcClient.top - rcWindow.top;
                int borderRight = rcWindow.right - rcClient.right;
                int borderBottom = rcWindow.bottom - rcClient.bottom;

                // Fill the entire NC border area with background color
                HBRUSH hBrush = CreateSolidBrush(DarkMode::BackgroundColor);

                // Top border
                RECT rcTop = { 0, 0, width, borderTop };
                FillRect(hdc, &rcTop, hBrush);

                // Bottom border
                RECT rcBottom = { 0, height - borderBottom, width, height };
                FillRect(hdc, &rcBottom, hBrush);

                // Left border
                RECT rcLeft = { 0, borderTop, borderLeft, height - borderBottom };
                FillRect(hdc, &rcLeft, hBrush);

                // Right border
                RECT rcRight = { width - borderRight, borderTop, width, height - borderBottom };
                FillRect(hdc, &rcRight, hBrush);

                DeleteObject(hBrush);

                // Draw thin border around the control
                HPEN hPen = CreatePen(PS_SOLID, 1, DarkMode::BorderColor);
                HPEN hOldPen = static_cast<HPEN>(SelectObject(hdc, hPen));
                HBRUSH hOldBrush = static_cast<HBRUSH>(SelectObject(hdc, GetStockObject(NULL_BRUSH)));
                Rectangle(hdc, 0, 0, width, height);
                SelectObject(hdc, hOldBrush);
                SelectObject(hdc, hOldPen);
                DeleteObject(hPen);

                ReleaseDC(hWnd, hdc);
            }
            return 0;
        }
        break;

    case WM_NCDESTROY:
        RemoveWindowSubclass(hWnd, HotkeyControlSubclassProc, uIdSubclass);
        break;
    }
    return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}

//----------------------------------------------------------------------------
//
// EditControlSubclassProc
//
// Subclass procedure for edit controls to handle dark mode (no border)
//
//----------------------------------------------------------------------------
LRESULT CALLBACK EditControlSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
    // Helper to adjust formatting rectangle for vertical text centering
    auto AdjustTextRect = [](HWND hEdit) {
        RECT rcClient;
        GetClientRect(hEdit, &rcClient);

        // Get font metrics to calculate text height
        HDC hdc = GetDC(hEdit);
        HFONT hFont = reinterpret_cast<HFONT>(SendMessage(hEdit, WM_GETFONT, 0, 0));
        HFONT hOldFont = hFont ? static_cast<HFONT>(SelectObject(hdc, hFont)) : nullptr;

        TEXTMETRIC tm;
        GetTextMetrics(hdc, &tm);
        int textHeight = tm.tmHeight;

        if (hOldFont)
            SelectObject(hdc, hOldFont);
        ReleaseDC(hEdit, hdc);

        // Calculate vertical offset to center text
        int clientHeight = rcClient.bottom - rcClient.top;
        int topOffset = (clientHeight - textHeight) / 2;
        if (topOffset < 0) topOffset = 0;

        RECT rcFormat = rcClient;
        rcFormat.top = topOffset;
        rcFormat.left += 2;  // Small left margin
        rcFormat.right -= 2; // Small right margin

        SendMessage(hEdit, EM_SETRECT, 0, reinterpret_cast<LPARAM>(&rcFormat));
    };

    switch (uMsg)
    {
    case WM_SIZE:
    {
        // Adjust the formatting rectangle to vertically center text
        LRESULT result = DefSubclassProc(hWnd, uMsg, wParam, lParam);
        AdjustTextRect(hWnd);
        return result;
    }

    case WM_SETFONT:
    {
        // After font is set, adjust formatting rectangle
        LRESULT result = DefSubclassProc(hWnd, uMsg, wParam, lParam);
        AdjustTextRect(hWnd);
        return result;
    }

    case WM_NCPAINT:
        if (IsDarkModeEnabled())
        {
            OutputDebugStringW(L"[Edit] WM_NCPAINT in dark mode\n");

            // Get the window DC which includes NC area
            HDC hdc = GetWindowDC(hWnd);
            if (hdc)
            {
                RECT rc;
                GetWindowRect(hWnd, &rc);
                int width = rc.right - rc.left;
                int height = rc.bottom - rc.top;
                rc.left = 0;
                rc.top = 0;
                rc.right = width;
                rc.bottom = height;

                // Get NC border size
                RECT rcClient;
                GetClientRect(hWnd, &rcClient);
                MapWindowPoints(hWnd, nullptr, reinterpret_cast<LPPOINT>(&rcClient), 2);

                RECT rcWindow;
                GetWindowRect(hWnd, &rcWindow);

                int borderLeft = rcClient.left - rcWindow.left;
                int borderTop = rcClient.top - rcWindow.top;
                int borderRight = rcWindow.right - rcClient.right;
                int borderBottom = rcWindow.bottom - rcClient.bottom;

                OutputDebugStringW((L"[Edit] Border: L=" + std::to_wstring(borderLeft) + L" T=" + std::to_wstring(borderTop) +
                    L" R=" + std::to_wstring(borderRight) + L" B=" + std::to_wstring(borderBottom) + L"\n").c_str());

                // Fill the entire NC border area with background color
                HBRUSH hBrush = CreateSolidBrush(DarkMode::BackgroundColor);

                // Top border
                RECT rcTop = { 0, 0, width, borderTop };
                FillRect(hdc, &rcTop, hBrush);

                // Bottom border
                RECT rcBottom = { 0, height - borderBottom, width, height };
                FillRect(hdc, &rcBottom, hBrush);

                // Left border
                RECT rcLeft = { 0, borderTop, borderLeft, height - borderBottom };
                FillRect(hdc, &rcLeft, hBrush);

                // Right border
                RECT rcRight = { width - borderRight, borderTop, width, height - borderBottom };
                FillRect(hdc, &rcRight, hBrush);

                DeleteObject(hBrush);

                // Draw thin border around the control
                HPEN hPen = CreatePen(PS_SOLID, 1, DarkMode::BorderColor);
                HPEN hOldPen = static_cast<HPEN>(SelectObject(hdc, hPen));
                HBRUSH hOldBrush = static_cast<HBRUSH>(SelectObject(hdc, GetStockObject(NULL_BRUSH)));
                Rectangle(hdc, 0, 0, width, height);
                SelectObject(hdc, hOldBrush);
                SelectObject(hdc, hOldPen);
                DeleteObject(hPen);

                ReleaseDC(hWnd, hdc);
            }
            return 0;
        }
        break;

    case WM_NCDESTROY:
        RemoveWindowSubclass(hWnd, EditControlSubclassProc, uIdSubclass);
        break;
    }
    return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}

//----------------------------------------------------------------------------
//
// SliderSubclassProc
//
// Subclass procedure for slider/trackbar controls to handle dark mode
//
//----------------------------------------------------------------------------
LRESULT CALLBACK SliderSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
    switch (uMsg)
    {
    case WM_LBUTTONDOWN:
    case WM_MOUSEMOVE:
    case WM_LBUTTONUP:
        if (IsDarkModeEnabled())
        {
            // Let the default handler process the message first
            LRESULT result = DefSubclassProc(hWnd, uMsg, wParam, lParam);
            // Force full repaint to avoid artifacts at high DPI
            InvalidateRect(hWnd, nullptr, TRUE);
            return result;
        }
        break;

    case WM_PAINT:
        if (IsDarkModeEnabled())
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);

            RECT rc;
            GetClientRect(hWnd, &rc);

            // Fill background
            FillRect(hdc, &rc, GetDarkModeBrush());

            // Get slider info
            RECT rcChannel = { 0 };
            SendMessage(hWnd, TBM_GETCHANNELRECT, 0, reinterpret_cast<LPARAM>(&rcChannel));

            RECT rcThumb = { 0 };
            SendMessage(hWnd, TBM_GETTHUMBRECT, 0, reinterpret_cast<LPARAM>(&rcThumb));

            // Draw channel (track) - simple dark line
            int channelHeight = 4;
            int channelY = (rc.bottom + rc.top) / 2 - channelHeight / 2;
            RECT rcTrack = { rcChannel.left, channelY, rcChannel.right, channelY + channelHeight };
            HBRUSH hTrackBrush = CreateSolidBrush(RGB(80, 80, 85));
            FillRect(hdc, &rcTrack, hTrackBrush);
            DeleteObject(hTrackBrush);

            // Center thumb vertically - at high DPI the thumb rect may not be centered
            int thumbHeight = rcThumb.bottom - rcThumb.top;
            int thumbCenterY = (rc.bottom + rc.top) / 2;
            rcThumb.top = thumbCenterY - thumbHeight / 2;
            rcThumb.bottom = rcThumb.top + thumbHeight;

            // Draw thumb - dark rectangle
            HBRUSH hThumbBrush = CreateSolidBrush(RGB(160, 160, 165));
            FillRect(hdc, &rcThumb, hThumbBrush);
            DeleteObject(hThumbBrush);

            // Draw thumb border
            HPEN hPen = CreatePen(PS_SOLID, 1, RGB(100, 100, 105));
            HPEN hOldPen = static_cast<HPEN>(SelectObject(hdc, hPen));
            HBRUSH hOldBrush = static_cast<HBRUSH>(SelectObject(hdc, GetStockObject(NULL_BRUSH)));
            Rectangle(hdc, rcThumb.left, rcThumb.top, rcThumb.right, rcThumb.bottom);
            SelectObject(hdc, hOldBrush);
            SelectObject(hdc, hOldPen);
            DeleteObject(hPen);

            EndPaint(hWnd, &ps);
            return 0;
        }
        break;

    case WM_ERASEBKGND:
        if (IsDarkModeEnabled())
        {
            HDC hdc = reinterpret_cast<HDC>(wParam);
            RECT rc;
            GetClientRect(hWnd, &rc);
            FillRect(hdc, &rc, GetDarkModeBrush());
            return TRUE;
        }
        break;

    case WM_NCDESTROY:
        RemoveWindowSubclass(hWnd, SliderSubclassProc, uIdSubclass);
        break;
    }
    return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}

//----------------------------------------------------------------------------
//
// GroupBoxSubclassProc
//
// Subclass procedure for group box controls to handle dark mode painting
//
//----------------------------------------------------------------------------
LRESULT CALLBACK GroupBoxSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
    switch (uMsg)
    {
    case WM_PAINT:
        if (IsDarkModeEnabled())
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);

            RECT rc;
            GetClientRect(hWnd, &rc);

            // Get the text and font
            HFONT hFont = reinterpret_cast<HFONT>(SendMessage(hWnd, WM_GETFONT, 0, 0));
            HFONT hOldFont = hFont ? static_cast<HFONT>(SelectObject(hdc, hFont)) : nullptr;

            TCHAR szText[256] = { 0 };
            GetWindowText(hWnd, szText, _countof(szText));

            // Measure text
            SIZE textSize = { 0 };
            GetTextExtentPoint32(hdc, szText, static_cast<int>(_tcslen(szText)), &textSize);

            // Text starts at left + 8 pixels
            const int textLeft = 8;
            const int textPadding = 4;
            int frameTop = textSize.cy / 2;

            // Only fill the frame border areas, not the interior (to avoid painting over child controls)
            // Fill top strip (above frame line)
            RECT rcTop = { rc.left, rc.top, rc.right, frameTop + 1 };
            FillRect(hdc, &rcTop, GetDarkModeBrush());

            // Fill left edge strip
            RECT rcLeft = { rc.left, frameTop, rc.left + 1, rc.bottom };
            FillRect(hdc, &rcLeft, GetDarkModeBrush());

            // Fill right edge strip
            RECT rcRight = { rc.right - 1, frameTop, rc.right, rc.bottom };
            FillRect(hdc, &rcRight, GetDarkModeBrush());

            // Fill bottom edge strip
            RECT rcBottom = { rc.left, rc.bottom - 1, rc.right, rc.bottom };
            FillRect(hdc, &rcBottom, GetDarkModeBrush());

            // Draw the group box frame (with gap for text)
            HPEN hPen = CreatePen(PS_SOLID, 1, DarkMode::BorderColor);
            HPEN hOldPen = static_cast<HPEN>(SelectObject(hdc, hPen));

            // Top line - left segment (before text)
            MoveToEx(hdc, rc.left, frameTop, NULL);
            LineTo(hdc, textLeft - textPadding, frameTop);

            // Top line - right segment (after text)
            MoveToEx(hdc, textLeft + textSize.cx + textPadding, frameTop, NULL);
            LineTo(hdc, rc.right - 1, frameTop);

            // Right line
            LineTo(hdc, rc.right - 1, rc.bottom - 1);

            // Bottom line
            LineTo(hdc, rc.left, rc.bottom - 1);

            // Left line
            LineTo(hdc, rc.left, frameTop);

            SelectObject(hdc, hOldPen);
            DeleteObject(hPen);

            // Draw text with background
            SetBkMode(hdc, OPAQUE);
            SetBkColor(hdc, DarkMode::BackgroundColor);
            SetTextColor(hdc, DarkMode::TextColor);
            RECT rcText = { textLeft, 0, textLeft + textSize.cx, textSize.cy };
            DrawText(hdc, szText, -1, &rcText, DT_LEFT | DT_SINGLELINE);

            if (hOldFont)
                SelectObject(hdc, hOldFont);

            EndPaint(hWnd, &ps);
            return 0;
        }
        break;

    case WM_ERASEBKGND:
        // Don't erase background - let parent handle it
        if (IsDarkModeEnabled())
        {
            return TRUE;
        }
        break;

    case WM_NCDESTROY:
        RemoveWindowSubclass(hWnd, GroupBoxSubclassProc, uIdSubclass);
        break;
    }
    return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}

//----------------------------------------------------------------------------
//
// StaticTextSubclassProc
//
// Subclass procedure for static text controls to handle dark mode painting
//
//----------------------------------------------------------------------------
LRESULT CALLBACK StaticTextSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
    const int ctrlId = GetDlgCtrlID( hWnd );
    const bool isOptionsHeader = (ctrlId == IDC_VERSION || ctrlId == IDC_COPYRIGHT);

    auto paintStaticText = [](HWND hWnd, HDC hdc) -> void
    {
        RECT rc;
        GetClientRect(hWnd, &rc);

        // Fill background
        if( IsDarkModeEnabled() )
        {
            FillRect(hdc, &rc, GetDarkModeBrush());
        }
        else
        {
            FillRect(hdc, &rc, GetSysColorBrush( COLOR_BTNFACE ));
        }

        // Get text
        TCHAR text[512] = { 0 };
        GetWindowText(hWnd, text, _countof(text));

        // Set up text drawing
        SetBkMode(hdc, TRANSPARENT);
        bool isEnabled = IsWindowEnabled(hWnd);
        if( IsDarkModeEnabled() )
        {
            SetTextColor(hdc, isEnabled ? DarkMode::TextColor : RGB(100, 100, 100));
        }
        else
        {
            SetTextColor( hdc, isEnabled ? GetSysColor( COLOR_WINDOWTEXT ) : GetSysColor( COLOR_GRAYTEXT ) );
        }

        // Try to get the font from a window property first (for header controls where
        // WM_GETFONT may not work reliably), then fall back to WM_GETFONT.
        HFONT hFont = static_cast<HFONT>(GetPropW( hWnd, L"ZoomIt.HeaderFont" ));
        HFONT hCreatedFont = nullptr; // Track if we created a font that needs cleanup

        // For IDC_VERSION, create a large title font on-demand if the property font doesn't work
        const int thisCtrlId = GetDlgCtrlID( hWnd );
        if( thisCtrlId == IDC_VERSION )
        {
            // Create a title font that is proportionally larger than the dialog font
            LOGFONT lf{};
            HFONT hDialogFont = reinterpret_cast<HFONT>(SendMessage( GetParent( hWnd ), WM_GETFONT, 0, 0 ));
            if( hDialogFont )
            {
                GetObject( hDialogFont, sizeof( lf ), &lf );
            }
            else
            {
                GetObject( GetStockObject( DEFAULT_GUI_FONT ), sizeof( lf ), &lf );
            }
            lf.lfWeight = FW_BOLD;
            // Make title 50% larger than dialog font (lfHeight is negative for character height)
            lf.lfHeight = MulDiv( lf.lfHeight, 3, 2 );
            hCreatedFont = CreateFontIndirect( &lf );
            if( hCreatedFont )
            {
                hFont = hCreatedFont;
            }
        }

        if( !hFont )
        {
            hFont = reinterpret_cast<HFONT>(SendMessage(hWnd, WM_GETFONT, 0, 0));
        }
        HFONT hOldFont = nullptr;
        if (hFont)
        {
            hOldFont = static_cast<HFONT>(SelectObject(hdc, hFont));
        }

#if _DEBUG
        if( thisCtrlId == IDC_VERSION )
        {
            TEXTMETRIC tm{};
            GetTextMetrics( hdc, &tm );
            OutputDebug(L"IDC_VERSION paint: tmHeight=%d selectResult=%p hFont=%p created=%p rc=(%d,%d,%d,%d)\n",
                tm.tmHeight, hOldFont, hFont, hCreatedFont, rc.left, rc.top, rc.right, rc.bottom );
        }
#endif

        // Get style to determine alignment and wrapping behavior
        LONG style = GetWindowLong(hWnd, GWL_STYLE);
        const LONG staticType = style & SS_TYPEMASK;

        UINT format = 0;
        if (style & SS_CENTER)
            format |= DT_CENTER;
        else if (style & SS_RIGHT)
            format |= DT_RIGHT;
        else
            format |= DT_LEFT;

        if (style & SS_NOPREFIX)
            format |= DT_NOPREFIX;

        bool noWrap = (staticType == SS_LEFTNOWORDWRAP) || (staticType == SS_SIMPLE);
        if( GetDlgCtrlID( hWnd ) == IDC_VERSION )
        {
            // The header title is intended to be a single line.
            noWrap = true;
        }
        if (noWrap)
        {
            // Single-line labels should match the classic static control behavior.
            format |= DT_SINGLELINE | DT_VCENTER | DT_END_ELLIPSIS;
        }
        else
        {
            // Multi-line/static text (LTEXT) should wrap like the default control.
            format |= DT_WORDBREAK | DT_EDITCONTROL;
        }

        DrawText(hdc, text, -1, &rc, format);

        if (hOldFont)
        {
            SelectObject(hdc, hOldFont);
        }

        // Clean up any font we created on-demand
        if( hCreatedFont )
        {
            DeleteObject( hCreatedFont );
        }
    };

    if (IsDarkModeEnabled() || isOptionsHeader)
    {
        switch (uMsg)
        {
        case WM_ERASEBKGND:
            {
                HDC hdc = reinterpret_cast<HDC>(wParam);
                RECT rc;
                GetClientRect(hWnd, &rc);
                if( IsDarkModeEnabled() )
                {
                    FillRect(hdc, &rc, GetDarkModeBrush());
                }
                else
                {
                    FillRect(hdc, &rc, GetSysColorBrush( COLOR_BTNFACE ));
                }
                return TRUE;
            }

        case WM_PAINT:
            {
                PAINTSTRUCT ps;
                HDC hdc = BeginPaint(hWnd, &ps);
                paintStaticText(hWnd, hdc);
                EndPaint(hWnd, &ps);
                return 0;
            }

        case WM_PRINTCLIENT:
            {
                HDC hdc = reinterpret_cast<HDC>(wParam);
                paintStaticText(hWnd, hdc);
                return 0;
            }

        case WM_SETTEXT:
            {
                // Let the default handle the text change, then repaint
                DefWindowProc(hWnd, uMsg, wParam, lParam);
                InvalidateRect(hWnd, nullptr, TRUE);
                return TRUE;
            }

        case WM_ENABLE:
            {
                // Let base window proc handle enable state change, but avoid any subclass chain
                // that might trigger themed drawing
                LRESULT result = DefWindowProc(hWnd, uMsg, wParam, lParam);
                // Force immediate repaint with our custom painting
                InvalidateRect(hWnd, nullptr, TRUE);
                UpdateWindow(hWnd);
                return result;
            }

        case WM_NCDESTROY:
#if _DEBUG
            RemovePropW( hWnd, L"ZoomIt.VersionFontLogged" );
#endif
            RemoveWindowSubclass(hWnd, StaticTextSubclassProc, uIdSubclass);
            break;
        }
    }
    else
    {
        if (uMsg == WM_NCDESTROY)
        {
#if _DEBUG
            RemovePropW( hWnd, L"ZoomIt.VersionFontLogged" );
#endif
            RemoveWindowSubclass(hWnd, StaticTextSubclassProc, uIdSubclass);
        }
    }
    return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}



//----------------------------------------------------------------------------
//
// TabControlSubclassProc
//
// Subclass procedure for tab control to handle dark mode background
//
//----------------------------------------------------------------------------
LRESULT CALLBACK TabControlSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
    switch (uMsg)
    {
    case WM_ERASEBKGND:
        if (IsDarkModeEnabled())
        {
            HDC hdc = reinterpret_cast<HDC>(wParam);
            RECT rc;
            GetClientRect(hWnd, &rc);
            FillRect(hdc, &rc, GetDarkModeBrush());
            return TRUE;
        }
        break;

    case WM_PAINT:
        if (IsDarkModeEnabled())
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);

            RECT rcClient;
            GetClientRect(hWnd, &rcClient);

            // Fill entire background with dark color
            FillRect(hdc, &rcClient, GetDarkModeBrush());

            // Get the display area (content area below tabs)
            RECT rcDisplay = rcClient;
            TabCtrl_AdjustRect(hWnd, FALSE, &rcDisplay);

            // Debug output
            TCHAR dbg[256];
            _stprintf_s(dbg, _T("TabCtrl: client=(%d,%d,%d,%d) display=(%d,%d,%d,%d)\n"),
                rcClient.left, rcClient.top, rcClient.right, rcClient.bottom,
                rcDisplay.left, rcDisplay.top, rcDisplay.right, rcDisplay.bottom);
            OutputDebugString(dbg);

            // Draw grey border around the display area
            HPEN hPen = CreatePen(PS_SOLID, 1, DarkMode::BorderColor);
            HPEN hOldPen = static_cast<HPEN>(SelectObject(hdc, hPen));
            HBRUSH hOldBrush = static_cast<HBRUSH>(SelectObject(hdc, GetStockObject(NULL_BRUSH)));

            // Draw border at the edges of the display area (inset by 1 to be visible)
            int left = rcDisplay.left;
            int top = rcDisplay.top - 1;
            int right = (rcDisplay.right < rcClient.right) ? rcDisplay.right : rcClient.right - 1;
            int bottom = (rcDisplay.bottom < rcClient.bottom) ? rcDisplay.bottom : rcClient.bottom - 1;

            _stprintf_s(dbg, _T("TabCtrl border: left=%d top=%d right=%d bottom=%d\n"), left, top, right, bottom);
            OutputDebugString(dbg);

            // Top line
            MoveToEx(hdc, left, top, NULL);
            LineTo(hdc, right, top);
            // Right line
            MoveToEx(hdc, right - 1, top, NULL);
            LineTo(hdc, right - 1, bottom);
            // Bottom line
            MoveToEx(hdc, left, bottom - 1, NULL);
            LineTo(hdc, right, bottom - 1);
            // Left line
            MoveToEx(hdc, left, top, NULL);
            LineTo(hdc, left, bottom);

            // Draw each tab
            int tabCount = TabCtrl_GetItemCount(hWnd);
            int selectedTab = TabCtrl_GetCurSel(hWnd);

            // Get the font from the tab control
            HFONT hFont = reinterpret_cast<HFONT>(SendMessage(hWnd, WM_GETFONT, 0, 0));
            HFONT hOldFont = hFont ? static_cast<HFONT>(SelectObject(hdc, hFont)) : nullptr;

            SetBkMode(hdc, TRANSPARENT);

            for (int i = 0; i < tabCount; i++)
            {
                RECT rcTab;
                TabCtrl_GetItemRect(hWnd, i, &rcTab);

                bool isSelected = (i == selectedTab);

                // Fill tab background
                FillRect(hdc, &rcTab, GetDarkModeBrush());

                // Draw grey border around tab (left, top, right)
                MoveToEx(hdc, rcTab.left, rcTab.bottom - 1, NULL);
                LineTo(hdc, rcTab.left, rcTab.top);
                LineTo(hdc, rcTab.right - 1, rcTab.top);
                LineTo(hdc, rcTab.right - 1, rcTab.bottom);

                // For selected tab, erase the bottom border to merge with content
                if (isSelected)
                {
                    HPEN hBgPen = CreatePen(PS_SOLID, 1, DarkMode::BackgroundColor);
                    SelectObject(hdc, hBgPen);
                    MoveToEx(hdc, rcTab.left + 1, rcTab.bottom - 1, NULL);
                    LineTo(hdc, rcTab.right - 1, rcTab.bottom - 1);
                    SelectObject(hdc, hPen);
                    DeleteObject(hBgPen);
                }

                // Get tab text
                TCITEM tci = {};
                tci.mask = TCIF_TEXT;
                TCHAR szText[128] = { 0 };
                tci.pszText = szText;
                tci.cchTextMax = _countof(szText);
                TabCtrl_GetItem(hWnd, i, &tci);

                // Draw text
                SetTextColor(hdc, isSelected ? DarkMode::TextColor : DarkMode::DisabledTextColor);
                RECT rcText = rcTab;
                rcText.top += 4;
                DrawText(hdc, szText, -1, &rcText, DT_CENTER | DT_VCENTER | DT_SINGLELINE);

                // Draw underline for selected tab
                if (isSelected)
                {
                    RECT rcUnderline = rcTab;
                    rcUnderline.top = rcUnderline.bottom - 2;
                    rcUnderline.left += 1;
                    rcUnderline.right -= 1;
                    HBRUSH hAccent = CreateSolidBrush(DarkMode::AccentColor);
                    FillRect(hdc, &rcUnderline, hAccent);
                    DeleteObject(hAccent);
                }
            }

            if (hOldFont)
                SelectObject(hdc, hOldFont);
            SelectObject(hdc, hOldBrush);
            SelectObject(hdc, hOldPen);
            DeleteObject(hPen);

            EndPaint(hWnd, &ps);
            return 0;
        }
        break;

    case WM_NCDESTROY:
        RemoveWindowSubclass(hWnd, TabControlSubclassProc, uIdSubclass);
        break;
    }
    return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}

//----------------------------------------------------------------------------
//
// OptionsProc
//
//----------------------------------------------------------------------------
INT_PTR CALLBACK OptionsProc( HWND hDlg, UINT message,
                             WPARAM wParam, LPARAM lParam )
{
    constexpr UINT WM_APPLY_HEADER_FONTS = WM_APP + 42;
    static HFONT	hFontBold = nullptr;
    static HFONT    hFontVersion = nullptr;
    PNMLINK			notify = nullptr;
    static int		curTabSel = 0;
    static HWND		hTabCtrl;
    static HWND		hOpacity;
    static HWND		hToggleKey;
    static UINT		currentDpi = DPI_BASELINE;
    static RECT     stableWindowRect{};
    static bool     stableWindowRectValid = false;
    TCHAR			text[32];
    DWORD			newToggleKey, newTimeout, newToggleMod, newBreakToggleKey, newDemoTypeToggleKey, newRecordToggleKey, newSnipToggleKey, newSnipPanoramaToggleKey, newSnipOcrToggleKey;
    DWORD			newDrawToggleKey, newDrawToggleMod, newBreakToggleMod, newDemoTypeToggleMod, newRecordToggleMod, newSnipToggleMod, newSnipPanoramaToggleMod, newSnipOcrToggleMod;
    DWORD			newLiveZoomToggleKey, newLiveZoomToggleMod;
    static std::vector<std::pair<std::wstring, std::wstring>>	microphones;

    auto CleanupFonts = [&]()
    {
        if( hFontBold )
        {
            DeleteObject( hFontBold );
            hFontBold = nullptr;
        }
        if( hFontVersion )
        {
            DeleteObject( hFontVersion );
            hFontVersion = nullptr;
        }
    };

    auto UpdateVersionFont = [&]()
    {
        if( hFontVersion )
        {
            DeleteObject( hFontVersion );
            hFontVersion = nullptr;
        }

        HWND hVersion = GetDlgItem( hDlg, IDC_VERSION );
        if( !hVersion )
        {
            return;
        }

        // Prefer the control's current font (it may already be DPI-scaled).
        HFONT hBaseFont = reinterpret_cast<HFONT>(SendMessage( hVersion, WM_GETFONT, 0, 0 ));
        if( !hBaseFont )
        {
            hBaseFont = reinterpret_cast<HFONT>(SendMessage( hDlg, WM_GETFONT, 0, 0 ));
        }
        if( !hBaseFont )
        {
            hBaseFont = static_cast<HFONT>(GetStockObject( DEFAULT_GUI_FONT ));
        }

        LOGFONT lf{};
        if( !GetObject( hBaseFont, sizeof( lf ), &lf ) )
        {
            return;
        }

        // Make the header version text title-sized using an explicit point size,
        // scaled by the current DPI.
        const UINT dpi = GetDpiForWindowHelper( hDlg );
        constexpr int kTitlePointSize = 22;

        lf.lfWeight = FW_BOLD;
        lf.lfHeight = -MulDiv( kTitlePointSize, static_cast<int>(dpi), 72 );
        hFontVersion = CreateFontIndirect( &lf );
        if( hFontVersion )
        {
            SendMessage( hVersion, WM_SETFONT, reinterpret_cast<WPARAM>(hFontVersion), TRUE );
            // Also store in a property so our subclass paint can reliably retrieve it.
            SetPropW( hVersion, L"ZoomIt.HeaderFont", reinterpret_cast<HANDLE>(hFontVersion) );
#if _DEBUG
            HFONT checkFont = static_cast<HFONT>(GetPropW( hVersion, L"ZoomIt.HeaderFont" ));
            OutputDebug( L"SetPropW HeaderFont: hwnd=%p font=%p verify=%p\n", hVersion, hFontVersion, checkFont );
#endif
        }

    #if _DEBUG
        OutputDebug(L"UpdateVersionFont: dpi=%u titlePt=%d lfHeight=%d font=%p\n",
            dpi, kTitlePointSize, lf.lfHeight, hFontVersion );

        {
            HFONT currentFont = reinterpret_cast<HFONT>(SendMessage( hVersion, WM_GETFONT, 0, 0 ));
            LOGFONT currentLf{};
            if( currentFont && GetObject( currentFont, sizeof( currentLf ), &currentLf ) )
            {
                OutputDebug( L"IDC_VERSION WM_GETFONT after set: font=%p lfHeight=%d lfWeight=%d\n",
                    currentFont, currentLf.lfHeight, currentLf.lfWeight );
            }
            else
            {
                OutputDebug( L"IDC_VERSION WM_GETFONT after set: font=%p (no logfont)\n", currentFont );
            }
        }
    #endif

        // Resize the version control to fit the new font, and reflow the lines below if needed.
        RECT rcVersion{};
        GetWindowRect( hVersion, &rcVersion );
        MapWindowPoints( nullptr, hDlg, reinterpret_cast<LPPOINT>(&rcVersion), 2 );
        const int oldVersionHeight = rcVersion.bottom - rcVersion.top;

        TCHAR versionText[128] = {};
        GetWindowText( hVersion, versionText, _countof( versionText ) );

        RECT rcCalc{ 0, 0, 0, 0 };
        HDC hdc = GetDC( hVersion );
        if( hdc )
        {
            HFONT oldFont = static_cast<HFONT>(SelectObject( hdc, hFontVersion ? hFontVersion : hBaseFont ));
            DrawText( hdc, versionText, -1, &rcCalc, DT_CALCRECT | DT_SINGLELINE | DT_LEFT | DT_VCENTER );
            SelectObject( hdc, oldFont );
            ReleaseDC( hVersion, hdc );
        }

        // Keep within dialog client width.
        RECT rcClient{};
        GetClientRect( hDlg, &rcClient );
        const int maxWidth = max( 0, rcClient.right - rcVersion.left - ScaleForDpi( 8, GetDpiForWindowHelper( hDlg ) ) );
        const int desiredWidth = min( maxWidth, (rcCalc.right - rcCalc.left) + ScaleForDpi( 6, GetDpiForWindowHelper( hDlg ) ) );
        const int desiredHeight = (rcCalc.bottom - rcCalc.top) + ScaleForDpi( 2, GetDpiForWindowHelper( hDlg ) );
        const int newVersionHeight = max( oldVersionHeight, desiredHeight );

        SetWindowPos( hVersion, nullptr,
            rcVersion.left, rcVersion.top,
            max( 1, desiredWidth ), newVersionHeight,
            SWP_NOZORDER | SWP_NOACTIVATE );

#if _DEBUG
        {
            RECT rcAfter{};
            GetClientRect( hVersion, &rcAfter );
            OutputDebug( L"UpdateVersionFont resize: desired=(%d,%d) oldH=%d newH=%d actual=(%d,%d)\n",
                desiredWidth, desiredHeight, oldVersionHeight, newVersionHeight,
                rcAfter.right - rcAfter.left, rcAfter.bottom - rcAfter.top );
        }
#endif

        InvalidateRect( hVersion, nullptr, TRUE );

        const int deltaY = newVersionHeight - oldVersionHeight;
        if( deltaY > 0 )
        {
            const int headerIdsToShift[] = { IDC_COPYRIGHT, IDC_LINK };
            for( int i = 0; i < _countof( headerIdsToShift ); i++ )
            {
                HWND hCtrl = GetDlgItem( hDlg, headerIdsToShift[i] );
                if( !hCtrl )
                {
                    continue;
                }
                RECT rc{};
                GetWindowRect( hCtrl, &rc );
                MapWindowPoints( nullptr, hDlg, reinterpret_cast<LPPOINT>(&rc), 2 );
                SetWindowPos( hCtrl, nullptr,
                    rc.left, rc.top + deltaY,
                    0, 0,
                    SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE );
            }
        }
    };

    switch ( message )  {
    case WM_INITDIALOG:
    {
        if( hWndOptions ) {

            BringWindowToTop( hWndOptions );
            SetFocus( hWndOptions );
            SetForegroundWindow( hWndOptions );
            EndDialog( hDlg, 0 );
            return FALSE;
        }
        hWndOptions = hDlg;

        // Set the dialog icon
        {
            HICON hIcon = LoadIcon( g_hInstance, L"APPICON" );
            if( hIcon )
            {
                SendMessage( hDlg, WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(hIcon) );
                SendMessage( hDlg, WM_SETICON, ICON_SMALL, reinterpret_cast<LPARAM>(hIcon) );
            }
        }

        SetForegroundWindow( hDlg );
        SetActiveWindow( hDlg );
        // Do not force-show the dialog here. DialogBox will show it after WM_INITDIALOG
        // returns, and showing early causes visible layout churn while we add tabs, scale,
        // and center the window.
#if 1
        // set version info
        TCHAR               filePath[MAX_PATH];
        const TCHAR* verString;

        GetModuleFileName(NULL, filePath, _countof(filePath));
        DWORD               zero = 0;
        DWORD               infoSize = GetFileVersionInfoSize(filePath, &zero);
        void* versionInfo = malloc(infoSize);
        GetFileVersionInfo(filePath, 0, infoSize, versionInfo);

        verString = GetVersionString(static_cast<VERSION_INFO*>(versionInfo), _T("FileVersion"));
        SetDlgItemText(hDlg, IDC_VERSION, (std::wstring(L"ZoomIt v") + verString).c_str());

        verString = GetVersionString(static_cast<VERSION_INFO*>(versionInfo), _T("LegalCopyright"));
        SetDlgItemText(hDlg, IDC_COPYRIGHT, verString);

        free(versionInfo);
#endif
        // Add tabs
        hTabCtrl = GetDlgItem( hDlg, IDC_TAB );

        // Set owner-draw style for tab control when in dark mode
        if (IsDarkModeEnabled())
        {
            LONG_PTR style = GetWindowLongPtr(hTabCtrl, GWL_STYLE);
            SetWindowLongPtr(hTabCtrl, GWL_STYLE, style | TCS_OWNERDRAWFIXED);
            // Subclass the tab control for dark mode background painting
            SetWindowSubclass(hTabCtrl, TabControlSubclassProc, 1, 0);
        }

        OptionsAddTabs( hDlg, hTabCtrl );

        // Configure options
        SendMessage( GetDlgItem( g_OptionsTabs[ZOOM_PAGE].hPage, IDC_HOTKEY), HKM_SETRULES,
            static_cast<WPARAM>(HKCOMB_NONE), // invalid key combinations
            MAKELPARAM(HOTKEYF_ALT, 0));     // add ALT to invalid entries

        if( g_ToggleKey )		SendMessage( GetDlgItem( g_OptionsTabs[ZOOM_PAGE].hPage, IDC_HOTKEY), HKM_SETHOTKEY, g_ToggleKey, 0 );
        if( pMagInitialize ) {

            if( g_LiveZoomToggleKey )	SendMessage( GetDlgItem( g_OptionsTabs[LIVE_PAGE].hPage, IDC_LIVE_HOTKEY), HKM_SETHOTKEY, g_LiveZoomToggleKey, 0 );

        } else {

            EnableWindow( GetDlgItem( g_OptionsTabs[LIVE_PAGE].hPage, IDC_LIVE_HOTKEY), FALSE );
            EnableWindow( GetDlgItem( g_OptionsTabs[LIVE_PAGE].hPage, IDC_ZOOM_LEVEL), FALSE );
            EnableWindow( GetDlgItem( g_OptionsTabs[LIVE_PAGE].hPage, IDC_ZOOM_SPIN), FALSE );
        }
        if( g_DrawToggleKey )	SendMessage( GetDlgItem( g_OptionsTabs[DRAW_PAGE].hPage, IDC_DRAW_HOTKEY), HKM_SETHOTKEY, g_DrawToggleKey, 0 );
        if( g_BreakToggleKey )	SendMessage( GetDlgItem( g_OptionsTabs[BREAK_PAGE].hPage, IDC_BREAK_HOTKEY), HKM_SETHOTKEY, g_BreakToggleKey, 0 );
        if( g_DemoTypeToggleKey ) SendMessage( GetDlgItem( g_OptionsTabs[DEMOTYPE_PAGE].hPage, IDC_DEMOTYPE_HOTKEY ), HKM_SETHOTKEY, g_DemoTypeToggleKey, 0 );
        if( g_RecordToggleKey )	SendMessage( GetDlgItem( g_OptionsTabs[RECORD_PAGE].hPage, IDC_RECORD_HOTKEY), HKM_SETHOTKEY, g_RecordToggleKey, 0 );
        if( g_SnipToggleKey) 	SendMessage( GetDlgItem( g_OptionsTabs[SNIP_PAGE].hPage, IDC_SNIP_HOTKEY), HKM_SETHOTKEY, g_SnipToggleKey, 0 );
        if( g_SnipPanoramaToggleKey) SendMessage( GetDlgItem( g_OptionsTabs[PANORAMA_PAGE].hPage, IDC_SNIP_PANORAMA_HOTKEY), HKM_SETHOTKEY, g_SnipPanoramaToggleKey, 0 );
        if( g_SnipOcrToggleKey) SendMessage( GetDlgItem( g_OptionsTabs[SNIP_PAGE].hPage, IDC_SNIP_OCR_HOTKEY), HKM_SETHOTKEY, g_SnipOcrToggleKey, 0 );
        CheckDlgButton( hDlg, IDC_SHOW_TRAY_ICON,
            g_ShowTrayIcon ? BST_CHECKED: BST_UNCHECKED );
        CheckDlgButton( hDlg, IDC_AUTOSTART,
            IsAutostartConfigured() ? BST_CHECKED: BST_UNCHECKED );
        CheckDlgButton( g_OptionsTabs[ZOOM_PAGE].hPage, IDC_ANIMATE_ZOOM,
            g_AnimateZoom ? BST_CHECKED: BST_UNCHECKED );
        CheckDlgButton( g_OptionsTabs[ZOOM_PAGE].hPage, IDC_SMOOTH_IMAGE,
            g_SmoothImage ? BST_CHECKED: BST_UNCHECKED );

        SendMessage( GetDlgItem(g_OptionsTabs[ZOOM_PAGE].hPage, IDC_ZOOM_SLIDER), TBM_SETRANGE, false, MAKELONG(0,_countof(g_ZoomLevels)-1) );
        SendMessage( GetDlgItem(g_OptionsTabs[ZOOM_PAGE].hPage, IDC_ZOOM_SLIDER), TBM_SETPOS, true, g_SliderZoomLevel );

        _stprintf( text, L"%d", g_PenWidth );
        SetDlgItemText( g_OptionsTabs[DRAW_PAGE].hPage, IDC_PEN_WIDTH, text );
        SendMessage( GetDlgItem( g_OptionsTabs[DRAW_PAGE].hPage, IDC_PEN_WIDTH ), EM_LIMITTEXT, 1, 0 );
        SendMessage (GetDlgItem( g_OptionsTabs[DRAW_PAGE].hPage, IDC_SPIN), UDM_SETRANGE, 0L,
                            MAKELPARAM (19, 1));

        _stprintf( text, L"%d", g_BreakTimeout );
        SetDlgItemText( g_OptionsTabs[BREAK_PAGE].hPage, IDC_TIMER, text );
        SendMessage( GetDlgItem( g_OptionsTabs[BREAK_PAGE].hPage, IDC_TIMER ), EM_LIMITTEXT, 2, 0 );
        SendMessage (GetDlgItem( g_OptionsTabs[BREAK_PAGE].hPage, IDC_SPIN_TIMER), UDM_SETRANGE, 0L,
                            MAKELPARAM (99, 1));
        CheckDlgButton( g_OptionsTabs[BREAK_PAGE].hPage, IDC_CHECK_SHOW_EXPIRED,
            g_ShowExpiredTime ? BST_CHECKED : BST_UNCHECKED );
        CheckDlgButton( g_OptionsTabs[BREAK_PAGE].hPage, IDC_CHECK_LOCK_WORKSTATION,
            g_BreakLockWorkstation ? BST_CHECKED : BST_UNCHECKED );

        CheckDlgButton( g_OptionsTabs[RECORD_PAGE].hPage, IDC_CAPTURE_SYSTEM_AUDIO,
            g_CaptureSystemAudio ? BST_CHECKED: BST_UNCHECKED );

        CheckDlgButton( g_OptionsTabs[RECORD_PAGE].hPage, IDC_CAPTURE_AUDIO,
            g_CaptureAudio ? BST_CHECKED: BST_UNCHECKED );

        CheckDlgButton( g_OptionsTabs[RECORD_PAGE].hPage, IDC_MIC_MONO_MIX,
            g_MicMonoMix ? BST_CHECKED: BST_UNCHECKED );

        //
        // The framerate drop down list is not used in the current version (might be added in the future)
        //
        /*for (int i = 0; i < _countof(g_FramerateOptions); i++) {

            _stprintf(text, L"%d", g_FramerateOptions[i]);
            SendMessage(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_RECORD_FRAME_RATE), static_cast<UINT>(CB_ADDSTRING),
                static_cast<WPARAM>(0), reinterpret_cast<LPARAM>(text));
            if (g_RecordFrameRate == g_FramerateOptions[i]) {

                SendMessage(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_RECORD_FRAME_RATE), CB_SETCURSEL, static_cast<WPARAM>(i), static_cast<LPARAM>(0));
            }
        }*/

        // Add the recording format to the combo box and set the current selection
        size_t selection = 0;
        const wchar_t* currentFormatString = (g_RecordingFormat == RecordingFormat::GIF) ? L"GIF" : L"MP4";

        for( size_t i = 0; i < (sizeof(g_RecordingFormats) / sizeof(g_RecordingFormats[0])); i++ )
        {
            SendMessage( GetDlgItem( g_OptionsTabs[RECORD_PAGE].hPage, IDC_RECORD_FORMAT ), static_cast<UINT>(CB_ADDSTRING), static_cast<WPARAM>(0), reinterpret_cast<LPARAM>(g_RecordingFormats[i]) );

            if( selection == 0 && wcscmp( g_RecordingFormats[i], currentFormatString ) == 0 )
            {
                selection = i;
            }
        }
        SendMessage( GetDlgItem( g_OptionsTabs[RECORD_PAGE].hPage, IDC_RECORD_FORMAT ), CB_SETCURSEL, static_cast<WPARAM>(selection), static_cast<LPARAM>(0) );

        for(unsigned int i = 1; i < 11; i++) {

            _stprintf(text, L"%2.1f", (static_cast<double>(i)) / 10 );
            SendMessage(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_RECORD_SCALING), static_cast<UINT>(CB_ADDSTRING),
                static_cast<WPARAM>(0), reinterpret_cast<LPARAM>(text));

            if (g_RecordingFormat == RecordingFormat::GIF && i*10 == g_RecordScalingGIF ) {

                SendMessage(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_RECORD_SCALING), CB_SETCURSEL, static_cast<WPARAM>(i)-1, static_cast<LPARAM>(0));
            }
            if (g_RecordingFormat == RecordingFormat::MP4 && i*10 == g_RecordScalingMP4 ) {

                SendMessage(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_RECORD_SCALING), CB_SETCURSEL, static_cast<WPARAM>(i)-1, static_cast<LPARAM>(0));
            }
        }

        // Get the current set of microphones
        microphones.clear();
        concurrency::create_task([]{
            auto devices = winrt::DeviceInformation::FindAllAsync( winrt::DeviceClass::AudioCapture ).get();
            for( auto device : devices )
            {
                microphones.emplace_back( device.Id().c_str(), device.Name().c_str() );
            }
        }).get();

        // Add the microphone devices to the combo box and set the current selection
        SendMessage( GetDlgItem( g_OptionsTabs[RECORD_PAGE].hPage, IDC_MICROPHONE ), static_cast<UINT>(CB_ADDSTRING), static_cast<WPARAM>(0), reinterpret_cast<LPARAM>(L"Default"));
        selection = 0;
        for( size_t i = 0; i < microphones.size(); i++ )
        {
            SendMessage( GetDlgItem( g_OptionsTabs[RECORD_PAGE].hPage, IDC_MICROPHONE ), static_cast<UINT>(CB_ADDSTRING), static_cast<WPARAM>(0), reinterpret_cast<LPARAM>(microphones[i].second.c_str()) );
            if( selection == 0 && wcscmp( microphones[i].first.c_str(), g_MicrophoneDeviceId ) == 0 )
            {
                selection = i + 1;
            }
        }
        SendMessage( GetDlgItem( g_OptionsTabs[RECORD_PAGE].hPage, IDC_MICROPHONE ), CB_SETCURSEL, static_cast<WPARAM>(selection), static_cast<LPARAM>(0) );

        // Set initial state of audio controls based on recording format (GIF has no audio)
        bool isGifSelected = (g_RecordingFormat == RecordingFormat::GIF);
        EnableWindow(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_CAPTURE_SYSTEM_AUDIO), !isGifSelected);
        EnableWindow(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_CAPTURE_AUDIO), !isGifSelected);
        EnableWindow(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_MICROPHONE_LABEL), !isGifSelected);
        EnableWindow(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_MICROPHONE), !isGifSelected);

        if( GetFileAttributes( g_DemoTypeFile ) == -1 )
        {
            memset( g_DemoTypeFile, 0, sizeof( g_DemoTypeFile ) );
        }
        else
        {
            SetDlgItemText( g_OptionsTabs[DEMOTYPE_PAGE].hPage, IDC_DEMOTYPE_FILE, g_DemoTypeFile );
        }
        SendMessage( GetDlgItem( g_OptionsTabs[DEMOTYPE_PAGE].hPage, IDC_DEMOTYPE_SPEED_SLIDER ), TBM_SETRANGE, false, MAKELONG( MAX_TYPING_SPEED, MIN_TYPING_SPEED ) );
        SendMessage( GetDlgItem( g_OptionsTabs[DEMOTYPE_PAGE].hPage, IDC_DEMOTYPE_SPEED_SLIDER ), TBM_SETPOS, true, g_DemoTypeSpeedSlider );
        CheckDlgButton( g_OptionsTabs[DEMOTYPE_PAGE].hPage, IDC_DEMOTYPE_USER_DRIVEN, g_DemoTypeUserDriven ? BST_CHECKED: BST_UNCHECKED );

        // Apply DPI scaling to the main dialog and to controls inside tab pages.
        // Note: Scaling the main dialog only scales its direct children (including the
        // tab page windows), but NOT the controls contained within the tab pages.
        // So we scale each tab page's child controls separately.
        currentDpi = GetDpiForWindowHelper( hDlg );
        if( currentDpi != DPI_BASELINE )
        {
            ScaleDialogForDpi( hDlg, currentDpi, DPI_BASELINE );

            for( int i = 0; i < sizeof( g_OptionsTabs ) / sizeof( g_OptionsTabs[0] ); i++ )
            {
                if( g_OptionsTabs[i].hPage )
                {
                    ScaleChildControlsForDpi( g_OptionsTabs[i].hPage, currentDpi, DPI_BASELINE );
                }
            }
        }
        // Always reposition tab pages to fit the tab control (whether scaled or not)
        RepositionTabPages( hTabCtrl );

        // Initialize DPI-aware fonts after scaling so text sizing is correct.
        InitializeFonts( hDlg, &hFontBold );
        UpdateDrawTabHeaderFont();
        UpdateVersionFont();

        // Always render the header labels using our static text subclass (even in light mode)
        // so the larger title font is honored.
        if( HWND hVersion = GetDlgItem( hDlg, IDC_VERSION ) )
        {
            SetWindowSubclass( hVersion, StaticTextSubclassProc, 55, 0 );
        }
        if( HWND hCopyright = GetDlgItem( hDlg, IDC_COPYRIGHT ) )
        {
            SetWindowSubclass( hCopyright, StaticTextSubclassProc, 56, 0 );
        }

        // Apply dark mode to the dialog and all tab pages
        ApplyDarkModeToDialog( hDlg );
        for( int i = 0; i < sizeof( g_OptionsTabs ) / sizeof( g_OptionsTabs[0] ); i++ )
        {
            if( g_OptionsTabs[i].hPage )
            {
                ApplyDarkModeToDialog( g_OptionsTabs[i].hPage );
            }
        }

        UnregisterAllHotkeys(GetParent( hDlg ));

        // Center dialog on screen, clamping to fit if it's too large for the work area
        {
            RECT rcDlg;
            GetWindowRect(hDlg, &rcDlg);
            int dlgWidth = rcDlg.right - rcDlg.left;
            int dlgHeight = rcDlg.bottom - rcDlg.top;

            // Get the monitor where the cursor is
            POINT pt;
            GetCursorPos(&pt);
            HMONITOR hMon = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
            MONITORINFO mi = { sizeof(mi) };
            GetMonitorInfo(hMon, &mi);

            // Calculate available work area size
            const int workWidth = mi.rcWork.right - mi.rcWork.left;
            const int workHeight = mi.rcWork.bottom - mi.rcWork.top;

            // Clamp dialog size to fit within work area (with a small margin)
            constexpr int kMargin = 8;
            if (dlgWidth > workWidth - kMargin * 2)
            {
                dlgWidth = workWidth - kMargin * 2;
            }
            if (dlgHeight > workHeight - kMargin * 2)
            {
                dlgHeight = workHeight - kMargin * 2;
            }

            // Apply clamped size if it changed
            if (dlgWidth != (rcDlg.right - rcDlg.left) || dlgHeight != (rcDlg.bottom - rcDlg.top))
            {
                SetWindowPos(hDlg, nullptr, 0, 0, dlgWidth, dlgHeight, SWP_NOMOVE | SWP_NOZORDER);
            }

            int x = mi.rcWork.left + (workWidth - dlgWidth) / 2;
            int y = mi.rcWork.top + (workHeight - dlgHeight) / 2;
            SetWindowPos(hDlg, nullptr, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
        }

        // Capture a stable window size so per-monitor DPI changes won't resize/reflow the dialog.
        GetWindowRect(hDlg, &stableWindowRect);
        stableWindowRectValid = true;

        PostMessage( hDlg, WM_USER, 0, 0 );
        // Reapply header fonts once the dialog has finished any late initialization.
        PostMessage( hDlg, WM_APPLY_HEADER_FONTS, 0, 0 );

        // Set focus to the tab control instead of the first hotkey control
        SetFocus( hTabCtrl );
        return FALSE;
    }

    case WM_APPLY_HEADER_FONTS:
        InitializeFonts( hDlg, &hFontBold );
        UpdateDrawTabHeaderFont();
        UpdateVersionFont();
        return TRUE;

    case WM_USER+100:
        BringWindowToTop( hDlg );
        SetFocus( hDlg );
        SetForegroundWindow( hDlg );
        return TRUE;

    case WM_DPICHANGED:
    {
        // Requirement: keep the Options dialog stable while it is open.
        // Windows may already have resized the window by the time this arrives,
        // so explicitly restore the previous size (but allow the suggested top-left).

        RECT* suggested = reinterpret_cast<RECT*>(lParam);
        if (stableWindowRectValid && suggested)
        {
            const int stableW = stableWindowRect.right - stableWindowRect.left;
            const int stableH = stableWindowRect.bottom - stableWindowRect.top;
            SetWindowPos(hDlg, nullptr,
                suggested->left,
                suggested->top,
                stableW,
                stableH,
                SWP_NOZORDER | SWP_NOACTIVATE);
        }
        return TRUE;
    }

    case WM_ERASEBKGND:
        if (IsDarkModeEnabled())
        {
            HDC hdc = reinterpret_cast<HDC>(wParam);
            RECT rc;
            GetClientRect(hDlg, &rc);
            FillRect(hdc, &rc, GetDarkModeBrush());
            return TRUE;
        }
        break;

    case WM_CTLCOLORDLG:
    case WM_CTLCOLORSTATIC:
    case WM_CTLCOLORBTN:
    case WM_CTLCOLOREDIT:
    case WM_CTLCOLORLISTBOX:
    {
        HDC hdc = reinterpret_cast<HDC>(wParam);
        HWND hCtrl = reinterpret_cast<HWND>(lParam);

        // Always force the Options header title to use the large version font.
        // Note: We must also return a brush in light mode
        // dialog proc may ignore our HDC changes.
        if( message == WM_CTLCOLORSTATIC && hCtrl == GetDlgItem( hDlg, IDC_VERSION ) && hFontVersion )
        {
            SetBkMode( hdc, TRANSPARENT );
            SelectObject( hdc, hFontVersion );

#if _DEBUG
            OutputDebug( L"WM_CTLCOLORSTATIC IDC_VERSION: dark=%d font=%p\n", IsDarkModeEnabled() ? 1 : 0, hFontVersion );
#endif

            if( !IsDarkModeEnabled() )
            {
                // Light mode: explicitly return the dialog background brush.
                return reinterpret_cast<INT_PTR>(GetSysColorBrush( COLOR_BTNFACE ));
            }
        }

        // Handle dark mode colors
        HBRUSH hBrush = HandleDarkModeCtlColor(hdc, hCtrl, message);
        if (hBrush)
        {
            // Ensure the header version text uses the title font in dark mode.
            if( message == WM_CTLCOLORSTATIC && hCtrl == GetDlgItem( hDlg, IDC_VERSION ) && hFontVersion )
            {
                SelectObject( hdc, hFontVersion );
            }

            // For bold title controls, also set the bold font
            if (message == WM_CTLCOLORSTATIC &&
                (hCtrl == GetDlgItem(hDlg, IDC_TITLE) ||
                 hCtrl == GetDlgItem(hDlg, IDC_DRAWING) ||
                 hCtrl == GetDlgItem(hDlg, IDC_ZOOM) ||
                 hCtrl == GetDlgItem(hDlg, IDC_BREAK) ||
                 hCtrl == GetDlgItem(hDlg, IDC_TYPE)))
            {
                SelectObject(hdc, hFontBold);
            }
            return reinterpret_cast<INT_PTR>(hBrush);
        }

        // Light mode handling for bold title controls
        if (message == WM_CTLCOLORSTATIC &&
            (hCtrl == GetDlgItem(hDlg, IDC_TITLE) ||
             hCtrl == GetDlgItem(hDlg, IDC_DRAWING) ||
             hCtrl == GetDlgItem(hDlg, IDC_ZOOM) ||
             hCtrl == GetDlgItem(hDlg, IDC_BREAK) ||
             hCtrl == GetDlgItem(hDlg, IDC_TYPE)))
        {
            SetBkMode(hdc, TRANSPARENT);
            SelectObject(hdc, hFontBold);
            return reinterpret_cast<INT_PTR>(GetSysColorBrush(COLOR_BTNFACE));
        }
        break;
    }

    case WM_SETTINGCHANGE:
        // Handle theme change (dark/light mode toggle)
        if (lParam && (wcscmp(reinterpret_cast<LPCWSTR>(lParam), L"ImmersiveColorSet") == 0))
        {
            RefreshDarkModeState();
            ApplyDarkModeToDialog(hDlg);
            for (int i = 0; i < sizeof(g_OptionsTabs) / sizeof(g_OptionsTabs[0]); i++)
            {
                if (g_OptionsTabs[i].hPage)
                {
                    ApplyDarkModeToDialog(g_OptionsTabs[i].hPage);
                }
            }
            InvalidateRect(hDlg, nullptr, TRUE);
            for (int i = 0; i < sizeof(g_OptionsTabs) / sizeof(g_OptionsTabs[0]); i++)
            {
                if (g_OptionsTabs[i].hPage)
                {
                    InvalidateRect(g_OptionsTabs[i].hPage, nullptr, TRUE);
                }
            }
        }
        break;

    case WM_DRAWITEM:
    {
        // Handle owner-draw for tab control in dark mode
        DRAWITEMSTRUCT* pDIS = reinterpret_cast<DRAWITEMSTRUCT*>(lParam);
        if (pDIS->CtlID == IDC_TAB && IsDarkModeEnabled())
        {
            // Fill tab background
            HBRUSH hBrush = GetDarkModeBrush();
            FillRect(pDIS->hDC, &pDIS->rcItem, hBrush);

            // Get tab text
            TCITEM tci = {};
            tci.mask = TCIF_TEXT;
            TCHAR szText[128] = { 0 };
            tci.pszText = szText;
            tci.cchTextMax = _countof(szText);
            TabCtrl_GetItem(hTabCtrl, pDIS->itemID, &tci);

            // Draw text
            SetBkMode(pDIS->hDC, TRANSPARENT);
            bool isSelected = (pDIS->itemState & ODS_SELECTED) != 0;
            SetTextColor(pDIS->hDC, isSelected ? DarkMode::TextColor : DarkMode::DisabledTextColor);

            // Draw underline for selected tab
            if (isSelected)
            {
                RECT rcUnderline = pDIS->rcItem;
                rcUnderline.top = rcUnderline.bottom - 2;
                HBRUSH hAccent = CreateSolidBrush(DarkMode::AccentColor);
                FillRect(pDIS->hDC, &rcUnderline, hAccent);
                DeleteObject(hAccent);
            }

            RECT rcText = pDIS->rcItem;
            rcText.top += 4;
            DrawText(pDIS->hDC, szText, -1, &rcText, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
            return TRUE;
        }
        break;
    }

    case WM_NOTIFY:
        notify = reinterpret_cast<PNMLINK>(lParam);
        if( notify->hdr.idFrom == IDC_LINK )
        {
            switch( notify->hdr.code )
            {
            case NM_CLICK:
            case NM_RETURN:
                ShellExecute( hDlg, _T("open"), notify->item.szUrl, NULL, NULL, SW_SHOWNORMAL );
                break;
            }
        }
        else switch( notify->hdr.code )
        {
        case TCN_SELCHANGE:
            ShowWindow( g_OptionsTabs[curTabSel].hPage, SW_HIDE );
            curTabSel = TabCtrl_GetCurSel(hTabCtrl);
            ShowWindow( g_OptionsTabs[curTabSel].hPage, SW_SHOW );
            break;
        }
        break;

    case WM_COMMAND:
        switch ( LOWORD( wParam )) {
        case IDOK:
        {
            if( !ConfigureAutostart( hDlg, IsDlgButtonChecked( hDlg, IDC_AUTOSTART) == BST_CHECKED )) {

                break;
            }
            g_ShowTrayIcon = IsDlgButtonChecked( hDlg, IDC_SHOW_TRAY_ICON ) == BST_CHECKED;
            g_AnimateZoom = IsDlgButtonChecked( g_OptionsTabs[ZOOM_PAGE].hPage, IDC_ANIMATE_ZOOM ) == BST_CHECKED;
            g_SmoothImage = IsDlgButtonChecked( g_OptionsTabs[ZOOM_PAGE].hPage, IDC_SMOOTH_IMAGE ) == BST_CHECKED;
            g_DemoTypeUserDriven = IsDlgButtonChecked( g_OptionsTabs[DEMOTYPE_PAGE].hPage, IDC_DEMOTYPE_USER_DRIVEN ) == BST_CHECKED;

            newToggleKey = static_cast<DWORD>(SendMessage( GetDlgItem( g_OptionsTabs[ZOOM_PAGE].hPage, IDC_HOTKEY), HKM_GETHOTKEY, 0, 0 ));
            newLiveZoomToggleKey = static_cast<DWORD>(SendMessage( GetDlgItem( g_OptionsTabs[LIVE_PAGE].hPage, IDC_LIVE_HOTKEY), HKM_GETHOTKEY, 0, 0 ));
            newDrawToggleKey = static_cast<DWORD>(SendMessage( GetDlgItem( g_OptionsTabs[DRAW_PAGE].hPage, IDC_DRAW_HOTKEY), HKM_GETHOTKEY, 0, 0 ));
            newBreakToggleKey = static_cast<DWORD>(SendMessage( GetDlgItem( g_OptionsTabs[BREAK_PAGE].hPage, IDC_BREAK_HOTKEY), HKM_GETHOTKEY, 0, 0 ));
            newDemoTypeToggleKey = static_cast<DWORD>(SendMessage( GetDlgItem( g_OptionsTabs[DEMOTYPE_PAGE].hPage, IDC_DEMOTYPE_HOTKEY ), HKM_GETHOTKEY, 0, 0 ));
            newRecordToggleKey = static_cast<DWORD>(SendMessage(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_RECORD_HOTKEY), HKM_GETHOTKEY, 0, 0));
            newSnipToggleKey = static_cast<DWORD>(SendMessage( GetDlgItem( g_OptionsTabs[SNIP_PAGE].hPage, IDC_SNIP_HOTKEY), HKM_GETHOTKEY, 0, 0 ));
            newSnipPanoramaToggleKey = static_cast<DWORD>(SendMessage( GetDlgItem( g_OptionsTabs[PANORAMA_PAGE].hPage, IDC_SNIP_PANORAMA_HOTKEY), HKM_GETHOTKEY, 0, 0 ));
            newSnipOcrToggleKey = static_cast<DWORD>(SendMessage( GetDlgItem( g_OptionsTabs[SNIP_PAGE].hPage, IDC_SNIP_OCR_HOTKEY), HKM_GETHOTKEY, 0, 0 ));

            newToggleMod = GetKeyMod( newToggleKey );
            newLiveZoomToggleMod = GetKeyMod( newLiveZoomToggleKey );
            newDrawToggleMod = GetKeyMod( newDrawToggleKey );
            newBreakToggleMod = GetKeyMod( newBreakToggleKey );
            newDemoTypeToggleMod = GetKeyMod( newDemoTypeToggleKey );
            newRecordToggleMod = GetKeyMod(newRecordToggleKey);
            newSnipToggleMod = GetKeyMod( newSnipToggleKey );
            newSnipPanoramaToggleMod = GetKeyMod( newSnipPanoramaToggleKey );
            newSnipOcrToggleMod = GetKeyMod( newSnipOcrToggleKey );

            g_SliderZoomLevel = static_cast<int>(SendMessage( GetDlgItem(g_OptionsTabs[ZOOM_PAGE].hPage, IDC_ZOOM_SLIDER), TBM_GETPOS, 0, 0 ));
            g_DemoTypeSpeedSlider = static_cast<int>(SendMessage( GetDlgItem( g_OptionsTabs[DEMOTYPE_PAGE].hPage, IDC_DEMOTYPE_SPEED_SLIDER ), TBM_GETPOS, 0, 0 ));

            g_ShowExpiredTime = IsDlgButtonChecked(  g_OptionsTabs[BREAK_PAGE].hPage, IDC_CHECK_SHOW_EXPIRED ) == BST_CHECKED;
            g_BreakLockWorkstation = IsDlgButtonChecked( g_OptionsTabs[BREAK_PAGE].hPage, IDC_CHECK_LOCK_WORKSTATION ) == BST_CHECKED;
            g_CaptureSystemAudio = IsDlgButtonChecked(g_OptionsTabs[RECORD_PAGE].hPage, IDC_CAPTURE_SYSTEM_AUDIO) == BST_CHECKED;
            g_CaptureAudio = IsDlgButtonChecked(g_OptionsTabs[RECORD_PAGE].hPage, IDC_CAPTURE_AUDIO) == BST_CHECKED;
            g_MicMonoMix = IsDlgButtonChecked(g_OptionsTabs[RECORD_PAGE].hPage, IDC_MIC_MONO_MIX) == BST_CHECKED;
            GetDlgItemText( g_OptionsTabs[BREAK_PAGE].hPage, IDC_TIMER, text, 3 );
            text[2] = 0;
            newTimeout = _tstoi( text );

            g_RecordingFormat = static_cast<RecordingFormat>(SendMessage(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_RECORD_FORMAT), static_cast<UINT>(CB_GETCURSEL), static_cast<WPARAM>(0), static_cast<LPARAM>(0)));
            g_RecordFrameRate = (g_RecordingFormat == RecordingFormat::GIF) ? RECORDING_FORMAT_GIF_DEFAULT_FRAMERATE : RECORDING_FORMAT_MP4_DEFAULT_FRAMERATE;
            g_RecordScaling = static_cast<int>(SendMessage(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_RECORD_SCALING), static_cast<UINT>(CB_GETCURSEL), static_cast<WPARAM>(0), static_cast<LPARAM>(0)) * 10 + 10);

            // Get the selected microphone
            int index = static_cast<int>(SendMessage( GetDlgItem( g_OptionsTabs[RECORD_PAGE].hPage, IDC_MICROPHONE ), static_cast<UINT>(CB_GETCURSEL), static_cast<WPARAM>(0), static_cast<LPARAM>(0) ));
            _tcscpy( g_MicrophoneDeviceId, index == 0 ? L"" : microphones[static_cast<size_t>(index) - 1].first.c_str() );

            if( newToggleKey && !RegisterHotKey( GetParent( hDlg ), ZOOM_HOTKEY, newToggleMod, newToggleKey & 0xFF )) {

                MessageBox( hDlg, L"The specified zoom toggle hotkey is already in use.\nSelect a different zoom toggle hotkey.",
                    APPNAME, MB_ICONERROR );
                UnregisterAllHotkeys(GetParent( hDlg ));
                break;

            } else if(newLiveZoomToggleKey &&
                (!RegisterHotKey( GetParent( hDlg ), LIVE_HOTKEY, newLiveZoomToggleMod, newLiveZoomToggleKey & 0xFF ) ||
                !RegisterHotKey(GetParent(hDlg), LIVE_DRAW_HOTKEY, (newLiveZoomToggleMod ^ MOD_SHIFT), newLiveZoomToggleKey & 0xFF))) {

                MessageBox( hDlg, L"The specified live-zoom toggle hotkey is already in use.\nSelect a different zoom toggle hotkey.",
                    APPNAME, MB_ICONERROR );
                UnregisterAllHotkeys(GetParent( hDlg ));
                break;

            } else if( newDrawToggleKey && !RegisterHotKey( GetParent( hDlg ), DRAW_HOTKEY, newDrawToggleMod, newDrawToggleKey & 0xFF )) {

                MessageBox( hDlg, L"The specified draw w/out zoom hotkey is already in use.\nSelect a different draw w/out zoom hotkey.",
                    APPNAME, MB_ICONERROR );
                UnregisterAllHotkeys(GetParent( hDlg ));
                break;

            } else if( newBreakToggleKey && !RegisterHotKey( GetParent( hDlg ), BREAK_HOTKEY, newBreakToggleMod, newBreakToggleKey & 0xFF )) {

                MessageBox( hDlg, L"The specified break timer hotkey is already in use.\nSelect a different break timer hotkey.",
                    APPNAME, MB_ICONERROR );
                UnregisterAllHotkeys(GetParent( hDlg ));
                break;

            } else if( newDemoTypeToggleKey &&
                (!RegisterHotKey( GetParent( hDlg ), DEMOTYPE_HOTKEY, newDemoTypeToggleMod, newDemoTypeToggleKey & 0xFF ) ||
                    !RegisterHotKey(GetParent(hDlg), DEMOTYPE_RESET_HOTKEY, (newDemoTypeToggleMod ^ MOD_SHIFT), newDemoTypeToggleKey & 0xFF))) {

                MessageBox( hDlg, L"The specified live-type hotkey is already in use.\nSelect a different live-type hotkey.",
                    APPNAME, MB_ICONERROR );
                UnregisterAllHotkeys( GetParent( hDlg ) );
                break;

            }
            else if (newSnipToggleKey &&
                (!RegisterHotKey(GetParent(hDlg), SNIP_HOTKEY, newSnipToggleMod, newSnipToggleKey & 0xFF) ||
                 !RegisterHotKey(GetParent(hDlg), SNIP_SAVE_HOTKEY, (newSnipToggleMod ^ MOD_SHIFT), newSnipToggleKey & 0xFF))) {

                MessageBox(hDlg, L"The specified snip hotkey is already in use.\nSelect a different snip hotkey.",
                    APPNAME, MB_ICONERROR);
                UnregisterAllHotkeys(GetParent(hDlg));
                break;

            }
            else if (newSnipPanoramaToggleKey &&
                (newSnipPanoramaToggleKey != newSnipToggleKey || newSnipPanoramaToggleMod != newSnipToggleMod) &&
                (!RegisterHotKey(GetParent(hDlg), SNIP_PANORAMA_HOTKEY, newSnipPanoramaToggleMod | MOD_NOREPEAT, newSnipPanoramaToggleKey & 0xFF) ||
                 !RegisterHotKey(GetParent(hDlg), SNIP_PANORAMA_SAVE_HOTKEY, ( newSnipPanoramaToggleMod ^ MOD_SHIFT ) | MOD_NOREPEAT, newSnipPanoramaToggleKey & 0xFF))) {

                MessageBox(hDlg, L"The specified panorama snip hotkey is already in use.\nSelect a different panorama snip hotkey.",
                    APPNAME, MB_ICONERROR);
                UnregisterAllHotkeys(GetParent(hDlg));
                break;

            }
            else if (newSnipOcrToggleKey &&
                !RegisterHotKey(GetParent(hDlg), SNIP_OCR_HOTKEY, newSnipOcrToggleMod, newSnipOcrToggleKey & 0xFF)) {

                MessageBox(hDlg, L"The specified snip OCR hotkey is already in use.\nSelect a different snip OCR hotkey.",
                    APPNAME, MB_ICONERROR);
                UnregisterAllHotkeys(GetParent(hDlg));
                break;

            }
            else if( newRecordToggleKey &&
                (!RegisterHotKey(GetParent(hDlg), RECORD_HOTKEY,      newRecordToggleMod | MOD_NOREPEAT, newRecordToggleKey & 0xFF) ||
                 !RegisterHotKey(GetParent(hDlg), RECORD_CROP_HOTKEY, (newRecordToggleMod ^ MOD_SHIFT) | MOD_NOREPEAT, newRecordToggleKey & 0xFF) ||
                 !RegisterHotKey(GetParent(hDlg), RECORD_WINDOW_HOTKEY, (newRecordToggleMod ^ MOD_ALT) | MOD_NOREPEAT, newRecordToggleKey & 0xFF))) {

                MessageBox(hDlg, L"The specified record hotkey is already in use.\nSelect a different record hotkey.",
                    APPNAME, MB_ICONERROR);
                UnregisterAllHotkeys(GetParent(hDlg));
                break;

            } else {

                g_BreakTimeout = newTimeout;
                g_ToggleKey = newToggleKey;
                g_LiveZoomToggleKey = newLiveZoomToggleKey;
                g_ToggleMod = newToggleMod;
                g_DrawToggleKey = newDrawToggleKey;
                g_DrawToggleMod = newDrawToggleMod;
                g_BreakToggleKey = newBreakToggleKey;
                g_BreakToggleMod = newBreakToggleMod;
                g_DemoTypeToggleKey = newDemoTypeToggleKey;
                g_DemoTypeToggleMod = newDemoTypeToggleMod;
                g_RecordToggleKey = newRecordToggleKey;
                g_RecordToggleMod = newRecordToggleMod;
                g_SnipToggleKey = newSnipToggleKey;
                g_SnipToggleMod = newSnipToggleMod;
                g_SnipPanoramaToggleKey = newSnipPanoramaToggleKey;
                g_SnipPanoramaToggleMod = newSnipPanoramaToggleMod;
                g_SnipOcrToggleKey = newSnipOcrToggleKey;
                g_SnipOcrToggleMod = newSnipOcrToggleMod;
                reg.WriteRegSettings( RegSettings );
                EnableDisableTrayIcon( GetParent( hDlg ), g_ShowTrayIcon );

                hWndOptions = NULL;
                CleanupFonts();
                EndDialog( hDlg, 0 );
                return TRUE;
            }
            break;
        }

        case IDCANCEL:
            RegisterAllHotkeys(GetParent(hDlg));
            hWndOptions = NULL;
            CleanupFonts();
            EndDialog( hDlg, 0 );
            return TRUE;
        }
        break;

    case WM_CLOSE:
        hWndOptions = NULL;
        RegisterAllHotkeys(GetParent(hDlg));
        CleanupFonts();
        EndDialog( hDlg, 0 );
        return TRUE;

    default:
        break;
    }
    return FALSE;
}

//----------------------------------------------------------------------------
//
// DeleteDrawUndoList
//
//----------------------------------------------------------------------------
void DeleteDrawUndoList( P_DRAW_UNDO *DrawUndoList )
{
    P_DRAW_UNDO	nextUndo;

    nextUndo = *DrawUndoList;
    while( nextUndo ) {

        *DrawUndoList = nextUndo->Next;
        DeleteObject( nextUndo->hBitmap );
        DeleteDC( nextUndo->hDc );
        free( nextUndo );
        nextUndo = *DrawUndoList;
    }
    *DrawUndoList = NULL;
}

//----------------------------------------------------------------------------
//
// PopDrawUndo
//
//----------------------------------------------------------------------------
BOOLEAN PopDrawUndo( HDC hDc, P_DRAW_UNDO *DrawUndoList,
                  int width, int height )
{
    P_DRAW_UNDO	nextUndo;

    nextUndo = *DrawUndoList;
    if( nextUndo ) {

        BitBlt( hDc, 0, 0, width, height,
            nextUndo->hDc, 0, 0, SRCCOPY|CAPTUREBLT );
        *DrawUndoList = nextUndo->Next;
        DeleteObject( nextUndo->hBitmap );
        DeleteDC( nextUndo->hDc );
        free( nextUndo );
        return TRUE;

    } else {

        Beep( 700, 200 );
        return FALSE;
    }
}


//----------------------------------------------------------------------------
//
// DeleteOldestUndo
//
//----------------------------------------------------------------------------
void DeleteOldestUndo( P_DRAW_UNDO *DrawUndoList )
{
    P_DRAW_UNDO	nextUndo, freeUndo = NULL, prevUndo = NULL;

    nextUndo = *DrawUndoList;
    freeUndo = nextUndo;
    do {

        prevUndo = freeUndo;
        freeUndo = nextUndo;
        nextUndo = nextUndo->Next;

    } while( nextUndo );

    if( freeUndo ) {

        DeleteObject( freeUndo->hBitmap );
        DeleteDC( freeUndo->hDc );
        free( freeUndo );
        if( prevUndo != *DrawUndoList ) prevUndo->Next = NULL;
        else *DrawUndoList = NULL;
    }
}

//----------------------------------------------------------------------------
//
// GetOldestUndo
//
//----------------------------------------------------------------------------
P_DRAW_UNDO GetOldestUndo(P_DRAW_UNDO DrawUndoList)
{
    P_DRAW_UNDO	nextUndo, oldestUndo = NULL;

    nextUndo = DrawUndoList;
    oldestUndo = nextUndo;
    do {

        oldestUndo = nextUndo;
        nextUndo = nextUndo->Next;

    } while( nextUndo );
    return oldestUndo;
}


//----------------------------------------------------------------------------
//
// PushDrawUndo
//
//----------------------------------------------------------------------------
void PushDrawUndo( HDC hDc, P_DRAW_UNDO *DrawUndoList, int width, int height )
{
    P_DRAW_UNDO	nextUndo, newUndo;
    int			i = 0;
    HBITMAP		hUndoBitmap;

    OutputDebug(L"PushDrawUndo\n");

    // Don't store more than 8 undo's (XP gets really upset when we
    // exhaust heap with them)
    nextUndo = *DrawUndoList;
    do {

        i++;
        if( i == MAX_UNDO_HISTORY ) {

            DeleteOldestUndo( DrawUndoList );
            break;
        }
        if( nextUndo ) nextUndo = nextUndo->Next;

    } while( nextUndo );

    hUndoBitmap = CreateCompatibleBitmap( hDc, width, height );
    if( !hUndoBitmap && *DrawUndoList ) {

        // delete the oldest and try again
        DeleteOldestUndo( DrawUndoList );
        hUndoBitmap = CreateCompatibleBitmap( hDc, width, height );
    }
    if( hUndoBitmap ) {

        newUndo = static_cast<P_DRAW_UNDO>(malloc( sizeof( DRAW_UNDO )));
        if (newUndo != NULL)
        {
            newUndo->hDc = CreateCompatibleDC(hDc);
            newUndo->hBitmap = hUndoBitmap;
            SelectObject(newUndo->hDc, newUndo->hBitmap);
            BitBlt(newUndo->hDc, 0, 0, width, height, hDc, 0, 0, SRCCOPY | CAPTUREBLT);
            newUndo->Next = *DrawUndoList;
            *DrawUndoList = newUndo;
        }
    }
}

//----------------------------------------------------------------------------
//
// DeleteTypedText
//
//----------------------------------------------------------------------------
void DeleteTypedText( P_TYPED_KEY *TypedKeyList )
{
    P_TYPED_KEY	nextKey;

    while( *TypedKeyList ) {

        nextKey = (*TypedKeyList)->Next;
        free( *TypedKeyList );
        *TypedKeyList = nextKey;
    }
}

//----------------------------------------------------------------------------
//
// BlankScreenArea
//
//----------------------------------------------------------------------------
void BlankScreenArea( HDC hDc, PRECT Rc, int BlankMode )
{
    if( BlankMode == 'K' ) {

        HBRUSH hBrush = CreateSolidBrush( RGB( 0, 0, 0 ));
        FillRect( hDc, Rc, hBrush );
        DeleteObject( static_cast<HGDIOBJ>(hBrush) );

    } else {

        FillRect( hDc, Rc, GetSysColorBrush( COLOR_WINDOW ));
    }
}

//----------------------------------------------------------------------------
//
// ClearTypingCursor
//
//----------------------------------------------------------------------------
void ClearTypingCursor( HDC hdcScreenCompat, HDC hdcScreenCursorCompat, RECT rc,
                            int BlankMode )
{
    if( false ) { // BlankMode ) {

        BlankScreenArea( hdcScreenCompat, &rc, BlankMode );

    } else {

        BitBlt(hdcScreenCompat, rc.left, rc.top, rc.right - rc.left,
            rc.bottom - rc.top, hdcScreenCursorCompat,0, 0, SRCCOPY|CAPTUREBLT );
    }
}

//----------------------------------------------------------------------------
//
// DrawTypingCursor
//
//----------------------------------------------------------------------------
void DrawTypingCursor( HWND hWnd, POINT *textPt, HDC hdcScreenCompat,
	HDC hdcScreenCursorCompat, RECT *rc, bool centerUnderSystemCursor = false )
{
	// Draw the typing cursor
	rc->left = textPt->x;
	rc->top = textPt->y;
	TCHAR vKey = '|';
	DrawText( hdcScreenCompat, static_cast<PTCHAR>(&vKey), 1, rc, DT_CALCRECT );

	// LiveDraw uses a layered window which means mouse messages pass through
	//   to lower windows unless the system cursor is above a painted area.
	// Centering the typing cursor directly under the system cursor allows
	//   us to capture the mouse wheel input required to change font size.
	if( centerUnderSystemCursor )
	{
		const LONG halfWidth  = static_cast<LONG>( (rc->right - rc->left) / 2 );
		const LONG halfHeight = static_cast<LONG>( (rc->bottom - rc->top) / 2 );

		rc->left   -= halfWidth;
		rc->right  -= halfWidth;
		rc->top    -= halfHeight;
		rc->bottom -= halfHeight;

		textPt->x   = rc->left;
		textPt->y   = rc->top;
	}

	BitBlt(hdcScreenCursorCompat, 0, 0, rc->right -rc->left, rc->bottom - rc->top,
		hdcScreenCompat, rc->left, rc->top, SRCCOPY|CAPTUREBLT );

	DrawText( hdcScreenCompat, static_cast<PTCHAR>(&vKey), 1, rc, DT_LEFT );
	InvalidateRect( hWnd, NULL, TRUE );
}

//----------------------------------------------------------------------------
//
// BoundMouse
//
//----------------------------------------------------------------------------
RECT BoundMouse( float zoomLevel, MONITORINFO *monInfo, int width, int height,
                    POINT *cursorPos )
{
    RECT		rc;
    int			x, y;

    GetZoomedTopLeftCoordinates( zoomLevel, cursorPos, &x, width, &y, height );
    rc.left = monInfo->rcMonitor.left + x;
    rc.right = rc.left + static_cast<int>(width/zoomLevel);
    rc.top = monInfo->rcMonitor.top + y;
    rc.bottom = rc.top + static_cast<int>(height/zoomLevel);

    OutputDebug( L"x: %d y: %d width: %d height: %d zoomLevel: %g\n",
        cursorPos->x, cursorPos->y, width, height, zoomLevel);
    OutputDebug( L"left: %d top: %d right: %d bottom: %d\n",
            rc.left, rc.top, rc.right, rc.bottom);
    OutputDebug( L"mon.left: %d mon.top: %d mon.right: %d mon.bottom: %d\n",
        monInfo->rcMonitor.left, monInfo->rcMonitor.top, monInfo->rcMonitor.right, monInfo->rcMonitor.bottom);

    ClipCursor( &rc );
    return rc;
}

//----------------------------------------------------------------------------
//
// DrawArrow
//
//----------------------------------------------------------------------------
void DrawArrow( HDC hdc, int x1, int y1, int x2, int y2, double length, double width,
        bool UseGdiplus )
{
    // get normalized dx/dy
    double dx = static_cast<double>(x2) - x1;
    double dy = static_cast<double>(y2) - y1;
    double bodyLen = sqrt( dx*dx + dy*dy );
    if ( bodyLen )  {
        dx /= bodyLen;
        dy /= bodyLen;
    } else {
        dx = 1;
        dy = 0;
    }

    // get midpoint of base
    int xMid = x2 - static_cast<int>(length*dx+0.5);
    int yMid = y2 - static_cast<int>(length*dy+0.5);

    // get left wing
    int xLeft = xMid - static_cast<int>(dy*width+0.5);
    int yLeft = yMid + static_cast<int>(dx*width+0.5);

    // get right wing
    int xRight = xMid + static_cast<int>(dy*width+0.5);
    int yRight = yMid - static_cast<int>(dx*width+0.5);

    // Bring in midpoint to make a nicer arrow
    xMid = x2 - static_cast<int>(length/2*dx+0.5);
    yMid = y2 - static_cast<int>(length/2*dy+0.5);
    if (UseGdiplus) {

        Gdiplus::Graphics	dstGraphics(hdc);

        if( ( GetWindowLong( g_hWndMain, GWL_EXSTYLE ) & WS_EX_LAYERED ) == 0 )
        {
            dstGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
        }
        Gdiplus::Color	color = ColorFromColorRef(g_PenColor);
        Gdiplus::Pen pen(color, static_cast<Gdiplus::REAL>(g_PenWidth));
        pen.SetLineCap(Gdiplus::LineCapRound, Gdiplus::LineCapRound, Gdiplus::DashCapRound);
#if 0
        Gdiplus::PointF	pts[] = {
            {(Gdiplus::REAL)x1, (Gdiplus::REAL)y1},
            {(Gdiplus::REAL)xMid, (Gdiplus::REAL)yMid},
            {(Gdiplus::REAL)xLeft, (Gdiplus::REAL)yLeft},
            {(Gdiplus::REAL)x2, (Gdiplus::REAL)y2},
            {(Gdiplus::REAL)xRight, (Gdiplus::REAL)yRight},
            {(Gdiplus::REAL)xMid, (Gdiplus::REAL)yMid}
        };
        dstGraphics.DrawPolygon(&pen, pts, _countof(pts));
#else
        Gdiplus::GraphicsPath path;
        path.StartFigure();
        path.AddLine(static_cast<INT>(x1), static_cast<INT>(y1), static_cast<INT>(x2), static_cast<INT>(y2));
        path.AddLine(static_cast<INT>(x2), static_cast<INT>(y2), static_cast<INT>(xMid), static_cast<INT>(yMid));
        path.AddLine(static_cast<INT>(xMid), static_cast<INT>(yMid), static_cast<INT>(xLeft), static_cast<INT>(yLeft));
        path.AddLine(static_cast<INT>(xLeft), static_cast<INT>(yLeft), static_cast<INT>(x2), static_cast<INT>(y2));
        path.AddLine(static_cast<INT>(x2), static_cast<INT>(y2), static_cast<INT>(xRight), static_cast<INT>(yRight));
        path.AddLine(static_cast<INT>(xRight), static_cast<INT>(yRight), static_cast<INT>(xMid), static_cast<INT>(yMid));
        pen.SetLineJoin(Gdiplus::LineJoinRound);
        dstGraphics.DrawPath(&pen, &path);
#endif
    }
    else {
        POINT	pts[] = {
            x1, y1,
            xMid, yMid,
            xLeft, yLeft,
            x2, y2,
            xRight, yRight,
            xMid, yMid
        };

        // draw arrow head filled with current color
        HBRUSH hBrush = CreateSolidBrush(g_PenColor);
        HBRUSH hOldBrush = SelectBrush(hdc, hBrush);
        Polygon(hdc, pts, sizeof(pts) / sizeof(pts[0]));

        DeleteObject(hBrush);
        SelectObject(hdc, hOldBrush);
    }
}



//----------------------------------------------------------------------------
//
// DrawShape
//
//----------------------------------------------------------------------------
VOID DrawShape( DWORD Shape, HDC hDc, RECT *Rect, bool UseGdiPlus = false )
{
    bool	isBlur = false;

    Gdiplus::Graphics	dstGraphics(hDc);
	if( ( GetWindowLong( g_hWndMain, GWL_EXSTYLE ) & WS_EX_LAYERED ) == 0 )
	{
		dstGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
	}
    Gdiplus::Color	color = ColorFromColorRef(g_PenColor);
    Gdiplus::Pen pen(color, static_cast<Gdiplus::REAL>(g_PenWidth));
    pen.SetLineCap(Gdiplus::LineCapRound, Gdiplus::LineCapRound, Gdiplus::DashCapRound);

    // Check for highlighting or blur
    Gdiplus::Brush *pBrush = NULL;
    if (PEN_COLOR_HIGHLIGHT(g_PenColor)) {
        // Use half the alpha for higher contrast
        DWORD newColor = g_PenColor & 0xFFFFFF | ((g_AlphaBlend / 2) << 24);
        pBrush = new Gdiplus::SolidBrush(ColorFromColorRef(newColor));
        if(UseGdiPlus && Shape != DRAW_LINE && Shape != DRAW_ARROW)
            InflateRect(Rect, g_PenWidth/2, g_PenWidth/2);
    }
    else if ((g_PenColor & 0xFFFFFF) == COLOR_BLUR) {
        if (UseGdiPlus && Shape != DRAW_LINE && Shape != DRAW_ARROW)
            InflateRect(Rect, g_PenWidth / 2, g_PenWidth / 2);
        isBlur = true;
    }
    OutputDebug(L"Draw shape: highlight: %d pbrush: %d\n", PEN_COLOR_HIGHLIGHT(g_PenColor), pBrush != NULL);

    switch (Shape) {
    case DRAW_RECTANGLE:
        if (UseGdiPlus)
            if(pBrush)
                DrawHighlightedShape(DRAW_RECTANGLE, hDc, pBrush, NULL,
                    static_cast<int>(Rect->left - 1), static_cast<int>(Rect->top - 1),
                    static_cast<int>(Rect->right), static_cast<int>(Rect->bottom));
            else if (isBlur)
                DrawBlurredShape( DRAW_RECTANGLE, &pen, hDc, &dstGraphics,
                    static_cast<int>(Rect->left - 1), static_cast<int>(Rect->top - 1),
                    static_cast<int>(Rect->right), static_cast<int>(Rect->bottom) );
            else
                dstGraphics.DrawRectangle(&pen,
                    Gdiplus::Rect::Rect(Rect->left - 1, Rect->top - 1,
                        Rect->right - Rect->left, Rect->bottom - Rect->top));
        else
            Rectangle(hDc, Rect->left, Rect->top,
                Rect->right, Rect->bottom);
        break;
    case DRAW_ELLIPSE:
        if (UseGdiPlus)
            if (pBrush)
                DrawHighlightedShape(DRAW_ELLIPSE, hDc, pBrush, NULL,
                    static_cast<int>(Rect->left - 1), static_cast<int>(Rect->top - 1),
                    static_cast<int>(Rect->right), static_cast<int>(Rect->bottom));
            else if (isBlur)
                DrawBlurredShape( DRAW_ELLIPSE, &pen, hDc, &dstGraphics,
                    static_cast<int>(Rect->left - 1), static_cast<int>(Rect->top - 1),
                    static_cast<int>(Rect->right), static_cast<int>(Rect->bottom));
            else
                dstGraphics.DrawEllipse(&pen,
                    Gdiplus::Rect::Rect(Rect->left - 1, Rect->top - 1,
                        Rect->right - Rect->left, Rect->bottom - Rect->top));
        else
            Ellipse(hDc, Rect->left, Rect->top,
                Rect->right, Rect->bottom);
        break;
    case DRAW_LINE:
        if (UseGdiPlus)
            if (pBrush)
                DrawHighlightedShape(DRAW_LINE, hDc, NULL, &pen,
                    static_cast<int>(Rect->left), static_cast<int>(Rect->top),
                    static_cast<int>(Rect->right), static_cast<int>(Rect->bottom));
            else if (isBlur)
                DrawBlurredShape(DRAW_LINE, &pen, hDc, &dstGraphics,
                    static_cast<int>(Rect->left), static_cast<int>(Rect->top),
                    static_cast<int>(Rect->right), static_cast<int>(Rect->bottom));
            else
                dstGraphics.DrawLine(&pen,
                    static_cast<INT>(Rect->left - 1), static_cast<INT>(Rect->top - 1),
                    static_cast<INT>(Rect->right), static_cast<INT>(Rect->bottom));
        else {
            MoveToEx(hDc, Rect->left, Rect->top, NULL);
            LineTo(hDc, Rect->right + 1, Rect->bottom + 1);
        }
        break;
    case DRAW_ARROW:
        DrawArrow(hDc, Rect->right + 1, Rect->bottom + 1,
            Rect->left, Rect->top,
            static_cast<double>(g_PenWidth) * 2.5, static_cast<double>(g_PenWidth) * 1.5, UseGdiPlus);
        break;
    }
    if( pBrush ) delete pBrush;
}

//----------------------------------------------------------------------------
//
// SendPenMessage
//
// Inserts the pen message marker.
//
//----------------------------------------------------------------------------
VOID SendPenMessage(HWND hWnd, UINT Message, LPARAM lParam)
{
    WPARAM		wParam = 0;
    //
    // Get key states
    //
    if(GetKeyState(VK_LCONTROL) < 0 ) {

        wParam |= MK_CONTROL;
    }
    if( GetKeyState( VK_LSHIFT) < 0 || GetKeyState( VK_RSHIFT) < 0 ) {

        wParam |= MK_SHIFT;
    }
    SetMessageExtraInfo(static_cast<LPARAM>(MI_WP_SIGNATURE));
    SendMessage(hWnd, Message, wParam, lParam);
}


//----------------------------------------------------------------------------
//
// ScalePenPosition
//
// Maps pen input to mouse input coordinates based on zoom level. Returns
// 0 if pen is active but we didn't send this message to ourselves (pen
// signature will be missing).
//
//----------------------------------------------------------------------------
LPARAM ScalePenPosition( float zoomLevel, MONITORINFO *monInfo, RECT boundRc,
                    UINT message, LPARAM lParam )
{
    RECT	rc;
    WORD	x, y;
    LPARAM	extraInfo;

    extraInfo = GetMessageExtraInfo();
    if( g_PenDown ) {

        // ignore messages we didn't tag as pen
        if (extraInfo == MI_WP_SIGNATURE) {

            OutputDebug( L"Tablet Pen message\n");

            // tablet input: don't bound the cursor
            ClipCursor(NULL);

            x = LOWORD(lParam);
            y = HIWORD(lParam);

            x = static_cast<WORD>((x - static_cast<WORD>(monInfo->rcMonitor.left))/ zoomLevel) + static_cast<WORD>(boundRc.left - monInfo->rcMonitor.left);
            y = static_cast<WORD>((y - static_cast<WORD>(monInfo->rcMonitor.top)) / zoomLevel) + static_cast<WORD>(boundRc.top - monInfo->rcMonitor.top);

            lParam = MAKELPARAM(x, y);
        }
        else {

            OutputDebug(L"Ignore pen message we didn't send\n");
            lParam = 0;
        }

    } else {

        if( !GetClipCursor( &rc )) {

            ClipCursor( &boundRc );
        }
        OutputDebug( L"Mouse message\n");
    }
    return lParam;
}


//----------------------------------------------------------------------------
//
// DrawHighlightedCursor
//
//----------------------------------------------------------------------------
BOOLEAN DrawHighlightedCursor( float ZoomLevel, int Width, int Height )
{
    DWORD zoomWidth = static_cast<DWORD> (static_cast<float>(Width)/ZoomLevel);
    DWORD zoomHeight = static_cast<DWORD> (static_cast<float>(Height)/ZoomLevel);
    if( g_PenWidth < 5 && zoomWidth > g_PenWidth * 100 && zoomHeight > g_PenWidth * 100 ) {

        return TRUE;

    } else {

        return FALSE;
    }
}

//----------------------------------------------------------------------------
//
// InvalidateCursorMoveArea
//
//----------------------------------------------------------------------------
void InvalidateCursorMoveArea( HWND hWnd, float zoomLevel, int width, int height,
                              POINT currentPt, POINT prevPt, POINT cursorPos )
{
    int		x, y;
    RECT	rc;
    int		invWidth = g_PenWidth + CURSOR_SAVE_MARGIN;

    if( DrawHighlightedCursor( zoomLevel, width, height ) ) {

        invWidth = g_PenWidth * 3 + 1;
    }
    GetZoomedTopLeftCoordinates( zoomLevel, &cursorPos, &x, width, &y, height );
    rc.left = static_cast<int>(max( 0, (int) ((min( prevPt.x, currentPt.x)-invWidth - x) * zoomLevel)));
    rc.right = static_cast<int>((max( prevPt.x, currentPt.x)+invWidth - x) * zoomLevel);
    rc.top = static_cast<int>(max( 0, (int) ((min( prevPt.y, currentPt.y)-invWidth - y) * zoomLevel)));
    rc.bottom = static_cast<int>((max( prevPt.y, currentPt.y)+invWidth -y) * zoomLevel);
    InvalidateRect( hWnd, &rc, FALSE );

    OutputDebug( L"INVALIDATE: (%d, %d) - (%d, %d)\n", rc.left, rc.top, rc.right, rc.bottom);
}


//----------------------------------------------------------------------------
//
// SavCursorArea
//
//----------------------------------------------------------------------------
void SaveCursorArea( HDC hDcTarget, HDC hDcSource, POINT pt )
{
    OutputDebug( L"SaveCursorArea\n");
    int penWidth = g_PenWidth + CURSOR_SAVE_MARGIN;
    BitBlt( hDcTarget, 0, 0, penWidth +CURSOR_ARM_LENGTH*2, penWidth +CURSOR_ARM_LENGTH*2,
        hDcSource, static_cast<INT> (pt.x- penWidth /2)-CURSOR_ARM_LENGTH,
        static_cast<INT>(pt.y- penWidth /2)-CURSOR_ARM_LENGTH, SRCCOPY|CAPTUREBLT );
}

//----------------------------------------------------------------------------
//
// RestoreCursorArea
//
//----------------------------------------------------------------------------
void RestoreCursorArea( HDC hDcTarget, HDC hDcSource, POINT pt )
{
    OutputDebug( L"RestoreCursorArea\n");
    int penWidth = g_PenWidth + CURSOR_SAVE_MARGIN;
    BitBlt( hDcTarget, static_cast<INT>(pt.x- penWidth /2)-CURSOR_ARM_LENGTH,
        static_cast<INT>(pt.y- penWidth /2)-CURSOR_ARM_LENGTH, penWidth +CURSOR_ARM_LENGTH*2,
        penWidth + CURSOR_ARM_LENGTH*2, hDcSource, 0, 0, SRCCOPY|CAPTUREBLT );
}


//----------------------------------------------------------------------------
//
// DrawCursor
//
//----------------------------------------------------------------------------
void DrawCursor( HDC hDcTarget, POINT pt, float ZoomLevel, int Width, int Height )
{
    RECT	rc;

    if( g_DrawPointer ) {

        Gdiplus::Graphics	dstGraphics(hDcTarget);
        if( ( GetWindowLong( g_hWndMain, GWL_EXSTYLE ) & WS_EX_LAYERED ) == 0 )
        {
            dstGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
        }
        Gdiplus::Color	color = ColorFromColorRef(g_PenColor);
        Gdiplus::Pen pen(color, static_cast<Gdiplus::REAL>(g_PenWidth));

        rc.left = pt.x - CURSOR_ARM_LENGTH;
        rc.right = pt.x + CURSOR_ARM_LENGTH;
        rc.top = pt.y - CURSOR_ARM_LENGTH;
        rc.bottom = pt.y + CURSOR_ARM_LENGTH;

        Gdiplus::GraphicsPath path;
        path.StartFigure();
        path.AddLine(static_cast<INT>(rc.left) - 1, static_cast<INT>(rc.top) - 1, static_cast<INT>(rc.right), static_cast<INT>(rc.bottom));
        path.AddLine(static_cast<INT>(rc.left) - 2, static_cast<INT>(rc.top) - 1, rc.left + (rc.right - rc.left) / 2, rc.top - 1);
        path.AddLine(static_cast<INT>(rc.left) - 1, static_cast<INT>(rc.top) - 2, rc.left - 1, rc.top + (rc.bottom - rc.top) / 2);
        path.AddLine(static_cast<INT>(rc.left) - 1, static_cast<INT>(rc.top) - 2, rc.left - 1, rc.top + (rc.bottom - rc.top) / 2);
        path.AddLine(static_cast<INT>(rc.left + (rc.right - rc.left) / 2), rc.top - 1, rc.left - 1, rc.top + (rc.bottom - rc.top) / 2);
        pen.SetLineJoin(Gdiplus::LineJoinRound);
        dstGraphics.DrawPath(&pen, &path);
        OutputDebug(L"DrawPointer: %d %d %d %d\n", rc.left, rc.top, rc.right, rc.bottom);

    } else if( DrawHighlightedCursor( ZoomLevel, Width, Height )) {

        OutputDebug(L"DrawHighlightedCursor: %d %d %d %d\n", pt.x, pt.y, g_PenWidth, g_PenWidth);
        Gdiplus::Graphics	dstGraphics(hDcTarget);
        if( ( GetWindowLong( g_hWndMain, GWL_EXSTYLE ) & WS_EX_LAYERED ) == 0 )
        {
            dstGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
        }
        Gdiplus::Color	color = ColorFromColorRef(g_PenColor);
        Gdiplus::Pen pen(color, static_cast<Gdiplus::REAL>(g_PenWidth));
        Gdiplus::GraphicsPath path;
        path.StartFigure();
        pen.SetLineJoin(Gdiplus::LineJoinRound);
        path.AddLine(static_cast<INT>(pt.x - CURSOR_ARM_LENGTH), pt.y, pt.x + CURSOR_ARM_LENGTH, pt.y);
        path.CloseFigure();
        path.StartFigure();
        pen.SetLineJoin(Gdiplus::LineJoinRound);
        path.AddLine(static_cast<INT>(pt.x), pt.y - CURSOR_ARM_LENGTH, pt.x, pt.y + CURSOR_ARM_LENGTH);
        path.CloseFigure();
        dstGraphics.DrawPath(&pen, &path);

    } else {

        Gdiplus::Graphics	dstGraphics(hDcTarget);
        {
            if( ( GetWindowLong( g_hWndMain, GWL_EXSTYLE ) & WS_EX_LAYERED ) == 0 )
            {
                dstGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
            }
            Gdiplus::Color	color = ColorFromColorRef(g_PenColor);

            Gdiplus::SolidBrush solidBrush(color);

            dstGraphics.FillEllipse(&solidBrush, static_cast<INT>(pt.x-g_PenWidth/2), static_cast<INT>(pt.y-g_PenWidth/2),
                        static_cast<INT>(g_PenWidth), static_cast<INT>(g_PenWidth));
        }
    }
}

//----------------------------------------------------------------------------
//
// ResizePen
//
//----------------------------------------------------------------------------
void ResizePen( HWND hWnd, HDC hdcScreenCompat, HDC hdcScreenCursorCompat, POINT prevPt,
                BOOLEAN g_Tracing, BOOLEAN *g_Drawing, float g_LiveZoomLevel,
                BOOLEAN isUser, int newWidth )
{
    if( !g_Tracing ) {

        RestoreCursorArea( hdcScreenCompat, hdcScreenCursorCompat, prevPt );
    }

    OutputDebug( L"RESIZE_PEN-PRE: penWidth: %d ", g_PenWidth );
    int prevWidth = g_PenWidth;
    if( g_ZoomOnLiveZoom )
    {
        if( isUser )
        {
            // Amplify user delta proportional to LiveZoomLevel
            newWidth = g_PenWidth + static_cast<int> ((newWidth - static_cast<int>(g_PenWidth))*g_LiveZoomLevel);
        }

        g_PenWidth = min( max( newWidth, MIN_PEN_WIDTH ),
            min( static_cast<int>(MAX_PEN_WIDTH * g_LiveZoomLevel), MAX_LIVE_PEN_WIDTH ) );
        g_RootPenWidth = static_cast<int>(g_PenWidth / g_LiveZoomLevel);
    }
    else
    {
        g_PenWidth = min( max( newWidth, MIN_PEN_WIDTH ), MAX_PEN_WIDTH );
        g_RootPenWidth = g_PenWidth;
    }

    if(prevWidth == static_cast<int>(g_PenWidth) ) {
        // No change
        return;
    }

    OutputDebug( L"newWidth: %d\nRESIZE_PEN-POST: penWidth: %d\n", newWidth, g_PenWidth );
    reg.WriteRegSettings( RegSettings );
    SaveCursorArea( hdcScreenCursorCompat, hdcScreenCompat, prevPt );
    *g_Drawing = FALSE;
    EnableDisableStickyKeys( TRUE );
    SendMessage( hWnd, WM_LBUTTONDOWN, -1, MAKELPARAM(prevPt.x, prevPt.y) );
}

//----------------------------------------------------------------------------
//
// IsPenInverted
//
//----------------------------------------------------------------------------
bool IsPenInverted( WPARAM wParam )
{
    POINTER_INPUT_TYPE pointerType;
    POINTER_PEN_INFO penInfo;
    return
        pGetPointerType( GET_POINTERID_WPARAM( wParam ), &pointerType ) && ( pointerType == PT_PEN ) &&
        pGetPointerPenInfo( GET_POINTERID_WPARAM( wParam ), &penInfo ) && ( penInfo.penFlags & PEN_FLAG_INVERTED );
}


//----------------------------------------------------------------------------
//
// CaptureScreenshotAsync
//
// Captures the specified screen using the capture APIs
//
//----------------------------------------------------------------------------
wil::task<winrt::com_ptr<ID3D11Texture2D>> CaptureScreenshotAsync(winrt::IDirect3DDevice const& device, winrt::GraphicsCaptureItem const& item, winrt::DirectXPixelFormat const& pixelFormat)
{
    auto d3dDevice = GetDXGIInterfaceFromObject<ID3D11Device>(device);
    winrt::com_ptr<ID3D11DeviceContext> d3dContext;
    d3dDevice->GetImmediateContext(d3dContext.put());

    // Creating our frame pool with CreateFreeThreaded means that we
    // will be called back from the frame pool's internal worker thread
    // instead of the thread we are currently on. It also disables the
    // DispatcherQueue requirement.
    auto framePool = winrt::Direct3D11CaptureFramePool::CreateFreeThreaded(
        device,
        pixelFormat,
        1,
        item.Size());
    auto session = framePool.CreateCaptureSession(item);

    wil::shared_event captureEvent(wil::EventOptions::ManualReset);
    winrt::Direct3D11CaptureFrame frame{ nullptr };
    framePool.FrameArrived([&frame, captureEvent](auto& framePool, auto&)
        {
            frame = framePool.TryGetNextFrame();

            // Complete the operation
            captureEvent.SetEvent();
        });

    session.IsCursorCaptureEnabled( false );
    session.StartCapture();
    co_await winrt::resume_on_signal(captureEvent.get());

    // End the capture
    session.Close();
    framePool.Close();

    auto texture = GetDXGIInterfaceFromObject<ID3D11Texture2D>(frame.Surface());
    co_return util::CopyD3DTexture(d3dDevice, texture, true);
}

//----------------------------------------------------------------------------
//
// CaptureScreenshot
//
// Captures the specified screen using the capture APIs
//
//----------------------------------------------------------------------------
winrt::com_ptr<ID3D11Texture2D>CaptureScreenshot(winrt::DirectXPixelFormat const& pixelFormat)
{
    auto d3dDevice = util::CreateD3D11Device();
    auto dxgiDevice = d3dDevice.as<IDXGIDevice>();
    auto device = CreateDirect3DDevice(dxgiDevice.get());

    // Get the active MONITOR capture device
    HMONITOR hMon = NULL;
    POINT cursorPos = { 0, 0 };
    if (pMonitorFromPoint) {

        GetCursorPos(&cursorPos);
        hMon = pMonitorFromPoint(cursorPos, MONITOR_DEFAULTTONEAREST);
    }

    auto item = util::CreateCaptureItemForMonitor(hMon);

    return CaptureScreenshotAsync(device, item, pixelFormat).get();
}


//----------------------------------------------------------------------------
//
// CopyD3DTexture
//
//----------------------------------------------------------------------------
inline auto CopyD3DTexture(winrt::com_ptr<ID3D11Device> const& device,
            winrt::com_ptr<ID3D11Texture2D> const& texture, bool asStagingTexture)
{
    winrt::com_ptr<ID3D11DeviceContext> context;
    device->GetImmediateContext(context.put());

    D3D11_TEXTURE2D_DESC desc = {};
    texture->GetDesc(&desc);
    // Clear flags that we don't need
    desc.Usage = asStagingTexture ? D3D11_USAGE_STAGING : D3D11_USAGE_DEFAULT;
    desc.BindFlags = asStagingTexture ? 0 : D3D11_BIND_SHADER_RESOURCE;
    desc.CPUAccessFlags = asStagingTexture ? D3D11_CPU_ACCESS_READ : 0;
    desc.MiscFlags = 0;

    // Create and fill the texture copy
    winrt::com_ptr<ID3D11Texture2D> textureCopy;
    winrt::check_hresult(device->CreateTexture2D(&desc, nullptr, textureCopy.put()));
    context->CopyResource(textureCopy.get(), texture.get());

    return textureCopy;
}


//----------------------------------------------------------------------------
//
// PrepareStagingTexture
//
//----------------------------------------------------------------------------
inline auto PrepareStagingTexture(winrt::com_ptr<ID3D11Device> const& device,
            winrt::com_ptr<ID3D11Texture2D> const& texture)
{
    // If our texture is already set up for staging, then use it.
    D3D11_TEXTURE2D_DESC desc = {};
    texture->GetDesc(&desc);
    if (desc.Usage == D3D11_USAGE_STAGING && desc.CPUAccessFlags & D3D11_CPU_ACCESS_READ)
    {
        return texture;
    }

    return CopyD3DTexture(device, texture, true);
}

//----------------------------------------------------------------------------
//
// GetBytesPerPixel
//
//----------------------------------------------------------------------------
inline size_t
GetBytesPerPixel(DXGI_FORMAT pixelFormat)
{
    switch (pixelFormat)
    {
    case DXGI_FORMAT_R32G32B32A32_TYPELESS:
    case DXGI_FORMAT_R32G32B32A32_FLOAT:
    case DXGI_FORMAT_R32G32B32A32_UINT:
    case DXGI_FORMAT_R32G32B32A32_SINT:
        return 16;
    case DXGI_FORMAT_R32G32B32_TYPELESS:
    case DXGI_FORMAT_R32G32B32_FLOAT:
    case DXGI_FORMAT_R32G32B32_UINT:
    case DXGI_FORMAT_R32G32B32_SINT:
        return 12;
    case DXGI_FORMAT_R16G16B16A16_TYPELESS:
    case DXGI_FORMAT_R16G16B16A16_FLOAT:
    case DXGI_FORMAT_R16G16B16A16_UNORM:
    case DXGI_FORMAT_R16G16B16A16_UINT:
    case DXGI_FORMAT_R16G16B16A16_SNORM:
    case DXGI_FORMAT_R16G16B16A16_SINT:
    case DXGI_FORMAT_R32G32_TYPELESS:
    case DXGI_FORMAT_R32G32_FLOAT:
    case DXGI_FORMAT_R32G32_UINT:
    case DXGI_FORMAT_R32G32_SINT:
    case DXGI_FORMAT_R32G8X24_TYPELESS:
        return 8;
    case DXGI_FORMAT_D32_FLOAT_S8X24_UINT:
    case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS:
    case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT:
    case DXGI_FORMAT_R10G10B10A2_TYPELESS:
    case DXGI_FORMAT_R10G10B10A2_UNORM:
    case DXGI_FORMAT_R10G10B10A2_UINT:
    case DXGI_FORMAT_R11G11B10_FLOAT:
    case DXGI_FORMAT_R8G8B8A8_TYPELESS:
    case DXGI_FORMAT_R8G8B8A8_UNORM:
    case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
    case DXGI_FORMAT_R8G8B8A8_UINT:
    case DXGI_FORMAT_R8G8B8A8_SNORM:
    case DXGI_FORMAT_R8G8B8A8_SINT:
    case DXGI_FORMAT_R16G16_TYPELESS:
    case DXGI_FORMAT_R16G16_FLOAT:
    case DXGI_FORMAT_UNKNOWN:
    case DXGI_FORMAT_R16G16_UINT:
    case DXGI_FORMAT_R16G16_SNORM:
    case DXGI_FORMAT_R16G16_SINT:
    case DXGI_FORMAT_R32_TYPELESS:
    case DXGI_FORMAT_D32_FLOAT:
    case DXGI_FORMAT_R32_FLOAT:
    case DXGI_FORMAT_R32_UINT:
    case DXGI_FORMAT_R32_SINT:
    case DXGI_FORMAT_R24G8_TYPELESS:
    case DXGI_FORMAT_D24_UNORM_S8_UINT:
    case DXGI_FORMAT_R24_UNORM_X8_TYPELESS:
    case DXGI_FORMAT_X24_TYPELESS_G8_UINT:
    case DXGI_FORMAT_R8G8_B8G8_UNORM:
    case DXGI_FORMAT_G8R8_G8B8_UNORM:
    case DXGI_FORMAT_B8G8R8A8_UNORM:
    case DXGI_FORMAT_B8G8R8X8_UNORM:
    case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM:
    case DXGI_FORMAT_B8G8R8A8_TYPELESS:
    case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
    case DXGI_FORMAT_B8G8R8X8_TYPELESS:
    case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB:
        return 4;
    case DXGI_FORMAT_R8G8_TYPELESS:
    case DXGI_FORMAT_R8G8_UNORM:
    case DXGI_FORMAT_R8G8_UINT:
    case DXGI_FORMAT_R8G8_SNORM:
    case DXGI_FORMAT_R8G8_SINT:
    case DXGI_FORMAT_R16_TYPELESS:
    case DXGI_FORMAT_R16_FLOAT:
    case DXGI_FORMAT_D16_UNORM:
    case DXGI_FORMAT_R16_UNORM:
    case DXGI_FORMAT_R16_UINT:
    case DXGI_FORMAT_R16_SNORM:
    case DXGI_FORMAT_R16_SINT:
    case DXGI_FORMAT_B5G6R5_UNORM:
    case DXGI_FORMAT_B5G5R5A1_UNORM:
    case DXGI_FORMAT_B4G4R4A4_UNORM:
        return 2;
    case DXGI_FORMAT_R8_TYPELESS:
    case DXGI_FORMAT_R8_UNORM:
    case DXGI_FORMAT_R8_UINT:
    case DXGI_FORMAT_R8_SNORM:
    case DXGI_FORMAT_R8_SINT:
    case DXGI_FORMAT_A8_UNORM:
        return 1;
    default:
        throw winrt::hresult_invalid_argument(L"Invalid pixel format!");
    }
}

//----------------------------------------------------------------------------
//
// CopyBytesFromTexture
//
//----------------------------------------------------------------------------
inline auto CopyBytesFromTexture(winrt::com_ptr<ID3D11Texture2D> const& texture, uint32_t subresource = 0)
{
    winrt::com_ptr<ID3D11Device> device;
    texture->GetDevice(device.put());
    winrt::com_ptr<ID3D11DeviceContext> context;
    device->GetImmediateContext(context.put());

    auto stagingTexture = PrepareStagingTexture(device, texture);

    D3D11_TEXTURE2D_DESC desc = {};
    stagingTexture->GetDesc(&desc);
    auto bytesPerPixel = GetBytesPerPixel(desc.Format);

    // Copy the bits
    D3D11_MAPPED_SUBRESOURCE mapped = {};
    winrt::check_hresult(context->Map(stagingTexture.get(), subresource, D3D11_MAP_READ, 0, &mapped));

    auto bytesStride = static_cast<size_t>(desc.Width) * bytesPerPixel;
    std::vector<byte> bytes(bytesStride * static_cast<size_t>(desc.Height), 0);
    auto source = static_cast<byte*>(mapped.pData);
    auto dest = bytes.data();
    for (auto i = 0; i < static_cast<int>(desc.Height); i++)
    {
        memcpy(dest, source, bytesStride);

        source += mapped.RowPitch;
        dest += bytesStride;
    }
    context->Unmap(stagingTexture.get(), 0);

    return bytes;
}


//----------------------------------------------------------------------------
//
// StopRecording
//
//----------------------------------------------------------------------------
void StopRecording()
{
    OutputDebugStringW(L"[Recording] StopRecording called\n");
    if( g_RecordToggle == TRUE ) {

        OutputDebugStringW(L"[Recording] g_RecordToggle was TRUE, stopping...\n");
        g_SelectRectangle.Stop();

        if ( g_RecordingSession != nullptr ) {

            OutputDebugStringW(L"[Recording] Closing VideoRecordingSession\n");
            g_RecordingSession->Close();
            // NOTE: Do NOT null the session here - let the coroutine finish first
        }

        if ( g_GifRecordingSession != nullptr ) {

            OutputDebugStringW(L"[Recording] Closing GifRecordingSession\n");
            g_GifRecordingSession->Close();
            // NOTE: Do NOT null the session here - let the coroutine finish first
        }

        g_RecordToggle = FALSE;
#if WINDOWS_CURSOR_RECORDING_WORKAROUND

        if( g_hWndLiveZoom != NULL && g_LiveZoomLevelOne ) {

            if( IsWindowVisible( g_hWndLiveZoom ) ) {

                ShowWindow( g_hWndLiveZoom, SW_HIDE );
                DestroyWindow( g_hWndLiveZoom );
                g_LiveZoomLevelOne = false;
            }
        }
#endif
    }
}


//----------------------------------------------------------------------------
//
// GetUniqueFilename
//
// Returns a unique filename by checking for existing files and adding (1), (2), etc.
// suffixes as needed. Uses the folder from lastSavePath if available
//
//----------------------------------------------------------------------------
std::wstring GetUniqueFilename(const std::wstring& lastSavePath, const wchar_t* defaultFilename, REFKNOWNFOLDERID defaultFolderId)
{
    // Get the folder where the file will be saved
    std::filesystem::path saveFolder;
    if (!lastSavePath.empty())
    {
        // Use folder from last save location
        saveFolder = std::filesystem::path(lastSavePath).parent_path();
    }

    if (saveFolder.empty())
    {
        // Default to specified known folder
        wil::unique_cotaskmem_string folderPath;
        if (SUCCEEDED(SHGetKnownFolderPath(defaultFolderId, KF_FLAG_DEFAULT, nullptr, folderPath.put())))
        {
            saveFolder = folderPath.get();
        }
    }

    // Build base name and extension
    std::filesystem::path defaultPath = defaultFilename;
    auto base = defaultPath.stem().wstring();
    auto ext = defaultPath.extension().wstring();

    // Check for existing files and find unique name
    std::wstring candidateName = base + ext;
    std::filesystem::path checkPath = saveFolder / candidateName;

    int index = 1;
    std::error_code ec;
    while (std::filesystem::exists(checkPath, ec))
    {
        candidateName = base + L" (" + std::to_wstring(index) + L")" + ext;
        checkPath = saveFolder / candidateName;
        index++;
    }

    return candidateName;
}

//----------------------------------------------------------------------------
//
// GetUniqueRecordingFilename
//
// Gets a unique file name for recording saves, using the " (N)" suffix
// approach so that the user can hit OK without worrying about overwriting
// if they are making multiple recordings in one session or don't want to
// always see an overwrite dialog or stop to clean up files.
//
//----------------------------------------------------------------------------
auto GetUniqueRecordingFilename()
{
    const wchar_t* defaultFile = (g_RecordingFormat == RecordingFormat::GIF)
        ? DEFAULT_GIF_RECORDING_FILE
        : DEFAULT_RECORDING_FILE;

    return GetUniqueFilename(g_RecordingSaveLocation, defaultFile, FOLDERID_Videos);
}

//----------------------------------------------------------------------------
//
// GetUniqueScreenshotFilename
//
// Gets a unique file name for screenshot saves, using the current date and
// time as a suffix. This reduces the chance that the user could overwrite an
// existing file if they are saving multiple captures in the same folder, and
// also ensures that ordering is correct when sorted by name.
//
//----------------------------------------------------------------------------
auto GetUniqueScreenshotFilename()
{
    SYSTEMTIME lt;
    GetLocalTime(&lt);

    // Format: "ZoomIt YYYY-MM-DD HHMMSS.png"
    wchar_t buffer[MAX_PATH];
    swprintf_s(buffer, L"%s %04d-%02d-%02d %02d%02d%02d.png",
        APPNAME,
        lt.wYear, lt.wMonth, lt.wDay,
        lt.wHour, lt.wMinute, lt.wSecond);

    return std::wstring(buffer);
}

//----------------------------------------------------------------------------
//
// StartRecordingAsync
//
// Starts the screen recording.
//
//----------------------------------------------------------------------------
winrt::fire_and_forget StartRecordingAsync( HWND hWnd, LPRECT rcCrop, HWND hWndRecord ) try
{
    // Capture the UI thread context so we can resume on it for the save dialog
    winrt::apartment_context uiThread;

    auto tempFolderPath = std::filesystem::temp_directory_path().wstring();
    auto tempFolder = co_await winrt::StorageFolder::GetFolderFromPathAsync( tempFolderPath );
    auto appFolder = co_await tempFolder.CreateFolderAsync( L"ZoomIt", winrt::CreationCollisionOption::OpenIfExists );

    // Choose temp file extension based on format
    const wchar_t* tempFileName = (g_RecordingFormat == RecordingFormat::GIF) ? L"zoomit.gif" : L"zoomit.mp4";
    auto file = co_await appFolder.CreateFileAsync( tempFileName, winrt::CreationCollisionOption::ReplaceExisting );

    // Get the device
    auto d3dDevice = util::CreateD3D11Device();
    auto dxgiDevice = d3dDevice.as<IDXGIDevice>();
    g_RecordDevice = CreateDirect3DDevice( dxgiDevice.get() );

    // Get the active MONITOR capture device
    HMONITOR hMon = NULL;
    POINT cursorPos = { 0, 0 };
    if( pMonitorFromPoint )	{

        GetCursorPos( &cursorPos );
        hMon = pMonitorFromPoint( cursorPos, MONITOR_DEFAULTTONEAREST );
    }

    winrt::Windows::Graphics::Capture::GraphicsCaptureItem item{ nullptr };
    if( hWndRecord )
        item = util::CreateCaptureItemForWindow( hWndRecord );
    else
        item = util::CreateCaptureItemForMonitor( hMon );

    auto stream = co_await file.OpenAsync( winrt::FileAccessMode::ReadWrite );

    // Create the appropriate recording session based on format
    OutputDebugStringW((L"Starting recording session. Framerate:  " + std::to_wstring(g_RecordFrameRate) + L" scaling: " + std::to_wstring(g_RecordScaling) + L" Format: " + (g_RecordingFormat == RecordingFormat::GIF ? L"GIF" : L"MP4") + L"\n").c_str());
    bool recordingStarted = false;
    HRESULT captureStatus = S_OK;

    if (g_RecordingFormat == RecordingFormat::GIF)
    {
        g_GifRecordingSession = GifRecordingSession::Create(
                                        g_RecordDevice,
                                        item,
                                        *rcCrop,
                                        g_RecordFrameRate,
                                        stream );

        recordingStarted = (g_GifRecordingSession != nullptr);

        if( g_hWndLiveZoom != NULL )
            g_GifRecordingSession->EnableCursorCapture( false );

        if (recordingStarted)
        {
            try
            {
                co_await g_GifRecordingSession->StartAsync();
            }
            catch (const winrt::hresult_error& error)
            {
                captureStatus = error.code();
                OutputDebugStringW((L"Recording session failed: " + error.message() + L"\n").c_str());
            }
        }

        // If no frames were captured, behave as if the hotkey was never pressed.
        if (recordingStarted && g_GifRecordingSession && !g_GifRecordingSession->HasCapturedFrames())
        {
            if (stream)
            {
                stream.Close();
                stream = nullptr;
            }
            try { co_await file.DeleteAsync(); } catch (...) {}
            g_RecordingSession = nullptr;
            g_GifRecordingSession = nullptr;
            co_return;
        }
    }
    else
    {
        g_RecordingSession = VideoRecordingSession::Create(
                                        g_RecordDevice,
                                        item,
                                        *rcCrop,
                                        g_RecordFrameRate,
                                        g_CaptureAudio,
                                        g_CaptureSystemAudio,
                                        g_MicMonoMix,
                                        stream );

        recordingStarted = (g_RecordingSession != nullptr);

        if( g_hWndLiveZoom != NULL )
            g_RecordingSession->EnableCursorCapture( false );

        if (recordingStarted)
        {
            try
            {
                co_await g_RecordingSession->StartAsync();
            }
            catch (const winrt::hresult_error& error)
            {
                captureStatus = error.code();
                OutputDebugStringW((L"Recording session failed: " + error.message() + L"\n").c_str());
            }
        }

        // If no frames were captured, behave as if the hotkey was never pressed.
        if (recordingStarted && g_RecordingSession && !g_RecordingSession->HasCapturedVideoFrames())
        {
            if (stream)
            {
                stream.Close();
                stream = nullptr;
            }
            try { co_await file.DeleteAsync(); } catch (...) {}
            g_RecordingSession = nullptr;
            g_GifRecordingSession = nullptr;
            co_return;
        }
    }

    // If we never created a session, bail and clean up the temp file silently
    if( !recordingStarted ) {

        if (stream) {
            stream.Close();
            stream = nullptr;
        }
        try { co_await file.DeleteAsync(); } catch (...) {}
        co_return;
    }

    // Recording completed (closed via hotkey or item close). Proceed to save/trim workflow.
    OutputDebugStringW(L"[Recording] StartAsync completed, entering save workflow\n");

    // Release the writer stream and session objects before trim/save. Keeping the temp file
    // open here can cause trimming and later MoveAndReplaceAsync calls to fail on the same file.
    if (stream)
    {
        stream.Close();
        stream = nullptr;
    }
    g_RecordingSession = nullptr;
    g_GifRecordingSession = nullptr;

    // Resume on the UI thread for the save dialog
    co_await uiThread;
    OutputDebugStringW(L"[Recording] Resumed on UI thread\n");

    {

        g_bSaveInProgress = true;

        SendMessage( g_hWndMain, WM_USER_SAVE_CURSOR, 0, 0 );

        winrt::StorageFile destFile = nullptr;
        HRESULT hr = S_OK;
        try {
            // Show trim dialog option and save dialog
            std::wstring trimmedFilePath;
            auto suggestedName = GetUniqueRecordingFilename();
            auto finalPath = VideoRecordingSession::ShowSaveDialogWithTrim(
                hWnd,
                suggestedName,
                std::wstring{ file.Path() },
                trimmedFilePath
            );

            if (!finalPath.empty())
            {
                auto path = std::filesystem::path(finalPath);
                winrt::StorageFolder folder{ co_await winrt::StorageFolder::GetFolderFromPathAsync(path.parent_path().c_str()) };
                destFile = co_await folder.CreateFileAsync(path.filename().c_str(), winrt::CreationCollisionOption::ReplaceExisting);

                // If user trimmed, use the trimmed file
                winrt::StorageFile sourceFile = file;
                if (!trimmedFilePath.empty())
                {
                    sourceFile = co_await winrt::StorageFile::GetFileFromPathAsync(trimmedFilePath);
                }

                // Move the chosen source into the user-selected destination
                co_await sourceFile.MoveAndReplaceAsync(destFile);

                // If we moved a trimmed copy, clean up the original temp capture file
                if (sourceFile != file)
                {
                    try { co_await file.DeleteAsync(); } catch (...) {}
                }

                // Use finalPath directly - destFile.Path() may be stale after MoveAndReplaceAsync
                g_RecordingSaveLocation = finalPath;
                // Update the registry buffer and save to persist across app restarts
                wcsncpy_s(g_RecordingSaveLocationBuffer, g_RecordingSaveLocation.c_str(), _TRUNCATE);
                reg.WriteRegSettings(RegSettings);
                SaveToClipboard(g_RecordingSaveLocation.c_str(), hWnd);
            }
            else
            {
                // User cancelled
                hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
            }

            //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() );

            //// Set file type based on the recording format
            //if (g_RecordingFormat == RecordingFormat::GIF)
            //{
            //    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 );
            //}

            //// Peek the folder Windows has chosen to display
            //static std::filesystem::path lastSaveFolder;
            //wil::unique_cotaskmem_string chosenFolderPath;
            //wil::com_ptr<IShellItem> currentSelectedFolder;
            //bool bFolderChanged = false;
            //if (SUCCEEDED(saveDialog->GetFolder(currentSelectedFolder.put())))
            //{
            //    if (SUCCEEDED(currentSelectedFolder->GetDisplayName(SIGDN_FILESYSPATH, chosenFolderPath.put())))
            //    {
            //        if (lastSaveFolder != chosenFolderPath.get())
            //        {
            //            lastSaveFolder = chosenFolderPath.get() ? chosenFolderPath.get() : std::filesystem::path{};
            //            bFolderChanged = true;
            //        }
            //    }
            //}

            //if( (g_RecordingFormat == RecordingFormat::GIF && g_RecordingSaveLocationGIF.size() == 0) || (g_RecordingFormat == RecordingFormat::MP4 && g_RecordingSaveLocation.size() == 0) || (bFolderChanged)) {

            //    wil::com_ptr<IShellItem> shellItem;
            //    wil::unique_cotaskmem_string folderPath;
            //    if (SUCCEEDED(saveDialog->GetFolder(shellItem.put())) && SUCCEEDED(shellItem->GetDisplayName(SIGDN_FILESYSPATH, folderPath.put()))) {
            //        if (g_RecordingFormat == RecordingFormat::GIF) {
            //            g_RecordingSaveLocationGIF = folderPath.get();
            //            std::filesystem::path currentPath{ g_RecordingSaveLocationGIF };
            //            g_RecordingSaveLocationGIF = currentPath / DEFAULT_GIF_RECORDING_FILE;
            //        }
            //        else {
            //            g_RecordingSaveLocation = folderPath.get();
            //            if (g_RecordingFormat == RecordingFormat::MP4) {
            //                std::filesystem::path currentPath{ g_RecordingSaveLocation };
            //                g_RecordingSaveLocation = currentPath / DEFAULT_RECORDING_FILE;
            //            }
            //        }
            //    }
            //}

            //// Always use appropriate default filename based on current format
            //auto suggestedName = GetUniqueRecordingFilename();
            //saveDialog->SetFileName( suggestedName.c_str() );

            //THROW_IF_FAILED( saveDialog->Show( hWnd ) );
            //wil::com_ptr<IShellItem> shellItem;
            //THROW_IF_FAILED(saveDialog->GetResult(shellItem.put()));
            //wil::unique_cotaskmem_string filePath;
            //THROW_IF_FAILED(shellItem->GetDisplayName(SIGDN_FILESYSPATH, filePath.put()));
            //auto path = std::filesystem::path( filePath.get() );

            //winrt::StorageFolder folder{ co_await winrt::StorageFolder::GetFolderFromPathAsync( path.parent_path().c_str() ) };
            //destFile = co_await folder.CreateFileAsync( path.filename().c_str(), winrt::CreationCollisionOption::ReplaceExisting );
        }
        catch( const wil::ResultException& error ) {
            OutputDebugStringW((L"[Recording] wil exception: hr=0x" + std::to_wstring(error.GetErrorCode()) + L"\n").c_str());
            hr = error.GetErrorCode();
        }
        catch( const std::exception& ex ) {
            OutputDebugStringA("[Recording] std::exception: ");
            OutputDebugStringA(ex.what());
            OutputDebugStringA("\n");
            hr = E_FAIL;
        }
        catch( ... ) {
            OutputDebugStringW(L"[Recording] Unknown exception in save workflow\n");
            hr = E_FAIL;
        }
        if( destFile == nullptr ) {

            if (stream) {
                stream.Close();
                stream = nullptr;
            }
            try { co_await file.DeleteAsync(); } catch (...) {}
        }
        g_bSaveInProgress = false;

        SendMessage( g_hWndMain, WM_USER_RESTORE_CURSOR, 0, 0 );
        if( hWnd == g_hWndMain )
            RestoreForeground();

        if( FAILED( hr ) )
        {
            if( hr != HRESULT_FROM_WIN32( ERROR_CANCELLED ) )
            {
                throw winrt::hresult_error( hr );
            }
        }
    }

    // Ensure globals are reset after the save/cleanup path completes
    if (stream) {
        stream.Close();
        stream = nullptr;
    }
    g_RecordingSession = nullptr;
    g_GifRecordingSession = nullptr;
} catch( const winrt::hresult_error& error ) {

    // Reset the save-in-progress flag so that hotkeys are not blocked after an error or cancellation
    g_bSaveInProgress = false;

    PostMessage( g_hWndMain, WM_USER_STOP_RECORDING, 0, 0 );

    // Suppress the error from canceling the save dialog
    if( error.code() == HRESULT_FROM_WIN32( ERROR_CANCELLED ))
        co_return;

    if (g_RecordToggle == FALSE) {

        MessageBox(g_hWndMain, L"Recording cancelled before started", APPNAME, MB_OK | MB_ICONERROR | MB_SYSTEMMODAL);
    }
    else {

        ErrorDialogString(g_hWndMain, L"Error starting recording", error.message().c_str());
    }
}

//----------------------------------------------------------------------------
//
// UpdateMonitorInfo
//
//----------------------------------------------------------------------------
void UpdateMonitorInfo( POINT point, MONITORINFO* monInfo )
{
    HMONITOR hMon{};
    if( pMonitorFromPoint != nullptr )
    {
        hMon = pMonitorFromPoint( point, MONITOR_DEFAULTTONEAREST );
    }
    if( hMon != nullptr )
    {
        monInfo->cbSize = sizeof *monInfo;
        pGetMonitorInfo( 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 );
        }
    }
}

#ifdef __ZOOMIT_POWERTOYS__
HRESULT OpenPowerToysSettingsApp()
{
    std::wstring path = get_module_folderpath(g_hInstance);
    path += L"\\PowerToys.exe";

    std::wstring openSettings = L"--open-settings=ZoomIt";

    std::wstring full_command_path = path + L" " + openSettings;

    STARTUPINFO startupInfo;
    ZeroMemory(&startupInfo, sizeof(STARTUPINFO));
    startupInfo.cb = sizeof(STARTUPINFO);
    startupInfo.wShowWindow = SW_SHOWNORMAL;

    PROCESS_INFORMATION processInformation;

    CreateProcess(
        path.c_str(),
        full_command_path.data(),
        NULL,
        NULL,
        TRUE,
        0,
        NULL,
        NULL,
        &startupInfo,
        &processInformation);

    if (!CloseHandle(processInformation.hProcess))
    {
        return HRESULT_FROM_WIN32(GetLastError());
    }
    if (!CloseHandle(processInformation.hThread))
    {
        return HRESULT_FROM_WIN32(GetLastError());
    }
    return S_OK;
}
#endif // __ZOOMIT_POWERTOYS__

//----------------------------------------------------------------------------
//
// ShowMainWindow
//
//----------------------------------------------------------------------------
void ShowMainWindow(HWND hWnd, const MONITORINFO& monInfo, int width, int height)
{
    // Show the window first
    SetWindowPos(hWnd, HWND_TOPMOST, monInfo.rcMonitor.left, monInfo.rcMonitor.top,
        width, height, SWP_SHOWWINDOW | SWP_NOCOPYBITS);

    // Now invalidate and update the window
    InvalidateRect(hWnd, NULL, TRUE);
    UpdateWindow(hWnd);

    SetForegroundWindow(hWnd);
    SetActiveWindow(hWnd);
}

//----------------------------------------------------------------------------
//
// MainWndProc
//
//----------------------------------------------------------------------------
LRESULT APIENTRY MainWndProc(
    HWND hWnd,
    UINT message,
    WPARAM wParam,
    LPARAM lParam)
{
    static int		width, height;
    static HDC		hdcScreen, hdcScreenCompat, hdcScreenCursorCompat, hdcScreenSaveCompat;
    static HBITMAP	hbmpCompat, hbmpDrawingCompat, hbmpCursorCompat;
    static RECT     cropRc{};
    static BITMAP	bmp;
    static BOOLEAN	g_TimerActive = FALSE;
    static BOOLEAN	g_Zoomed = FALSE;
    static TypeModeState g_TypeMode = TypeModeOff;
    static BOOLEAN	g_HaveTyped = FALSE;
    static DEVMODE	secondaryDevMode;
    static RECT		g_LiveZoomSourceRect;
    static float	g_LiveZoomLevel;
    static float	zoomLevel;
    static float	zoomTelescopeStep;
    static float	zoomTelescopeTarget;
    static POINT	cursorPos;
    static POINT	savedCursorPos;
    static RECT		cursorRc;
    static RECT		boundRc;
    static POINT	prevPt;
    static POINT	textStartPt;
    static POINT	textPt;
    static P_DRAW_UNDO drawUndoList = NULL;
    static P_TYPED_KEY	typedKeyList = NULL;
    static BOOLEAN	g_HaveDrawn = FALSE;
    static DWORD	g_DrawingShape = 0;
    static DWORD    prevPenWidth = g_PenWidth;
    static POINT	g_RectangleAnchor;
    static RECT		g_rcRectangle;
    static BOOLEAN	g_Tracing = FALSE;
    static int		g_BlankedScreen = 0;
    static int		g_StraightDirection = 0;
    static BOOLEAN	g_Drawing = FALSE;
    static HWND		g_ActiveWindow = NULL;
    static int		breakTimeout;
    static HBITMAP	g_hBackgroundBmp = NULL;
    static HDC		g_hDcBackgroundFile;
    static HPEN		hDrawingPen;
    static HFONT	hTimerFont;
    static HFONT	hNegativeTimerFont;
    static HFONT	hTypingFont;
    static MONITORINFO	monInfo;
    static MONITORINFO  lastMonInfo;
    static HWND		hTargetWindow = NULL;
    static RECT		rcTargetWindow;
    static BOOLEAN  forcePenResize = TRUE;
    static BOOLEAN  activeBreakShowDesktop = g_BreakShowDesktop;
    static BOOLEAN	activeBreakShowBackgroundFile = g_BreakShowBackgroundFile;
    static TCHAR    activeBreakBackgroundFile[MAX_PATH] = {0};
    static UINT     wmTaskbarCreated;
#if 0
    TITLEBARINFO	titleBarInfo;
    WINDOWINFO		targetWindowInfo;
#endif
    bool			isCaptureSupported = false;
    RECT			rc, rc1;
    PAINTSTRUCT		ps;
    TCHAR			timerText[16];
    TCHAR			negativeTimerText[16];
    BOOLEAN			penInverted;
    BOOLEAN			zoomIn;
    HDC				hDc;
    HWND			hWndRecord;
    int				x, y, delta;
    HMENU			hPopupMenu;
    static TCHAR	filePath[MAX_PATH] = {L"zoomit"};
    NOTIFYICONDATA	tNotifyIconData;
    static DWORD64  g_TelescopingZoomLastTick = 0ull;

    const auto drawAllRightJustifiedLines = [&rc]( long lineHeight, bool doPop = false ) {
        rc.top = textPt.y - static_cast<LONG>(g_TextBufferPreviousLines.size()) * lineHeight;

        for( const auto& line : g_TextBufferPreviousLines )
        {
            DrawText( hdcScreenCompat, line.c_str(), static_cast<int>(line.length()), &rc, DT_CALCRECT );
            const auto textWidth = rc.right - rc.left;
            rc.left = textPt.x - textWidth;
            rc.right = textPt.x;
            DrawText( hdcScreenCompat, line.c_str(), static_cast<int>(line.length()), &rc, DT_LEFT );
            rc.top += lineHeight;
        }
        if( !g_TextBuffer.empty() )
        {
            if( doPop )
            {
                g_TextBuffer.pop_back();
            }
            DrawText( hdcScreenCompat, g_TextBuffer.c_str(), static_cast<int>(g_TextBuffer.length()), &rc, DT_CALCRECT );
            rc.left = textPt.x - (rc.right - rc.left);
            rc.right = textPt.x;
            DrawText( hdcScreenCompat, g_TextBuffer.c_str(), static_cast<int>(g_TextBuffer.length()), &rc, DT_LEFT );
        }
    };

    const auto doTelescopingZoomTimer = [hWnd, wParam, lParam, &x, &y]( bool invalidate = true ) {
        if( zoomTelescopeStep != 0.0f )
        {
            zoomLevel *= zoomTelescopeStep;
            g_TelescopingZoomLastTick = GetTickCount64();
            if( (zoomTelescopeStep > 1 && zoomLevel >= zoomTelescopeTarget) ||
                (zoomTelescopeStep < 1 && zoomLevel <= zoomTelescopeTarget) )
            {
                zoomLevel = zoomTelescopeTarget;

                g_TelescopingZoomLastTick = 0ull;
                KillTimer( hWnd, wParam );
                OutputDebug( L"SETCURSOR mon_left: %x mon_top: %x x: %d y: %d\n",
                            monInfo.rcMonitor.left,
                            monInfo.rcMonitor.top,
                            cursorPos.x,
                            cursorPos.y );
                SetCursorPos( monInfo.rcMonitor.left + cursorPos.x,
                             monInfo.rcMonitor.top + cursorPos.y );
            }
        }
        else
        {
            // Case where we didn't zoom at all
            g_TelescopingZoomLastTick = 0ull;
            KillTimer( hWnd, wParam );
        }
        if( wParam == 2 && zoomLevel == 1 )
        {
            g_Zoomed = FALSE;

            // Unregister Ctrl+C and Ctrl+S hotkeys when exiting static zoom
            UnregisterHotKey( hWnd, COPY_IMAGE_HOTKEY );
            UnregisterHotKey( hWnd, COPY_CROP_HOTKEY );
            UnregisterHotKey( hWnd, SAVE_IMAGE_HOTKEY );
            UnregisterHotKey( hWnd, SAVE_CROP_HOTKEY );

            if( g_ZoomOnLiveZoom )
            {
                GetCursorPos( &cursorPos );
                cursorPos = ScalePointInRects( cursorPos, monInfo.rcMonitor, g_LiveZoomSourceRect );
                SetCursorPos( cursorPos.x, cursorPos.y );
                SendMessage( hWnd, WM_HOTKEY, LIVE_HOTKEY, 0 );
            }
            else if( lParam != SHALLOW_ZOOM )
            {
                // Figure out where final unzoomed cursor should be
                if( g_Drawing )
                {
                    cursorPos = prevPt;
                }
                OutputDebug( L"FINAL MOUSE: x: %d y: %d\n", cursorPos.x, cursorPos.y );
                GetZoomedTopLeftCoordinates( zoomLevel, &cursorPos, &x, width, &y, height );
                cursorPos.x = monInfo.rcMonitor.left + x + static_cast<int>((cursorPos.x - x) * zoomLevel);
                cursorPos.y = monInfo.rcMonitor.top + y + static_cast<int>((cursorPos.y - y) * zoomLevel);
                SetCursorPos( cursorPos.x, cursorPos.y );
            }
            if( hTargetWindow )
            {
                SetWindowPos( hTargetWindow, HWND_BOTTOM, rcTargetWindow.left, rcTargetWindow.top, rcTargetWindow.right - rcTargetWindow.left, rcTargetWindow.bottom - rcTargetWindow.top, 0 );
                hTargetWindow = NULL;
            }
            DeleteDrawUndoList( &drawUndoList );

            // Restore live zoom if we came from that mode
            if( g_ZoomOnLiveZoom )
            {
                SendMessage( g_hWndLiveZoom, WM_USER_SET_ZOOM, static_cast<WPARAM>(g_LiveZoomLevel), reinterpret_cast<LPARAM>(&g_LiveZoomSourceRect) );
                g_ZoomOnLiveZoom = FALSE;
                forcePenResize = TRUE;
            }

            SetForegroundWindow( g_ActiveWindow );
            ClipCursor( NULL );
            g_HaveDrawn = FALSE;
            g_TypeMode = TypeModeOff;
            g_HaveTyped = FALSE;
            g_Drawing = FALSE;
            EnableDisableStickyKeys( TRUE );
            DeleteObject( hTypingFont );
            DeleteDC( hdcScreen );
            DeleteDC( hdcScreenCompat );
            DeleteDC( hdcScreenCursorCompat );
            DeleteDC( hdcScreenSaveCompat );
            DeleteObject( hbmpCompat );
            DeleteObject (hbmpCursorCompat );
            DeleteObject( hbmpDrawingCompat );
            DeleteObject( hDrawingPen );

            SetFocus( g_ActiveWindow );
            ShowWindow( hWnd, SW_HIDE );
        }
        if( invalidate )
        {
            InvalidateRect( hWnd, NULL, FALSE );
        }
    };

    switch (message) {
    case WM_CREATE:

        // get default font
        GetObject( GetStockObject(DEFAULT_GUI_FONT), sizeof g_LogFont, &g_LogFont );
        g_LogFont.lfWeight = FW_NORMAL;
        hDc = CreateCompatibleDC( NULL );
        g_LogFont.lfHeight = -MulDiv(8, GetDeviceCaps(hDc, LOGPIXELSY), 72);
        DeleteDC( hDc );

        reg.ReadRegSettings( RegSettings );

        // Recover screensaver settings if ZoomIt crashed while the break
        // screensaver was active (or was force-killed).
        if( HasOrphanedScreenSaverSettings() )
        {
            RestoreScreenSaverSettings();
        }

        // Register for session change notifications so we can restore
        // screensaver settings after the break timer screensaver is dismissed
        // via user authentication (session unlock).
        WTSRegisterSessionNotification( hWnd, NOTIFY_FOR_THIS_SESSION );

        // Refresh dark mode state after loading theme override from registry
        RefreshDarkModeState();

        // Initialize save location strings from registry buffers
        g_RecordingSaveLocation = g_RecordingSaveLocationBuffer;
        g_ScreenshotSaveLocation = g_ScreenshotSaveLocationBuffer;

        // Set g_RecordScaling based on the current recording format
        if (g_RecordingFormat == RecordingFormat::GIF) {
            g_RecordScaling = g_RecordScalingGIF;
            g_RecordFrameRate = RECORDING_FORMAT_GIF_DEFAULT_FRAMERATE;
        } else {
            g_RecordScaling = g_RecordScalingMP4;
            g_RecordFrameRate = RECORDING_FORMAT_MP4_DEFAULT_FRAMERATE;
        }

        // to support migrating from
        if ((g_PenColor >> 24) == 0) {
            g_PenColor |= 0xFF << 24;
        }

        g_PenWidth = g_RootPenWidth;

        g_ToggleMod = GetKeyMod( g_ToggleKey );
        g_LiveZoomToggleMod = GetKeyMod( g_LiveZoomToggleKey );
        g_DrawToggleMod = GetKeyMod( g_DrawToggleKey );
        g_BreakToggleMod = GetKeyMod( g_BreakToggleKey );
        g_DemoTypeToggleMod = GetKeyMod( g_DemoTypeToggleKey );
        g_SnipToggleMod = GetKeyMod( g_SnipToggleKey );
        g_SnipPanoramaToggleMod = GetKeyMod( g_SnipPanoramaToggleKey );
        g_SnipOcrToggleMod = GetKeyMod( g_SnipOcrToggleKey );
        g_RecordToggleMod = GetKeyMod( g_RecordToggleKey );

        if( !g_OptionsShown && !g_StartedByPowerToys ) {
            // First run should show options when running as standalone. If not running as standalone,
            // options screen won't show and we should register keys instead.
            SendMessage( hWnd, WM_COMMAND, IDC_OPTIONS, 0 );
            g_OptionsShown = TRUE;
            reg.WriteRegSettings( RegSettings );
        } else {
            BOOL	showOptions = FALSE;

            if( g_ToggleKey && !RegisterHotKey( hWnd, ZOOM_HOTKEY, g_ToggleMod, g_ToggleKey & 0xFF)) {

                MessageBox( hWnd, L"The specified zoom toggle hotkey is already in use.\nSelect a different zoom toggle hotkey.",
                    APPNAME, MB_ICONERROR );
                showOptions = TRUE;

            } else if( g_LiveZoomToggleKey &&
                (!RegisterHotKey( hWnd, LIVE_HOTKEY, g_LiveZoomToggleMod, g_LiveZoomToggleKey & 0xFF) ||
                    !RegisterHotKey(hWnd, LIVE_DRAW_HOTKEY, (g_LiveZoomToggleMod ^ MOD_SHIFT), g_LiveZoomToggleKey & 0xFF))) {

                MessageBox( hWnd, L"The specified live-zoom toggle hotkey is already in use.\nSelect a different zoom toggle hotkey.",
                    APPNAME, MB_ICONERROR );
                showOptions = TRUE;

            } else if( g_DrawToggleKey && !RegisterHotKey( hWnd, DRAW_HOTKEY, g_DrawToggleMod, g_DrawToggleKey & 0xFF )) {

                MessageBox( hWnd, L"The specified draw w/out zoom hotkey is already in use.\nSelect a different draw w/out zoom hotkey.",
                    APPNAME, MB_ICONERROR );
                showOptions = TRUE;

            }
            else if (g_BreakToggleKey && !RegisterHotKey(hWnd, BREAK_HOTKEY, g_BreakToggleMod, g_BreakToggleKey & 0xFF)) {

                MessageBox(hWnd, L"The specified break timer hotkey is already in use.\nSelect a different break timer hotkey.",
                    APPNAME, MB_ICONERROR);
                showOptions = TRUE;

            }
            else if( g_DemoTypeToggleKey &&
                (!RegisterHotKey( hWnd, DEMOTYPE_HOTKEY, g_DemoTypeToggleMod, g_DemoTypeToggleKey & 0xFF ) ||
                 !RegisterHotKey(hWnd, DEMOTYPE_RESET_HOTKEY, (g_DemoTypeToggleMod ^ MOD_SHIFT), g_DemoTypeToggleKey & 0xFF))) {

                MessageBox( hWnd, L"The specified live-type hotkey is already in use.\nSelect a different live-type hotkey.",
                    APPNAME, MB_ICONERROR );
                showOptions = TRUE;

            }
            else if (g_SnipToggleKey &&
                (!RegisterHotKey(hWnd, SNIP_HOTKEY, g_SnipToggleMod, g_SnipToggleKey & 0xFF) ||
                 !RegisterHotKey(hWnd, SNIP_SAVE_HOTKEY, (g_SnipToggleMod ^ MOD_SHIFT), g_SnipToggleKey & 0xFF))) {

                MessageBox(hWnd, L"The specified snip hotkey is already in use.\nSelect a different snip hotkey.",
                    APPNAME, MB_ICONERROR);
                showOptions = TRUE;

            }
            else if (g_SnipPanoramaToggleKey &&
                (g_SnipPanoramaToggleKey != g_SnipToggleKey || g_SnipPanoramaToggleMod != g_SnipToggleMod) &&
                (!RegisterHotKey(hWnd, SNIP_PANORAMA_HOTKEY, g_SnipPanoramaToggleMod | MOD_NOREPEAT, g_SnipPanoramaToggleKey & 0xFF) ||
                 !RegisterHotKey(hWnd, SNIP_PANORAMA_SAVE_HOTKEY, ( g_SnipPanoramaToggleMod ^ MOD_SHIFT ) | MOD_NOREPEAT, g_SnipPanoramaToggleKey & 0xFF))) {

                MessageBox(hWnd, L"The specified panorama snip hotkey is already in use.\nSelect a different panorama snip hotkey.",
                    APPNAME, MB_ICONERROR);
                showOptions = TRUE;

            }
            else if (g_SnipOcrToggleKey &&
                !RegisterHotKey(hWnd, SNIP_OCR_HOTKEY, g_SnipOcrToggleMod, g_SnipOcrToggleKey & 0xFF)) {

                MessageBox(hWnd, L"The specified snip OCR hotkey is already in use.\nSelect a different snip OCR hotkey.",
                    APPNAME, MB_ICONERROR);
                showOptions = TRUE;

            }
            else if (g_RecordToggleKey &&
                (!RegisterHotKey(hWnd, RECORD_HOTKEY, g_RecordToggleMod | MOD_NOREPEAT, g_RecordToggleKey & 0xFF) ||
                 !RegisterHotKey(hWnd, RECORD_CROP_HOTKEY, (g_RecordToggleMod ^ MOD_SHIFT) | MOD_NOREPEAT, g_RecordToggleKey & 0xFF) ||
                 !RegisterHotKey(hWnd, RECORD_WINDOW_HOTKEY, (g_RecordToggleMod ^ MOD_ALT) | MOD_NOREPEAT, g_RecordToggleKey & 0xFF))) {

                MessageBox(hWnd, L"The specified record hotkey is already in use.\nSelect a different record hotkey.",
                    APPNAME, MB_ICONERROR);
                showOptions = TRUE;
            }
            if( showOptions ) {

                SendMessage( hWnd, WM_COMMAND, IDC_OPTIONS, 0 );
            }
        }
        SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL );
        wmTaskbarCreated = RegisterWindowMessage(_T("TaskbarCreated"));
        return TRUE;

    case WM_CLOSE:
        // Do not allow users to close the main window, for example with Alt-F4.
        return 0;

    case WM_HOTKEY:
        OutputDebug( L"[Hotkey] WM_HOTKEY id=%ld(%s) lParam=0x%llX\n",
                     static_cast<long>( wParam ),
                     HotkeyIdToString( wParam ),
                     static_cast<unsigned long long>( lParam ) );
        LogPanoramaState( L"WM_HOTKEY entry", wParam );

        if( g_PanoramaCaptureActive )
        {
            if( wParam == SNIP_PANORAMA_HOTKEY || wParam == SNIP_PANORAMA_SAVE_HOTKEY )
            {
                OutputDebug( L"[Panorama] Stop hotkey received while capture is active\n" );
                g_PanoramaStopRequested = true;

                // If we're still selecting the panorama source area, stop selection
                // immediately so the capture path can unwind cleanly.
                if( g_RecordCropping == TRUE )
                {
                    OutputDebug( L"[Panorama] Stop requested during crop selection; stopping SelectRectangle\n" );
                    g_SelectRectangle.Stop();
                    g_RecordCropping = FALSE;
                }
            }

            // Do not process any other hotkeys while panorama capture is active.
            LogPanoramaState( L"WM_HOTKEY consumed while panorama active", wParam );
            return 0;
        }

        if( g_RecordCropping == TRUE )
        {
            // If the crop overlay has already been torn down, clear stale state.
            if( !g_SelectRectangle.IsActive() )
            {
                OutputDebug( L"[Hotkey] Clearing stale crop state (g_RecordCropping=TRUE but SelectRectangle inactive)\n" );
                g_RecordCropping = FALSE;
            }
        }

        if( g_RecordCropping == TRUE )
        {
            if( wParam != RECORD_CROP_HOTKEY )
            {
                // Cancel cropping on any hotkey.
                OutputDebug( L"[Hotkey] Cancelling crop due to hotkey id=%ld(%s)\n",
                             static_cast<long>( wParam ),
                             HotkeyIdToString( wParam ) );
                g_SelectRectangle.Stop();
                g_RecordCropping = FALSE;

                // Cropping is handled by a blocking call in WM_HOTKEY, so post
                // this message to the window for processing after the previous
                // WM_HOTKEY message completes processing.
                PostMessage( hWnd, message, wParam, lParam );
            }
            return 0;
        }

        //
        // Magic value that comes from tray context menu
        //
        if (lParam == 1) {

            //
            // Sleep to let context menu dismiss
            //
            Sleep(250);
        }
        switch( wParam ) {
        case LIVE_DRAW_HOTKEY:
        {
            OutputDebug(L"LIVE_DRAW_HOTKEY\n");
            LONG_PTR exStyle = GetWindowLongPtr(hWnd, GWL_EXSTYLE);

            if ((exStyle & WS_EX_LAYERED)) {
                OutputDebug(L"LiveDraw reactivate\n");

                // Just focus on the window and re-enter drawing mode
                SetFocus(hWnd);
                SetForegroundWindow(hWnd);
                SendMessage(hWnd, WM_LBUTTONDOWN, 0, MAKELPARAM(cursorPos.x, cursorPos.y));
                SendMessage(hWnd, WM_MOUSEMOVE, 0, MAKELPARAM(cursorPos.x, cursorPos.y));
                if( IsWindowVisible( g_hWndLiveZoom ) )
                {
                    SendMessage( g_hWndLiveZoom, WM_USER_MAGNIFY_CURSOR, FALSE, 0 );
                }
                break;
            }
            else {
                OutputDebug(L"LiveDraw create\n");

                exStyle = GetWindowLongPtr(hWnd, GWL_EXSTYLE);
                SetWindowLongPtr(hWnd, GWL_EXSTYLE, exStyle | WS_EX_LAYERED);
                SetLayeredWindowAttributes(hWnd, COLORREF(RGB(0, 0, 0)), 0, LWA_COLORKEY);
                pMagSetWindowFilterList( g_hWndLiveZoomMag, MW_FILTERMODE_EXCLUDE, 0, nullptr );
            }
            [[fallthrough]];
        }
        case DRAW_HOTKEY:
            //
            // Enter drawing mode without zoom
            //
#ifdef __ZOOMIT_POWERTOYS__
            if( g_StartedByPowerToys )
            {
                Trace::ZoomItActivateDraw();
            }
#endif // __ZOOMIT_POWERTOYS__

            if( !g_Zoomed ) {
                OutputDebug(L"LiveDraw: %d (%d)\n", wParam, (wParam == LIVE_DRAW_HOTKEY));

#if WINDOWS_CURSOR_RECORDING_WORKAROUND
                if( IsWindowVisible( g_hWndLiveZoom ) && !g_LiveZoomLevelOne ) {
#else
                if( IsWindowVisible( g_hWndLiveZoom )) {
#endif

                    OutputDebug(L"   In Live zoom\n");
                    SendMessage(hWnd, WM_HOTKEY, ZOOM_HOTKEY, wParam == LIVE_DRAW_HOTKEY ? LIVE_DRAW_ZOOM : 0);

                } else {
                    OutputDebug(L"   Not in Live zoom\n");
                    SendMessage( hWnd, WM_HOTKEY, ZOOM_HOTKEY, wParam == LIVE_DRAW_HOTKEY ? LIVE_DRAW_ZOOM : 0 );
                    zoomLevel = zoomTelescopeTarget = 1;
                    SendMessage( hWnd, WM_LBUTTONDOWN, 0, MAKELPARAM( cursorPos.x, cursorPos.y ));
                }
                if(wParam == LIVE_DRAW_HOTKEY) {

                    SetLayeredWindowAttributes(hWnd, COLORREF(RGB(0, 0, 0)), 0, LWA_COLORKEY);
                    SendMessage(hWnd, WM_KEYDOWN, 'K', LIVE_DRAW_ZOOM);
                    SetTimer(hWnd, 3, 10, NULL);
                    SendMessage(hWnd, WM_MOUSEMOVE, 0, MAKELPARAM(cursorPos.x, cursorPos.y));
                    ShowMainWindow(hWnd, monInfo, width, height);
                    if( ( g_PenColor & 0xFFFFFF ) == COLOR_BLUR )
                    {
                        // Blur is not supported in LiveDraw
                        g_PenColor = COLOR_RED;
                    }
                    // Highlight is not supported in LiveDraw
                    g_PenColor |= 0xFF << 24;
				}
            }
            break;

        case SNIP_PANORAMA_HOTKEY:
        case SNIP_PANORAMA_SAVE_HOTKEY:
        case SNIP_SAVE_HOTKEY:
        case SNIP_HOTKEY:
        {
            OutputDebugStringW((L"[Snip] Hotkey received: " + std::to_wstring(LOWORD(wParam)) +
                L" (SNIP_SAVE=" + std::to_wstring(SNIP_SAVE_HOTKEY) +
                L" SNIP=" + std::to_wstring(SNIP_HOTKEY) + L")\n").c_str());

            const bool panoramaRequested = (LOWORD(wParam) == SNIP_PANORAMA_HOTKEY || LOWORD(wParam) == SNIP_PANORAMA_SAVE_HOTKEY);
            const bool panoramaSaveToFile = (LOWORD(wParam) == SNIP_PANORAMA_SAVE_HOTKEY);

            if( panoramaRequested )
            {
                LogPanoramaState( L"Panorama requested", wParam );
                // Block liveZoom liveDraw snip due to mirroring bug
                if( IsWindowVisible( g_hWndLiveZoom )
                    && ( GetWindowLongPtr( hWnd, GWL_EXSTYLE ) & WS_EX_LAYERED ) )
                {
                    OutputDebug( L"[Panorama] Request ignored because liveDraw overlay is active\n" );
                    break;
                }

#ifdef __ZOOMIT_POWERTOYS__
                if( g_StartedByPowerToys )
                {
                    Trace::ZoomItActivateSnip();
                }
#endif // __ZOOMIT_POWERTOYS__

                if( g_Drawing )
                {
                    OutputDebug( L"[Panorama] Exiting drawing mode before capture\n" );
                    SendMessage( hWnd, WM_USER_EXIT_MODE, 0, 0 );
                    if( g_Drawing )
                    {
                        SendMessage( hWnd, WM_USER_EXIT_MODE, 0, 0 );
                    }
                }

                if( g_Zoomed )
                {
                    OutputDebug( L"[Panorama] Exiting zoom before capture; zoomOnLiveZoom=%d\n", g_ZoomOnLiveZoom ? 1 : 0 );
                    if( g_ZoomOnLiveZoom )
                    {
                        ShowCursor( false );
                        SendMessage( hWnd, WM_HOTKEY, ZOOM_HOTKEY, 0 );
                        ShowCursor( true );
                    }
                    else
                    {
                        SendMessage( hWnd, WM_HOTKEY, ZOOM_HOTKEY, SHALLOW_ZOOM );
                    }
                }

                g_PanoramaCaptureActive = true;
                g_PanoramaStopRequested = false;
                LogPanoramaState( L"Panorama capture armed", wParam );
                auto panoramaCaptureCleanup = wil::scope_exit( [hWnd] {
                    LogPanoramaState( L"Panorama cleanup begin" );
                    g_PanoramaCaptureActive = false;
                    g_PanoramaStopRequested = false;
                    g_RecordCropping = FALSE;
                    g_SelectRectangle.Stop();
                    UNREFERENCED_PARAMETER( hWnd );
                    LogPanoramaState( L"Panorama cleanup end" );
                } );
                const bool captureSuccess = panoramaSaveToFile
                    ? RunPanoramaCaptureToFile( hWnd )
                    : RunPanoramaCaptureToClipboard( hWnd );
                OutputDebug( L"[Panorama] RunPanoramaCapture%s result=%d\n",
                             panoramaSaveToFile ? L"ToFile" : L"ToClipboard",
                             captureSuccess ? 1 : 0 );
                if( !captureSuccess )
                {
                    OutputDebugStringW( panoramaSaveToFile
                        ? L"[Panorama] Failed to save capture to file\n"
                        : L"[Panorama] Failed to copy capture to clipboard\n" );
                }
                break;
            }

            // Block liveZoom liveDraw snip due to mirroring bug
            if( IsWindowVisible( g_hWndLiveZoom )
                && ( GetWindowLongPtr( hWnd, GWL_EXSTYLE ) & WS_EX_LAYERED ) )
            {
                break;
            }

            bool zoomed = true;
#ifdef __ZOOMIT_POWERTOYS__
            if( g_StartedByPowerToys )
            {
                Trace::ZoomItActivateSnip();
            }
#endif // __ZOOMIT_POWERTOYS__

            // First, static zoom
            if( !g_Zoomed )
            {
                zoomed = false;
                if( IsWindowVisible( g_hWndLiveZoom ) && !g_LiveZoomLevelOne )
                {
                    SendMessage( hWnd, WM_HOTKEY, ZOOM_HOTKEY, SHALLOW_ZOOM );
                }
                else
                {
                    SendMessage( hWnd, WM_HOTKEY, ZOOM_HOTKEY, LIVE_DRAW_ZOOM);
                }
                zoomLevel = zoomTelescopeTarget = 1;
            }
            else if( g_Drawing )
            {
                // Exit drawing mode to hide the drawing cursor
                SendMessage( hWnd, WM_USER_EXIT_MODE, 0, 0 );

                // Exit again if still in drawing mode, which happens from type mode
                if( g_Drawing )
                {
                    SendMessage( hWnd, WM_USER_EXIT_MODE, 0, 0 );
                }
            }
            ShowMainWindow(hWnd, monInfo, width, height);

            if( LOWORD( wParam ) == SNIP_SAVE_HOTKEY )
            {
                // IDC_SAVE_CROP handles cursor hiding internally after region selection
                SendMessage( hWnd, WM_COMMAND, IDC_SAVE_CROP, ( zoomed ? 0 : SHALLOW_ZOOM ) );
            }
            else
            {
                SendMessage( hWnd, WM_COMMAND, IDC_COPY_CROP, ( zoomed ? 0 : SHALLOW_ZOOM ) );
            }

            // Now if we weren't zoomed, unzoom
            if( !zoomed )
            {
                if( g_ZoomOnLiveZoom )
                {
                    // hiding the cursor allows for a cleaner transition back to the magnified cursor
                    ShowCursor( false );
                    SendMessage( hWnd, WM_HOTKEY, ZOOM_HOTKEY, 0 );
                    ShowCursor( true );
                }
                else
                {
                    SendMessage( hWnd, WM_HOTKEY, ZOOM_HOTKEY, SHALLOW_ZOOM );
                }
            }

            // exit zoom
            if( g_Zoomed )
            {
                // If from liveDraw, extra care is needed to destruct
                if( GetWindowLong( hWnd, GWL_EXSTYLE ) & WS_EX_LAYERED )
                {
                    OutputDebug( L"Exiting liveDraw after snip\n" );
                    SendMessage( hWnd, WM_KEYDOWN, VK_ESCAPE, 0 );
                }
            }
            break;
        }

        case SNIP_OCR_HOTKEY:
        {
            OutputDebugStringW( L"[SnipOCR] Hotkey received\n" );

            // Block liveZoom liveDraw snip OCR due to mirroring bug
            if( IsWindowVisible( g_hWndLiveZoom )
                && ( GetWindowLongPtr( hWnd, GWL_EXSTYLE ) & WS_EX_LAYERED ) )
            {
                break;
            }

            bool zoomed = true;
#ifdef __ZOOMIT_POWERTOYS__
            if( g_StartedByPowerToys )
            {
                Trace::ZoomItActivateSnipOcr();
            }
#endif // __ZOOMIT_POWERTOYS__

            // First, static zoom at 1x
            if( !g_Zoomed )
            {
                zoomed = false;
                if( IsWindowVisible( g_hWndLiveZoom ) && !g_LiveZoomLevelOne )
                {
                    SendMessage( hWnd, WM_HOTKEY, ZOOM_HOTKEY, SHALLOW_ZOOM );
                }
                else
                {
                    SendMessage( hWnd, WM_HOTKEY, ZOOM_HOTKEY, LIVE_DRAW_ZOOM );
                }
                zoomLevel = zoomTelescopeTarget = 1;
            }
            else if( g_Drawing )
            {
                SendMessage( hWnd, WM_USER_EXIT_MODE, 0, 0 );
                if( g_Drawing )
                {
                    SendMessage( hWnd, WM_USER_EXIT_MODE, 0, 0 );
                }
            }
            ShowMainWindow( hWnd, monInfo, width, height );

            // Perform OCR on the selected region
            SendMessage( hWnd, WM_COMMAND, IDC_COPY_OCR, ( zoomed ? 0 : SHALLOW_ZOOM ) );

            // Now if we weren't zoomed, unzoom
            if( !zoomed )
            {
                if( g_ZoomOnLiveZoom )
                {
                    ShowCursor( false );
                    SendMessage( hWnd, WM_HOTKEY, ZOOM_HOTKEY, 0 );
                    ShowCursor( true );
                }
                else
                {
                    SendMessage( hWnd, WM_HOTKEY, ZOOM_HOTKEY, SHALLOW_ZOOM );
                }
            }

            // exit zoom
            if( g_Zoomed )
            {
                if( GetWindowLong( hWnd, GWL_EXSTYLE ) & WS_EX_LAYERED )
                {
                    OutputDebug( L"Exiting liveDraw after snip OCR\n" );
                    SendMessage( hWnd, WM_KEYDOWN, VK_ESCAPE, 0 );
                }
            }
            break;
        }

        case SAVE_IMAGE_HOTKEY:
            SendMessage(hWnd, WM_COMMAND, IDC_SAVE, 0);
            break;

        case SAVE_CROP_HOTKEY:
            SendMessage(hWnd, WM_COMMAND, IDC_SAVE_CROP, 0);
            break;

        case COPY_IMAGE_HOTKEY:
            SendMessage(hWnd, WM_COMMAND, IDC_COPY, 0);
            break;

        case COPY_CROP_HOTKEY:
            SendMessage(hWnd, WM_COMMAND, IDC_COPY_CROP, 0);
            break;

        case BREAK_HOTKEY:
            //
            // Go to break timer
            //
#if WINDOWS_CURSOR_RECORDING_WORKAROUND
            if( !g_Zoomed && ( !IsWindowVisible( g_hWndLiveZoom ) || g_LiveZoomLevelOne ) ) {
#else
            if( !g_Zoomed && !IsWindowVisible( g_hWndLiveZoom )) {
#endif
                SendMessage( hWnd, WM_COMMAND, IDC_BREAK, 0 );
            }
            break;

        case DEMOTYPE_RESET_HOTKEY:
            ResetDemoTypeIndex();
            break;

        case DEMOTYPE_HOTKEY:
        {
            //
            // Live type
            //
            switch( StartDemoType( g_DemoTypeFile, g_DemoTypeSpeedSlider, g_DemoTypeUserDriven ) )
            {
            case ERROR_LOADING_FILE:
                ErrorDialog( hWnd, L"Error loading DemoType file", GetLastError() );
                break;

            case NO_FILE_SPECIFIED:
                MessageBox( hWnd, L"No DemoType file specified", APPNAME, MB_OK );
                break;

            case FILE_SIZE_OVERFLOW:
            {
                std::wstring msg = L"Unsupported DemoType file size ("
                    + std::to_wstring( MAX_INPUT_SIZE ) + L" byte limit)";
                MessageBox( hWnd, msg.c_str(), APPNAME, MB_OK );
                break;
            }

            case UNKNOWN_FILE_DATA:
                MessageBox( hWnd, L"Unrecognized DemoType file content", APPNAME, MB_OK );
                break;
            default:
#ifdef __ZOOMIT_POWERTOYS__
                if( g_StartedByPowerToys )
                {
                    Trace::ZoomItActivateDemoType();
                }
#endif // __ZOOMIT_POWERTOYS__
                break;
            }
            break;
        }

        case LIVE_HOTKEY:
            //
            // Live zoom
            //
            OutputDebug(L"*** LIVE_HOTKEY\n");

            // If LiveZoom and LiveDraw are active then exit both
            if( g_Zoomed && IsWindowVisible( g_hWndLiveZoom ) && ( GetWindowLongPtr( hWnd, GWL_EXSTYLE ) & WS_EX_LAYERED ) )
            {
                SendMessage( hWnd, WM_KEYDOWN, VK_ESCAPE, 0 );
                PostMessage(hWnd, WM_HOTKEY, LIVE_HOTKEY, 0);
                break;
            }

            if( !g_Zoomed && !g_TimerActive && ( !g_fullScreenWorkaround || !g_RecordToggle ) ) {
#ifdef __ZOOMIT_POWERTOYS__
                if( g_StartedByPowerToys )
                {
                    Trace::ZoomItActivateLiveZoom();
                }
#endif // __ZOOMIT_POWERTOYS__

                if( g_hWndLiveZoom == NULL ) {
                    OutputDebug(L"Create LIVEZOOM\n");
                    g_hWndLiveZoom = CreateWindowEx( WS_EX_TOOLWINDOW | WS_EX_LAYERED | WS_EX_TRANSPARENT,
                        L"MagnifierClass", L"ZoomIt Live Zoom",
                        WS_POPUP | WS_CLIPSIBLINGS,
                        0, 0, 0, 0, NULL, NULL, g_hInstance, static_cast<PVOID>(GetForegroundWindow()) );
                    pSetLayeredWindowAttributes( hWnd, 0, 0, LWA_ALPHA );
                    EnableWindow( g_hWndLiveZoom, FALSE );
                    pMagSetWindowFilterList( g_hWndLiveZoomMag, MW_FILTERMODE_EXCLUDE, 1, &hWnd );

                } else {
#if WINDOWS_CURSOR_RECORDING_WORKAROUND
                    if( g_LiveZoomLevelOne ) {
                        OutputDebug(L"liveZoom level one\n");
                        SendMessage( g_hWndLiveZoom, WM_USER_SET_ZOOM, static_cast<WPARAM>(g_LiveZoomLevel), 0 );
                    }
                    else {
#endif

                    if( IsWindowVisible( g_hWndLiveZoom )) {
#if WINDOWS_CURSOR_RECORDING_WORKAROUND

                        if( g_RecordToggle )
                            g_LiveZoomLevel = g_ZoomLevels[g_SliderZoomLevel];
#endif
                        // Unzoom
                        SendMessage( g_hWndLiveZoom, WM_KEYDOWN, VK_ESCAPE, 0 );

                    } else {

                        OutputDebug(L"Show liveZoom\n");
                        ShowWindow( g_hWndLiveZoom, SW_SHOW );
                    }
#if WINDOWS_CURSOR_RECORDING_WORKAROUND
                    }
#endif
                }
                OutputDebug(L"LIVEDRAW SMOOTHING: %d\n", g_SmoothImage);
                if (!pMagSetLensUseBitmapSmoothing(g_hWndLiveZoomMag, g_SmoothImage))
                {
                    OutputDebug(L"MagSetLensUseBitmapSmoothing failed: %d\n", GetLastError());
                }

                if ( g_RecordToggle )
                {
                    g_SelectRectangle.UpdateOwner( g_hWndLiveZoom );
                }
            }
            break;

        case RECORD_HOTKEY:
        case RECORD_CROP_HOTKEY:
        case RECORD_WINDOW_HOTKEY:
        case RECORD_GIF_HOTKEY:
        case RECORD_GIF_WINDOW_HOTKEY:

            //
            // Recording
            // This gets entered twice per recording:
            // 1. When the hotkey is pressed to start recording
            // 2. When the hotkey is pressed to stop recording
            //
            if( g_fullScreenWorkaround && g_hWndLiveZoom != NULL && IsWindowVisible( g_hWndLiveZoom ) != FALSE )
            {
                break;
            }

            if( g_RecordCropping == TRUE )
            {
                break;
            }

            // Ignore recording hotkey when save dialog is open
            if( g_bSaveInProgress )
            {
                break;
            }

            // Start screen recording
            try
            {
                isCaptureSupported = winrt::GraphicsCaptureSession::IsSupported();
            }
            catch( const winrt::hresult_error& ) {}
            if( !isCaptureSupported )
            {
                MessageBox( hWnd, L"Screen recording requires Windows 10, May 2019 Update or higher.", APPNAME, MB_OK );
                break;
            }

            // If shift, then we're cropping
            hWndRecord = 0;
            if( wParam == RECORD_CROP_HOTKEY )
            {
                if( g_RecordToggle == TRUE )
                {
                    // Already recording
                    break;
                }

                g_RecordCropping = TRUE;

                POINT savedPoint{};
                RECT savedClip = {};

                // Handle the cursor for live zoom and static zoom modes.
                if( ( g_hWndLiveZoom != nullptr ) || ( g_Zoomed == TRUE ) )
                {
                    GetCursorPos( &savedPoint );
                    UpdateMonitorInfo( savedPoint, &monInfo );
                }
                if( g_hWndLiveZoom != nullptr )
                {
                    // Hide the magnified cursor.
                    SendMessage( g_hWndLiveZoom, WM_USER_MAGNIFY_CURSOR, FALSE, 0 );

                    // Show the system cursor where the magnified was.
                    g_LiveZoomSourceRect = *reinterpret_cast<RECT *>( SendMessage( g_hWndLiveZoom, WM_USER_GET_SOURCE_RECT, 0, 0 ) );
                    savedPoint = ScalePointInRects( savedPoint, g_LiveZoomSourceRect, monInfo.rcMonitor );
                    SetCursorPos( savedPoint.x, savedPoint.y );
                    if ( pMagShowSystemCursor != nullptr )
                    {
                        pMagShowSystemCursor( TRUE );
                    }
                }
                else if( ( g_Zoomed == TRUE ) && ( g_Drawing == TRUE ) )
                {
                    // Unclip the cursor.
                    GetClipCursor( &savedClip );
                    ClipCursor( nullptr );

                    // Scale the cursor position to the zoomed and move it.
                    auto point = ScalePointInRects( savedPoint, boundRc, monInfo.rcMonitor );
                    SetCursorPos( point.x, point.y );
                }

                if( g_Zoomed == FALSE )
                {
                    SetWindowPos( hWnd, HWND_TOPMOST, monInfo.rcMonitor.left, monInfo.rcMonitor.top, width, height, SWP_SHOWWINDOW );
                }

                // This call blocks with a message loop while cropping.
                auto canceled = !g_SelectRectangle.Start( ( g_hWndLiveZoom != nullptr ) ? g_hWndLiveZoom : hWnd );
                g_RecordCropping = FALSE;

                // Restore the cursor if applicable.
                if( g_hWndLiveZoom != nullptr )
                {
                    // Hide the system cursor.
                    if ( pMagShowSystemCursor != nullptr )
                    {
                        pMagShowSystemCursor( FALSE );
                    }

                    // Show the magnified cursor where the system cursor was.
                    GetCursorPos( &savedPoint );
                    savedPoint = ScalePointInRects( savedPoint, monInfo.rcMonitor, g_LiveZoomSourceRect );
                    SetCursorPos( savedPoint.x, savedPoint.y );
                    SendMessage( g_hWndLiveZoom, WM_USER_MAGNIFY_CURSOR, TRUE, 0 );
                }
                else if( g_Zoomed == TRUE )
                {
                    SetCursorPos( savedPoint.x, savedPoint.y );

                    if ( g_Drawing == TRUE )
                    {
                        ClipCursor( &savedClip );
                    }
                }

                SetForegroundWindow( hWnd );
                if( g_Zoomed == FALSE )
                {
                    SetActiveWindow( hWnd );
                    ShowWindow( hWnd, SW_HIDE );
                }

                if( canceled )
                {
                    break;
                }

                g_SelectRectangle.UpdateOwner( ( g_hWndLiveZoom != nullptr ) ? g_hWndLiveZoom : hWnd );
                cropRc = g_SelectRectangle.SelectedRect();
            }
            else
            {
                cropRc = {};

                // if we're recording a window, get the window
                if (wParam == RECORD_WINDOW_HOTKEY || wParam == RECORD_GIF_WINDOW_HOTKEY)
                {
                    GetCursorPos(&cursorPos);
                    hWndRecord = WindowFromPoint(cursorPos);
                    while( GetParent(hWndRecord) != NULL)
                    {
                        hWndRecord = GetParent(hWndRecord);
                    }
                    if( hWndRecord == GetDesktopWindow()) {

                        hWndRecord = NULL;
                    }
                }
            }

            if( g_RecordToggle == FALSE )
            {
                g_RecordToggle = TRUE;

#ifdef __ZOOMIT_POWERTOYS__
                if( g_StartedByPowerToys )
                {
                    Trace::ZoomItActivateRecord();
                }
#endif // __ZOOMIT_POWERTOYS__

                StartRecordingAsync( hWnd, &cropRc, hWndRecord );
            }
            else
            {
                StopRecording();
            }
            break;

        case ZOOM_HOTKEY:
            //
            // Zoom
            //
            // Don't react to hotkey while options are open or we're
            // saving the screen or live zoom is active
            //
            if( hWndOptions ) {

                break;
            }

            OutputDebug( L"ZOOM HOTKEY: %d\n", lParam);
            if( g_TimerActive ) {

                //
                // Finished with break timer
                //
                if( g_BreakOnSecondary )
                {
                    EnableDisableSecondaryDisplay( hWnd, FALSE, &secondaryDevMode );
                }

                if( lParam != SHALLOW_DESTROY )
                {
                    ShowWindow( hWnd, SW_HIDE );
                    if( g_hBackgroundBmp )
                    {
                        DeleteObject( g_hBackgroundBmp );
                        DeleteDC( g_hDcBackgroundFile );
                        g_hBackgroundBmp = NULL;
                    }
                }

                SetFocus( GetDesktopWindow() );
                KillTimer( hWnd, 0 );
                g_TimerActive = FALSE;

                DeleteObject( hTimerFont );
                DeleteObject( hNegativeTimerFont );
                DeleteDC( hdcScreen );
                DeleteDC( hdcScreenCompat );
                DeleteDC( hdcScreenSaveCompat );
                DeleteDC( hdcScreenCursorCompat );
                DeleteObject( hbmpCompat );
                EnableDisableScreenSaver( TRUE );
                EnableDisableOpacity( hWnd, FALSE );

            } else {

                SendMessage( hWnd, WM_USER_TYPING_OFF, 0, 0 );
                if( !g_Zoomed ) {

                    g_Zoomed = TRUE;
                    g_DrawingShape = FALSE;
                    OutputDebug( L"Zoom on\n");

                    // Register Ctrl+C and Ctrl+S hotkeys only during static zoom
                    RegisterHotKey(hWnd, COPY_IMAGE_HOTKEY, MOD_CONTROL | MOD_NOREPEAT, 'C');
                    RegisterHotKey(hWnd, COPY_CROP_HOTKEY, MOD_CONTROL | MOD_SHIFT | MOD_NOREPEAT, 'C');
                    RegisterHotKey(hWnd, SAVE_IMAGE_HOTKEY, MOD_CONTROL | MOD_NOREPEAT, 'S');
                    RegisterHotKey(hWnd, SAVE_CROP_HOTKEY, MOD_CONTROL | MOD_SHIFT | MOD_NOREPEAT, 'S');

#ifdef __ZOOMIT_POWERTOYS__
                    if( g_StartedByPowerToys )
                    {
                        Trace::ZoomItActivateZoom();
                    }
#endif // __ZOOMIT_POWERTOYS__

                    // Hide the cursor before capturing if in live zoom
                    if( g_hWndLiveZoom != nullptr )
                    {
                        OutputDebug(L"Hide cursor\n");
                        SendMessage( g_hWndLiveZoom, WM_USER_MAGNIFY_CURSOR, FALSE, 0 );
                        SendMessage( g_hWndLiveZoom, WM_TIMER, 0, 0 );
                        SendMessage( g_hWndLiveZoom, WM_USER_MAGNIFY_CURSOR, FALSE, 0 );
                    }

                    // Get screen DCs
                    hdcScreen = CreateDC(L"DISPLAY", static_cast<PTCHAR>(NULL),
                            static_cast<PTCHAR>(NULL), static_cast<CONST DEVMODE *>(NULL));
                    hdcScreenCompat = CreateCompatibleDC(hdcScreen);
                    hdcScreenSaveCompat = CreateCompatibleDC(hdcScreen);
                    hdcScreenCursorCompat = CreateCompatibleDC(hdcScreen);

                    // Determine what monitor we're on
                    GetCursorPos(&cursorPos);
                    UpdateMonitorInfo( cursorPos, &monInfo );
                    width = monInfo.rcMonitor.right - monInfo.rcMonitor.left;
                    height = monInfo.rcMonitor.bottom - monInfo.rcMonitor.top;
                    OutputDebug( L"ZOOM x: %d y: %d width: %d height: %d zoomLevel: %g\n",
                            cursorPos.x, cursorPos.y, width, height, zoomLevel );

                    // Create display bitmap
                    bmp.bmBitsPixel = static_cast<BYTE>(GetDeviceCaps(hdcScreen, BITSPIXEL));
                    bmp.bmPlanes = static_cast<BYTE>(GetDeviceCaps(hdcScreen, PLANES));
                    bmp.bmWidth = width;
                    bmp.bmHeight = height;
                    bmp.bmWidthBytes = ((bmp.bmWidth + 15) &~15)/8;
                    hbmpCompat = CreateBitmap(bmp.bmWidth, bmp.bmHeight,
                        bmp.bmPlanes, bmp.bmBitsPixel, static_cast<CONST VOID *>(NULL));
                     SelectObject(hdcScreenCompat, hbmpCompat);

                    // Create saved bitmap
                    hbmpDrawingCompat = CreateBitmap(bmp.bmWidth, bmp.bmHeight,
                        bmp.bmPlanes, bmp.bmBitsPixel, static_cast<CONST VOID *>(NULL));
                    SelectObject(hdcScreenSaveCompat, hbmpDrawingCompat);

                    // Create cursor save bitmap
                    // (have to accomodate large fonts and LiveZoom pen scaling)
                    hbmpCursorCompat = CreateBitmap( MAX_LIVE_PEN_WIDTH+CURSOR_ARM_LENGTH*2,
                        MAX_LIVE_PEN_WIDTH+CURSOR_ARM_LENGTH*2, bmp.bmPlanes,
                        bmp.bmBitsPixel, static_cast<CONST VOID *>(NULL));
                    SelectObject(hdcScreenCursorCompat, hbmpCursorCompat);

                    // Create typing font
                    g_LogFont.lfHeight = height / 15;
                    if (g_LogFont.lfHeight < 20)
                        g_LogFont.lfQuality = NONANTIALIASED_QUALITY;
                    else
                        g_LogFont.lfQuality = ANTIALIASED_QUALITY;
                    hTypingFont = CreateFontIndirect(&g_LogFont);
                    SelectObject(hdcScreenCompat, hTypingFont);
                    SetTextColor(hdcScreenCompat, g_PenColor & 0xFFFFFF);
                    SetBkMode(hdcScreenCompat, TRANSPARENT);

                    // Use the screen DC unless recording, because it contains the yellow border
                    HDC hdcSource = hdcScreen;
                    if( g_RecordToggle ) try {

                        auto capture = CaptureScreenshot( winrt::DirectXPixelFormat::B8G8R8A8UIntNormalized );
                        auto bytes = CopyBytesFromTexture( capture );

                        D3D11_TEXTURE2D_DESC desc;
                        capture->GetDesc( &desc );
                        BITMAPINFO bitmapInfo = {};
                        bitmapInfo.bmiHeader.biSize = sizeof bitmapInfo.bmiHeader;
                        bitmapInfo.bmiHeader.biWidth = desc.Width;
                        bitmapInfo.bmiHeader.biHeight = -static_cast<LONG>(desc.Height);
                        bitmapInfo.bmiHeader.biPlanes = 1;
                        bitmapInfo.bmiHeader.biBitCount = 32;
                        bitmapInfo.bmiHeader.biCompression = BI_RGB;
                        void *bits;
                        auto dib = CreateDIBSection( NULL, &bitmapInfo, DIB_RGB_COLORS, &bits, nullptr, 0 );
                        if( dib ) {

                            CopyMemory( bits, bytes.data(), bytes.size() );
                            auto hdcCapture = CreateCompatibleDC( hdcScreen );
                            SelectObject( hdcCapture, dib );
                            hdcSource = hdcCapture;
                        }

                    } catch( const winrt::hresult_error& ) {} // on any failure, fall back to the screen DC

                    bool captured = hdcSource != hdcScreen;

                    // paint the initial bitmap
                    BitBlt( hdcScreenCompat, 0, 0, bmp.bmWidth, bmp.bmHeight, hdcSource,
                        captured ? 0 : monInfo.rcMonitor.left, captured ? 0 : monInfo.rcMonitor.top, SRCCOPY|CAPTUREBLT );
                    BitBlt( hdcScreenSaveCompat, 0, 0, bmp.bmWidth, bmp.bmHeight, hdcSource,
                        captured ? 0 : monInfo.rcMonitor.left, captured ? 0 : monInfo.rcMonitor.top, SRCCOPY|CAPTUREBLT );

                    if( captured )
                    {
                        OutputDebug(L"Captured screen\n");
                        auto bitmap = GetCurrentObject( hdcSource, OBJ_BITMAP );
                        DeleteObject( bitmap );
                        DeleteDC( hdcSource );
                    }

                    // Create drawing pen
                    hDrawingPen = CreatePen(PS_SOLID, g_PenWidth, g_PenColor & 0xFFFFFF);

                    g_BlankedScreen = FALSE;
                    g_HaveTyped = FALSE;
                    g_Drawing = FALSE;
                    g_TypeMode = TypeModeOff;
                    g_HaveDrawn = FALSE;
                    EnableDisableStickyKeys( TRUE );

                    // Go full screen
                    g_ActiveWindow = GetForegroundWindow();
                    OutputDebug( L"active window: %x\n", PtrToLong(g_ActiveWindow) );

                    if( lParam != LIVE_DRAW_ZOOM) {

                        OutputDebug(L"Calling ShowMainWindow\n");
                        ShowMainWindow(hWnd, monInfo, width, height);
                    }

                    // Start telescoping zoom. Lparam is non-zero if this
                    // was a real hotkey and not the message we send ourself to enter
                    // unzoomed drawing mode.

                    //
                    // Are we switching from live zoom to standard zoom?
                    //
#if WINDOWS_CURSOR_RECORDING_WORKAROUND
                    if( IsWindowVisible( g_hWndLiveZoom ) && !g_LiveZoomLevelOne ) {
#else
                    if( IsWindowVisible( g_hWndLiveZoom )) {
#endif

                        // Enter drawing mode
                        OutputDebug(L"Enter liveZoom draw\n");
                        g_LiveZoomSourceRect = *reinterpret_cast<RECT *>(SendMessage( g_hWndLiveZoom, WM_USER_GET_SOURCE_RECT, 0, 0 ));
                        g_LiveZoomLevel = *reinterpret_cast<float*>(SendMessage(g_hWndLiveZoom, WM_USER_GET_ZOOM_LEVEL, 0, 0));

                        // Set live zoom level to 1 in preparation of us being full screen static
                        zoomLevel = 1.0;
                        zoomTelescopeTarget = 1.0;
                        if (lParam != LIVE_DRAW_ZOOM) {

                            g_ZoomOnLiveZoom = TRUE;
                        }

                        UpdateWindow( hWnd ); // overwrites where cursor erased
                        if( lParam != SHALLOW_ZOOM )
                        {
                            // Put the drawing cursor where the magnified cursor was
                            OutputDebug(L"Setting cursor\n");

                            if (lParam != LIVE_DRAW_ZOOM)
                            {
                                cursorPos = ScalePointInRects( cursorPos, g_LiveZoomSourceRect, monInfo.rcMonitor );
                                SetCursorPos( cursorPos.x, cursorPos.y );
                                UpdateWindow( hWnd ); // overwrites where cursor erased
                                SendMessage( hWnd, WM_LBUTTONDOWN, 0, MAKELPARAM( cursorPos.x, cursorPos.y ));
                            }
                        }
                        else
                        {
                            InvalidateRect( hWnd, NULL, FALSE );
                        }
                        UpdateWindow( hWnd );
                        if( g_RecordToggle )
                        {
                            g_SelectRectangle.UpdateOwner( hWnd );
                        }
                        if( lParam != LIVE_DRAW_ZOOM ) {

                            OutputDebug(L"Calling ShowMainWindow 2\n");

                            ShowWindow( g_hWndLiveZoom, SW_HIDE );
                        }

                    } else if( lParam != 0 && lParam != LIVE_DRAW_ZOOM ) {

                        zoomTelescopeStep = ZOOM_LEVEL_STEP_IN;
                        zoomTelescopeTarget = g_ZoomLevels[g_SliderZoomLevel];
                        if( g_AnimateZoom )
                        {
                            zoomLevel = static_cast<float>(1.0) * zoomTelescopeStep;
                            g_TelescopingZoomLastTick = GetTickCount64();
                        }
                        else
                            zoomLevel = zoomTelescopeTarget;
                        SetTimer( hWnd, 1, ZOOM_LEVEL_STEP_TIME, NULL );
                    }

                } else {

                    OutputDebug( L"Zoom off: don't animate=%d\n", lParam );
                    // turn off liveDraw
                    SetLayeredWindowAttributes(hWnd, 0, 255, LWA_ALPHA);

                    if( lParam != SHALLOW_DESTROY && !g_ZoomOnLiveZoom && g_AnimateZoom &&
                        g_TelescopeZoomOut && zoomTelescopeTarget != 1 ) {

                        // Start telescoping zoom.
                        zoomTelescopeStep = ZOOM_LEVEL_STEP_OUT;
                        zoomTelescopeTarget = 1.0;
                        g_TelescopingZoomLastTick = GetTickCount64();
                        SetTimer( hWnd, 2, ZOOM_LEVEL_STEP_TIME, NULL );

                    } else {

                        // Simulate timer expiration
                        zoomTelescopeStep = 0;
                        zoomTelescopeTarget = zoomLevel = 1.0;
                        SendMessage( hWnd, WM_TIMER, 2, lParam );
                    }
                }
            }
            break;
        }
        return TRUE;

    case WM_POINTERUPDATE: {
        penInverted = IsPenInverted(wParam);
        OutputDebug( L"WM_POINTERUPDATE: contact: %d button down: %d X: %d Y: %d\n",
            IS_POINTER_INCONTACT_WPARAM(wParam),
            penInverted,
            GET_X_LPARAM(lParam),
            GET_Y_LPARAM(lParam));
        if( penInverted != g_PenInverted) {

            g_PenInverted = penInverted;
            if (g_PenInverted) {
                if (PopDrawUndo(hdcScreenCompat, &drawUndoList, width, height)) {

                    SaveCursorArea(hdcScreenCursorCompat, hdcScreenCompat, prevPt);
                    InvalidateRect(hWnd, NULL, FALSE);
                }
            }
        } else if( g_PenDown && !penInverted) {

            SendPenMessage(hWnd, WM_MOUSEMOVE, lParam);
        }
        }
        return TRUE;

    case WM_POINTERUP:
        OutputDebug(L"WM_POINTERUP\n");
        penInverted = IsPenInverted(wParam);
        if (!penInverted) {

            SendPenMessage(hWnd, WM_LBUTTONUP, lParam);
            SendPenMessage(hWnd, WM_RBUTTONDOWN, lParam);
            g_PenDown = FALSE;
        }
        break;

    case WM_POINTERDOWN:
        OutputDebug(L"WM_POINTERDOWN\n");
        penInverted = IsPenInverted(wParam);
        if (!penInverted) {

            g_PenDown = TRUE;

            // Enter drawing mode
            SendPenMessage(hWnd, WM_LBUTTONDOWN, lParam);
            SendPenMessage(hWnd, WM_MOUSEMOVE, lParam);
            SendPenMessage(hWnd, WM_LBUTTONUP, lParam);
            SendPenMessage(hWnd, WM_MOUSEMOVE, lParam);
            PopDrawUndo(hdcScreenCompat, &drawUndoList, width, height);

            // Enter tracing mode
            SendPenMessage(hWnd, WM_LBUTTONDOWN, lParam);
        }
        break;

    case WM_KILLFOCUS:
        if( ( g_RecordCropping == FALSE ) && g_Zoomed && !g_bSaveInProgress ) {

            // Turn off zoom if not in liveDraw
            DWORD layeringFlag;
            GetLayeredWindowAttributes(hWnd, NULL, NULL, &layeringFlag);
            if( !(layeringFlag & LWA_COLORKEY)) {

                PostMessage(hWnd, WM_HOTKEY, ZOOM_HOTKEY, 0);
            }
        }
        break;

    case WM_MOUSEWHEEL:

        //
        // Zoom or modify break timer
        //
        if( GET_WHEEL_DELTA_WPARAM(wParam) < 0 )
            wParam -= (WHEEL_DELTA-1) << 16;
        else
            wParam += (WHEEL_DELTA-1) << 16;
        delta = GET_WHEEL_DELTA_WPARAM(wParam)/WHEEL_DELTA;
        OutputDebug( L"mousewheel: wParam: %d delta: %d\n",
                GET_WHEEL_DELTA_WPARAM(wParam), delta );
        if( g_Zoomed ) {

            if( g_TypeMode == TypeModeOff ) {

                if( g_Drawing && (LOWORD( wParam ) & MK_CONTROL) ) {

                    ResizePen( hWnd, hdcScreenCompat, hdcScreenCursorCompat, prevPt,
                        g_Tracing, &g_Drawing, g_LiveZoomLevel, TRUE, g_PenWidth + delta );

                // Perform static zoom unless in liveDraw
                } else if( !( GetWindowLongPtr( hWnd, GWL_EXSTYLE ) & WS_EX_LAYERED ) ) {

                    if( delta > 0 ) zoomIn = TRUE;
                    else {
                        zoomIn = FALSE;
                        delta = -delta;
                    }
                    while( delta-- ) {

                        if( zoomIn ) {

                            if( zoomTelescopeTarget < ZOOM_LEVEL_MAX ) {

                                if( zoomTelescopeTarget < 2 ) {

                                    zoomTelescopeTarget = 2;

                                } else {

                                    // Start telescoping zoom
                                    zoomTelescopeTarget = zoomTelescopeTarget * 2;
                                }
                                zoomTelescopeStep = ZOOM_LEVEL_STEP_IN;
                                if( g_AnimateZoom )
                                    zoomLevel *= zoomTelescopeStep;
                                else
                                    zoomLevel = zoomTelescopeTarget;

                                if( zoomLevel > zoomTelescopeTarget )
                                    zoomLevel = zoomTelescopeTarget;
                                else
                                    SetTimer( hWnd, 1, ZOOM_LEVEL_STEP_TIME, NULL );
                            }

                        } else if( zoomTelescopeTarget > ZOOM_LEVEL_MIN ) {

                            // Let them more gradually zoom out from 2x to 1x
                            if( zoomTelescopeTarget <= 2 ) {

                                zoomTelescopeTarget *= .75;
                                if( zoomTelescopeTarget < ZOOM_LEVEL_MIN )
                                    zoomTelescopeTarget = ZOOM_LEVEL_MIN;

                            } else {

                                zoomTelescopeTarget = zoomTelescopeTarget/2;
                            }
                            zoomTelescopeStep = ZOOM_LEVEL_STEP_OUT;
                            if( g_AnimateZoom )
                                zoomLevel *= zoomTelescopeStep;
                            else
                                zoomLevel = zoomTelescopeTarget;

                            if( zoomLevel < zoomTelescopeTarget )
                            {
                                zoomLevel = zoomTelescopeTarget;
                                // Force update on final step out
                                InvalidateRect( hWnd, NULL, FALSE );
                            }
                            else
                            {
                                SetTimer( hWnd, 1, ZOOM_LEVEL_STEP_TIME, NULL );
                            }
                        }
                    }
                    if( zoomLevel != zoomTelescopeTarget ) {

                        if( g_Drawing ) {

                            if( !g_Tracing ) {

                                RestoreCursorArea( hdcScreenCompat, hdcScreenCursorCompat, prevPt );
                            }
                            //SetCursorPos( monInfo.rcMonitor.left + cursorPos.x,
                            //		monInfo.rcMonitor.top + cursorPos.y );
                        }
                        InvalidateRect( hWnd, NULL, FALSE );
                    }
                }
            } else {

                // Resize the text font
                if( (delta > 0 && g_FontScale > -20) || (delta < 0 && g_FontScale < 50 )) {

                    ClearTypingCursor(hdcScreenCompat, hdcScreenCursorCompat, cursorRc, g_BlankedScreen);

                    g_FontScale -= delta;
                    if( g_FontScale == 0 ) g_FontScale = 1;
                    // Set lParam to 0 as part of message to keyup hander
                    DeleteObject(hTypingFont);
                    g_LogFont.lfHeight = max((int)(height / zoomLevel) / g_FontScale, 12);
                    if (g_LogFont.lfHeight < 20)
                        g_LogFont.lfQuality = NONANTIALIASED_QUALITY;
                    else
                        g_LogFont.lfQuality = ANTIALIASED_QUALITY;
                    hTypingFont = CreateFontIndirect(&g_LogFont);
                    SelectObject(hdcScreenCompat, hTypingFont);

                    DrawTypingCursor( hWnd, &textPt, hdcScreenCompat, hdcScreenCursorCompat, &cursorRc );
                }
            }
        } else if( g_TimerActive && (breakTimeout > 0 || delta )) {

            if( delta ) {

                if( breakTimeout < 0 ) breakTimeout = 0;
                if( breakTimeout % 60 ) {
                    breakTimeout += (60 - breakTimeout % 60);
                    delta--;
                }
                breakTimeout += delta * 60;

            } else {

                if( breakTimeout % 60 ) {
                    breakTimeout -= breakTimeout % 60;
                    delta--;
                }
                breakTimeout -= delta * 60;
            }
            if( breakTimeout < 0 ) breakTimeout = 0;
            KillTimer( hWnd, 0 );
            SetTimer( hWnd, 0, 1000, NULL );
            InvalidateRect( hWnd, NULL, TRUE );
        }

        if( zoomLevel != 1 && g_Drawing ) {

            // Constrain the mouse to the visible region
            boundRc = BoundMouse( zoomTelescopeTarget, &monInfo, width, height, &cursorPos );

        } else {

            ClipCursor( NULL );
        }
        return TRUE;

    case WM_IME_CHAR:
    case WM_CHAR:

        if( (g_TypeMode != TypeModeOff) &&
            (iswprint(static_cast<TCHAR>(wParam)) || (static_cast<TCHAR>(wParam) == L'&')) ) {
            g_HaveTyped = TRUE;

            TCHAR	 vKey = static_cast<TCHAR>(wParam);

            g_HaveDrawn = TRUE;

            // Clear typing cursor
            rc.left = textPt.x;
            rc.top = textPt.y;
            ClearTypingCursor( hdcScreenCompat, hdcScreenCursorCompat, cursorRc, g_BlankedScreen );
            if (g_TypeMode == TypeModeRightJustify) {

                if( !g_TextBuffer.empty() || !g_TextBufferPreviousLines.empty() ) {

                    PopDrawUndo(hdcScreenCompat, &drawUndoList, width, height); //***
                }
                PushDrawUndo(hdcScreenCompat, &drawUndoList, width, height);

                // Restore previous lines.
                wParam = 'X';
                DrawText(hdcScreenCompat, reinterpret_cast<PTCHAR>(&wParam), 1, &rc, DT_CALCRECT);
                const auto lineHeight = rc.bottom - rc.top;

                rc.top -= static_cast< LONG >( g_TextBufferPreviousLines.size() ) * lineHeight;

                // Draw the current character on the current line.
                g_TextBuffer += vKey;
                drawAllRightJustifiedLines( lineHeight );
            }
            else {
                DrawText( hdcScreenCompat, &vKey, 1, &rc, DT_CALCRECT|DT_NOPREFIX);
                DrawText( hdcScreenCompat, &vKey, 1, &rc, DT_LEFT|DT_NOPREFIX);
                textPt.x += rc.right - rc.left;
            }
            InvalidateRect( hWnd, NULL, TRUE );

            // Save the key for undo
            P_TYPED_KEY newKey = static_cast<P_TYPED_KEY>(malloc( sizeof(TYPED_KEY) ));
            newKey->rc = rc;
            newKey->Next = typedKeyList;
            typedKeyList = newKey;

            // Draw the typing cursor
            DrawTypingCursor( hWnd, &textPt, hdcScreenCompat, hdcScreenCursorCompat, &cursorRc );
            return FALSE;
        }
        break;

    case WM_KEYUP:
        if( wParam == 'T' && (g_TypeMode == TypeModeOff)) {

            // lParam is 0 when we're resizing the font and so don't have a cursor that
            // we need to restore
            if( !g_Drawing && lParam == 0 ) {

                OutputDebug(L"Entering typing mode and resetting cursor position\n");
                SendMessage( hWnd, WM_LBUTTONDOWN, 0, MAKELPARAM( cursorPos.x, cursorPos.y));
            }

            // Do they want to right-justify text?
            OutputDebug(L"Keyup Shift: %x\n", GetAsyncKeyState(VK_SHIFT));
            if(GetAsyncKeyState(VK_SHIFT) != 0 ) {

                g_TypeMode = TypeModeRightJustify;
                g_TextBuffer.clear();

                // Also empty all previous lines
                g_TextBufferPreviousLines = {};
            }
            else {

                g_TypeMode = TypeModeLeftJustify;
            }
            textStartPt = cursorPos;
            textPt = prevPt;

            g_HaveTyped = FALSE;

            // Get a font of a decent size
            DeleteObject( hTypingFont );
            g_LogFont.lfHeight = max( (int) (height / zoomLevel)/g_FontScale, 12 );
            if (g_LogFont.lfHeight < 20)
                g_LogFont.lfQuality = NONANTIALIASED_QUALITY;
            else
                g_LogFont.lfQuality = ANTIALIASED_QUALITY;
            hTypingFont = CreateFontIndirect( &g_LogFont );
            SelectObject( hdcScreenCompat, hTypingFont );

            // If lparam == 0 that means that we sent the message as part of a font resize
            if( g_Drawing && lParam != 0) {

                RestoreCursorArea( hdcScreenCompat, hdcScreenCursorCompat, prevPt );
                PushDrawUndo( hdcScreenCompat, &drawUndoList, width, height );

            } else if( !g_Drawing ) {

                textPt = cursorPos;
            }

            // Draw the typing cursor
            DrawTypingCursor( hWnd, &textPt, hdcScreenCompat, hdcScreenCursorCompat, &cursorRc, true );
            prevPt = textPt;
        }
        break;

    case WM_KEYDOWN:

        if( (g_TypeMode != TypeModeOff) && g_HaveTyped &&
            (wParam == VK_RETURN || wParam == VK_DELETE || wParam == VK_BACK) ) {

            if( wParam == VK_RETURN ) {

                // Clear the typing cursor
                ClearTypingCursor( hdcScreenCompat, hdcScreenCursorCompat, cursorRc, g_BlankedScreen );

                if( g_TypeMode == TypeModeRightJustify )
                {
                    g_TextBufferPreviousLines.push_back( g_TextBuffer );
                    g_TextBuffer.clear();
                }
                else
                {
                    // Insert a fake return key in the list to undo.
                    P_TYPED_KEY newKey = static_cast<P_TYPED_KEY>(malloc(sizeof(TYPED_KEY)));
                    newKey->rc.left = textPt.x;
                    newKey->rc.top = textPt.y;
                    newKey->rc.right = newKey->rc.left;
                    newKey->rc.bottom = newKey->rc.top;
                    newKey->Next = typedKeyList;
                    typedKeyList = newKey;
                }

                wParam = 'X';
                DrawText( hdcScreenCompat, reinterpret_cast<PTCHAR>(&wParam), 1, &rc, DT_CALCRECT );
                textPt.x = prevPt.x; // + g_PenWidth;
                textPt.y += rc.bottom - rc.top;

                // Draw the typing cursor
                DrawTypingCursor( hWnd, &textPt, hdcScreenCompat, hdcScreenCursorCompat, &cursorRc );
            } else if( wParam == VK_DELETE || wParam == VK_BACK ) {

                P_TYPED_KEY	deletedKey = typedKeyList;
                if( deletedKey ) {

                    // Clear the typing cursor
                    ClearTypingCursor( hdcScreenCompat, hdcScreenCursorCompat, cursorRc, g_BlankedScreen );

                    if( g_TypeMode == TypeModeRightJustify ) {

                        if( !g_TextBuffer.empty() || !g_TextBufferPreviousLines.empty() ) {

                            PopDrawUndo( hdcScreenCompat, &drawUndoList, width, height );
                        }
                        PushDrawUndo( hdcScreenCompat, &drawUndoList, width, height );

                        rc.left = textPt.x;
                        rc.top = textPt.y;

                        // Restore the previous lines.
                        wParam = 'X';
                        DrawText( hdcScreenCompat, reinterpret_cast<PTCHAR>(&wParam), 1, &rc, DT_CALCRECT );
                        const auto lineHeight = rc.bottom - rc.top;

                        const bool lineWasEmpty = g_TextBuffer.empty();
                        drawAllRightJustifiedLines( lineHeight, true );
                        if( lineWasEmpty && !g_TextBufferPreviousLines.empty() )
                        {
                            g_TextBuffer = g_TextBufferPreviousLines.back();
                            g_TextBufferPreviousLines.pop_back();
                            textPt.y -= lineHeight;
                        }
                    }
                    else {
                        RECT rect = deletedKey->rc;
                        if (g_BlankedScreen) {

                            BlankScreenArea(hdcScreenCompat, &rect, g_BlankedScreen);
                        }
                        else {

                            BitBlt(hdcScreenCompat, rect.left, rect.top, rect.right - rect.left,
                                rect.bottom - rect.top, hdcScreenSaveCompat, rect.left, rect.top, SRCCOPY | CAPTUREBLT );
                        }
                        InvalidateRect( hWnd, NULL, FALSE );

                        textPt.x = rect.left;
                        textPt.y = rect.top;

                        typedKeyList = deletedKey->Next;
                        free(deletedKey);

                        // Refresh cursor if we deleted the last key
                        if( typedKeyList == NULL ) {

                            SendMessage( hWnd, WM_MOUSEMOVE, 0, MAKELPARAM( prevPt.x, prevPt.y ) );
                        }
                    }
                    DrawTypingCursor( hWnd, &textPt, hdcScreenCompat, hdcScreenCursorCompat, &cursorRc );
                }
            }
            break;
        }
        switch (wParam) {
        case 'R':
        case 'B':
        case 'Y':
        case 'O':
        case 'G':
        case 'X':
        case 'P':
            if( (g_Zoomed || g_TimerActive) && (g_TypeMode == TypeModeOff)) {

                PDWORD	penColor;
                if( g_TimerActive )
                    penColor = &g_BreakPenColor;
                else
                    penColor = &g_PenColor;

                if( wParam == 'R' )		 *penColor = COLOR_RED;
                else if( wParam == 'G' ) *penColor = COLOR_GREEN;
                else if( wParam == 'B' ) *penColor = COLOR_BLUE;
                else if( wParam == 'Y' ) *penColor = COLOR_YELLOW;
                else if( wParam == 'O' ) *penColor = COLOR_ORANGE;
                else if( wParam == 'P' ) *penColor = COLOR_PINK;
                else if( wParam == 'X' )
                {
                    if( GetWindowLong( hWnd, GWL_EXSTYLE ) & WS_EX_LAYERED )
                    {
                        // Blur is not supported in LiveDraw
                        break;
                    }
                    *penColor = COLOR_BLUR;
                }

                bool shift = GetKeyState( VK_SHIFT ) & 0x8000;
                if( shift && ( GetWindowLong( hWnd, GWL_EXSTYLE ) & WS_EX_LAYERED ) )
                {
                    // Highlight is not supported in LiveDraw
                    break;
                }

                reg.WriteRegSettings( RegSettings );
                DeleteObject( hDrawingPen );
                SetTextColor( hdcScreenCompat, *penColor );

                // Highlight and blur level
                if( shift && *penColor != COLOR_BLUR )
                {
                    *penColor |= (g_AlphaBlend << 24);
                }
                else
                {
                    if( *penColor == COLOR_BLUR )
                    {
                        g_BlurRadius = shift ? STRONG_BLUR_RADIUS : NORMAL_BLUR_RADIUS;
                    }
                    *penColor |= (0xFF << 24);
                }
                hDrawingPen = CreatePen(PS_SOLID, g_PenWidth, *penColor & 0xFFFFFF);

                SelectObject( hdcScreenCompat, hDrawingPen );
                if( g_Drawing ) {

                    SendMessage( hWnd, WM_MOUSEMOVE, 0, MAKELPARAM( prevPt.x, prevPt.y ));

                } else if( g_TimerActive ) {

                    InvalidateRect( hWnd, NULL, FALSE );

                } else if( g_TypeMode != TypeModeOff ) {

                    ClearTypingCursor( hdcScreenCompat, hdcScreenCursorCompat, cursorRc, g_BlankedScreen );
                    DrawTypingCursor( hWnd, &textPt, hdcScreenCompat, hdcScreenCursorCompat, &cursorRc );
                    InvalidateRect( hWnd, NULL, FALSE );
                }
            }
            break;

        case 'Z':
            if( (GetKeyState( VK_CONTROL ) & 0x8000 ) && g_HaveDrawn && !g_Tracing ) {

                if( PopDrawUndo( hdcScreenCompat, &drawUndoList, width, height )) {

                    if( g_Drawing ) {

                        SaveCursorArea( hdcScreenCursorCompat, hdcScreenCompat, prevPt );
                        SendMessage( hWnd, WM_MOUSEMOVE, 0, MAKELPARAM( prevPt.x, prevPt.y ));
                    }
                    else {

                        SaveCursorArea(hdcScreenCursorCompat, hdcScreenCompat, prevPt);
                    }
                    InvalidateRect( hWnd, NULL, FALSE );
                }
            }
            break;

        case VK_SPACE:
            if( g_Drawing && !g_Tracing ) {

                SetCursorPos(  boundRc.left + (boundRc.right - boundRc.left)/2,
                         boundRc.top + (boundRc.bottom - boundRc.top)/2 );
                SendMessage( hWnd, WM_MOUSEMOVE, 0,
                    MAKELPARAM( (boundRc.right - boundRc.left)/2,
                                (boundRc.bottom - boundRc.top)/2 ));
            }
            break;

        case 'W':
        case 'K':
            // Break timer: change background color
            if( g_TimerActive )
            {
                g_BreakBackgroundColor = ( wParam == 'K' ) ? 1 : 0;
                reg.WriteRegSettings( RegSettings );
                InvalidateRect( hWnd, NULL, FALSE );
                break;
            }

            // Block user-driven sketch pad in liveDraw
            if( lParam != LIVE_DRAW_ZOOM
                && ( GetWindowLongPtr( hWnd, GWL_EXSTYLE ) & WS_EX_LAYERED ) )
            {
                break;
            }

            // Don't allow screen blanking while we've got the typing cursor active
            // because we don't really handle going from white to black.
            if( g_Zoomed && (g_TypeMode == TypeModeOff)) {

                if( !g_Drawing ) {

                    SendMessage( hWnd, WM_LBUTTONDOWN, 0, MAKELPARAM( cursorPos.x, cursorPos.y));
                }
                // Restore area where cursor was previously
                RestoreCursorArea( hdcScreenCompat, hdcScreenCursorCompat, prevPt );
                PushDrawUndo( hdcScreenCompat, &drawUndoList, width, height );
                g_BlankedScreen = static_cast<int>(wParam);
                rc.top = rc.left = 0;
                rc.bottom = height;
                rc.right = width;
                BlankScreenArea( hdcScreenCompat, &rc, g_BlankedScreen );
                InvalidateRect( hWnd, NULL, FALSE );

                // Save area that's going to be occupied by new cursor position
                SaveCursorArea( hdcScreenCursorCompat, hdcScreenCompat, prevPt );
                SendMessage( hWnd, WM_MOUSEMOVE, 0, MAKELPARAM( prevPt.x, prevPt.y ));
            }
            break;

        case 'E':
            // Don't allow erase while we have the typing cursor active
            if( g_HaveDrawn && (g_TypeMode == TypeModeOff)) {

                DeleteDrawUndoList( &drawUndoList );
                g_HaveDrawn = FALSE;
                OutputDebug(L"Erase\n");
                if(GetWindowLong(hWnd, GWL_EXSTYLE) & WS_EX_LAYERED) {
                    SendMessage(hWnd, WM_KEYDOWN, 'K', 0);
                }
                else {
                    BitBlt(hdcScreenCompat, 0, 0, bmp.bmWidth,
                        bmp.bmHeight, hdcScreenSaveCompat, 0, 0, SRCCOPY | CAPTUREBLT);

                    if (g_Drawing) {

                        OutputDebug(L"Erase: draw cursor\n");
                        SaveCursorArea(hdcScreenCursorCompat, hdcScreenCompat, prevPt);
                        DrawCursor(hdcScreenCompat, prevPt, zoomLevel, width, height);
                        g_HaveDrawn = TRUE;
                    }
                }
                InvalidateRect( hWnd, NULL, FALSE );
                g_BlankedScreen = FALSE;
            }
            break;

        case VK_UP:
            SendMessage( hWnd, WM_MOUSEWHEEL,
                MAKEWPARAM( GetAsyncKeyState( VK_LCONTROL ) != 0 || GetAsyncKeyState( VK_RCONTROL ) != 0 ?
                        MK_CONTROL: 0, WHEEL_DELTA), 0 );
            return TRUE;

        case VK_DOWN:
            SendMessage( hWnd, WM_MOUSEWHEEL,
                MAKEWPARAM( GetAsyncKeyState( VK_LCONTROL ) != 0 || GetAsyncKeyState( VK_RCONTROL ) != 0 ?
                        MK_CONTROL: 0, -WHEEL_DELTA), 0 );
            return TRUE;

        case VK_LEFT:
        case VK_RIGHT:
            if( wParam == VK_RIGHT ) delta = 10;
            else					  delta = -10;
            if( g_TimerActive && (breakTimeout > 0 || delta )) {

                if( breakTimeout < 0 ) breakTimeout = 0;
                breakTimeout += delta;
                breakTimeout -= (breakTimeout % 10);
                if( breakTimeout < 0 ) breakTimeout = 0;
                KillTimer( hWnd, 0 );
                SetTimer( hWnd, 0, 1000, NULL );
                InvalidateRect( hWnd, NULL, TRUE );
            }
            break;

        case VK_ESCAPE:
            if( g_TypeMode != TypeModeOff) {

                // Turn off
                SendMessage( hWnd, WM_USER_TYPING_OFF, 0, 0 );

            } else {

                forcePenResize = TRUE;
                PostMessage( hWnd, WM_HOTKEY, ZOOM_HOTKEY, 0 );

                // In case we were in liveDraw
                if( GetWindowLong(hWnd, GWL_EXSTYLE) & WS_EX_LAYERED) {

                    KillTimer(hWnd, 3);
                    LONG_PTR exStyle = GetWindowLongPtr(hWnd, GWL_EXSTYLE);
                    SetWindowLongPtr(hWnd, GWL_EXSTYLE, exStyle & ~WS_EX_LAYERED);
                    pMagSetWindowFilterList( g_hWndLiveZoomMag, MW_FILTERMODE_EXCLUDE, 1, &hWnd );
                    SendMessage( g_hWndLiveZoom, WM_USER_MAGNIFY_CURSOR, TRUE, 0 );
                }
            }
            break;
        }
        return TRUE;

    case WM_RBUTTONDOWN:
        SendMessage( hWnd, WM_USER_EXIT_MODE, 0, 0 );
        break;

    case WM_MOUSEMOVE:
        OutputDebug(L"MOUSEMOVE: zoomed: %d drawing: %d tracing: %d\n",
            g_Zoomed, g_Drawing, g_Tracing);

        OutputDebug(L"Window visible: %d Topmost: %d\n", IsWindowVisible(hWnd), GetWindowLong(hWnd, GWL_EXSTYLE)& WS_EX_TOPMOST);
        if( g_Zoomed && g_TelescopingZoomLastTick != 0ull && !g_Drawing && !g_Tracing )
        {
            ULONG64 now = GetTickCount64();
            if( now - g_TelescopingZoomLastTick >= ZOOM_LEVEL_STEP_TIME )
            {
                doTelescopingZoomTimer( false );
            }
        }

        if( g_Zoomed && (g_TypeMode == TypeModeOff) && !g_bSaveInProgress ) {

            if( g_Drawing ) {

                OutputDebug(L"Mousemove: Drawing\n");

                POINT currentPt;

                // Are we in pen mode on a tablet?
                lParam = ScalePenPosition( zoomLevel, &monInfo, boundRc, message, lParam);
                currentPt.x = LOWORD(lParam);
                currentPt.y = HIWORD(lParam);

                if(lParam == 0) {

                    // Drop it
                    OutputDebug(L"Mousemove: Dropping\n");
                    break;

                } else if(g_DrawingShape) {

                    SetROP2(hdcScreenCompat, R2_NOTXORPEN);

                    // If a previous target rectangle exists, erase
                    // it by drawing another rectangle on top.
                    if( g_rcRectangle.top != g_rcRectangle.bottom ||
                        g_rcRectangle.left != g_rcRectangle.right )
                    {
                        if( prevPenWidth != g_PenWidth )
                        {
                            auto penWidth = g_PenWidth;
                            g_PenWidth = prevPenWidth;

                            auto prevPen = CreatePen( PS_SOLID, g_PenWidth, g_PenColor & 0xFFFFFF );
                            SelectObject( hdcScreenCompat, prevPen );

                            DrawShape( g_DrawingShape, hdcScreenCompat, &g_rcRectangle );

                            g_PenWidth = penWidth;
                            SelectObject( hdcScreenCompat, hDrawingPen );
                            DeleteObject( prevPen );
                        }
                        else
                        {
                            if (PEN_COLOR_HIGHLIGHT(g_PenColor))
                            {
                                // copy original bitmap to screen bitmap to erase previous highlight
                                BitBlt(hdcScreenCompat, 0, 0, bmp.bmWidth, bmp.bmHeight, drawUndoList->hDc, 0, 0, SRCCOPY | CAPTUREBLT);
                            }
                            else
                            {
                                DrawShape(g_DrawingShape, hdcScreenCompat, &g_rcRectangle, PEN_COLOR_HIGHLIGHT(g_PenColor));
                            }
                        }
                    }

                    // Save the coordinates of the target rectangle.
                    // Avoid invalid rectangles by ensuring that the
                    // value of the left coordinate is greater than
                    // that of the right, and that the value of the
                    // bottom coordinate is greater than that of
                    // the top.
                    if( g_DrawingShape == DRAW_LINE ||
                        g_DrawingShape == DRAW_ARROW ) {

                        g_rcRectangle.right = static_cast<LONG>(LOWORD(lParam));
                        g_rcRectangle.bottom = static_cast<LONG>(HIWORD(lParam));

                    } else {

                        if ((g_RectangleAnchor.x < currentPt.x) &&
                                (g_RectangleAnchor.y > currentPt.y)) {

                            SetRect(&g_rcRectangle, g_RectangleAnchor.x, currentPt.y,
                                currentPt.x, g_RectangleAnchor.y);

                        } else if ((g_RectangleAnchor.x > currentPt.x) &&
                                (g_RectangleAnchor.y > currentPt.y )) {

                            SetRect(&g_rcRectangle, currentPt.x,
                                currentPt.y, g_RectangleAnchor.x,g_RectangleAnchor.y);

                        } else if ((g_RectangleAnchor.x > currentPt.x) &&
                                (g_RectangleAnchor.y < currentPt.y )) {

                            SetRect(&g_rcRectangle, currentPt.x, g_RectangleAnchor.y,
                                g_RectangleAnchor.x, currentPt.y );
                        } else {

                            SetRect(&g_rcRectangle, g_RectangleAnchor.x, g_RectangleAnchor.y,
                                currentPt.x, currentPt.y );
                        }
                    }

                    if (g_rcRectangle.left != g_rcRectangle.right ||
                        g_rcRectangle.top != g_rcRectangle.bottom) {

                        // Draw the new target rectangle.
                        DrawShape(g_DrawingShape, hdcScreenCompat, &g_rcRectangle, PEN_COLOR_HIGHLIGHT(g_PenColor));
                        OutputDebug(L"SHAPE: (%d, %d) - (%d, %d)\n", g_rcRectangle.left, g_rcRectangle.top,
                            g_rcRectangle.right, g_rcRectangle.bottom);
                    }

                    prevPenWidth = g_PenWidth;
                    SetROP2( hdcScreenCompat, R2_NOP );
                }
                else if (g_Tracing) {

                    OutputDebug(L"Mousemove: Tracing\n");

                    g_HaveDrawn = TRUE;
                    Gdiplus::Graphics	dstGraphics(hdcScreenCompat);
                    if( ( GetWindowLong( g_hWndMain, GWL_EXSTYLE ) & WS_EX_LAYERED ) == 0 )
                    {
                        dstGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
                    }
                    Gdiplus::Color	color = ColorFromColorRef(g_PenColor);
                    Gdiplus::Pen pen(color, static_cast<Gdiplus::REAL>(g_PenWidth));
                    pen.SetLineCap(Gdiplus::LineCapRound, Gdiplus::LineCapRound, Gdiplus::DashCapRound);

                    // If highlighting, use a double layer approach
                    OutputDebug(L"PenColor: %x\n", g_PenColor);
                    OutputDebug(L"Blur color: %x\n", COLOR_BLUR);
                    if( (g_PenColor & 0xFFFFFF) == COLOR_BLUR) {

                        OutputDebug(L"BLUR\n");

                        // Restore area where cursor was previously
                        RestoreCursorArea(hdcScreenCompat, hdcScreenCursorCompat, prevPt);

                        // Create a new bitmap that's the size of the area covered by the line + 2 * g_PenWidth
                        Gdiplus::Rect lineBounds = GetLineBounds( prevPt, currentPt, g_PenWidth );
                        Gdiplus::Bitmap* lineBitmap = DrawBitmapLine(lineBounds, prevPt, currentPt, &pen);
                        Gdiplus::BitmapData* lineData = LockGdiPlusBitmap(lineBitmap);
                        BYTE* pPixels = static_cast<BYTE*>(lineData->Scan0);

                        // Create a GDI bitmap that's the size of the lineBounds rectangle
                        Gdiplus::Bitmap *blurBitmap = CreateGdiplusBitmap( hdcScreenCompat, // oldestUndo->hDc,
                                            lineBounds.X, lineBounds.Y, lineBounds.Width, lineBounds.Height);

                        // Blur it
                        BitmapBlur(blurBitmap);
                        BlurScreen(hdcScreenCompat, &lineBounds, blurBitmap, pPixels);

                        // Unlock the bits
                        lineBitmap->UnlockBits(lineData);
                        delete lineBitmap;
                        delete blurBitmap;

                        // Invalidate the updated rectangle
                        InvalidateGdiplusRect( hWnd, lineBounds );

                        // Save area that's going to be occupied by new cursor position
                        SaveCursorArea(hdcScreenCursorCompat, hdcScreenCompat, currentPt);

                        // Draw new cursor
                        DrawCursor(hdcScreenCompat, currentPt, zoomLevel, width, height);
                    }
                    else if(PEN_COLOR_HIGHLIGHT(g_PenColor)) {

                        OutputDebug(L"HIGHLIGHT\n");

                        // This is a highlighting pen color
                        Gdiplus::Rect lineBounds = GetLineBounds(prevPt, currentPt, g_PenWidth);
                        Gdiplus::Bitmap* lineBitmap = DrawBitmapLine(lineBounds, prevPt, currentPt, &pen);
                        Gdiplus::BitmapData* lineData = LockGdiPlusBitmap(lineBitmap);
                        BYTE* pPixels = static_cast<BYTE*>(lineData->Scan0);

                        // Create a DIB section for efficient pixel manipulation
                        BITMAPINFO bmi = { 0 };
                        bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
                        bmi.bmiHeader.biWidth = lineBounds.Width;
                        bmi.bmiHeader.biHeight = -lineBounds.Height;  // Top-down DIB
                        bmi.bmiHeader.biPlanes = 1;
                        bmi.bmiHeader.biBitCount = 32;  // 32 bits per pixel
                        bmi.bmiHeader.biCompression = BI_RGB;

                        VOID* pDIBBits;
                        HBITMAP hDIB = CreateDIBSection(hdcScreenCompat, &bmi, DIB_RGB_COLORS, &pDIBBits, NULL, 0);

                        HDC hdcDIB = CreateCompatibleDC(hdcScreenCompat);
                        SelectObject(hdcDIB, hDIB);

                        // Copy the relevant part of hdcScreenCompat to the DIB
                        BitBlt(hdcDIB, 0, 0, lineBounds.Width, lineBounds.Height, hdcScreenCompat, lineBounds.X, lineBounds.Y, SRCCOPY);

                        // Pointer to the DIB bits
                        BYTE* pDestPixels = static_cast<BYTE*>(pDIBBits);

                        // Pointer to screen bits
                        HDC hdcDIBOrig;
                        HBITMAP hDibOrigBitmap, hDibBitmap;
                        P_DRAW_UNDO oldestUndo = GetOldestUndo(drawUndoList);
                        BYTE* pDestPixels2 = CreateBitmapMemoryDIB(hdcScreenCompat, oldestUndo->hDc, &lineBounds,
                                                &hdcDIBOrig, &hDibBitmap, &hDibOrigBitmap);

                        for (int local_y = 0; local_y < lineBounds.Height; ++local_y) {
                            for (int local_x = 0; local_x < lineBounds.Width; ++local_x) {
                                int index = (local_y * lineBounds.Width * 4) + (local_x * 4); // Assuming 4 bytes per pixel
                                // BYTE b = pPixels[index + 0];  // Blue channel
                                // BYTE g = pPixels[index + 1];  // Green channel
                                // BYTE r = pPixels[index + 2];  // Red channel
                                BYTE a = pPixels[index + 3];  // Alpha channel

                                // Check if this is a drawn pixel
                                if (a != 0) {
                                    // Assuming pDestPixels is a valid pointer to the destination bitmap's pixel data
                                    BYTE destB = pDestPixels2[index + 0];  // Blue channel
                                    BYTE destG = pDestPixels2[index + 1];  // Green channel
                                    BYTE destR = pDestPixels2[index + 2];  // Red channel

                                    // Create a COLORREF value from the destination pixel data
                                    COLORREF currentPixel = RGB(destR, destG, destB);
                                    // Blend the colors
                                    COLORREF newPixel = BlendColors(currentPixel, g_PenColor);
                                    // Update the destination pixel data with the new color
                                    pDestPixels[index + 0] = GetBValue(newPixel);
                                    pDestPixels[index + 1] = GetGValue(newPixel);
                                    pDestPixels[index + 2] = GetRValue(newPixel);
                                }
                            }
                        }

                        // Copy the updated DIB back to hdcScreenCompat
                        BitBlt(hdcScreenCompat, lineBounds.X, lineBounds.Y, lineBounds.Width, lineBounds.Height, hdcDIB, 0, 0, SRCCOPY);

                        // Clean up
                        DeleteObject(hDIB);
                        DeleteDC(hdcDIB);

                        SelectObject(hdcDIBOrig, hDibOrigBitmap);
                        DeleteObject(hDibBitmap);
                        DeleteDC(hdcDIBOrig);

                        // Invalidate the updated rectangle
                        InvalidateGdiplusRect(hWnd, lineBounds);
                    }
                    else {

                        // Normal tracing
                        dstGraphics.DrawLine(&pen, static_cast<INT>(prevPt.x), static_cast<INT>(prevPt.y),
                                static_cast<INT>(currentPt.x), static_cast<INT>(currentPt.y));
                    }

                } else {

                    OutputDebug(L"Mousemove: Moving cursor\n");

                    // Restore area where cursor was previously
                    RestoreCursorArea( hdcScreenCompat, hdcScreenCursorCompat, prevPt );

                    // Save area that's going to be occupied by new cursor position
                    SaveCursorArea( hdcScreenCursorCompat, hdcScreenCompat, currentPt );

                    // Draw new cursor
                    DrawCursor( hdcScreenCompat, currentPt, zoomLevel, width, height );
                }

                if( g_DrawingShape ) {

                    InvalidateRect( hWnd, NULL, FALSE );

                } else {

                    // Invalidate area just modified
                    InvalidateCursorMoveArea( hWnd, zoomLevel, width, height, currentPt, prevPt, cursorPos );
                }
                prevPt = currentPt;

                // In liveDraw we miss the mouse up
                if( GetWindowLong(hWnd, GWL_EXSTYLE) & WS_EX_LAYERED) {

                    if((GetAsyncKeyState(VK_LBUTTON) & 0x8000) == 0) {

                        OutputDebug(L"LIVE_DRAW missed mouse up. Sending synthetic.\n");
                        SendMessage(hWnd, WM_LBUTTONUP, wParam, lParam);
                    }
                }

            } else {

                cursorPos.x = LOWORD( lParam );
                cursorPos.y = HIWORD( lParam );
                InvalidateRect( hWnd, NULL, FALSE );
            }
        } else if( g_Zoomed && (g_TypeMode != TypeModeOff) && !g_HaveTyped ) {

            ClearTypingCursor( hdcScreenCompat, hdcScreenCursorCompat, cursorRc, g_BlankedScreen );
            textPt.x = prevPt.x = LOWORD( lParam );
            textPt.y = prevPt.y = HIWORD( lParam );

            // Draw the typing cursor
            DrawTypingCursor( hWnd, &textPt, hdcScreenCompat, hdcScreenCursorCompat, &cursorRc, true );
            prevPt = textPt;
            InvalidateRect( hWnd, NULL, FALSE );
        }
#if 0
        {
            static int index = 0;
            OutputDebug( L"%d: foreground: %x focus: %x (hwnd: %x)\n",
                index++, (DWORD) PtrToUlong(GetForegroundWindow()),  PtrToUlong(GetFocus()), PtrToUlong(hWnd));
        }
#endif
        return TRUE;

    case WM_LBUTTONDOWN:
        g_StraightDirection = 0;

        if( g_Zoomed && (g_TypeMode == TypeModeOff) && zoomTelescopeTarget == zoomLevel ) {

            OutputDebug(L"LBUTTONDOWN: drawing\n");

            // Save current bitmap to undo history
            if( g_HaveDrawn ) {

                RestoreCursorArea( hdcScreenCompat, hdcScreenCursorCompat, prevPt );
            }

            // don't push undo if we sent this to ourselves for a pen resize
            if( wParam != -1 ) {

                PushDrawUndo( hdcScreenCompat, &drawUndoList, width, height );

            } else {

                wParam = 0;
            }

            // Are we in pen mode on a tablet?
            lParam = ScalePenPosition( zoomLevel, &monInfo, boundRc,
                        message, lParam);

            if (lParam == 0) {

                // Drop it
                break;

            } else if( g_Drawing ) {

                // is the user drawing a rectangle?
                if( wParam & MK_CONTROL ||
                    wParam & MK_SHIFT ||
                    GetKeyState( VK_TAB ) < 0 ) {

                    // Restore area where cursor was previously
                    RestoreCursorArea( hdcScreenCompat, hdcScreenCursorCompat, prevPt );

                    if( wParam & MK_SHIFT && wParam & MK_CONTROL )
                        g_DrawingShape = DRAW_ARROW;
                    else if( wParam & MK_CONTROL )
                        g_DrawingShape = DRAW_RECTANGLE;
                    else if( wParam & MK_SHIFT )
                        g_DrawingShape = DRAW_LINE;
                    else
                        g_DrawingShape = DRAW_ELLIPSE;
                    g_RectangleAnchor.x = LOWORD(lParam);
                    g_RectangleAnchor.y = HIWORD(lParam);
                    SetRect(&g_rcRectangle, g_RectangleAnchor.x, g_RectangleAnchor.y,
                            g_RectangleAnchor.x, g_RectangleAnchor.y);

                } else {

                    Gdiplus::Graphics	dstGraphics(hdcScreenCompat);
                    if( ( GetWindowLong( g_hWndMain, GWL_EXSTYLE ) & WS_EX_LAYERED ) == 0 )
                    {
                        dstGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
                    }
                    Gdiplus::Color	color = ColorFromColorRef(g_PenColor);
                    Gdiplus::Pen pen(color, static_cast<Gdiplus::REAL>(g_PenWidth));
                    Gdiplus::GraphicsPath path;
                    pen.SetLineJoin(Gdiplus::LineJoinRound);
                    path.AddLine(static_cast<INT>(prevPt.x), prevPt.y, prevPt.x, prevPt.y);
                    dstGraphics.DrawPath(&pen, &path);
                }
                g_Tracing = TRUE;
                SetROP2( hdcScreenCompat, R2_COPYPEN );
                prevPt.x = LOWORD(lParam);
                prevPt.y = HIWORD(lParam);
                g_HaveDrawn = TRUE;

            } else {

                OutputDebug(L"Tracing on\n");

                // Turn on drawing
                if( !g_HaveDrawn ) {

                    // refresh drawing bitmap with original screen image
                    BitBlt(hdcScreenCompat, 0, 0, bmp.bmWidth,
                        bmp.bmHeight, hdcScreenSaveCompat, 0, 0, SRCCOPY|CAPTUREBLT );
                    g_HaveDrawn = TRUE;
                }
                DeleteObject( hDrawingPen );
                hDrawingPen = CreatePen(PS_SOLID, g_PenWidth, g_PenColor & 0xFFFFFF);
                SelectObject( hdcScreenCompat, hDrawingPen );

                // is the user drawing a rectangle?
                if( wParam & MK_CONTROL && g_Drawing ) {

                    // Restore area where cursor was previously
                    RestoreCursorArea( hdcScreenCompat, hdcScreenCursorCompat, prevPt );

                    // Configure rectangle drawing
                    g_DrawingShape = TRUE;
                    g_RectangleAnchor.x = LOWORD(lParam);
                    g_RectangleAnchor.y = HIWORD(lParam);
                    SetRect(&g_rcRectangle, g_RectangleAnchor.x, g_RectangleAnchor.y,
                        g_RectangleAnchor.x, g_RectangleAnchor.y);
                    OutputDebug( L"RECTANGLE: %d, %d\n", prevPt.x, prevPt.y );

                } else {

                    prevPt.x = LOWORD( lParam );
                    prevPt.y = HIWORD( lParam );
                    SaveCursorArea( hdcScreenCursorCompat, hdcScreenCompat, prevPt );

                    Gdiplus::Graphics	dstGraphics(hdcScreenCursorCompat);
                    if( ( GetWindowLong( g_hWndMain, GWL_EXSTYLE ) & WS_EX_LAYERED ) == 0 )
                    {
                        dstGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
                    }
                    Gdiplus::Color	color = ColorFromColorRef(g_PenColor);
                    Gdiplus::Pen pen(color, static_cast<Gdiplus::REAL>(g_PenWidth));
                    Gdiplus::GraphicsPath path;
                    pen.SetLineJoin(Gdiplus::LineJoinRound);
                    path.AddLine(static_cast<INT>(prevPt.x), prevPt.y, prevPt.x, prevPt.y);
                    dstGraphics.DrawPath(&pen, &path);
                }
                InvalidateRect( hWnd, NULL, FALSE );

                // If we're in live zoom, make the drawing pen larger to compensate
                if( g_ZoomOnLiveZoom && forcePenResize )
                {
                    forcePenResize = FALSE;

                    ResizePen( hWnd, hdcScreenCompat, hdcScreenCursorCompat, prevPt, g_Tracing,
                        &g_Drawing, g_LiveZoomLevel, FALSE, min( static_cast<int>(g_LiveZoomLevel * g_RootPenWidth),
                        static_cast<int>(g_LiveZoomLevel * MAX_PEN_WIDTH) ) );
                    OutputDebug( L"LIVEZOOM_DRAW: zoomLevel: %d rootPenWidth: %d penWidth: %d\n",
                        static_cast<int>(g_LiveZoomLevel), g_RootPenWidth, g_PenWidth );
                }
                else if( !g_ZoomOnLiveZoom && forcePenResize )
                {
                    forcePenResize = FALSE;
                    // Scale pen down to root for regular drawing mode
                    ResizePen( hWnd, hdcScreenCompat, hdcScreenCursorCompat, prevPt, g_Tracing,
                        &g_Drawing, g_LiveZoomLevel, FALSE, g_RootPenWidth );
                }
                g_Drawing = TRUE;

                EnableDisableStickyKeys( FALSE );
                OutputDebug( L"LBUTTONDOWN: %d, %d\n", prevPt.x, prevPt.y );

                // Constrain the mouse to the visible region
                boundRc = BoundMouse( zoomLevel, &monInfo, width, height, &cursorPos );
            }
        } else if( g_TypeMode != TypeModeOff ) {

            if( !g_HaveTyped ) {

                g_HaveTyped = TRUE;

            } else {

                SendMessage( hWnd, WM_USER_TYPING_OFF, 0, 0 );
            }
        }
        return TRUE;

    case WM_LBUTTONUP:
        OutputDebug(L"LBUTTONUP: zoomed: %d drawing: %d tracing: %d\n",
            g_Zoomed, g_Drawing, g_Tracing);

        if( g_Zoomed && g_Drawing && g_Tracing ) {

            // Are we in pen mode on a tablet?
            lParam = ScalePenPosition( zoomLevel, &monInfo, boundRc,
                        message, lParam);
            OutputDebug(L"LBUTTONUP: %d, %d\n", LOWORD(lParam), HIWORD(lParam));
            if (lParam == 0) {

                // Drop it
                break;
            }

            POINT adjustPos;
            adjustPos.x = LOWORD(lParam);
            adjustPos.y = HIWORD(lParam);
            if( g_StraightDirection == -1 ) {

                adjustPos.x = prevPt.x;

            } else {

                adjustPos.y = prevPt.y;
            }
            lParam = MAKELPARAM( adjustPos.x, adjustPos.y );

            if( !g_DrawingShape ) {

                // If the point has changed, draw a line to it
                if (!PEN_COLOR_HIGHLIGHT(g_PenColor))
                {
                    if (prevPt.x != LOWORD(lParam) || prevPt.y != HIWORD(lParam))
                    {
                        Gdiplus::Graphics dstGraphics(hdcScreenCompat);
                        if ((GetWindowLong(g_hWndMain, GWL_EXSTYLE) & WS_EX_LAYERED) == 0)
                        {
                            dstGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
                        }
                        Gdiplus::Color color = ColorFromColorRef(g_PenColor);
                        Gdiplus::Pen pen(color, static_cast<Gdiplus::REAL>(g_PenWidth));
                        Gdiplus::GraphicsPath path;
                        pen.SetLineJoin(Gdiplus::LineJoinRound);
                        path.AddLine(prevPt.x, prevPt.y, LOWORD(lParam), HIWORD(lParam));
                        dstGraphics.DrawPath(&pen, &path);
                    }
                    // Draw a dot at the current point, if the point hasn't changed
                    else
                    {
                        MoveToEx(hdcScreenCompat, prevPt.x, prevPt.y, NULL);
                        LineTo(hdcScreenCompat, LOWORD(lParam), HIWORD(lParam));
                        InvalidateRect(hWnd, NULL, FALSE);
                    }
                }
                prevPt.x = LOWORD( lParam );
                prevPt.y = HIWORD( lParam );

                if ((g_PenColor & 0xFFFFFF) == COLOR_BLUR) {

                    RestoreCursorArea(hdcScreenCompat, hdcScreenCursorCompat, prevPt);
                }
                SaveCursorArea( hdcScreenCursorCompat, hdcScreenCompat, prevPt );
                DrawCursor( hdcScreenCompat, prevPt, zoomLevel, width, height );

            } else if (g_rcRectangle.top != g_rcRectangle.bottom ||
                        g_rcRectangle.left != g_rcRectangle.right ) {

                // erase previous
                if (!PEN_COLOR_HIGHLIGHT(g_PenColor))
                {
                    SetROP2(hdcScreenCompat, R2_NOTXORPEN);
                    DrawShape(g_DrawingShape, hdcScreenCompat, &g_rcRectangle);
                }

                // Draw the final shape
                HBRUSH hBrush = static_cast<HBRUSH>(GetStockObject( NULL_BRUSH ));
                HBRUSH oldHbrush = static_cast<HBRUSH>(SelectObject( hdcScreenCompat, hBrush ));
                SetROP2( hdcScreenCompat, R2_COPYPEN );

                // smooth line
                if( g_SnapToGrid ) {

                    if( g_DrawingShape == DRAW_LINE ||
                        g_DrawingShape == DRAW_ARROW ) {

                        if( abs(g_rcRectangle.bottom - g_rcRectangle.top) <
                            abs(g_rcRectangle.right - g_rcRectangle.left)/10 ) {

                            g_rcRectangle.bottom = g_rcRectangle.top-1;
                        }
                        if( abs(g_rcRectangle.right - g_rcRectangle.left) <
                            abs(g_rcRectangle.bottom - g_rcRectangle.top)/10 ) {

                            g_rcRectangle.right = g_rcRectangle.left-1;
                        }
                    }
                }
                // Draw final one using Gdi+
                DrawShape( g_DrawingShape, hdcScreenCompat, &g_rcRectangle, true );

                InvalidateRect( hWnd, NULL, FALSE );
                DeleteObject( hBrush );
                SelectObject( hdcScreenCompat, oldHbrush );

                prevPt.x = LOWORD( lParam );
                prevPt.y = HIWORD( lParam );
                SaveCursorArea( hdcScreenCursorCompat, hdcScreenCompat, prevPt );
            }
            g_Tracing = FALSE;
            g_DrawingShape = FALSE;
            OutputDebug( L"LBUTTONUP:" );
        }
        return TRUE;

    case WM_GETMINMAXINFO:

        reinterpret_cast<MINMAXINFO *>(lParam)->ptMaxSize.x = width;
        reinterpret_cast<MINMAXINFO*>(lParam)->ptMaxSize.y = height;
        reinterpret_cast<MINMAXINFO*>(lParam)->ptMaxPosition.x = 0;
        reinterpret_cast<MINMAXINFO*>(lParam)->ptMaxPosition.y = 0;
        return TRUE;

    case WM_USER_TYPING_OFF:	{

        if( g_TypeMode != TypeModeOff ) {

            g_TypeMode = TypeModeOff;
            ClearTypingCursor( hdcScreenCompat, hdcScreenCursorCompat, cursorRc, g_BlankedScreen );
            InvalidateRect( hWnd, NULL, FALSE );
            DeleteTypedText( &typedKeyList );

            // 1 means don't reset the cursor. We get that for font resizing
            // Only move the cursor if we're drawing, else the screen moves to center
            // on the new cursor position
            if( wParam != 1 && g_Drawing ) {

                prevPt.x = cursorRc.left;
                prevPt.y = cursorRc.top;
                SetCursorPos( monInfo.rcMonitor.left + prevPt.x,
                        monInfo.rcMonitor.top + prevPt.y );

                SaveCursorArea( hdcScreenCursorCompat, hdcScreenCompat, prevPt );
                SendMessage( hWnd, WM_MOUSEMOVE, 0, MAKELPARAM( prevPt.x, prevPt.y ));

            } else if( !g_Drawing) {

                // FIX: would be nice to reset cursor so screen doesn't move
                prevPt = textStartPt;
                SaveCursorArea( hdcScreenCursorCompat, hdcScreenCompat, prevPt );
                SetCursorPos( prevPt.x, prevPt.y );
                SendMessage( hWnd, WM_MOUSEMOVE, 0, MAKELPARAM( prevPt.x, prevPt.y ));
            }
        }
        }
        return TRUE;

    case WM_USER_TRAY_ACTIVATE:

        switch( lParam ) {
        case WM_RBUTTONUP:
        case WM_LBUTTONUP:
        case WM_CONTEXTMENU:
        {
            // Set the foreground window so the menu can be closed by clicking elsewhere when
            // opened via right click, and so keyboard navigation works when opened with the menu
            // key or Shift-F10.
            SetForegroundWindow( hWndOptions ? hWndOptions : hWnd );

            // Pop up context menu
            POINT pt;
            GetCursorPos( &pt );
            hPopupMenu = CreatePopupMenu();
            if(!g_StartedByPowerToys) {
                // Exiting will happen through disabling in PowerToys, not the context menu.
                InsertMenu( hPopupMenu, 0, MF_BYPOSITION, IDCANCEL, L"E&xit" );
                InsertMenu( hPopupMenu, 0, MF_BYPOSITION|MF_SEPARATOR, 0, NULL );
            }
            InsertMenu( hPopupMenu, 0, MF_BYPOSITION | ( g_RecordToggle ? MF_CHECKED : 0 ), IDC_RECORD, L"&Record" );
            InsertMenu( hPopupMenu, 0, MF_BYPOSITION, IDC_ZOOM, L"&Zoom" );
            InsertMenu( hPopupMenu, 0, MF_BYPOSITION, IDC_DRAW, L"&Draw" );
            InsertMenu( hPopupMenu, 0, MF_BYPOSITION, IDC_BREAK, L"&Break Timer" );
            if(!g_StartedByPowerToys) {
                // When started by PowerToys, options are configured through the PowerToys Settings.
                InsertMenu( hPopupMenu, 0, MF_BYPOSITION|MF_SEPARATOR, 0, NULL );
                InsertMenu( hPopupMenu, 0, MF_BYPOSITION, IDC_OPTIONS, L"&Options" );
            }
            // Apply dark mode theme to the menu
            ApplyDarkModeToMenu( hPopupMenu );
            TrackPopupMenu( hPopupMenu, 0, pt.x , pt.y, 0, hWnd, NULL );
            DestroyMenu( hPopupMenu );
            break;
        }
        case WM_LBUTTONDBLCLK:
            if( !g_TimerActive ) {

                SendMessage( hWnd, WM_COMMAND, IDC_OPTIONS, 0 );

            } else {

                SetForegroundWindow( hWnd );
            }
            break;
        }
        break;

    case WM_USER_STOP_RECORDING:
        StopRecording();
        break;

    case WM_USER_SAVE_CURSOR:
        if( g_Zoomed == TRUE )
        {
            GetCursorPos( &savedCursorPos );
            if( g_Drawing == TRUE )
            {
                ClipCursor( NULL );
            }
        }
        break;

    case WM_USER_RESTORE_CURSOR:
        if( g_Zoomed == TRUE )
        {
            if( g_Drawing == TRUE )
            {
                boundRc = BoundMouse( zoomLevel, &monInfo, width, height, &cursorPos );
            }
            SetCursorPos( savedCursorPos.x, savedCursorPos.y );
        }
        break;

    case WM_USER_EXIT_MODE:
        if( g_Zoomed )
        {
            // Turn off
            if( g_TypeMode != TypeModeOff )
            {
                SendMessage( hWnd, WM_USER_TYPING_OFF, 0, 0 );
            }
            else if( !g_Drawing )
            {
                // Turn off
                PostMessage( hWnd, WM_HOTKEY, ZOOM_HOTKEY, 0 );
            }
            else
            {
                if( !g_Tracing )
                {
                    RestoreCursorArea( hdcScreenCompat, hdcScreenCursorCompat, prevPt );

                    // Ensure the cursor area is painted before returning
                    InvalidateRect( hWnd, NULL, FALSE );
                    UpdateWindow( hWnd );

                    // Make the magnified cursor visible again if LiveDraw is on in LiveZoom
                    if( GetWindowLong( hWnd, GWL_EXSTYLE ) & WS_EX_LAYERED )
                    {
                        if( IsWindowVisible( g_hWndLiveZoom ) )
                        {
                            SendMessage( g_hWndLiveZoom, WM_USER_MAGNIFY_CURSOR, TRUE, 0 );
                        }
                    }
                }
                if( zoomLevel != 1 )
                {
                    // Restore the cursor position to prevent moving the view in static zoom
                    SetCursorPos( monInfo.rcMonitor.left + cursorPos.x, monInfo.rcMonitor.top + cursorPos.y );
                }
                g_Drawing = FALSE;
                g_Tracing = FALSE;
                EnableDisableStickyKeys( TRUE );
                SendMessage( hWnd, WM_USER_TYPING_OFF, 0, 0 );

                // Unclip cursor
                ClipCursor( NULL );
            }
        }
        else if( g_TimerActive )
        {
            // Turn off
            PostMessage( hWnd, WM_HOTKEY, ZOOM_HOTKEY, 0 );
        }
        break;

    case WM_USER_RELOAD_SETTINGS:
    {
        // Reload the settings. This message is called from PowerToys after a setting is changed by the user.
        reg.ReadRegSettings(RegSettings);

        // Refresh dark mode state after loading theme override from registry
        RefreshDarkModeState();

        if (g_RecordingFormat == RecordingFormat::GIF)
        {
            g_RecordScaling = g_RecordScalingGIF;
            g_RecordFrameRate = RECORDING_FORMAT_GIF_DEFAULT_FRAMERATE;
        }
        else
        {
            g_RecordScaling = g_RecordScalingMP4;
            g_RecordFrameRate = RECORDING_FORMAT_MP4_DEFAULT_FRAMERATE;
        }

        // Apply tray icon setting
        EnableDisableTrayIcon(hWnd, g_ShowTrayIcon);

        // This is also called by ZoomIt when it starts and loads the Settings. Opacity is added after loading from registry, so we use the same pattern.
        if ((g_PenColor >> 24) == 0)
        {
            g_PenColor |= 0xFF << 24;
        }

        // Apply hotkey settings
        UnregisterAllHotkeys(hWnd);
        g_ToggleMod = GetKeyMod(g_ToggleKey);
        g_LiveZoomToggleMod = GetKeyMod(g_LiveZoomToggleKey);
        g_DrawToggleMod = GetKeyMod(g_DrawToggleKey);
        g_BreakToggleMod = GetKeyMod(g_BreakToggleKey);
        g_DemoTypeToggleMod = GetKeyMod(g_DemoTypeToggleKey);
        g_SnipToggleMod = GetKeyMod(g_SnipToggleKey);
        g_SnipPanoramaToggleMod = GetKeyMod(g_SnipPanoramaToggleKey);
        g_SnipOcrToggleMod = GetKeyMod(g_SnipOcrToggleKey);
        g_RecordToggleMod = GetKeyMod(g_RecordToggleKey);
        BOOL showOptions = FALSE;
        if (g_ToggleKey)
        {
            if (!RegisterHotKey(hWnd, ZOOM_HOTKEY, g_ToggleMod, g_ToggleKey & 0xFF))
            {
                MessageBox(hWnd, L"The specified zoom toggle hotkey is already in use.\nSelect a different zoom toggle hotkey.", APPNAME, MB_ICONERROR);
                showOptions = TRUE;
            }
        }
        if (g_LiveZoomToggleKey)
        {
            if (!RegisterHotKey(hWnd, LIVE_HOTKEY, g_LiveZoomToggleMod, g_LiveZoomToggleKey & 0xFF) ||
                !RegisterHotKey(hWnd, LIVE_DRAW_HOTKEY, g_LiveZoomToggleMod ^ MOD_SHIFT, g_LiveZoomToggleKey & 0xFF))
            {
                MessageBox(hWnd, L"The specified live-zoom toggle hotkey is already in use.\nSelect a different zoom toggle hotkey.", APPNAME, MB_ICONERROR);
                showOptions = TRUE;
            }
        }
        if (g_DrawToggleKey)
        {
            if (!RegisterHotKey(hWnd, DRAW_HOTKEY, g_DrawToggleMod, g_DrawToggleKey & 0xFF))
            {
                MessageBox(hWnd, L"The specified draw w/out zoom hotkey is already in use.\nSelect a different draw w/out zoom hotkey.", APPNAME, MB_ICONERROR);
                showOptions = TRUE;
            }
        }
        if (g_BreakToggleKey)
        {
            if (!RegisterHotKey(hWnd, BREAK_HOTKEY, g_BreakToggleMod, g_BreakToggleKey & 0xFF))
            {
                MessageBox(hWnd, L"The specified break timer hotkey is already in use.\nSelect a different break timer hotkey.", APPNAME, MB_ICONERROR);
                showOptions = TRUE;
            }
        }
        if (g_DemoTypeToggleKey)
        {
            if (!RegisterHotKey(hWnd, DEMOTYPE_HOTKEY, g_DemoTypeToggleMod, g_DemoTypeToggleKey & 0xFF) ||
                !RegisterHotKey(hWnd, DEMOTYPE_RESET_HOTKEY, (g_DemoTypeToggleMod ^ MOD_SHIFT), g_DemoTypeToggleKey & 0xFF))
            {
                MessageBox(hWnd, L"The specified live-type hotkey is already in use.\nSelect a different live-type hotkey.", APPNAME, MB_ICONERROR);
                showOptions = TRUE;
            }
        }
        if (g_SnipToggleKey)
        {
            if (!RegisterHotKey(hWnd, SNIP_HOTKEY, g_SnipToggleMod, g_SnipToggleKey & 0xFF) ||
                !RegisterHotKey(hWnd, SNIP_SAVE_HOTKEY, (g_SnipToggleMod ^ MOD_SHIFT), g_SnipToggleKey & 0xFF))
            {
                MessageBox(hWnd, L"The specified snip hotkey is already in use.\nSelect a different snip hotkey.", APPNAME, MB_ICONERROR);
                showOptions = TRUE;
            }
        }
        if (g_SnipPanoramaToggleKey &&
            (g_SnipPanoramaToggleKey != g_SnipToggleKey || g_SnipPanoramaToggleMod != g_SnipToggleMod))
        {
            if (!RegisterHotKey(hWnd, SNIP_PANORAMA_HOTKEY, g_SnipPanoramaToggleMod | MOD_NOREPEAT, g_SnipPanoramaToggleKey & 0xFF) ||
                !RegisterHotKey(hWnd, SNIP_PANORAMA_SAVE_HOTKEY, ( g_SnipPanoramaToggleMod ^ MOD_SHIFT ) | MOD_NOREPEAT, g_SnipPanoramaToggleKey & 0xFF))
            {
                MessageBox(hWnd, L"The specified panorama snip hotkey is already in use.\nSelect a different panorama snip hotkey.", APPNAME, MB_ICONERROR);
                showOptions = TRUE;
            }
        }
        if (g_SnipOcrToggleKey)
        {
            if (!RegisterHotKey(hWnd, SNIP_OCR_HOTKEY, g_SnipOcrToggleMod, g_SnipOcrToggleKey & 0xFF))
            {
                MessageBox(hWnd, L"The specified snip OCR hotkey is already in use.\nSelect a different snip OCR hotkey.", APPNAME, MB_ICONERROR);
                showOptions = TRUE;
            }
        }
        if (g_RecordToggleKey)
        {
            if (!RegisterHotKey(hWnd, RECORD_HOTKEY, g_RecordToggleMod | MOD_NOREPEAT, g_RecordToggleKey & 0xFF) ||
                !RegisterHotKey(hWnd, RECORD_CROP_HOTKEY, (g_RecordToggleMod ^ MOD_SHIFT) | MOD_NOREPEAT, g_RecordToggleKey & 0xFF) ||
                !RegisterHotKey(hWnd, RECORD_WINDOW_HOTKEY, (g_RecordToggleMod ^ MOD_ALT) | MOD_NOREPEAT, g_RecordToggleKey & 0xFF))
            {
                MessageBox(hWnd, L"The specified record hotkey is already in use.\nSelect a different record hotkey.", APPNAME, MB_ICONERROR);
                showOptions = TRUE;
            }
        }
        if (showOptions)
        {
            // To open the PowerToys settings in the ZoomIt page.
            SendMessage(hWnd, WM_COMMAND, IDC_OPTIONS, 0);
        }
        break;
    }
    case WM_COMMAND:

        switch(LOWORD( wParam )) {

        case IDC_SAVE_CROP:
        case IDC_SAVE:
        {
            POINT local_savedCursorPos{};
            if( lParam != SHALLOW_ZOOM )
            {
                GetCursorPos(&local_savedCursorPos);
            }

            // Determine the user's desired save area in zoomed viewport coordinates.
            // This will be the entire viewport if the user does not select a crop
            // rectangle.
            int copyX = 0, copyY = 0, copyWidth = width, copyHeight = height;

            if ( LOWORD( wParam ) == IDC_SAVE_CROP )
            {
                g_RecordCropping = TRUE;
                SelectRectangle selectRectangle;
                if( !selectRectangle.Start( hWnd ) )
                {
                    g_RecordCropping = FALSE;
                    if( lParam != SHALLOW_ZOOM )
                    {
                        SetCursorPos(local_savedCursorPos.x, local_savedCursorPos.y);
                    }
                    break;
                }

                auto copyRc = selectRectangle.SelectedRect();
                selectRectangle.Stop();
                g_RecordCropping = FALSE;

                copyX = copyRc.left;
                copyY = copyRc.top;
                copyWidth = copyRc.right - copyRc.left;
                copyHeight = copyRc.bottom - copyRc.top;
            }
            OutputDebug( L"***x: %d, y: %d, width: %d, height: %d\n", copyX, copyY, copyWidth, copyHeight );

            RECT oldClipRect{};
            GetClipCursor( &oldClipRect );
            ClipCursor( NULL );

            // Translate the viewport selection into coordinates for the 1:1 source
            // bitmap hdcScreenCompat.
            int viewportX, viewportY;
            GetZoomedTopLeftCoordinates(
                zoomLevel, &cursorPos, &viewportX, width, &viewportY, height );

            int saveX = viewportX + static_cast<int>( copyX / zoomLevel );
            int saveY = viewportY + static_cast<int>( copyY / zoomLevel );
            int saveWidth = static_cast<int>( copyWidth / zoomLevel );
            int saveHeight = static_cast<int>( copyHeight / zoomLevel );

            // Create a pixel-accurate copy of the desired area from the source bitmap.
            wil::unique_hdc hdcActualSize( CreateCompatibleDC( hdcScreen ) );
            wil::unique_hbitmap hbmActualSize(
                CreateCompatibleBitmap( hdcScreen, saveWidth, saveHeight ) );
            // Note: we do not need to restore the existing context later. The objects
            // are transient and not reused.
            SelectObject( hdcActualSize.get(), hbmActualSize.get() );

            // Perform a direct 1:1 copy from the backing bitmap.
            BitBlt( hdcActualSize.get(),
                    0, 0,
                    saveWidth, saveHeight,
                    hdcScreenCompat,
                    saveX, saveY,
                    SRCCOPY | CAPTUREBLT );

            // Open the Save As dialog and capture the desired file path and whether to
            // save the zoomed display or the source bitmap pixels.
            g_bSaveInProgress = true;

            // Get a unique filename suggestion
            auto suggestedName = GetUniqueScreenshotFilename();

            // Create modern IFileSaveDialog
            auto saveDialog = wil::CoCreateInstance<IFileSaveDialog>( CLSID_FileSaveDialog );

            FILEOPENDIALOGOPTIONS options;
            if( SUCCEEDED( saveDialog->GetOptions( &options ) ) )
                saveDialog->SetOptions( options | FOS_FORCEFILESYSTEM | FOS_OVERWRITEPROMPT );

            // Set file types - index is 1-based when retrieved via GetFileTypeIndex
            COMDLG_FILTERSPEC fileTypes[] = {
                { L"Zoomed PNG", L"*.png" },
                { L"Actual size PNG", L"*.png" }
            };
            saveDialog->SetFileTypes( _countof( fileTypes ), fileTypes );
            saveDialog->SetFileTypeIndex( 1 ); // Default to "Zoomed PNG"
            saveDialog->SetDefaultExtension( L"png" );
            saveDialog->SetFileName( suggestedName.c_str() );
            saveDialog->SetTitle( L"ZoomIt: Save Zoomed Screen..." );

            // Set default folder to the last save location if available
            if( !g_ScreenshotSaveLocation.empty() )
            {
                std::filesystem::path lastPath( g_ScreenshotSaveLocation );
                if( lastPath.has_parent_path() )
                {
                    wil::com_ptr<IShellItem> folderItem;
                    if( SUCCEEDED( SHCreateItemFromParsingName( lastPath.parent_path().c_str(),
                        nullptr, IID_PPV_ARGS( &folderItem ) ) ) )
                    {
                        saveDialog->SetFolder( folderItem.get() );
                    }
                }
            }

            OpenSaveDialogEvents* pEvents = new OpenSaveDialogEvents();
            DWORD dwCookie = 0;
            saveDialog->Advise(pEvents, &dwCookie);

            UINT selectedFilterIndex = 1;
            std::wstring selectedFilePath;

            if( SUCCEEDED( saveDialog->Show( hWnd ) ) )
            {
                wil::com_ptr<IShellItem> resultItem;
                if( SUCCEEDED( saveDialog->GetResult( &resultItem ) ) )
                {
                    wil::unique_cotaskmem_string pathStr;
                    if( SUCCEEDED( resultItem->GetDisplayName( SIGDN_FILESYSPATH, &pathStr ) ) )
                    {
                        selectedFilePath = pathStr.get();
                    }
                }
                saveDialog->GetFileTypeIndex( &selectedFilterIndex );
            }

            saveDialog->Unadvise(dwCookie);
            pEvents->Release();

            if( !selectedFilePath.empty() )
            {
                std::wstring targetFilePath = selectedFilePath;
                if( targetFilePath.find(L'.') == std::wstring::npos )
                {
                    targetFilePath += L".png";
                }

                if( selectedFilterIndex == 2 )
                {
                    // Save at actual size.
                    SavePng( targetFilePath.c_str(), hbmActualSize.get() );
                }
                else
                {
                    // Save zoomed-in image at screen resolution.
#if SCALE_HALFTONE
                    const int bltMode = HALFTONE;
#else
                    // Use HALFTONE for better quality when smooth image is enabled
                    const int bltMode = g_SmoothImage ? HALFTONE : COLORONCOLOR;
#endif
                    // Recreate the zoomed-in view by upscaling from our source bitmap.
                    wil::unique_hdc hdcZoomed( CreateCompatibleDC(hdcScreen) );
                    wil::unique_hbitmap hbmZoomed(
                        CreateCompatibleBitmap( hdcScreen, copyWidth, copyHeight ) );
                    SelectObject( hdcZoomed.get(), hbmZoomed.get() );

                    SetStretchBltMode( hdcZoomed.get(), bltMode );

                    StretchBlt( hdcZoomed.get(),
                                0, 0,
                                copyWidth, copyHeight,
                                hdcActualSize.get(),
                                0, 0,
                                saveWidth, saveHeight,
                                SRCCOPY | CAPTUREBLT );

                    SavePng(targetFilePath.c_str(), hbmZoomed.get());
                }

                // Remember the save location for next time and persist to registry
                g_ScreenshotSaveLocation = targetFilePath;
                wcsncpy_s(g_ScreenshotSaveLocationBuffer, g_ScreenshotSaveLocation.c_str(), _TRUNCATE);
                reg.WriteRegSettings(RegSettings);
            }
            g_bSaveInProgress = false;

            if( lParam != SHALLOW_ZOOM )
            {
                SetCursorPos( local_savedCursorPos.x, local_savedCursorPos.y );
            }
            ClipCursor( &oldClipRect );

            break;
        }

        case IDC_COPY_CROP:
        case IDC_COPY: {
            HBITMAP		hSaveBitmap;
            HDC			hSaveDc;
            int         copyX, copyY;
            int         copyWidth, copyHeight;

            if( LOWORD( wParam ) == IDC_COPY_CROP )
            {
                g_RecordCropping = TRUE;
                POINT local_savedCursorPos{};
                if( lParam != SHALLOW_ZOOM )
                {
                    GetCursorPos(&local_savedCursorPos);
                }
                SelectRectangle selectRectangle;
                if( !selectRectangle.Start( hWnd ) )
                {
                    g_RecordCropping = FALSE;
                    break;
                }
                auto copyRc = selectRectangle.SelectedRect();
                selectRectangle.Stop();
                if( lParam != SHALLOW_ZOOM )
                {
                    SetCursorPos(local_savedCursorPos.x, local_savedCursorPos.y);
                }
                g_RecordCropping = FALSE;

                copyX = copyRc.left;
                copyY = copyRc.top;
                copyWidth = copyRc.right - copyRc.left;
                copyHeight = copyRc.bottom - copyRc.top;
            }
            else
            {
                copyX = 0;
                copyY = 0;
                copyWidth = width;
                copyHeight = height;
            }
            OutputDebug( L"***x: %d, y: %d, width: %d, height: %d\n", copyX, copyY, copyWidth, copyHeight );

            hSaveBitmap = CreateCompatibleBitmap( hdcScreen, copyWidth, copyHeight );
            hSaveDc = CreateCompatibleDC( hdcScreen );
            SelectObject( hSaveDc, hSaveBitmap );
#if SCALE_HALFTONE
            SetStretchBltMode( hSaveDc, HALFTONE );
#else
            // Use HALFTONE for better quality when smooth image is enabled
            if (g_SmoothImage) {
                SetStretchBltMode( hSaveDc, HALFTONE );
            } else {
                SetStretchBltMode( hSaveDc, COLORONCOLOR );
            }
#endif
			StretchBlt( hSaveDc,
                        0, 0,
                        copyWidth, copyHeight,
                        hdcScreen,
                        monInfo.rcMonitor.left + copyX,
                        monInfo.rcMonitor.top + copyY,
                        copyWidth, copyHeight,
                        SRCCOPY|CAPTUREBLT );

            if( OpenClipboard( hWnd )) {

                EmptyClipboard();
                SetClipboardData( CF_BITMAP, hSaveBitmap );
                CloseClipboard();
            }

            DeleteDC( hSaveDc );
            }
            break;

        case IDC_COPY_OCR:
        {
            int         copyX, copyY;
            int         copyWidth, copyHeight;

            g_RecordCropping = TRUE;
            POINT local_savedCursorPos{};
            if( lParam != SHALLOW_ZOOM )
            {
                GetCursorPos( &local_savedCursorPos );
            }
            SelectRectangle selectRectangle;
            if( !selectRectangle.Start( hWnd ) )
            {
                g_RecordCropping = FALSE;
                break;
            }
            auto copyRc = selectRectangle.SelectedRect();
            selectRectangle.Stop();
            if( lParam != SHALLOW_ZOOM )
            {
                SetCursorPos( local_savedCursorPos.x, local_savedCursorPos.y );
            }
            g_RecordCropping = FALSE;

            copyX = copyRc.left;
            copyY = copyRc.top;
            copyWidth = copyRc.right - copyRc.left;
            copyHeight = copyRc.bottom - copyRc.top;

            if( copyWidth <= 0 || copyHeight <= 0 )
            {
                break;
            }

            // Capture the selected region into an HBITMAP
            HBITMAP hSaveBitmap = CreateCompatibleBitmap( hdcScreen, copyWidth, copyHeight );
            HDC hSaveDc = CreateCompatibleDC( hdcScreen );
            SelectObject( hSaveDc, hSaveBitmap );
            if( g_SmoothImage )
            {
                SetStretchBltMode( hSaveDc, HALFTONE );
            }
            else
            {
                SetStretchBltMode( hSaveDc, COLORONCOLOR );
            }
            StretchBlt( hSaveDc,
                        0, 0,
                        copyWidth, copyHeight,
                        hdcScreen,
                        monInfo.rcMonitor.left + copyX,
                        monInfo.rcMonitor.top + copyY,
                        copyWidth, copyHeight,
                        SRCCOPY | CAPTUREBLT );

            // Run OCR on the captured bitmap
            std::wstring ocrText = OcrFromHBITMAP( hSaveBitmap );

            // Copy OCR text to clipboard
            if( !ocrText.empty() && OpenClipboard( hWnd ) )
            {
                EmptyClipboard();
                size_t cbSize = ( ocrText.size() + 1 ) * sizeof( wchar_t );
                HGLOBAL hGlobal = GlobalAlloc( GMEM_MOVEABLE, cbSize );
                if( hGlobal )
                {
                    void* pMem = GlobalLock( hGlobal );
                    if( pMem )
                    {
                        memcpy( pMem, ocrText.c_str(), cbSize );
                        GlobalUnlock( hGlobal );
                        SetClipboardData( CF_UNICODETEXT, hGlobal );
                    }
                    else
                    {
                        GlobalFree( hGlobal );
                    }
                }
                CloseClipboard();
            }

            DeleteObject( hSaveBitmap );
            DeleteDC( hSaveDc );
            break;
        }

        case IDC_DRAW:
            PostMessage( hWnd, WM_HOTKEY, DRAW_HOTKEY, 1 );
            break;

        case IDC_ZOOM:
            PostMessage( hWnd, WM_HOTKEY, ZOOM_HOTKEY, 1 );
            break;

        case IDC_RECORD:
            PostMessage( hWnd, WM_HOTKEY, RECORD_HOTKEY, 1 );
            break;

        case IDC_OPTIONS:
            // Don't show win32 forms options if started by PowerToys.
            // Show the PowerToys Settings application instead.

            if( g_StartedByPowerToys )
            {
#ifdef __ZOOMIT_POWERTOYS__
                OpenPowerToysSettingsApp();
#endif // __ZOOMIT_POWERTOYS__
            }
            else
            {
                DialogBox( g_hInstance, L"OPTIONS", hWnd, OptionsProc );
            }
            break;

        case IDC_BREAK:
        {
            // Manage handles, clean visual transitions, and Options delta
            if( g_TimerActive )
            {
                if( activeBreakShowBackgroundFile != g_BreakShowBackgroundFile ||
                    activeBreakShowDesktop != g_BreakShowDesktop )
                {
                    if( g_BreakShowBackgroundFile && !g_BreakShowDesktop )
                    {
                        SendMessage( hWnd, WM_HOTKEY, ZOOM_HOTKEY, SHALLOW_DESTROY );
                    }
                    else
                    {
                        SendMessage( hWnd, WM_HOTKEY, ZOOM_HOTKEY, 0 );
                    }
                }
                else
                {
                    SendMessage( hWnd, WM_HOTKEY, ZOOM_HOTKEY, SHALLOW_DESTROY );
                    g_TimerActive = TRUE;
                }
            }

            hdcScreen = CreateDC( L"DISPLAY", static_cast<PTCHAR>(NULL), static_cast<PTCHAR>(NULL), static_cast<CONST DEVMODE*>(NULL) );

            // toggle second monitor
            // FIX: we should save whether or not we've switched to a second monitor
            // rather than just assume that the setting hasn't changed since the break timer
            // became active
            if( g_BreakOnSecondary )
            {
                EnableDisableSecondaryDisplay( hWnd, TRUE, &secondaryDevMode );
            }

            // Determine what monitor we're on
            GetCursorPos( &cursorPos );
            UpdateMonitorInfo( cursorPos, &monInfo );
            width = monInfo.rcMonitor.right - monInfo.rcMonitor.left;
            height = monInfo.rcMonitor.bottom - monInfo.rcMonitor.top;

            // Trigger desktop recapture as necessary when switching monitors
            if( g_TimerActive && g_BreakShowDesktop && lastMonInfo.rcMonitor != monInfo.rcMonitor )
            {
                lastMonInfo = monInfo;
                SendMessage( hWnd, WM_HOTKEY, ZOOM_HOTKEY, 0 );
                PostMessage( hWnd, WM_COMMAND, IDC_BREAK, 0 );
                break;
            }
            lastMonInfo = monInfo;

            // If the background is a file that hasn't been collected, grab it now
            if( g_BreakShowBackgroundFile && !g_BreakShowDesktop &&
                ( !g_TimerActive || wcscmp( activeBreakBackgroundFile, g_BreakBackgroundFile ) ) )
            {
                _tcscpy( activeBreakBackgroundFile, g_BreakBackgroundFile );

                DeleteObject( g_hBackgroundBmp );
                DeleteDC( g_hDcBackgroundFile );

                g_hBackgroundBmp = NULL;
                g_hBackgroundBmp = LoadImageFile( g_BreakBackgroundFile );
                if( g_hBackgroundBmp == NULL )
                {
                    // Clean up hanging handles
                    SendMessage( hWnd, WM_HOTKEY, ZOOM_HOTKEY, 0 );
                    ErrorDialog( hWnd, L"Error loading background bitmap", GetLastError() );
                    break;
                }
                g_hDcBackgroundFile = CreateCompatibleDC( hdcScreen );
                SelectObject( g_hDcBackgroundFile, g_hBackgroundBmp );
            }
            // If the background is a desktop that hasn't been collected, grab it now
            else if( g_BreakShowBackgroundFile && g_BreakShowDesktop && !g_TimerActive )
            {
                g_hBackgroundBmp = CreateFadedDesktopBackground( GetDC(NULL), & monInfo.rcMonitor, NULL );
                g_hDcBackgroundFile = CreateCompatibleDC( hdcScreen );
                SelectObject( g_hDcBackgroundFile, g_hBackgroundBmp );
            }

            // Track Options.Break delta
            activeBreakShowBackgroundFile = g_BreakShowBackgroundFile;
            activeBreakShowDesktop = g_BreakShowDesktop;

#ifdef __ZOOMIT_POWERTOYS__
            if( g_StartedByPowerToys )
            {
                Trace::ZoomItActivateBreak();
            }
#endif // __ZOOMIT_POWERTOYS__

            breakTimeout = g_BreakTimeout * 60 + 1;

            //
            // If lock workstation is enabled, skip the local break timer
            // window entirely — the embedded screensaver will run it on
            // the ScreenSaver Desktop with authentication required.
            //
            if( g_BreakLockWorkstation )
            {
                DeleteDC( hdcScreen );
                ActivateBreakScreenSaver( hWnd, breakTimeout );
                break;
            }

            g_TimerActive = TRUE;

            // Create font
            g_LogFont.lfHeight = height / 5;
            hTimerFont = CreateFontIndirect( &g_LogFont );
            g_LogFont.lfHeight = height / 8;
            hNegativeTimerFont = CreateFontIndirect( &g_LogFont );

            // Create backing bitmap
            hdcScreenCompat = CreateCompatibleDC(hdcScreen);
            bmp.bmBitsPixel = static_cast<BYTE>(GetDeviceCaps(hdcScreen, BITSPIXEL));
            bmp.bmPlanes = static_cast<BYTE>(GetDeviceCaps(hdcScreen, PLANES));
            bmp.bmWidth = width;
            bmp.bmHeight = height;
            bmp.bmWidthBytes = ((bmp.bmWidth + 15) &~15)/8;
            hbmpCompat = CreateBitmap(bmp.bmWidth, bmp.bmHeight,
                bmp.bmPlanes, bmp.bmBitsPixel, static_cast<CONST VOID *>(NULL));
             SelectObject(hdcScreenCompat, hbmpCompat);

            SetTextColor( hdcScreenCompat, g_BreakPenColor );
            SetBkMode( hdcScreenCompat, TRANSPARENT );
            SelectObject( hdcScreenCompat, hTimerFont );

            EnableDisableOpacity( hWnd, TRUE );
            EnableDisableScreenSaver( FALSE );

            SendMessage( hWnd, WM_TIMER, 0, 0 );
            SetTimer( hWnd, 0, 1000, NULL );

            BringWindowToTop( hWnd );
            SetForegroundWindow( hWnd );
            SetActiveWindow( hWnd );
            SetWindowPos( hWnd, HWND_NOTOPMOST, monInfo.rcMonitor.left, monInfo.rcMonitor.top,
                    width, height, SWP_SHOWWINDOW );
        }
        break;

        case IDCANCEL:

            memset( &tNotifyIconData, 0, sizeof(tNotifyIconData));
            tNotifyIconData.cbSize = sizeof(NOTIFYICONDATA);
            tNotifyIconData.hWnd = hWnd;
            tNotifyIconData.uID = 1;
            Shell_NotifyIcon(NIM_DELETE, &tNotifyIconData);
            reg.WriteRegSettings( RegSettings );

            if( hWndOptions )
            {
                DestroyWindow( hWndOptions );
            }
            DestroyWindow( hWnd );
            break;
        }
        break;

    case WM_TIMER:
        switch( wParam ) {
        case 0:
            //
            // Break timer
            //
            breakTimeout -= 1;
            InvalidateRect( hWnd, NULL, FALSE );
            if( breakTimeout == 0 && g_BreakPlaySoundFile ) {

                PlaySound( g_BreakSoundFile, NULL, SND_FILENAME|SND_ASYNC );
            }
            break;

        case 2:
        case 1:
            doTelescopingZoomTimer();
            break;

        case 3:
            POINT mousePos;
            GetCursorPos(&mousePos);
            if (mousePos.x != cursorPos.x || mousePos.y != cursorPos.y)
            {
                MONITORINFO monitorInfo = { sizeof(MONITORINFO) };
                UpdateMonitorInfo(mousePos, &monitorInfo);

                mousePos.x -= monitorInfo.rcMonitor.left;
                mousePos.y -= monitorInfo.rcMonitor.top;

                OutputDebug(L"RETRACKING MOUSE: x: %d y: %d\n", mousePos.x, mousePos.y);
                SendMessage(hWnd, WM_MOUSEMOVE, 0, MAKELPARAM(mousePos.x, mousePos.y));
            }
            break;

        case 4:
            // Break screensaver post-cleanup watchdog.
            if( HasOrphanedScreenSaverSettings() )
            {
                if( !IsBreakScreenSaverRunning() )
                {
                    RestoreScreenSaverSettings();
                    KillTimer( hWnd, 4 );
                }
            }
            else
            {
                KillTimer( hWnd, 4 );
            }
            break;

        case 5:
        {
            //
            // Deferred SPI re-apply after session unlock.
            // Winlogon may override our SPI_SETSCREENSAVEACTIVE /
            // SPI_SETSCREENSAVETIMEOUT calls during the desktop switch.
            // Re-read the (already-restored) registry values and push
            // them into the runtime SPI state.
            //
            KillTimer( hWnd, 5 );

            TCHAR regActive[16] = {};
            DWORD cbActive = sizeof( regActive );
            BOOL rtActive = FALSE;
            if( RegGetValue( HKEY_CURRENT_USER, L"Control Panel\\Desktop",
                             L"ScreenSaveActive", RRF_RT_REG_SZ, NULL,
                             regActive, &cbActive ) == ERROR_SUCCESS )
            {
                rtActive = ( wcstol( regActive, nullptr, 10 ) != 0 );
            }
            SystemParametersInfo( SPI_SETSCREENSAVEACTIVE, rtActive, 0, SPIF_SENDCHANGE );

            TCHAR regTimeout[16] = {};
            DWORD cbTimeout = sizeof( regTimeout );
            UINT timeout = 300;  // safe default
            if( RegGetValue( HKEY_CURRENT_USER, L"Control Panel\\Desktop",
                             L"ScreenSaveTimeOut", RRF_RT_REG_SZ, NULL,
                             regTimeout, &cbTimeout ) == ERROR_SUCCESS )
            {
                long parsed = wcstol( regTimeout, nullptr, 10 );
                if( parsed > 0 )
                    timeout = static_cast<UINT>( parsed );
            }
            SystemParametersInfo( SPI_SETSCREENSAVETIMEOUT, timeout, 0, SPIF_SENDCHANGE );
            break;
        }
        }
        break;

    case WM_PAINT:

        hDc = BeginPaint(hWnd, &ps);

        if( ( ( g_RecordCropping == FALSE ) || ( zoomLevel == 1 ) ) && g_Zoomed ) {

            OutputDebug( L"PAINT x: %d y: %d width: %d height: %d zoomLevel: %g\n",
                    cursorPos.x, cursorPos.y, width, height, zoomLevel );
            GetZoomedTopLeftCoordinates( zoomLevel, &cursorPos, &x, width, &y, height );
#if SCALE_GDIPLUS
            if ( zoomLevel >= zoomTelescopeTarget )  {
                // do a high-quality render
                extern void ScaleImage( HDC hdcDst, float xDst, float yDst, float wDst, float hDst,
                                        HBITMAP bmSrc, float xSrc, float ySrc, float wSrc, float hSrc );

                ScaleImage( ps.hdc,
                            0, 0,
                            (float)bmp.bmWidth, (float)bmp.bmHeight,
                            hbmpCompat,
                            (float)x, (float)y,
                            width/zoomLevel, height/zoomLevel );
            } else {
                // do a fast, less accurate render (but use smooth if enabled)
                SetStretchBltMode( hDc, g_SmoothImage ? HALFTONE : COLORONCOLOR );
                StretchBlt( ps.hdc,
                        0, 0,
                        bmp.bmWidth, bmp.bmHeight,
                        hdcScreenCompat,
                        x, y,
                        (int) (width/zoomLevel), (int) (height/zoomLevel),
                        SRCCOPY);
            }
#else
#if SCALE_HALFTONE
            SetStretchBltMode( hDc, zoomLevel == zoomTelescopeTarget ? HALFTONE : COLORONCOLOR );
#else
            // Use HALFTONE for better quality when smooth image is enabled
            if (g_SmoothImage) {
                SetStretchBltMode( hDc, HALFTONE );
            } else {
                SetStretchBltMode( hDc, COLORONCOLOR );
            }
#endif
            StretchBlt( ps.hdc,
                    0, 0,
                    bmp.bmWidth, bmp.bmHeight,
                    hdcScreenCompat,
                    x, y,
                    static_cast<int>(width/zoomLevel), static_cast<int>(height/zoomLevel),
                    SRCCOPY|CAPTUREBLT );
#endif
        } else if( g_TimerActive ) {

            // Fill background (white by default, black if saved as 1)
            rc.top = rc.left = 0;
            rc.bottom = height;
            rc.right = width;
            BlankScreenArea( hdcScreenCompat, &rc, g_BreakBackgroundColor ? 'K' : 'W' );

            // If there's a background bitmap, draw it in the center
            if( g_hBackgroundBmp ) {

                BITMAP local_bmp;
                GetObject(g_hBackgroundBmp, sizeof(local_bmp), &local_bmp);
                SetStretchBltMode( hdcScreenCompat, g_SmoothImage ? HALFTONE : COLORONCOLOR );
                if( g_BreakBackgroundStretch ) {
                    StretchBlt( hdcScreenCompat, 0, 0, width, height,
                        g_hDcBackgroundFile, 0, 0, local_bmp.bmWidth, local_bmp.bmHeight, SRCCOPY|CAPTUREBLT  );
                } else {
                    BitBlt( hdcScreenCompat, width/2 - local_bmp.bmWidth/2, height/2 - local_bmp.bmHeight/2,
                        local_bmp.bmWidth, local_bmp.bmHeight, g_hDcBackgroundFile, 0, 0, SRCCOPY|CAPTUREBLT  );
                }
            }

            // Draw time
            if( breakTimeout > 0 ) {

                _stprintf( timerText, L"% 2d:%02d", breakTimeout/60, breakTimeout % 60 );

            } else {

                _tcscpy( timerText, L"0:00" );
            }
            rc.left = rc.top = 0;
            DrawText( hdcScreenCompat, timerText, -1, &rc,
                DT_NOCLIP|DT_LEFT|DT_NOPREFIX|DT_CALCRECT );

            rc1.left = rc1.right = rc1.bottom = rc1.top = 0;
            if( g_ShowExpiredTime && breakTimeout < 0 ) {

                _stprintf( negativeTimerText, L"(-% 2d:%02d)",
                        -breakTimeout/60, -breakTimeout % 60 );
                HFONT prevFont = static_cast<HFONT>(SelectObject( hdcScreenCompat, hNegativeTimerFont ));
                DrawText( hdcScreenCompat, negativeTimerText, -1, &rc1,
                    DT_NOCLIP|DT_LEFT|DT_NOPREFIX|DT_CALCRECT );
                SelectObject( hdcScreenCompat, prevFont );
            }

            // Position time vertically
            switch( g_BreakTimerPosition ) {
            case 0:
            case 1:
            case 2:
                rc.top = 50;
                break;
            case 3:
            case 4:
            case 5:
                rc.top = (height - (rc.bottom - rc.top))/2;
                break;
            case 6:
            case 7:
            case 8:
                rc.top = height - rc.bottom - 50 - rc1.bottom;
                break;
            }

            // Position time horizontally
            switch( g_BreakTimerPosition ) {
            case 0:
            case 3:
            case 6:
                rc.left = 50;
                break;
            case 1:
            case 4:
            case 7:
                rc.left = (width - (rc.right - rc.left))/2;
                break;
            case 2:
            case 5:
            case 8:
                rc.left = width - rc.right - 50;
                break;
            }
            rc.bottom += rc.top;
            rc.right += rc.left;

            DrawText( hdcScreenCompat, timerText, -1, &rc, DT_NOCLIP|DT_LEFT|DT_NOPREFIX );

            if( g_ShowExpiredTime && breakTimeout < 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( hdcScreenCompat, hNegativeTimerFont ));
                DrawText( hdcScreenCompat, negativeTimerText, -1, &rc1,
                    DT_NOCLIP|DT_LEFT|DT_NOPREFIX );
                SelectObject( hdcScreenCompat, prevFont );
            }

            // Copy to screen
            BitBlt( ps.hdc, 0, 0, width, height, hdcScreenCompat, 0, 0, SRCCOPY|CAPTUREBLT  );
        }
        EndPaint(hWnd, &ps);
        return TRUE;

    case WM_WTSSESSION_CHANGE:
        //
        // When the user unlocks the workstation after a break timer screensaver
        // session, restore the original screensaver settings so the break timer
        // doesn't reactivate.
        //
        if( wParam == WTS_SESSION_UNLOCK ||
            wParam == WTS_SESSION_LOGON ||
            wParam == WTS_CONSOLE_CONNECT )
        {
            RestoreScreenSaverSettings();

            //
            // Schedule a deferred SPI re-apply.  Winlogon may re-read the
            // (now-stale) screensaver policy during the desktop switch,
            // overriding the SPI_SETSCREENSAVEACTIVE / TIMEOUT calls we
            // just made.  Timer 5 fires after the transition completes
            // and re-asserts the correct runtime state.
            //
            SetTimer( hWnd, 5, 5000, NULL );
        }
        break;

    case WM_DESTROY:
        // Restore screensaver settings on clean shutdown in case the
        // break screensaver was still active.
        if( HasOrphanedScreenSaverSettings() )
            RestoreScreenSaverSettings();
        WTSUnRegisterSessionNotification( hWnd );
        CleanupDarkModeResources();
        PostQuitMessage( 0 );
        break;

    default:
        if( message == wmTaskbarCreated )
        {
            if( g_ShowTrayIcon )
            {
                EnableDisableTrayIcon( hWnd, TRUE );
            }
            return TRUE;
        }
       return DefWindowProc(hWnd, message, wParam, lParam );
    }
    return 0;
}


//----------------------------------------------------------------------------
//
// LiveZoomWndProc
//
//----------------------------------------------------------------------------
LRESULT CALLBACK LiveZoomWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    RECT		rc;
    POINT		cursorPos;
    static int	width, height;
    static MONITORINFO	monInfo;
    HDC			hdcScreen;
#if 0
    int			delta;
    BOOLEAN		zoomIn;
#endif
    static POINT	lastCursorPos;
    POINT			adjustedCursorPos, zoomCenterPos;
    int				moveWidth, moveHeight;
    int				sourceRectHeight, sourceRectWidth;
    DWORD			curTickCount;
    RECT			sourceRect{};
    static RECT		lastSourceRect;
    static float	zoomLevel;
    static float	zoomTelescopeStep;
    static float	zoomTelescopeTarget;
    static DWORD	prevZoomStepTickCount = 0;
    static BOOL		dwmEnabled = FALSE;
    static BOOLEAN	startedInPresentationMode = FALSE;
    MAGTRANSFORM matrix;

    switch (message)  {
    case WM_CREATE:

        // Initialize
        pMagInitialize();
        if (pDwmIsCompositionEnabled) pDwmIsCompositionEnabled(&dwmEnabled);

        // Create the zoom window
        if( !g_fullScreenWorkaround ) {

            g_hWndLiveZoomMag = CreateWindowEx( 0,
                                        WC_MAGNIFIER,
                                        TEXT("MagnifierWindow"),
                                        WS_CHILD | MS_SHOWMAGNIFIEDCURSOR | WS_VISIBLE,
                                        0, 0, 0, 0, hWnd, NULL, g_hInstance, NULL );
        }
        ShowWindow( hWnd, SW_SHOW );
        InvalidateRect( g_hWndLiveZoomMag, NULL, TRUE );

        if( !g_fullScreenWorkaround )
            SetForegroundWindow(static_cast<HWND>(reinterpret_cast<LPCREATESTRUCT>(lParam)->lpCreateParams));

        // If we're not on Win7+, then set a timer to go off two hours from
        // now
        if( g_OsVersion < WIN7_VERSION ) {

            startedInPresentationMode = IsPresentationMode();
            // if we're not in presentation mode, kill ourselves after a timeout
            if( !startedInPresentationMode ) {

                SetTimer( hWnd, 1, LIVEZOOM_WINDOW_TIMEOUT, NULL );
            }
        }
        break;

    case WM_SHOWWINDOW:
        if( wParam == TRUE ) {

            // Determine what monitor we're on
            lastCursorPos.x = -1;
            hdcScreen	= GetDC( NULL );
            GetCursorPos( &cursorPos );
            UpdateMonitorInfo( cursorPos, &monInfo );
            width = monInfo.rcMonitor.right - monInfo.rcMonitor.left;
            height = monInfo.rcMonitor.bottom - monInfo.rcMonitor.top;
            lastSourceRect.left = lastSourceRect.right = 0;
            lastSourceRect.right = width;
            lastSourceRect.bottom = height;

            // Set window size
            if( !g_fullScreenWorkaround ) {

                SetWindowPos( hWnd, NULL, monInfo.rcMonitor.left, monInfo.rcMonitor.top,
                    monInfo.rcMonitor.right - monInfo.rcMonitor.left,
                    monInfo.rcMonitor.bottom - monInfo.rcMonitor.top,
                    SWP_NOACTIVATE | SWP_NOZORDER );
                UpdateWindow(hWnd);
            }

            // Are we coming back from a static zoom that
            // was started while we were live zoomed?
            if( g_ZoomOnLiveZoom ) {

                // Force a zoom to 2x without telescope
                prevZoomStepTickCount = 0;
                zoomLevel = static_cast<float>(1.9);
                zoomTelescopeTarget = 2.0;
                zoomTelescopeStep = 2.0;

            } else {

                zoomTelescopeStep = ZOOM_LEVEL_STEP_IN;
                zoomTelescopeTarget = g_ZoomLevels[g_SliderZoomLevel];

                prevZoomStepTickCount = 0;
                if( dwmEnabled ) {

                    zoomLevel = static_cast<float>(1);

                } else {

                    zoomLevel = static_cast<float>(1.9);
                }
            }
            RegisterHotKey( hWnd, 0, MOD_CONTROL, VK_UP );
            RegisterHotKey( hWnd, 1, MOD_CONTROL, VK_DOWN );

            // Hide hardware cursor
            if( !g_fullScreenWorkaround )
                if( pMagShowSystemCursor ) pMagShowSystemCursor( FALSE );

            if( g_RecordToggle )
                g_RecordingSession->EnableCursorCapture( false );

            GetCursorPos( &lastCursorPos );
            SetCursorPos( lastCursorPos.x, lastCursorPos.y );

            SendMessage( hWnd, WM_TIMER, 0, 0);
            SetTimer( hWnd, 0, ZOOM_LEVEL_STEP_TIME, NULL );

        } else {

            KillTimer( hWnd, 0 );

            if( g_RecordToggle )
                g_RecordingSession->EnableCursorCapture();

            if( !g_fullScreenWorkaround )
                if( pMagShowSystemCursor ) pMagShowSystemCursor( TRUE );

            // Reset the timer to expire two hours from now
            if( g_OsVersion < WIN7_VERSION && !IsPresentationMode()) {

                KillTimer( hWnd, 1 );
                SetTimer( hWnd, 1, LIVEZOOM_WINDOW_TIMEOUT, NULL );
            } else {

                DestroyWindow( hWnd );
            }
            UnregisterHotKey( hWnd, 0 );
            UnregisterHotKey( hWnd, 1 );
        }
        break;

    case WM_TIMER:
        switch( wParam ) {
        case 0: {
            // if we're cropping, do not move
            if( g_RecordCropping == TRUE )
            {
                // Still redraw to keep the contents live
                InvalidateRect( g_hWndLiveZoomMag, nullptr, TRUE );
                break;
            }

            GetCursorPos(&cursorPos);

            // Reclaim topmost status, to prevent unmagnified menus from remaining in view.
            memset(&matrix, 0, sizeof(matrix));
            if( !g_fullScreenWorkaround ) {

                pSetLayeredWindowAttributes( hWnd, 0, 255, LWA_ALPHA );
                SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0,
                    SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);

                OutputDebug(L"LIVEZOOM RECLAIM\n");
            }

            sourceRectWidth = lastSourceRect.right - lastSourceRect.left;
            sourceRectHeight = lastSourceRect.bottom - lastSourceRect.top;
            moveWidth = sourceRectWidth/LIVEZOOM_MOVE_REGIONS;
            moveHeight = sourceRectHeight/LIVEZOOM_MOVE_REGIONS;
            curTickCount = GetTickCount();
            if( zoomLevel != zoomTelescopeTarget &&
                (prevZoomStepTickCount == 0 || (curTickCount - prevZoomStepTickCount > ZOOM_LEVEL_STEP_TIME)) ) {

                prevZoomStepTickCount = curTickCount;
                if( (zoomTelescopeStep > 1 && zoomLevel*zoomTelescopeStep >= zoomTelescopeTarget ) ||
                    (zoomTelescopeStep < 1 && zoomLevel*zoomTelescopeStep <= zoomTelescopeTarget )) {

                    zoomLevel = zoomTelescopeTarget;

                } else {

                    zoomLevel *= zoomTelescopeStep;
                }
                // Time to exit zoom mode?
                if( zoomTelescopeTarget == 1 && zoomLevel == 1 ) {

#if WINDOWS_CURSOR_RECORDING_WORKAROUND
                    if( g_RecordToggle )
                        g_LiveZoomLevelOne = true;
                    else
#endif
                    ShowWindow( hWnd, SW_HIDE );

                } else {

                    matrix.v[0][0] = zoomLevel;
                    matrix.v[0][2] = (static_cast<float>(-lastSourceRect.left) * zoomLevel);
                    matrix.v[1][1] = zoomLevel;
                    matrix.v[1][2] = (static_cast<float>(-lastSourceRect.top) * zoomLevel );
                    matrix.v[2][2] = 1.0f;
                }

                //
                // Pre-adjust for monitor boundary
                //
                adjustedCursorPos.x = cursorPos.x - monInfo.rcMonitor.left;
                adjustedCursorPos.y = cursorPos.y - monInfo.rcMonitor.top;
                GetZoomedTopLeftCoordinates( zoomLevel, &adjustedCursorPos, reinterpret_cast<int *>(&zoomCenterPos.x), width,
                                reinterpret_cast<int *>(&zoomCenterPos.y), height );

                //
                // Add back monitor boundary
                //
                zoomCenterPos.x += monInfo.rcMonitor.left + static_cast<LONG>(width/zoomLevel/2);
                zoomCenterPos.y += monInfo.rcMonitor.top + static_cast<LONG>(height/zoomLevel/2);

            } else {

                int xOffset = cursorPos.x - lastSourceRect.left;
                int yOffset = cursorPos.y - lastSourceRect.top;
                zoomCenterPos.x = 0;
                zoomCenterPos.y = 0;
                if( xOffset < moveWidth )
                    zoomCenterPos.x = lastSourceRect.left + sourceRectWidth/2 - (moveWidth - xOffset);
                else if( xOffset > moveWidth * (LIVEZOOM_MOVE_REGIONS-1) )
                    zoomCenterPos.x = lastSourceRect.left + sourceRectWidth/2 + (xOffset - moveWidth*(LIVEZOOM_MOVE_REGIONS-1));
                if( yOffset < moveHeight )
                    zoomCenterPos.y = lastSourceRect.top + sourceRectHeight/2 - (moveHeight - yOffset);
                else if( yOffset > moveHeight * (LIVEZOOM_MOVE_REGIONS-1) )
                    zoomCenterPos.y = lastSourceRect.top + sourceRectHeight/2 + (yOffset - moveHeight*(LIVEZOOM_MOVE_REGIONS-1));
            }
            if( matrix.v[0][0] || zoomCenterPos.x || zoomCenterPos.y ) {

                if( zoomCenterPos.y == 0 )
                    zoomCenterPos.y = lastSourceRect.top + sourceRectHeight/2;
                if( zoomCenterPos.x == 0 )
                    zoomCenterPos.x = lastSourceRect.left + sourceRectWidth/2;

                int zoomWidth = static_cast<int>(width / zoomLevel);
                int zoomHeight = static_cast<int>(height/ zoomLevel);
                sourceRect.left = zoomCenterPos.x - zoomWidth / 2;
                sourceRect.top = zoomCenterPos.y -  zoomHeight / 2;

                // Don't scroll outside desktop area.
                if (sourceRect.left < monInfo.rcMonitor.left)
                    sourceRect.left = monInfo.rcMonitor.left;
                else if (sourceRect.left > monInfo.rcMonitor.right - zoomWidth )
                    sourceRect.left = monInfo.rcMonitor.right - zoomWidth;
                sourceRect.right = sourceRect.left + zoomWidth;
                if (sourceRect.top < monInfo.rcMonitor.top)
                    sourceRect.top = monInfo.rcMonitor.top;
                else if (sourceRect.top > monInfo.rcMonitor.bottom - zoomHeight)
                    sourceRect.top = monInfo.rcMonitor.bottom - zoomHeight;
                sourceRect.bottom = sourceRect.top + zoomHeight;

                if( g_ZoomOnLiveZoom ) {

                    matrix.v[0][0] = static_cast<float>(1.0);
                    matrix.v[0][2] = (static_cast<float>(-monInfo.rcMonitor.left));

                    matrix.v[1][1] = static_cast<float>(1.0);
                    matrix.v[1][2] = (static_cast<float>(-monInfo.rcMonitor.top));

                    matrix.v[2][2] = 1.0f;

                } else if( lastSourceRect.left != sourceRect.left ||
                    lastSourceRect.top  != sourceRect.top ) {

                    matrix.v[0][0] = zoomLevel;
                    matrix.v[0][2] = (static_cast<float>(-sourceRect.left) * zoomLevel);

                    matrix.v[1][1] = zoomLevel;
                    matrix.v[1][2] = (static_cast<float>(-sourceRect.top) * zoomLevel);

                    matrix.v[2][2] = 1.0f;
                }
                lastSourceRect = sourceRect;
            }
            lastCursorPos = cursorPos;

            // Update source and zoom if necessary
            if( matrix.v[0][0] ) {

                OutputDebug(L"LIVEZOOM update\n");
                if( g_fullScreenWorkaround ) {

                    pMagSetFullscreenTransform(zoomLevel, sourceRect.left, sourceRect.top);
                    pMagSetInputTransform(TRUE, &sourceRect, &monInfo.rcMonitor);
                }
                else {

                    pMagSetWindowTransform(g_hWndLiveZoomMag, &matrix);
                }
            }

            if( !g_fullScreenWorkaround ) {

                // Force redraw to refresh screen contents
                InvalidateRect(g_hWndLiveZoomMag, NULL, TRUE);
            }

            // are we done zooming?
            if( zoomLevel == 1 ) {

#if WINDOWS_CURSOR_RECORDING_WORKAROUND
                if( g_RecordToggle ) {

                    g_LiveZoomLevelOne = true;
                }
                else {

#endif
                if( g_OsVersion < WIN7_VERSION ) {

                    ShowWindow( hWnd, SW_HIDE );

                } else {

                    DestroyWindow( hWnd );
                }
            }
#if WINDOWS_CURSOR_RECORDING_WORKAROUND
            }
#endif
            }
            break;
        case 1: {

            if( !IsWindowVisible( hWnd )) {

                // This is the cached window timeout. If not in presentation mode,
                // time to exit
                if( !IsPresentationMode()) {

                    DestroyWindow( hWnd );
                }
            }
            }
            break;
        }
        break;

    case WM_SETTINGCHANGE:
        if( g_OsVersion < WIN7_VERSION ) {

            if( startedInPresentationMode && !IsPresentationMode()) {

                // Existing presentation mode
                DestroyWindow( hWnd );

            } else if( !startedInPresentationMode && IsPresentationMode()) {

                // Kill the timer if one was configured, because now
                // we're going to go away when they exit presentation mode
                KillTimer( hWnd, 1 );
            }
        }
        break;

    case WM_HOTKEY: {
        float newZoomLevel = zoomLevel;
        switch( wParam ) {
        case 0:
            // zoom in
            if( newZoomLevel < ZOOM_LEVEL_MAX )
                newZoomLevel *= 2;
            zoomTelescopeStep = ZOOM_LEVEL_STEP_IN;
            break;

        case 1:
            if( newZoomLevel > 2 )
                newZoomLevel /= 2;
            else {

                newZoomLevel *= .75;
                if( newZoomLevel < ZOOM_LEVEL_MIN )
                    newZoomLevel = ZOOM_LEVEL_MIN;
            }
            zoomTelescopeStep = ZOOM_LEVEL_STEP_OUT;
            break;
        }
        zoomTelescopeTarget = newZoomLevel;
        if( !dwmEnabled ) {

            zoomLevel = newZoomLevel;
        }
        }
        break;

    // NOTE: keyboard and mouse input actually don't get sent to us at all when in live zoom mode
    case WM_KEYDOWN:
        switch( wParam ) {
        case VK_ESCAPE:
            zoomTelescopeStep = ZOOM_LEVEL_STEP_OUT;
            zoomTelescopeTarget = 1.0;
            if( !dwmEnabled ) {

                zoomLevel = static_cast<float>(1.1);
            }
            break;

        case VK_UP:
            SendMessage( hWnd, WM_MOUSEWHEEL,
                MAKEWPARAM( GetAsyncKeyState( VK_LCONTROL ) != 0 ? MK_CONTROL: 0, WHEEL_DELTA), 0 );
            return TRUE;

        case VK_DOWN:
            SendMessage( hWnd, WM_MOUSEWHEEL,
                MAKEWPARAM( GetAsyncKeyState( VK_LCONTROL ) != 0 ? MK_CONTROL: 0, -WHEEL_DELTA), 0 );
            return TRUE;
        }
        break;
    case WM_DESTROY:
        g_hWndLiveZoom = NULL;
        break;

    case WM_SIZE:
        GetClientRect(hWnd, &rc);
        SetWindowPos(g_hWndLiveZoomMag, NULL,
            rc.left, rc.top, rc.right, rc.bottom, 0 );
        break;

    case WM_USER_GET_ZOOM_LEVEL:
        return reinterpret_cast<LRESULT>(&zoomLevel);

    case WM_USER_GET_SOURCE_RECT:
        return reinterpret_cast<LRESULT>(&lastSourceRect);

    case WM_USER_MAGNIFY_CURSOR:
        {
            auto style = GetWindowLong( g_hWndLiveZoomMag, GWL_STYLE );
            if( wParam == TRUE )
            {
                style |= MS_SHOWMAGNIFIEDCURSOR;
            }
            else
            {
                style &= ~MS_SHOWMAGNIFIEDCURSOR;
            }
            SetWindowLong( g_hWndLiveZoomMag, GWL_STYLE, style );
            InvalidateRect( g_hWndLiveZoomMag, nullptr, TRUE );
            RedrawWindow( hWnd, nullptr, nullptr, RDW_ALLCHILDREN | RDW_UPDATENOW );
        }
        break;

    case WM_USER_SET_ZOOM:
        {
            if( g_RecordToggle )
            {
                g_SelectRectangle.UpdateOwner( hWnd );
            }

            if( lParam != NULL ) {

                lastSourceRect = *reinterpret_cast<RECT *>(lParam);
            }
#if WINDOWS_CURSOR_RECORDING_WORKAROUND
            if( g_LiveZoomLevelOne ) {

                g_LiveZoomLevelOne = FALSE;

                zoomTelescopeTarget = static_cast<float>(wParam);
                zoomTelescopeStep = ZOOM_LEVEL_STEP_IN;
                prevZoomStepTickCount = 0;
                zoomLevel = 1.0;

                break;
            }
#endif
            zoomLevel = static_cast<float>(wParam);
            zoomTelescopeTarget = zoomLevel;
            matrix.v[0][0] = zoomLevel;
            matrix.v[0][2] = (static_cast<float>(-lastSourceRect.left) * static_cast<float>(wParam));

            matrix.v[1][1] = zoomLevel;
            matrix.v[1][2] = (static_cast<float>(-lastSourceRect.top) * static_cast<float>(wParam));

            matrix.v[2][2] = 1.0f;

            if( g_fullScreenWorkaround ) {

                pMagSetFullscreenTransform(zoomLevel, lastSourceRect.left, lastSourceRect.top);
                pMagSetInputTransform(TRUE, &lastSourceRect, &monInfo.rcMonitor);
            }
            else {

                pMagSetWindowTransform(g_hWndLiveZoomMag, &matrix);
            }
        }
        break;

    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}


//----------------------------------------------------------------------------
//
// Wrapper functions for explicit linking to d3d11.dll
//
//----------------------------------------------------------------------------

HRESULT __stdcall WrapCreateDirect3D11DeviceFromDXGIDevice(
    IDXGIDevice		*dxgiDevice,
    IInspectable	**graphicsDevice)
{
    if( pCreateDirect3D11DeviceFromDXGIDevice == nullptr )
        return E_NOINTERFACE;

    return pCreateDirect3D11DeviceFromDXGIDevice( dxgiDevice, graphicsDevice );
}

HRESULT __stdcall WrapCreateDirect3D11SurfaceFromDXGISurface(
    IDXGISurface	*dxgiSurface,
    IInspectable	**graphicsSurface)
{
    if( pCreateDirect3D11SurfaceFromDXGISurface == nullptr )
        return E_NOINTERFACE;

    return pCreateDirect3D11SurfaceFromDXGISurface( dxgiSurface, graphicsSurface );
}

HRESULT __stdcall WrapD3D11CreateDevice(
    IDXGIAdapter			*pAdapter,
    D3D_DRIVER_TYPE			DriverType,
    HMODULE					Software,
    UINT					Flags,
    const D3D_FEATURE_LEVEL	*pFeatureLevels,
    UINT					FeatureLevels,
    UINT					SDKVersion,
    ID3D11Device			**ppDevice,
    D3D_FEATURE_LEVEL		*pFeatureLevel,
    ID3D11DeviceContext		**ppImmediateContext)
{
    if( pD3D11CreateDevice == nullptr )
        return E_NOINTERFACE;

    return pD3D11CreateDevice( pAdapter, DriverType, Software, Flags, pFeatureLevels,
                FeatureLevels, SDKVersion, ppDevice, pFeatureLevel, ppImmediateContext );
}


//----------------------------------------------------------------------------
//
// InitInstance
//
//----------------------------------------------------------------------------
HWND InitInstance( HINSTANCE hInstance, int nCmdShow )
{
    WNDCLASS  wcZoomIt;
    HWND	  hWndMain;

    g_hInstance = hInstance;

    // If magnification, set default hotkey for live zoom
    if( pMagInitialize ) {

        // register live zoom host window
        wcZoomIt.style          = CS_HREDRAW | CS_VREDRAW;
        wcZoomIt.lpfnWndProc    = LiveZoomWndProc;
        wcZoomIt.cbClsExtra     = 0;
        wcZoomIt.cbWndExtra     = 0;
        wcZoomIt.hInstance      = hInstance;
        wcZoomIt.hIcon          = 0;
        wcZoomIt.hCursor        = LoadCursor(NULL, IDC_ARROW);
        wcZoomIt.hbrBackground  = NULL;
        wcZoomIt.lpszMenuName   = NULL;
        wcZoomIt.lpszClassName  = L"MagnifierClass";
        RegisterClass(&wcZoomIt);

    } else {

        g_LiveZoomToggleKey = 0;
    }

    wcZoomIt.style = 0;
    wcZoomIt.lpfnWndProc	= (WNDPROC)MainWndProc;
    wcZoomIt.cbClsExtra		= 0;
    wcZoomIt.cbWndExtra		= 0;
    wcZoomIt.hInstance		= hInstance;       wcZoomIt.hIcon			= NULL;
    wcZoomIt.hCursor		= LoadCursor( hInstance, L"NULLCURSOR" );
    wcZoomIt.hbrBackground	= NULL;
    wcZoomIt.lpszMenuName	= NULL;
    wcZoomIt.lpszClassName	= L"ZoomitClass";
    if ( ! RegisterClass(&wcZoomIt) )
        return FALSE;

    hWndMain = CreateWindowEx( WS_EX_TOOLWINDOW, L"ZoomitClass",
                    L"Zoomit Zoom Window",
                    WS_POPUP,
                    0, 0,
                    0, 0,
                    NULL,
                    NULL,
                    hInstance,
                    NULL);

    // If window could not be created, return "failure"
    if (!hWndMain )
        return NULL;

    // Make the window visible; update its client area; and return "success"
    ShowWindow(hWndMain, SW_HIDE);

    // Add tray icon
    EnableDisableTrayIcon( hWndMain, g_ShowTrayIcon );
    return hWndMain;

}

// Dispatch commands coming from the PowerToys IPC channel.
#ifdef __ZOOMIT_POWERTOYS__
void ZoomIt_DispatchCommand(ZoomItCommand cmd)
{
    auto post_hotkey = [](WPARAM id)
    {
        if (g_hWndMain != nullptr)
        {
            PostMessage(g_hWndMain, WM_HOTKEY, id, 0);
        }
    };

    switch (cmd)
    {
    case ZoomItCommand::Zoom:
        if (g_hWndMain != nullptr)
        {
            PostMessage(g_hWndMain, WM_COMMAND, IDC_ZOOM, 0);
        }
        Trace::ZoomItActivateZoom();
        break;
    case ZoomItCommand::Draw:
        post_hotkey(DRAW_HOTKEY);
        Trace::ZoomItActivateDraw();
        break;
    case ZoomItCommand::Break:
        post_hotkey(BREAK_HOTKEY);
        Trace::ZoomItActivateBreak();
        break;
    case ZoomItCommand::LiveZoom:
        post_hotkey(LIVE_HOTKEY);
        Trace::ZoomItActivateLiveZoom();
        break;
    case ZoomItCommand::Snip:
        post_hotkey(SNIP_HOTKEY);
        Trace::ZoomItActivateSnip();
        break;
    case ZoomItCommand::SnipOcr:
        post_hotkey(SNIP_OCR_HOTKEY);
        Trace::ZoomItActivateSnipOcr();
        break;
    case ZoomItCommand::Record:
        post_hotkey(RECORD_HOTKEY);
        Trace::ZoomItActivateRecord();
        break;
    default:
        break;
    }
}
#endif

//----------------------------------------------------------------------------
//
// WinMain
//
//----------------------------------------------------------------------------
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance,
    _In_ PWSTR lpCmdLine, _In_ int nCmdShow )
{
    MSG					msg;
    HACCEL				hAccel;

    // Enable panorama frame/log dumps in release builds when requested.
    if( lpCmdLine != nullptr && wcsstr( lpCmdLine, L"/panorama-debug" ) != nullptr )
    {
        g_PanoramaDebugMode = true;
    }

#ifdef _DEBUG
    if( lpCmdLine != nullptr && wcsstr( lpCmdLine, L"/panorama-selftest" ) != nullptr )
    {
        const bool selfTestPassed = RunPanoramaStitchSelfTest();
        return selfTestPassed ? 0 : 2;
    }

    if( lpCmdLine != nullptr && wcsstr( lpCmdLine, L"/panorama-stitch-latest" ) != nullptr )
    {
        const bool replayStitchPassed = RunPanoramaStitchLatestDebugDump();
        return replayStitchPassed ? 0 : 3;
    }

    {
        const wchar_t* replayArg = lpCmdLine != nullptr ? wcsstr( lpCmdLine, L"/panorama-stitch-replay " ) : nullptr;
        if( replayArg != nullptr )
        {
            const wchar_t* path = replayArg + wcslen( L"/panorama-stitch-replay " );
            while( *path == L' ' || *path == L'\"' ) ++path;
            std::wstring replayPath( path );
            while( !replayPath.empty() && ( replayPath.back() == L'\"' || replayPath.back() == L' ' ) )
                replayPath.pop_back();
            const bool ok = RunPanoramaStitchDumpDirectory( replayPath.c_str() );
            return ok ? 0 : 3;
        }
    }
#endif // _DEBUG

    if( !ShowEula( APPNAME, NULL, NULL )) return 1;

#ifdef __ZOOMIT_POWERTOYS__
    if (powertoys_gpo::getConfiguredZoomItEnabledValue() == powertoys_gpo::gpo_rule_configured_disabled)
    {
        Logger::warn(L"Tried to start with a GPO policy setting the utility to always be disabled. Please contact your systems administrator.");
        return 1;
    }

    Shared::Trace::ETWTrace* trace = nullptr;
    std::wstring pid = std::wstring(lpCmdLine); // The PowerToys pid is the argument to the process.
    auto mainThreadId = GetCurrentThreadId();
    if (!pid.empty())
    {
        g_StartedByPowerToys = TRUE;

        trace = new Shared::Trace::ETWTrace();
        Trace::RegisterProvider();
        trace->UpdateState(true);
        Trace::ZoomItStarted();

        // Initialize logger
        LoggerHelpers::init_logger(L"ZoomIt", L"", LogSettings::zoomItLoggerName);
        ProcessWaiter::OnProcessTerminate(pid, [mainThreadId](int err) {
            if (err != ERROR_SUCCESS)
            {
                Logger::error(L"Failed to wait for parent process exit. {}", get_last_error_or_default(err));
            }
            else
            {
                Logger::trace(L"PowerToys runner exited.");
            }

            Logger::trace(L"Exiting ZoomIt");
            PostThreadMessage(mainThreadId, WM_QUIT, 0, 0);
        });
    }
#endif // __ZOOMIT_POWERTOYS__


#ifndef _WIN64

    if(!g_StartedByPowerToys)
    {
        // Launch 64-bit version if necessary
        SetAutostartFilePath();
        if( RunningOnWin64()) {

            // Record where we are if we're the 32-bit version
            return Run64bitVersion();
        }
    }
#endif

    // Single instance per desktop

    if( !CreateEvent( NULL, FALSE, FALSE, _T("Local\\ZoomitActive"))) {

        CreateEvent( NULL, FALSE, FALSE, _T("ZoomitActive"));
    }
    if( GetLastError() == ERROR_ALREADY_EXISTS ) {
        if (g_StartedByPowerToys)
        {
            MessageBox(NULL, L"We've detected another instance of ZoomIt is already running.\nCan't start a new ZoomIt instance from PowerToys.",
            APPNAME, MB_ICONERROR | MB_SETFOREGROUND);
            return 1;
        }

        // Tell the other instance to show the options dialog
        g_hWndMain = FindWindow( L"ZoomitClass", NULL );
        if( g_hWndMain != NULL ) {

            PostMessage( g_hWndMain, WM_COMMAND, IDC_OPTIONS, 0 );
            int count = 0;
            while( count++ < 5 ) {

                HWND local_hWndOptions = FindWindow( NULL, L"ZoomIt - Sysinternals: www.sysinternals.com" );
                if( local_hWndOptions ) {

                    SetForegroundWindow( local_hWndOptions );
                    SetWindowPos( local_hWndOptions, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE|SWP_SHOWWINDOW );
                    break;
                }
                Sleep( 100 );
            }
        }
        return 0;
    }

    g_OsVersion = GetVersion() & 0xFFFF;

    // load accelerators
    hAccel = LoadAccelerators( hInstance, TEXT("ACCELERATORS"));

    if (FAILED(CoInitialize(0)))
    {
        return 0;
    }

    pEnableThemeDialogTexture = (type_pEnableThemeDialogTexture) GetProcAddress( GetModuleHandle( L"uxtheme.dll" ),
                    "EnableThemeDialogTexture" );

    // Initialize dark mode support early, before any windows are created
    // This is required for popup menus to use dark mode
    InitializeDarkMode();

    pMonitorFromPoint = (type_MonitorFromPoint) GetProcAddress( LoadLibrarySafe( L"User32.dll", DLL_LOAD_LOCATION_SYSTEM),
                    "MonitorFromPoint" );
    pGetMonitorInfo = (type_pGetMonitorInfo) GetProcAddress( LoadLibrarySafe( L"User32.dll", DLL_LOAD_LOCATION_SYSTEM),
                    "GetMonitorInfoA" );
    pSHAutoComplete = (type_pSHAutoComplete) GetProcAddress( LoadLibrarySafe(L"Shlwapi.dll", DLL_LOAD_LOCATION_SYSTEM),
                    "SHAutoComplete" );
    pSetLayeredWindowAttributes = (type_pSetLayeredWindowAttributes) GetProcAddress( LoadLibrarySafe(L"user32.dll", DLL_LOAD_LOCATION_SYSTEM),
                    "SetLayeredWindowAttributes" );
    pMagSetWindowSource = (type_pMagSetWindowSource) GetProcAddress( LoadLibrarySafe(L"magnification.dll", DLL_LOAD_LOCATION_SYSTEM),
                    "MagSetWindowSource" );
    pGetPointerType = (type_pGetPointerType)GetProcAddress(LoadLibrarySafe(L"user32.dll", DLL_LOAD_LOCATION_SYSTEM),
                    "GetPointerType" );
    pGetPointerPenInfo = (type_pGetPointerPenInfo)GetProcAddress(LoadLibrarySafe(L"user32.dll", DLL_LOAD_LOCATION_SYSTEM),
                    "GetPointerPenInfo" );
    pMagInitialize = (type_pMagInitialize)GetProcAddress(LoadLibrarySafe(L"magnification.dll", DLL_LOAD_LOCATION_SYSTEM),
                    "MagInitialize");
    pMagSetWindowTransform = (type_pMagSetWindowTransform) GetProcAddress( LoadLibrarySafe(L"magnification.dll", DLL_LOAD_LOCATION_SYSTEM),
                    "MagSetWindowTransform" );
    pMagSetFullscreenTransform = (type_pMagSetFullscreenTransform)GetProcAddress(LoadLibrarySafe(L"magnification.dll", DLL_LOAD_LOCATION_SYSTEM),
                    "MagSetFullscreenTransform");
    pMagSetFullscreenUseBitmapSmoothing = (type_MagSetFullscreenUseBitmapSmoothing)GetProcAddress(LoadLibrarySafe(L"magnification.dll", DLL_LOAD_LOCATION_SYSTEM),
                    "MagSetFullscreenUseBitmapSmoothing");
    pMagSetLensUseBitmapSmoothing = (type_pMagSetLensUseBitmapSmoothing)GetProcAddress(LoadLibrarySafe(L"magnification.dll", DLL_LOAD_LOCATION_SYSTEM),
                    "MagSetLensUseBitmapSmoothing");
    pMagSetInputTransform = (type_pMagSetInputTransform)GetProcAddress(LoadLibrarySafe(L"magnification.dll", DLL_LOAD_LOCATION_SYSTEM),
                    "MagSetInputTransform");
    pMagShowSystemCursor = (type_pMagShowSystemCursor)GetProcAddress(LoadLibrarySafe(L"magnification.dll", DLL_LOAD_LOCATION_SYSTEM),
                    "MagShowSystemCursor");
    pMagSetWindowFilterList = (type_pMagSetWindowFilterList)GetProcAddress( LoadLibrarySafe( L"magnification.dll", DLL_LOAD_LOCATION_SYSTEM ),
                    "MagSetWindowFilterList" );
    pSHQueryUserNotificationState = (type_pSHQueryUserNotificationState) GetProcAddress( LoadLibrarySafe(L"shell32.dll", DLL_LOAD_LOCATION_SYSTEM),
                    "SHQueryUserNotificationState" );
    pDwmIsCompositionEnabled = (type_pDwmIsCompositionEnabled) GetProcAddress( LoadLibrarySafe(L"dwmapi.dll", DLL_LOAD_LOCATION_SYSTEM),
                    "DwmIsCompositionEnabled" );
    pSetProcessDPIAware = (type_pSetProcessDPIAware) GetProcAddress( LoadLibrarySafe(L"User32.dll", DLL_LOAD_LOCATION_SYSTEM),
                    "SetProcessDPIAware");
    pSystemParametersInfoForDpi = (type_pSystemParametersInfoForDpi)GetProcAddress(LoadLibrarySafe(L"User32.dll", DLL_LOAD_LOCATION_SYSTEM),
                    "SystemParametersInfoForDpi");
    pGetDpiForWindow = (type_pGetDpiForWindow)GetProcAddress(LoadLibrarySafe(L"User32.dll", DLL_LOAD_LOCATION_SYSTEM),
                    "GetDpiForWindow" );
    pCreateDirect3D11DeviceFromDXGIDevice = (type_pCreateDirect3D11DeviceFromDXGIDevice) GetProcAddress( LoadLibrarySafe(L"d3d11.dll", DLL_LOAD_LOCATION_SYSTEM),
                    "CreateDirect3D11DeviceFromDXGIDevice" );
    pCreateDirect3D11SurfaceFromDXGISurface = (type_pCreateDirect3D11SurfaceFromDXGISurface) GetProcAddress( LoadLibrarySafe(L"d3d11.dll", DLL_LOAD_LOCATION_SYSTEM),
                    "CreateDirect3D11SurfaceFromDXGISurface" );
    pD3D11CreateDevice = (type_pD3D11CreateDevice) GetProcAddress( LoadLibrarySafe(L"d3d11.dll", DLL_LOAD_LOCATION_SYSTEM),
                    "D3D11CreateDevice" );

    // Windows Server 2022 (and including Windows 11) introduced a bug where the cursor disappears
    // in live zoom. Use the full-screen magnifier as a workaround on those versions only. It is
    // currently impractical as a replacement; it requires calling MagSetInputTransform for all
    // input to be transformed. Else, some hit-testing is misdirected. MagSetInputTransform
    // fails without token UI access, which is impractical; it requires copying the executable
    // under either %ProgramFiles% or %SystemRoot%, which requires elevation.
    //
    // TODO: Update the Windows 11 21H2 revision check when the final number is known. Also add a
    //       check for the Windows Server 2022 revision if that bug (https://task.ms/38611091) is
    //       fixed.
    DWORD windowsRevision, windowsBuild = GetWindowsBuild( &windowsRevision );
    if( ( windowsBuild == BUILD_WINDOWS_SERVER_2022 ) ||
        ( ( windowsBuild == BUILD_WINDOWS_11_21H2 ) && ( windowsRevision < 829 ) ) ) {

        if( pMagSetFullscreenTransform && pMagSetInputTransform )
            g_fullScreenWorkaround = TRUE;
    }

#if 1
    // Calling this causes Windows to mess with our query of monitor height and width
    if( pSetProcessDPIAware ) {

        pSetProcessDPIAware();
    }
#endif
    /* Perform initializations that apply to a specific instance */
    g_hWndMain = InitInstance(hInstance, nCmdShow);
    if (!g_hWndMain )
        return FALSE;

#ifdef __ZOOMIT_POWERTOYS__
    HANDLE m_reload_settings_event_handle = NULL;
    HANDLE m_exit_event_handle = NULL;
    HANDLE m_zoom_event_handle = NULL;
    HANDLE m_draw_event_handle = NULL;
    HANDLE m_break_event_handle = NULL;
    HANDLE m_live_zoom_event_handle = NULL;
    HANDLE m_snip_event_handle = NULL;
    HANDLE m_snip_ocr_event_handle = NULL;
    HANDLE m_record_event_handle = NULL;
    std::thread m_event_triggers_thread;

    if( g_StartedByPowerToys ) {
        // Start a thread to listen to PowerToys Events.
        m_reload_settings_event_handle = CreateEventW(nullptr, false, false, CommonSharedConstants::ZOOMIT_REFRESH_SETTINGS_EVENT);
        m_exit_event_handle = CreateEventW(nullptr, false, false, CommonSharedConstants::ZOOMIT_EXIT_EVENT);
        m_zoom_event_handle = CreateEventW(nullptr, false, false, CommonSharedConstants::ZOOMIT_ZOOM_EVENT);
        m_draw_event_handle = CreateEventW(nullptr, false, false, CommonSharedConstants::ZOOMIT_DRAW_EVENT);
        m_break_event_handle = CreateEventW(nullptr, false, false, CommonSharedConstants::ZOOMIT_BREAK_EVENT);
        m_live_zoom_event_handle = CreateEventW(nullptr, false, false, CommonSharedConstants::ZOOMIT_LIVEZOOM_EVENT);
        m_snip_event_handle = CreateEventW(nullptr, false, false, CommonSharedConstants::ZOOMIT_SNIP_EVENT);
        m_snip_ocr_event_handle = CreateEventW(nullptr, false, false, CommonSharedConstants::ZOOMIT_SNIPOCR_EVENT);
        m_record_event_handle = CreateEventW(nullptr, false, false, CommonSharedConstants::ZOOMIT_RECORD_EVENT);
        if (!m_reload_settings_event_handle || !m_exit_event_handle || !m_zoom_event_handle || !m_draw_event_handle || !m_break_event_handle || !m_live_zoom_event_handle || !m_snip_event_handle || !m_snip_ocr_event_handle || !m_record_event_handle)
        {
            Logger::warn(L"Failed to create events. {}", get_last_error_or_default(GetLastError()));
            return 1;
        }
        const std::array<HANDLE, 9> event_handles{
            m_reload_settings_event_handle,
            m_exit_event_handle,
            m_zoom_event_handle,
            m_draw_event_handle,
            m_break_event_handle,
            m_live_zoom_event_handle,
            m_snip_event_handle,
            m_snip_ocr_event_handle,
            m_record_event_handle,
        };
        const DWORD handle_count = static_cast<DWORD>(event_handles.size());
        m_event_triggers_thread = std::thread([event_handles, handle_count]() {
            MSG msg;
            while (g_running)
            {
                DWORD dwEvt = MsgWaitForMultipleObjects(handle_count, event_handles.data(), false, INFINITE, QS_ALLINPUT);
                if (dwEvt == WAIT_FAILED)
                {
                    Logger::error(L"ZoomIt event wait failed. {}", get_last_error_or_default(GetLastError()));
                    break;
                }
                if (!g_running)
                {
                    break;
                }
                if (dwEvt == WAIT_OBJECT_0 + handle_count)
                {
                    if (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE))
                    {
                        TranslateMessage(&msg);
                        DispatchMessageW(&msg);
                    }
                    continue;
                }
                switch (dwEvt)
                {
                case WAIT_OBJECT_0:
                {
                    // Reload Settings Event
                    Logger::trace(L"Received a reload settings event.");
                    PostMessage(g_hWndMain, WM_USER_RELOAD_SETTINGS, 0, 0);
                    break;
                }
                case WAIT_OBJECT_0 + 1:
                {
                    // Exit Event
                    PostMessage(g_hWndMain, WM_QUIT, 0, 0);
                    break;
                }
                case WAIT_OBJECT_0 + 2:
                    ZoomIt_DispatchCommand(ZoomItCommand::Zoom);
                    break;
                case WAIT_OBJECT_0 + 3:
                    ZoomIt_DispatchCommand(ZoomItCommand::Draw);
                    break;
                case WAIT_OBJECT_0 + 4:
                    ZoomIt_DispatchCommand(ZoomItCommand::Break);
                    break;
                case WAIT_OBJECT_0 + 5:
                    ZoomIt_DispatchCommand(ZoomItCommand::LiveZoom);
                    break;
                case WAIT_OBJECT_0 + 6:
                    ZoomIt_DispatchCommand(ZoomItCommand::Snip);
                    break;
                case WAIT_OBJECT_0 + 7:
                    ZoomIt_DispatchCommand(ZoomItCommand::SnipOcr);
                    break;
                case WAIT_OBJECT_0 + 8:
                    ZoomIt_DispatchCommand(ZoomItCommand::Record);
                    break;
                default: break;
                }
            }
        });
    }
#endif // __ZOOMIT_POWERTOYS__

    /* Acquire and dispatch messages until a WM_QUIT message is received. */
    while (GetMessage(&msg,	NULL, 0, 0 ))  {
        if( !TranslateAccelerator( g_hWndMain, hAccel, &msg )) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    int retCode = (int) msg.wParam;

    g_running = FALSE;

#ifdef __ZOOMIT_POWERTOYS__
    if(g_StartedByPowerToys)
    {
        if (trace!=nullptr) {
            trace->Flush();
            delete trace;
        }
        Trace::UnregisterProvider();
        // Needed to unblock MsgWaitForMultipleObjects one last time
        SetEvent(m_reload_settings_event_handle);
        CloseHandle(m_reload_settings_event_handle);
        CloseHandle(m_exit_event_handle);
        CloseHandle(m_zoom_event_handle);
        CloseHandle(m_draw_event_handle);
        CloseHandle(m_break_event_handle);
        CloseHandle(m_live_zoom_event_handle);
        CloseHandle(m_snip_event_handle);
        CloseHandle(m_snip_ocr_event_handle);
        CloseHandle(m_record_event_handle);
        m_event_triggers_thread.join();
    }
#endif // __ZOOMIT_POWERTOYS__

    return retCode;
}
