Skip to content

Commit

Permalink
Add support for capturing 90 degree rotated displays and fix allowabl…
Browse files Browse the repository at this point in the history
…e rectangles on secondary displays to the right or above the primary display
  • Loading branch information
Kaldaien committed Jul 22, 2024
1 parent 8d465ce commit 91dea96
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 43 deletions.
66 changes: 37 additions & 29 deletions src/SKIV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@

#include <filesystem>
#include <concurrent_queue.h>
#include <unordered_set>
#include <oleidl.h>
#include <utility/droptarget.hpp>

Expand Down Expand Up @@ -1959,6 +1960,19 @@ wWinMain ( _In_ HINSTANCE hInstance,

extern skiv_image_desktop_s SKIV_DesktopImage;

ImVec2 resolution =
SKIV_DesktopImage._resolution;

if (SKIV_DesktopImage._rotation == DXGI_MODE_ROTATION_ROTATE90 ||
SKIV_DesktopImage._rotation == DXGI_MODE_ROTATION_ROTATE270)
{
std::swap (resolution.x, resolution.y);
}

// Desktop Pos, Desktop Pos + Desktop Size
ImRect allowable (monitor_extent.Min, monitor_extent.Min + resolution);
ImRect capture_area;

bool HDR_Image = SKIV_DesktopImage._hdr_image;
bool SKIV_HDR = (HDR_Image ? SKIF_ImGui_IsViewportHDR (SKIF_ImGui_hWnd) : false);

Expand Down Expand Up @@ -1987,7 +2001,7 @@ wWinMain ( _In_ HINSTANCE hInstance,
ImGui::GetForegroundDrawList ();

// Draw a slightly dark transparent overlay on top of the captured image
draw_list->AddRectFilled (ImVec2 (0, 0), SKIV_DesktopImage._resolution, ImGui::GetColorU32 (IM_COL32 (0, 0, 0, 20)));
draw_list->AddRectFilled (allowable.Min, allowable.Max, ImGui::GetColorU32 (IM_COL32 (0, 0, 0, 20)));
}

static ImRect selection;
Expand All @@ -1996,16 +2010,19 @@ wWinMain ( _In_ HINSTANCE hInstance,
if (GetForegroundWindow () != SKIF_ImGui_hWnd)
SetForegroundWindow ( SKIF_ImGui_hWnd);

// Let us store all ignored windows in a vector for quick reuse
static std::vector<HWND> ignoredWindows = { };
// Let us store all ignored windows in a hash set for quick reuse
static std::unordered_set <HWND> ignoredWindows = { };

auto _IgnoreWindow = [&](HWND hWnd) -> bool
{
if (std::find (ignoredWindows.begin(), ignoredWindows.end(), hWnd) != ignoredWindows.end())
if (hWnd == SKIF_ImGui_hWnd)
return true;

if (ignoredWindows.count (hWnd))
return true;

// Window class names to ignore
static const std::vector<std::wstring> ignoreClassNames =
static const std::unordered_set <std::wstring> ignoreClassNames =
{
L"Progman", // Program Manager
L"Button", // Start button?
Expand All @@ -2019,13 +2036,13 @@ wWinMain ( _In_ HINSTANCE hInstance,
if (
SKIF_ImGui_hWnd == hWnd
|| ! IsWindowVisible (hWnd)
|| (RealGetWindowClassW (hWnd, wszWindowTextBuffer, 64) && std::find (ignoreClassNames.begin(), ignoreClassNames.end(), wszWindowTextBuffer) != ignoreClassNames.end())
|| (RealGetWindowClassW (hWnd, wszWindowTextBuffer, 64) && ignoreClassNames.count (wszWindowTextBuffer))
|| ! GetWindowTextW (hWnd, wszWindowTextBuffer, 64)
//|| (GetWindowLongPtr (hWnd, GWL_EXSTYLE) & WS_EX_TOOLWINDOW)
//|| (GetWindowLongPtr (hWnd, GWL_STYLE) & WS_POPUP) // Ignores some games, but allows some invisible stupid windows (UWP)
)
{
ignoredWindows.push_back (hWnd);
ignoredWindows.insert (hWnd);
return true;
}

Expand All @@ -2034,7 +2051,16 @@ wWinMain ( _In_ HINSTANCE hInstance,

auto _GetRectBelowCursor = [&](void) -> void
{
std::vector<HWND> vHWNDs;
// This feature is unsupported on rotated displays
//
if (SKIV_DesktopImage._rotation == DXGI_MODE_ROTATION_ROTATE90 ||
SKIV_DesktopImage._rotation == DXGI_MODE_ROTATION_ROTATE270)
{
return;
}

std::vector <HWND> vHWNDs;
vHWNDs.reserve (2048);

// Retrieve the RECT of the window below the cursor
EnumWindows ( []( HWND hWnd,
Expand All @@ -2046,7 +2072,7 @@ wWinMain ( _In_ HINSTANCE hInstance,
return TRUE;
}, reinterpret_cast<LPARAM>(&vHWNDs));

if (! vHWNDs.empty())
if (! vHWNDs.empty ())
{
POINT point = { };

Expand All @@ -2061,10 +2087,9 @@ wWinMain ( _In_ HINSTANCE hInstance,

if (GetWindowRect (hWnd, &rect))
{
HRGN hRgn = CreateRectRgn (rect.left, rect.top, rect.right, rect.bottom);
bool breakLoop = false;

if (PtInRegion (hRgn, point.x, point.y))
if (PtInRect (&rect, point))
{
// Ensure the current selected window is the top-most
/* Does not seem to be required
Expand All @@ -2080,9 +2105,7 @@ wWinMain ( _In_ HINSTANCE hInstance,
RECT new_rect = { };
if (GetWindowRect (parent, &new_rect))
{
hRgn = CreateRectRgn (new_rect.left, new_rect.top, new_rect.right, new_rect.bottom);
if (PtInRegion (hRgn, point.x, point.y))
if (PtInRect (&new_rect, point))
{
rect = new_rect;
top_most = parent;
Expand Down Expand Up @@ -2113,8 +2136,6 @@ wWinMain ( _In_ HINSTANCE hInstance,

breakLoop = true;
}

DeleteObject (hRgn);

if (breakLoop)
break;
Expand All @@ -2124,19 +2145,6 @@ wWinMain ( _In_ HINSTANCE hInstance,
}
};

ImVec2 resolution =
SKIV_DesktopImage._resolution;

if (SKIV_DesktopImage._rotation == DXGI_MODE_ROTATION_ROTATE90 ||
SKIV_DesktopImage._rotation == DXGI_MODE_ROTATION_ROTATE270)
{
std::swap (resolution.x, resolution.y);
}

// Desktop Pos, Desktop Pos + Desktop Size
ImRect allowable (monitor_extent.Min, resolution);
ImRect capture_area;

static bool clicked = false;

if (HDR_Image && SKIV_HDR)
Expand Down
77 changes: 63 additions & 14 deletions src/utility/image.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1870,23 +1870,16 @@ SKIV_Image_CaptureDesktop (DirectX::ScratchImage& image, POINT point, int flags)
void
SKIV_Image_CaptureRegion (ImRect capture_area)
{
if (SKIV_DesktopImage._rotation == DXGI_MODE_ROTATION_ROTATE90 ||
SKIV_DesktopImage._rotation == DXGI_MODE_ROTATION_ROTATE270)
{
std::swap (capture_area.Min.x, capture_area.Min.y);
std::swap (capture_area.Max.x, capture_area.Max.y);
}
HMONITOR hMonCaptured =
MonitorFromPoint ({ static_cast <long> (capture_area.Min.x),
static_cast <long> (capture_area.Min.y) }, MONITOR_DEFAULTTONEAREST);

MONITORINFO minfo = { .cbSize = sizeof (MONITORINFO) };
GetMonitorInfo (hMonCaptured, &minfo);

// Fixes snipping rectangles on non-primary (origin != 0,0) displays
auto _AdjustCaptureAreaRelativeToDisplayOrigin = [&](void)
{
HMONITOR hMonCaptured =
MonitorFromPoint ({ static_cast <long> (capture_area.Min.x),
static_cast <long> (capture_area.Min.y) }, MONITOR_DEFAULTTONEAREST);

MONITORINFO minfo = { .cbSize = sizeof (MONITORINFO) };
GetMonitorInfo (hMonCaptured, &minfo);

capture_area.Min.x -= minfo.rcMonitor.left;
capture_area.Max.x -= minfo.rcMonitor.left;

Expand All @@ -1896,6 +1889,31 @@ SKIV_Image_CaptureRegion (ImRect capture_area)

_AdjustCaptureAreaRelativeToDisplayOrigin ();

if (SKIV_DesktopImage._rotation == DXGI_MODE_ROTATION_ROTATE90 ||
SKIV_DesktopImage._rotation == DXGI_MODE_ROTATION_ROTATE270)
{
float height = minfo.rcMonitor.right - minfo.rcMonitor.left;

Check warning on line 1895 in src/utility/image.cpp

View workflow job for this annotation

GitHub Actions / Build

float height = minfo.rcMonitor.right - minfo.rcMonitor.left; [D:\a\SKIV\SKIV\SKIV.vcxproj]

Check warning on line 1895 in src/utility/image.cpp

View workflow job for this annotation

GitHub Actions / Build

^ [D:\a\SKIV\SKIV\SKIV.vcxproj]

Check warning on line 1895 in src/utility/image.cpp

View workflow job for this annotation

GitHub Actions / Build

float height = minfo.rcMonitor.right - minfo.rcMonitor.left; [D:\a\SKIV\SKIV\SKIV.vcxproj]

Check warning on line 1895 in src/utility/image.cpp

View workflow job for this annotation

GitHub Actions / Build

^ [D:\a\SKIV\SKIV\SKIV.vcxproj]

Check warning on line 1895 in src/utility/image.cpp

View workflow job for this annotation

GitHub Actions / Build

float height = minfo.rcMonitor.right - minfo.rcMonitor.left; [D:\a\SKIV\SKIV\SKIV.vcxproj]

Check warning on line 1895 in src/utility/image.cpp

View workflow job for this annotation

GitHub Actions / Build

^ [D:\a\SKIV\SKIV\SKIV.vcxproj]

Check warning on line 1895 in src/utility/image.cpp

View workflow job for this annotation

GitHub Actions / Build

float height = minfo.rcMonitor.right - minfo.rcMonitor.left; [D:\a\SKIV\SKIV\SKIV.vcxproj]

Check warning on line 1895 in src/utility/image.cpp

View workflow job for this annotation

GitHub Actions / Build

^ [D:\a\SKIV\SKIV\SKIV.vcxproj]
float width = minfo.rcMonitor.bottom - minfo.rcMonitor.top;

Check warning on line 1896 in src/utility/image.cpp

View workflow job for this annotation

GitHub Actions / Build

float width = minfo.rcMonitor.bottom - minfo.rcMonitor.top; [D:\a\SKIV\SKIV\SKIV.vcxproj]

Check warning on line 1896 in src/utility/image.cpp

View workflow job for this annotation

GitHub Actions / Build

^ [D:\a\SKIV\SKIV\SKIV.vcxproj]

Check warning on line 1896 in src/utility/image.cpp

View workflow job for this annotation

GitHub Actions / Build

float width = minfo.rcMonitor.bottom - minfo.rcMonitor.top; [D:\a\SKIV\SKIV\SKIV.vcxproj]

Check warning on line 1896 in src/utility/image.cpp

View workflow job for this annotation

GitHub Actions / Build

^ [D:\a\SKIV\SKIV\SKIV.vcxproj]

Check warning on line 1896 in src/utility/image.cpp

View workflow job for this annotation

GitHub Actions / Build

float width = minfo.rcMonitor.bottom - minfo.rcMonitor.top; [D:\a\SKIV\SKIV\SKIV.vcxproj]

Check warning on line 1896 in src/utility/image.cpp

View workflow job for this annotation

GitHub Actions / Build

^ [D:\a\SKIV\SKIV\SKIV.vcxproj]

Check warning on line 1896 in src/utility/image.cpp

View workflow job for this annotation

GitHub Actions / Build

float width = minfo.rcMonitor.bottom - minfo.rcMonitor.top; [D:\a\SKIV\SKIV\SKIV.vcxproj]

Check warning on line 1896 in src/utility/image.cpp

View workflow job for this annotation

GitHub Actions / Build

^ [D:\a\SKIV\SKIV\SKIV.vcxproj]

Check warning on line 1896 in src/utility/image.cpp

View workflow job for this annotation

GitHub Actions / Build

float width = minfo.rcMonitor.bottom - minfo.rcMonitor.top; [D:\a\SKIV\SKIV\SKIV.vcxproj]

Check warning on line 1896 in src/utility/image.cpp

View workflow job for this annotation

GitHub Actions / Build

^ [D:\a\SKIV\SKIV\SKIV.vcxproj]

Check warning on line 1896 in src/utility/image.cpp

View workflow job for this annotation

GitHub Actions / Build

float width = minfo.rcMonitor.bottom - minfo.rcMonitor.top; [D:\a\SKIV\SKIV\SKIV.vcxproj]

Check warning on line 1896 in src/utility/image.cpp

View workflow job for this annotation

GitHub Actions / Build

^ [D:\a\SKIV\SKIV\SKIV.vcxproj]

Check warning on line 1896 in src/utility/image.cpp

View workflow job for this annotation

GitHub Actions / Build

float width = minfo.rcMonitor.bottom - minfo.rcMonitor.top; [D:\a\SKIV\SKIV\SKIV.vcxproj]

Check warning on line 1896 in src/utility/image.cpp

View workflow job for this annotation

GitHub Actions / Build

^ [D:\a\SKIV\SKIV\SKIV.vcxproj]

Check warning on line 1896 in src/utility/image.cpp

View workflow job for this annotation

GitHub Actions / Build

float width = minfo.rcMonitor.bottom - minfo.rcMonitor.top; [D:\a\SKIV\SKIV\SKIV.vcxproj]

Check warning on line 1896 in src/utility/image.cpp

View workflow job for this annotation

GitHub Actions / Build

^ [D:\a\SKIV\SKIV\SKIV.vcxproj]

std::swap (capture_area.Min.x, capture_area.Min.y);
std::swap (capture_area.Max.x, capture_area.Max.y);

float capture_height = capture_area.Max.y - capture_area.Min.y;
float capture_width = capture_area.Max.x - capture_area.Min.x;

if (SKIV_DesktopImage._rotation == DXGI_MODE_ROTATION_ROTATE90)
{
capture_area.Min.y = height - capture_area.Max.y;
capture_area.Max.y = height - capture_area.Max.y + capture_height;
}

else
{
//capture_area.Min.x = width - capture_width;
//capture_area.Max.x = width;
}
}

const size_t
x = static_cast <size_t> (std::max (0.0f, capture_area.Min.x)),
y = static_cast <size_t> (std::max (0.0f, capture_area.Min.y)),
Expand Down Expand Up @@ -1928,7 +1946,38 @@ SKIV_Image_CaptureRegion (ImRect capture_area)
{
PLOG_VERBOSE << "DirectX::CopyRectangle ( ): SUCCEEDED";

if (SKIV_Image_CopyToClipboard (subrect.GetImages (), true, SKIV_DesktopImage._hdr_image))
DirectX::ScratchImage rotated;
const DirectX::Image* final = subrect.GetImages ();

if (SKIV_DesktopImage._rotation > DXGI_MODE_ROTATION_IDENTITY)
{
DirectX::TEX_FR_FLAGS rotate_flags = DirectX::TEX_FR_ROTATE0;

switch (SKIV_DesktopImage._rotation)
{
case DXGI_MODE_ROTATION_ROTATE90:
rotate_flags = DirectX::TEX_FR_ROTATE90;
break;

case DXGI_MODE_ROTATION_ROTATE180:
rotate_flags = DirectX::TEX_FR_ROTATE180;
break;

case DXGI_MODE_ROTATION_ROTATE270:
rotate_flags = DirectX::TEX_FR_ROTATE270;
break;
}

if (SUCCEEDED (DirectX::FlipRotate (*subrect.GetImages (), DirectX::TEX_FR_ROTATE90, rotated)))
{
PLOG_VERBOSE << "DirectX::FlipRotate ( ): SUCCEEDED";

final = rotated.GetImages ();
} else
PLOG_VERBOSE << "DirectX::FlipRotate ( ): FAILED";
}

if (SKIV_Image_CopyToClipboard (final, true, SKIV_DesktopImage._hdr_image))
{
PLOG_VERBOSE << "SKIV_Image_CopyToClipboard ( ): SUCCEEDED";

Expand Down

0 comments on commit 91dea96

Please sign in to comment.