-
Notifications
You must be signed in to change notification settings - Fork 7.5k
Add non-updating mode for Crop-And-Lock #40720
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 2 commits
2d396ba
157eeb2
a5383ce
af5b785
a8a4292
04d3d1b
c84988c
9f40ebb
c9232b1
caf9db6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,181 @@ | ||||||||||||||||||||||||||||||||||||
| #include "pch.h" | ||||||||||||||||||||||||||||||||||||
| #include "ScreenshotCropAndLockWindow.h" | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| const std::wstring ScreenshotCropAndLockWindow::ClassName = L"CropAndLock.ScreenshotCropAndLockWindow"; | ||||||||||||||||||||||||||||||||||||
| std::once_flag ScreenshotCropAndLockWindowClassRegistration; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| void ScreenshotCropAndLockWindow::RegisterWindowClass() | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| auto instance = winrt::check_pointer(GetModuleHandleW(nullptr)); | ||||||||||||||||||||||||||||||||||||
| WNDCLASSEXW wcex = {}; | ||||||||||||||||||||||||||||||||||||
| wcex.cbSize = sizeof(wcex); | ||||||||||||||||||||||||||||||||||||
| wcex.style = CS_HREDRAW | CS_VREDRAW; | ||||||||||||||||||||||||||||||||||||
| wcex.lpfnWndProc = WndProc; | ||||||||||||||||||||||||||||||||||||
| wcex.hInstance = instance; | ||||||||||||||||||||||||||||||||||||
| wcex.hIcon = LoadIconW(instance, IDI_APPLICATION); | ||||||||||||||||||||||||||||||||||||
| wcex.hCursor = LoadCursorW(nullptr, IDC_ARROW); | ||||||||||||||||||||||||||||||||||||
| wcex.hbrBackground = static_cast<HBRUSH>(GetStockObject(BLACK_BRUSH)); | ||||||||||||||||||||||||||||||||||||
| wcex.lpszClassName = ClassName.c_str(); | ||||||||||||||||||||||||||||||||||||
| wcex.hIconSm = LoadIconW(wcex.hInstance, IDI_APPLICATION); | ||||||||||||||||||||||||||||||||||||
| winrt::check_bool(RegisterClassExW(&wcex)); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| ScreenshotCropAndLockWindow::ScreenshotCropAndLockWindow(std::wstring const& titleString, int width, int height) | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| auto instance = winrt::check_pointer(GetModuleHandleW(nullptr)); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| std::call_once(ScreenshotCropAndLockWindowClassRegistration, []() { RegisterWindowClass(); }); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| auto exStyle = 0; | ||||||||||||||||||||||||||||||||||||
| auto style = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| RECT rect = { 0, 0, width, height }; | ||||||||||||||||||||||||||||||||||||
| winrt::check_bool(AdjustWindowRectEx(&rect, style, false, exStyle)); | ||||||||||||||||||||||||||||||||||||
| auto adjustedWidth = rect.right - rect.left; | ||||||||||||||||||||||||||||||||||||
| auto adjustedHeight = rect.bottom - rect.top; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| winrt::check_bool(CreateWindowExW(exStyle, ClassName.c_str(), titleString.c_str(), style, CW_USEDEFAULT, CW_USEDEFAULT, adjustedWidth, adjustedHeight, nullptr, nullptr, instance, this)); | ||||||||||||||||||||||||||||||||||||
| WINRT_ASSERT(m_window); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| ScreenshotCropAndLockWindow::~ScreenshotCropAndLockWindow() | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| DestroyWindow(m_window); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| LRESULT ScreenshotCropAndLockWindow::MessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam) | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| switch (message) | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| case WM_DESTROY: | ||||||||||||||||||||||||||||||||||||
| if (m_closedCallback != nullptr && !m_destroyed) | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| m_destroyed = true; | ||||||||||||||||||||||||||||||||||||
| m_closedCallback(m_window); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||
| case WM_PAINT: | ||||||||||||||||||||||||||||||||||||
| if (m_captured && m_bitmap) | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| PAINTSTRUCT ps; | ||||||||||||||||||||||||||||||||||||
| HDC hdc = BeginPaint(m_window, &ps); | ||||||||||||||||||||||||||||||||||||
| HDC memDC = CreateCompatibleDC(hdc); | ||||||||||||||||||||||||||||||||||||
| SelectObject(memDC, m_bitmap.get()); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| RECT clientRect = {}; | ||||||||||||||||||||||||||||||||||||
| GetClientRect(m_window, &clientRect); | ||||||||||||||||||||||||||||||||||||
| int clientWidth = clientRect.right - clientRect.left; | ||||||||||||||||||||||||||||||||||||
| int clientHeight = clientRect.bottom - clientRect.top; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| int srcWidth = m_destRect.right - m_destRect.left; | ||||||||||||||||||||||||||||||||||||
| int srcHeight = m_destRect.bottom - m_destRect.top; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| float srcAspect = static_cast<float>(srcWidth) / srcHeight; | ||||||||||||||||||||||||||||||||||||
| float dstAspect = static_cast<float>(clientWidth) / clientHeight; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| int drawWidth = clientWidth; | ||||||||||||||||||||||||||||||||||||
| int drawHeight = static_cast<int>(clientWidth / srcAspect); | ||||||||||||||||||||||||||||||||||||
| if (dstAspect > srcAspect) | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| drawHeight = clientHeight; | ||||||||||||||||||||||||||||||||||||
| drawWidth = static_cast<int>(clientHeight * srcAspect); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| int offsetX = (clientWidth - drawWidth) / 2; | ||||||||||||||||||||||||||||||||||||
| int offsetY = (clientHeight - drawHeight) / 2; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| SetStretchBltMode(hdc, HALFTONE); | ||||||||||||||||||||||||||||||||||||
| StretchBlt(hdc, offsetX, offsetY, drawWidth, drawHeight, memDC, 0, 0, srcWidth, srcHeight, SRCCOPY); | ||||||||||||||||||||||||||||||||||||
| DeleteDC(memDC); | ||||||||||||||||||||||||||||||||||||
| EndPaint(m_window, &ps); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||
| default: | ||||||||||||||||||||||||||||||||||||
| return base_type::MessageHandler(message, wparam, lparam); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| return 0; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| void ScreenshotCropAndLockWindow::CropAndLock(HWND windowToCrop, RECT cropRect) | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| if (m_captured) | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| // Get full window bounds | ||||||||||||||||||||||||||||||||||||
| RECT windowRect{}; | ||||||||||||||||||||||||||||||||||||
| winrt::check_hresult(DwmGetWindowAttribute( | ||||||||||||||||||||||||||||||||||||
| windowToCrop, | ||||||||||||||||||||||||||||||||||||
| DWMWA_EXTENDED_FRAME_BOUNDS, | ||||||||||||||||||||||||||||||||||||
| &windowRect, | ||||||||||||||||||||||||||||||||||||
| sizeof(windowRect))); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| RECT clientRect = ClientAreaInScreenSpace(windowToCrop); | ||||||||||||||||||||||||||||||||||||
| auto offsetX = clientRect.left - windowRect.left; | ||||||||||||||||||||||||||||||||||||
| auto offsetY = clientRect.top - windowRect.top; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| m_sourceRect = { | ||||||||||||||||||||||||||||||||||||
| cropRect.left + offsetX, | ||||||||||||||||||||||||||||||||||||
| cropRect.top + offsetY, | ||||||||||||||||||||||||||||||||||||
| cropRect.right + offsetX, | ||||||||||||||||||||||||||||||||||||
| cropRect.bottom + offsetY | ||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| int fullWidth = windowRect.right - windowRect.left; | ||||||||||||||||||||||||||||||||||||
| int fullHeight = windowRect.bottom - windowRect.top; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| HDC fullDC = CreateCompatibleDC(nullptr); | ||||||||||||||||||||||||||||||||||||
| HBITMAP fullBitmap = CreateCompatibleBitmap(GetDC(nullptr), fullWidth, fullHeight); | ||||||||||||||||||||||||||||||||||||
| HGDIOBJ oldFullBitmap = SelectObject(fullDC, fullBitmap); | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
| HGDIOBJ oldFullBitmap = SelectObject(fullDC, fullBitmap); | |
| ScopedHDC fullDC(CreateCompatibleDC(nullptr)); | |
| HDC screenDC = GetDC(nullptr); | |
| ScopedHBITMAP fullBitmap(CreateCompatibleBitmap(screenDC, fullWidth, fullHeight)); | |
| ScopedSelectObject selectFullBitmap(fullDC, fullBitmap); |
Fixed
Show fixed
Hide fixed
fm-sys marked this conversation as resolved.
Show resolved
Hide resolved
fm-sys marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
Copilot
AI
Aug 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The pattern of selecting objects and restoring them should use RAII or at minimum ensure proper cleanup in case of early returns. Consider wrapping this in a helper class or function to prevent resource leaks if exceptions occur between selection and restoration.
| HGDIOBJ oldCropBitmap = SelectObject(cropDC, cropBitmap); | |
| ScopedSelectObject scopedCropBitmap(cropDC, cropBitmap); |
Copilot
AI
Aug 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The pattern of selecting objects and restoring them should use RAII or at minimum ensure proper cleanup in case of early returns. Consider wrapping this in a helper class or function to prevent resource leaks if exceptions occur between selection and restoration.
| SelectObject(cropDC, oldCropBitmap); | |
| // Use RAII for fullDC bitmap restoration as well | |
| // (Assuming oldFullBitmap was selected into fullDC earlier) | |
| struct ScopedSelectObjectFull | |
| { | |
| HDC dc; | |
| HGDIOBJ oldObj; | |
| ScopedSelectObjectFull(HDC dc_, HGDIOBJ obj) : dc(dc_), oldObj(obj) {} | |
| ~ScopedSelectObjectFull() { if (dc && oldObj) SelectObject(dc, oldObj); } | |
| ScopedSelectObjectFull(const ScopedSelectObjectFull&) = delete; | |
| ScopedSelectObjectFull& operator=(const ScopedSelectObjectFull&) = delete; | |
| }; | |
| ScopedSelectObjectFull fullSelect(fullDC, oldFullBitmap); | |
| DeleteObject(fullBitmap); | |
| DeleteDC(fullDC); |
Copilot
AI
Aug 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The pattern of selecting objects and restoring them should use RAII or at minimum ensure proper cleanup in case of early returns. Consider wrapping this in a helper class or function to prevent resource leaks if exceptions occur between selection and restoration.
| SelectObject(cropDC, oldCropBitmap); |
fm-sys marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| #pragma once | ||
| #include <robmikh.common/DesktopWindow.h> | ||
| #include "CropAndLockWindow.h" | ||
|
|
||
| struct ScreenshotCropAndLockWindow : robmikh::common::desktop::DesktopWindow<ScreenshotCropAndLockWindow>, CropAndLockWindow | ||
| { | ||
| static const std::wstring ClassName; | ||
| ScreenshotCropAndLockWindow(std::wstring const& titleString, int width, int height); | ||
| ~ScreenshotCropAndLockWindow() override; | ||
| LRESULT MessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam); | ||
|
|
||
| HWND Handle() override { return m_window; } | ||
| void CropAndLock(HWND windowToCrop, RECT cropRect) override; | ||
| void OnClosed(std::function<void(HWND)> callback) override { m_closedCallback = callback; } | ||
|
|
||
| private: | ||
| static void RegisterWindowClass(); | ||
|
|
||
| void Hide(); | ||
|
|
||
| private: | ||
| std::unique_ptr<void, decltype(&DeleteObject)> m_bitmap{ nullptr, &DeleteObject }; | ||
| RECT m_destRect = {}; | ||
| RECT m_sourceRect = {}; | ||
|
|
||
| bool m_captured = false; | ||
| bool m_destroyed = false; | ||
| std::function<void(HWND)> m_closedCallback; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,4 +4,5 @@ enum class CropAndLockType | |
| { | ||
| Reparent, | ||
| Thumbnail, | ||
| Screenshot, | ||
| }; | ||
Uh oh!
There was an error while loading. Please reload this page.