From 1b3fddf1e5cb2afeca8b4b3076d1684777711e0a Mon Sep 17 00:00:00 2001 From: Elias Steurer Date: Sat, 28 Oct 2023 21:26:52 +0200 Subject: [PATCH] Change to use more functions into windowsintegration --- .../src/ScreenPlayGodotWallpaper.cpp | 48 +-- .../src/ScreenPlayGodotWallpaper.h | 2 +- .../src/windowsintegration.cpp | 74 +++-- ScreenPlayWallpaper/src/windowsintegration.h | 298 ++++++++++++++--- ScreenPlayWallpaper/src/winwindow.cpp | 313 ++---------------- ScreenPlayWallpaper/src/winwindow.h | 2 - 6 files changed, 338 insertions(+), 399 deletions(-) diff --git a/ScreenPlayWallpaper/Godot/GDExtention/src/ScreenPlayGodotWallpaper.cpp b/ScreenPlayWallpaper/Godot/GDExtention/src/ScreenPlayGodotWallpaper.cpp index 60473c8b..8bc3ccda 100644 --- a/ScreenPlayWallpaper/Godot/GDExtention/src/ScreenPlayGodotWallpaper.cpp +++ b/ScreenPlayWallpaper/Godot/GDExtention/src/ScreenPlayGodotWallpaper.cpp @@ -61,21 +61,10 @@ bool ScreenPlayGodotWallpaper::configureWindowGeometry() UtilityFunctions::print("No worker window found"); return false; } - - RECT rect {}; - if (!GetWindowRect(m_hook->windowHandleWorker, &rect)) { - UtilityFunctions::print("Unable to get WindoeRect from worker"); - return false; - } - - // Windows coordante system begins at 0x0 at the - // main monitors upper left and not at the most left top monitor. - // This can be easily read from the worker window. - m_hook->zeroPoint = { std::abs(rect.left), std::abs(rect.top) }; - // WARNING: Setting Window flags must be called *here*! SetWindowLongPtr(m_hook->windowHandle, GWL_EXSTYLE, WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR | WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT); SetWindowLongPtr(m_hook->windowHandle, GWL_STYLE, WS_POPUPWINDOW); + return true; } @@ -84,7 +73,7 @@ bool ScreenPlayGodotWallpaper::init(int activeScreen) auto* displayServer = DisplayServer::get_singleton(); int64_t handle_int = displayServer->window_get_native_handle(godot::DisplayServer::HandleType::WINDOW_HANDLE, activeScreen); HWND hwnd = reinterpret_cast(static_cast(handle_int)); - m_hook = std::make_unique(); + m_hook = std::make_unique(); m_hook->windowHandle = hwnd; hideFromTaskbar(hwnd); if (!configureWindowGeometry()) { @@ -92,38 +81,13 @@ bool ScreenPlayGodotWallpaper::init(int activeScreen) } ShowWindow(m_hook->windowHandle, SW_HIDE); - const int borderWidth = 2; - const float scaling = m_hook->getScaling(activeScreen); // Assuming getScaling is your own function - const int borderOffset = -1; + WindowsIntegration windowsIntegration; + std::optional monitor = windowsIntegration.setupWallpaperForOneScreen(activeScreen,m_hook->windowHandle,m_hook->windowHandleWorker); + displayServer->window_set_size(godot::Vector2((real_t)monitor->size.cx, (real_t)monitor->size.cy)); - WinMonitorStats monitors; // Assuming this is your own function - const int width = static_cast(std::abs(monitors.rcMonitors[activeScreen].right - monitors.rcMonitors[activeScreen].left) / scaling) + borderWidth; - const int height = static_cast(std::abs(monitors.rcMonitors[activeScreen].top - monitors.rcMonitors[activeScreen].bottom) / scaling) + borderWidth; - - const int x = monitors.rcMonitors[activeScreen].left + m_hook->zeroPoint.x + borderOffset; // Assuming m_zeroPoint is a POINT struct - const int y = monitors.rcMonitors[activeScreen].top + m_hook->zeroPoint.y + borderOffset; - - String output = "Setup window activeScreen: " + itos(activeScreen) + " scaling: " + rtos(scaling) + " x: " + itos(x) + " y: " + itos(y) + " width: " + itos(width) + " height: " + itos(height); - UtilityFunctions::print(output); - displayServer->window_set_size(godot::Vector2((real_t)width, (real_t)height)); - - // Must be called twice for some reason when window has scaling... - if (!SetWindowPos(m_hook->windowHandle, nullptr, x, y, width, height, SWP_HIDEWINDOW)) { - UtilityFunctions::print("Could not set window pos"); - return false; - } - if (!SetWindowPos(m_hook->windowHandle, nullptr, x, y, width, height, SWP_HIDEWINDOW)) { - UtilityFunctions::print("Could not set window pos 2"); - return false; - } - - if (SetParent(m_hook->windowHandle, m_hook->windowHandleWorker) == nullptr) { - UtilityFunctions::print("Could not attach to parent window"); - return false; - } - ShowWindow(m_hook->windowHandle, SW_SHOW); const std::string windowTitle = "ScreenPlayWallpaperGodot"; SetWindowText(hwnd, windowTitle.c_str()); + ShowWindow(m_hook->windowHandle, SW_SHOW); return true; } diff --git a/ScreenPlayWallpaper/Godot/GDExtention/src/ScreenPlayGodotWallpaper.h b/ScreenPlayWallpaper/Godot/GDExtention/src/ScreenPlayGodotWallpaper.h index 6746a221..1ba1f92c 100644 --- a/ScreenPlayWallpaper/Godot/GDExtention/src/ScreenPlayGodotWallpaper.h +++ b/ScreenPlayWallpaper/Godot/GDExtention/src/ScreenPlayGodotWallpaper.h @@ -60,7 +60,7 @@ private: godot::String m_appID = ""; godot::String m_projectPath = ""; - std::unique_ptr m_hook; + std::unique_ptr m_hook; double m_timesinceLastRead = 0.0; bool m_pipeConnected = false; bool m_screenPlayConnected = false; diff --git a/ScreenPlayWallpaper/src/windowsintegration.cpp b/ScreenPlayWallpaper/src/windowsintegration.cpp index 4bde9a35..549e69bf 100644 --- a/ScreenPlayWallpaper/src/windowsintegration.cpp +++ b/ScreenPlayWallpaper/src/windowsintegration.cpp @@ -1,10 +1,7 @@ #include "windowsintegration.h" -WindowsIntegration::WindowsIntegration() -{ -} -bool WindowsHook::searchWorkerWindowToParentTo() +bool WindowsIntegration::searchWorkerWindowToParentTo() { HWND progman_hwnd = FindWindowW(L"Progman", L"Program Manager"); @@ -18,7 +15,7 @@ bool WindowsHook::searchWorkerWindowToParentTo() /*! \brief Returns scaling factor as reported by Windows. */ -float WindowsHook::getScaling(const int monitorIndex) const +float WindowsIntegration::getScaling(const int monitorIndex) const { // Get all monitors int monitorCount = GetSystemMetrics(SM_CMONITORS); @@ -54,14 +51,14 @@ float WindowsHook::getScaling(const int monitorIndex) const } } - // If we reach here, it means we couldn't find the monitor with the given index or couldn't get the DPI. + // If we reach here, it means we couldn't find the monitor with the given index or couldn't get the DPI. return 1.0f; } /*! \brief Returns true of at least one monitor has active scaling enabled. */ -bool WindowsHook::hasWindowScaling() const +bool WindowsIntegration::hasWindowScaling() const { auto enumMonitorCallback = [](HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) -> BOOL { int scaling = GetDeviceCaps(hdcMonitor, LOGPIXELSX) / 96; @@ -94,25 +91,7 @@ BOOL SearchForWorkerWindow(HWND hwnd, LPARAM lparam) return TRUE; } -ResizeResult ResizeWindowToMonitor(HWND hwnd, int monitorIndex) -{ - WinMonitorStats stats; - if (monitorIndex >= stats.rcMonitors.size()) { - return { 0, 0, 0, 0, false }; // Invalid monitor index - } - RECT monitorRect = stats.rcMonitors[monitorIndex]; - int x = monitorRect.left; - int y = monitorRect.top; - int width = monitorRect.right - monitorRect.left; - int height = monitorRect.bottom - monitorRect.top; - - BOOL result = SetWindowPos(hwnd, NULL, x, y, width, height, SWP_NOZORDER | SWP_NOACTIVATE); - - return { x, y, width, height, result != 0 }; -} - - -std::vector GetAllMonitors() { +std::vector WindowsIntegration::GetAllMonitors() { std::vector monitors; // Use the static MonitorEnumProc callback for EnumDisplayMonitors @@ -120,3 +99,46 @@ std::vector GetAllMonitors() { return monitors; } + +BOOL GetMonitorByHandle(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) +{ + + auto info = (sEnumInfo*)dwData; + if (info->hMonitor == hMonitor) + return FALSE; + ++info->iIndex; + return TRUE; +} + +BOOL FindTheDesiredWnd(HWND hWnd, LPARAM lParam) +{ + DWORD dwStyle = (DWORD)GetWindowLong(hWnd, GWL_STYLE); + if ((dwStyle & WS_MAXIMIZE) != 0) { + *(reinterpret_cast(lParam)) = hWnd; + return false; // stop enumerating + } + return true; // keep enumerating +} + +BOOL MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) { + std::vector* pMonitors = reinterpret_cast*>(dwData); + + MONITORINFOEX info; + info.cbSize = sizeof(info); + GetMonitorInfo(hMonitor, &info); + + UINT dpiX, dpiY; + GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY); + + Monitor monitor; + monitor.monitorID = hMonitor; + monitor.index = pMonitors->size(); // Set index based on the current size of the vector + monitor.position = info.rcMonitor; + monitor.size.cx = info.rcMonitor.right - info.rcMonitor.left; + monitor.size.cy = info.rcMonitor.bottom - info.rcMonitor.top; + monitor.scaleFactor = static_cast(dpiX) / 96.0f; + + pMonitors->push_back(monitor); + + return true; +} diff --git a/ScreenPlayWallpaper/src/windowsintegration.h b/ScreenPlayWallpaper/src/windowsintegration.h index 087e85b2..75f93860 100644 --- a/ScreenPlayWallpaper/src/windowsintegration.h +++ b/ScreenPlayWallpaper/src/windowsintegration.h @@ -8,6 +8,7 @@ #include #include #include +#include // Do not sort ! #include "WinUser.h" @@ -32,33 +33,9 @@ struct Monitor { }; // Static callback function for EnumDisplayMonitors -static BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) { - std::vector* pMonitors = reinterpret_cast*>(dwData); - - MONITORINFOEX info; - info.cbSize = sizeof(info); - GetMonitorInfo(hMonitor, &info); - - UINT dpiX, dpiY; - GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY); - - Monitor monitor; - monitor.monitorID = hMonitor; - monitor.index = pMonitors->size(); // Set index based on the current size of the vector - monitor.position = info.rcMonitor; - monitor.size.cx = info.rcMonitor.right - info.rcMonitor.left; - monitor.size.cy = info.rcMonitor.bottom - info.rcMonitor.top; - monitor.scaleFactor = static_cast(dpiX) / 96.0f; - - pMonitors->push_back(monitor); - - return true; -} - - -std::vector GetAllMonitors(); - - +BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData); +BOOL CALLBACK GetMonitorByHandle(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData); +BOOL CALLBACK FindTheDesiredWnd(HWND hWnd, LPARAM lParam); BOOL WINAPI SearchForWorkerWindow(HWND hwnd, LPARAM lparam); struct WinMonitorStats { @@ -96,30 +73,269 @@ struct WinMonitorStats { std::vector> sizes; }; -struct ResizeResult { - int x = 0; - int y = 0; - int width = 0; - int height = 0; - bool success = false; -}; -ResizeResult ResizeWindowToMonitor(HWND hwnd, int monitorIndex); - struct Point { int x = 0; int y = 0; }; -struct WindowsHook { +struct sEnumInfo { + int iIndex; + HMONITOR hMonitor; +}; + + +class WindowsIntegration { +public: + bool searchWorkerWindowToParentTo(); float getScaling(const int monitorIndex) const; bool hasWindowScaling() const; HWND windowHandle {}; HWND windowHandleWorker {}; Point zeroPoint; -}; + std::vector GetAllMonitors(); + + + + int GetMonitorIndex(HMONITOR hMonitor) + { + sEnumInfo info; + info.hMonitor = hMonitor; + + if (EnumDisplayMonitors(NULL, NULL, GetMonitorByHandle, (LPARAM)&info)) + return -1; + + return info.iIndex; + } + + /*! + \brief This method is called via a fixed interval to detect if a window completely + covers a monitor. If then sets visualPaused for QML to pause the content. + */ + bool checkForFullScreenWindow(HWND windowHandle) + { + + HWND hFoundWnd = nullptr; + EnumWindows(&FindTheDesiredWnd, reinterpret_cast(&hFoundWnd)); + + // True if one window has WS_MAXIMIZE + if (hFoundWnd != nullptr) { + DWORD dwFlags = 0; + HMONITOR monitor = MonitorFromWindow(hFoundWnd, dwFlags); + HMONITOR wallpaper = MonitorFromWindow(windowHandle, dwFlags); + int monitorIndex = GetMonitorIndex(monitor); + int wallpaperIndex = GetMonitorIndex(wallpaper); + // qDebug() << monitorIndex << wallpaperIndex; + + // If the window that has WS_MAXIMIZE is at the same monitor as this wallpaper + return monitorIndex == wallpaperIndex; + + } else { + return false; + } + } + + // Define the result structure + struct SpanResult { + int width = 0; + int height = 0; + bool success = false; + }; + + /* + * Adjusting a Window's Position and Size for Different Monitor DPI Scale Factors: + * * Windows allows users to set different DPI (dots per inch) scale factors for each monitor. This DPI scaling can lead to + * discrepancies in the positioning and size of windows, especially if we want to place a window on a monitor with a different + * scale factor than the one it was originally on. + * * In our scenario, we want to move and resize a window (`windowHwnd`) to fit perfectly within a target monitor. However, + * both the window and the target monitor can have different DPI scale factors, so we need to account for these when calculating + * the window's new position and size. + * * Steps: + * * 1. Retrieve the DPI scale factor for the window: + * - This gives us the current scale factor of the window based on its original monitor. + * * 2. Retrieve the DPI scale factor for the target monitor: + * - This gives us the scale factor of the monitor where we want to place the window. + * * 3. Calculate the window's new position: + * - The new position should be relative to the `WorkerW` window's coordinates. + * - Adjust the position based on the ratio of the window's DPI scale factor to the target monitor's DPI scale factor. + * This ensures that the window is positioned correctly on the monitor regardless of any differences in scale factors. + * * 4. Calculate the window's new size: + * - Adjust the size of the window based on the ratio of the window's DPI scale factor to the target monitor's DPI scale factor. + * This ensures that the window fits perfectly within the monitor, taking into account any differences in scale factors. + * * By following this approach, we can accurately position and resize the window on any monitor, regardless of differences in DPI + * scale factors. + */ + + std::optional setupWallpaperForOneScreen(const int activeScreen, HWND windowHwnd, HWND parentWindowHwnd) + { + std::vector monitors = GetAllMonitors(); + for (const auto& monitor : monitors) { + monitor.print(); + if (monitor.index != activeScreen) + continue; + + SetWindowPos(windowHwnd, HWND_TOP, + monitor.position.left, monitor.position.top, + monitor.size.cx, monitor.size.cy, + SWP_NOZORDER | SWP_NOACTIVATE); + + RECT oldRect; + GetWindowRect(windowHwnd, &oldRect); + std::cout << "Old Window Position: (" << oldRect.left << ", " << oldRect.top << ")" << std::endl; + + float windowDpiScaleFactor = static_cast(GetDpiForWindow(windowHwnd)) / 96.0f; + float targetMonitorDpiScaleFactor = monitor.scaleFactor; + std::cout << "Window DPI Scale Factor: " << windowDpiScaleFactor << std::endl; + std::cout << "Target Monitor DPI Scale Factor: " << targetMonitorDpiScaleFactor << std::endl; + + SetParent(windowHwnd, parentWindowHwnd); + RECT parentRect; + GetWindowRect(parentWindowHwnd, &parentRect); + std::cout << "WorkerW Window Position: (" << parentRect.left << ", " << parentRect.top << ")" << std::endl; + + int newX = static_cast((oldRect.left - parentRect.left) * (windowDpiScaleFactor / targetMonitorDpiScaleFactor)); + int newY = static_cast((oldRect.top - parentRect.top) * (windowDpiScaleFactor / targetMonitorDpiScaleFactor)); + std::cout << "Calculated New Position: (" << newX << ", " << newY << ")" << std::endl; + + int newWidth = static_cast(monitor.size.cx * (windowDpiScaleFactor / targetMonitorDpiScaleFactor)); + int newHeight = static_cast(monitor.size.cy * (windowDpiScaleFactor / targetMonitorDpiScaleFactor)); + std::cout << "Calculated New Size: (" << newWidth << "x" << newHeight << ")" << std::endl; + + SetWindowPos(windowHwnd, NULL, newX, newY, newWidth, newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + return {monitor}; + } + return std::nullopt; + } + /** + * Spans the window across multiple monitors. + * + * This function takes a vector of monitor indices and adjusts the window's + * size and position to span across the specified monitors. It determines the + * window's new size by finding the bounding rectangle that covers all selected + * monitors. The window's new position is set to the top-left corner of this + * bounding rectangle. + * + * Note: This function assumes that all monitors have the same DPI scaling. If + * they don't, the window may not fit perfectly across the monitors. + * + * @param activeScreens A vector containing the indices of monitors across + * which the window should span. + * + * Usage: + * setupWallpaperForMultipleScreens({0, 1}); // Spans the window across monitors 0 and 1. + * + * Internals: + * 1. For each monitor in `activeScreens`, determine its bounding rectangle. + * 2. Compute the window's new width as `rightmost - leftmost` and its new + * height as `bottommost - topmost`. + * 3. Adjust the window's position and size to fit this bounding rectangle. + */ + SpanResult setupWallpaperForMultipleScreens(const std::vector& activeScreens, HWND windowHwnd, HWND parentWindowHwnd) + { + std::vector monitors = GetAllMonitors(); + + int leftmost = INT_MAX; + int topmost = INT_MAX; + int rightmost = INT_MIN; + int bottommost = INT_MIN; + + for (const auto& monitorIndex : activeScreens) { + if (monitorIndex < monitors.size()) { + const Monitor& monitor = monitors[monitorIndex]; + leftmost = std::min(leftmost, static_cast(monitor.position.left)); + topmost = std::min(topmost, static_cast(monitor.position.top)); + rightmost = std::max(rightmost, static_cast(monitor.position.right)); + bottommost = std::max(bottommost, static_cast(monitor.position.bottom)); + } + } + + + int newWidth = rightmost - leftmost; + int newHeight = bottommost - topmost; + + RECT oldRect; + GetWindowRect(windowHwnd, &oldRect); + + float windowDpiScaleFactor = static_cast(GetDpiForWindow(windowHwnd)) / 96.0f; + + SetParent(windowHwnd, parentWindowHwnd); + RECT parentRect; + GetWindowRect(parentWindowHwnd, &parentRect); + + int newX = static_cast((leftmost - parentRect.left) * windowDpiScaleFactor); + int newY = static_cast((topmost - parentRect.top) * windowDpiScaleFactor); + + SetWindowPos(windowHwnd, NULL, newX, newY, newWidth, newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + SpanResult result; + result.width = rightmost - leftmost; + result.height = bottommost - topmost; + result.success = true; + + return result; + } + /** + * Sets up the wallpaper to span across all connected screens. + * * This function retrieves information about all connected monitors, including their positions, dimensions, and scale factors. + * It then calculates the combined dimensions needed to span the window across all these monitors, taking into account + * the scale factors. The function also handles reparenting the window to the WorkerW window, ensuring it remains below + * other desktop icons. The calculated position and dimensions are adjusted to account for any potential scaling differences + * between the window and the combined monitor setup. + * * @note This function assumes that the window's DPI scale factor is based on a default of 96 DPI. Adjustments are made + * based on this assumption. + * * @note The function currently multiplies the scale factors of all monitors to get an overall scale factor. This may need + * adjustments based on specific application needs. + * * @return SpanResult A structure containing the combined width and height of the monitors, and a success flag indicating + * whether the operation was successful. + * + * @retval SpanResult::width The combined width of all monitors after scaling. + * @retval SpanResult::height The combined height of all monitors after scaling. + * @retval SpanResult::success A boolean flag indicating the success of the operation. Currently, it always returns `true` + * assuming all operations are successful. This can be adjusted based on error checks as needed. + */ + SpanResult setupWallpaperForAllScreens( HWND windowHwnd, HWND parentWindowHwnd) { + std::vector monitors = GetAllMonitors(); + + int leftmost = INT_MAX; + int topmost = INT_MAX; + int rightmost = INT_MIN; + int bottommost = INT_MIN; + float overallScaleFactor = 1.0f; // assuming default is no scaling + + // Calculate the combined dimensions of all monitors + for (const auto& monitor : monitors) { + leftmost = std::min(leftmost, static_cast(monitor.position.left)); + topmost = std::min(topmost, static_cast(monitor.position.top)); + rightmost = std::max(rightmost, static_cast(monitor.position.right)); + bottommost = std::max(bottommost, static_cast(monitor.position.bottom)); + overallScaleFactor *= monitor.scaleFactor; // Adjust as per your scaling needs + } + + int scaledWidth = static_cast((rightmost - leftmost) * overallScaleFactor); + int scaledHeight = static_cast((bottommost - topmost) * overallScaleFactor); + + // Set the position and size of the window to span all monitors + SetWindowPos(windowHwnd, HWND_TOP, leftmost, topmost, scaledWidth, scaledHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + // Reparenting and scaling logic + RECT oldRect; + GetWindowRect(windowHwnd, &oldRect); + float windowDpiScaleFactor = static_cast(GetDpiForWindow(windowHwnd)) / 96.0f; + SetParent(windowHwnd, parentWindowHwnd); + RECT parentRect; + GetWindowRect(parentWindowHwnd, &parentRect); + int newX = static_cast((oldRect.left - parentRect.left) * (windowDpiScaleFactor / overallScaleFactor)); + int newY = static_cast((oldRect.top - parentRect.top) * (windowDpiScaleFactor / overallScaleFactor)); + + SetWindowPos(windowHwnd, NULL, newX, newY, scaledWidth, scaledHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + // Return the combined span of all monitors + SpanResult result; + result.width = scaledWidth; + result.height = scaledHeight; + result.success = true; // Assuming the operations are successful; adjust as needed + + return result; + } + -class WindowsIntegration { -public: - WindowsIntegration(); }; diff --git a/ScreenPlayWallpaper/src/winwindow.cpp b/ScreenPlayWallpaper/src/winwindow.cpp index 057d1301..78fa8483 100644 --- a/ScreenPlayWallpaper/src/winwindow.cpp +++ b/ScreenPlayWallpaper/src/winwindow.cpp @@ -149,12 +149,9 @@ ScreenPlay::WallpaperExitCode WinWindow::start() setupWindowMouseHook(); }); - - - qInfo() << "Setup " << width() << height(); m_window.setSource(QUrl("qrc:/qml/ScreenPlayWallpaper/qml/Wallpaper.qml")); - + m_window.show(); return ScreenPlay::WallpaperExitCode::Ok; } @@ -185,96 +182,15 @@ void WinWindow::destroyThis() emit qmlExit(); } -struct sEnumInfo { - int iIndex; - HMONITOR hMonitor; -}; - -BOOL CALLBACK GetMonitorByIndex(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) -{ - auto* info = (sEnumInfo*)dwData; - if (--info->iIndex < 0) { - info->hMonitor = hMonitor; - return FALSE; - } - return TRUE; -} - -/* - * Adjusting a Window's Position and Size for Different Monitor DPI Scale Factors: - * - * Windows allows users to set different DPI (dots per inch) scale factors for each monitor. This DPI scaling can lead to - * discrepancies in the positioning and size of windows, especially if we want to place a window on a monitor with a different - * scale factor than the one it was originally on. - * - * In our scenario, we want to move and resize a window (`m_windowHandle`) to fit perfectly within a target monitor. However, - * both the window and the target monitor can have different DPI scale factors, so we need to account for these when calculating - * the window's new position and size. - * - * Steps: - * - * 1. Retrieve the DPI scale factor for the window: - * - This gives us the current scale factor of the window based on its original monitor. - * - * 2. Retrieve the DPI scale factor for the target monitor: - * - This gives us the scale factor of the monitor where we want to place the window. - * - * 3. Calculate the window's new position: - * - The new position should be relative to the `WorkerW` window's coordinates. - * - Adjust the position based on the ratio of the window's DPI scale factor to the target monitor's DPI scale factor. - * This ensures that the window is positioned correctly on the monitor regardless of any differences in scale factors. - * - * 4. Calculate the window's new size: - * - Adjust the size of the window based on the ratio of the window's DPI scale factor to the target monitor's DPI scale factor. - * This ensures that the window fits perfectly within the monitor, taking into account any differences in scale factors. - * - * By following this approach, we can accurately position and resize the window on any monitor, regardless of differences in DPI - * scale factors. - */ void WinWindow::setupWallpaperForOneScreen(int activeScreen) { - std::vector monitors = GetAllMonitors(); - for (const auto& monitor : monitors) { - monitor.print(); - if (monitor.index != activeScreen) - continue; - - SetWindowPos(m_windowHandle, HWND_TOP, - monitor.position.left, monitor.position.top, - monitor.size.cx, monitor.size.cy, - SWP_NOZORDER | SWP_NOACTIVATE); - setWidth(monitor.size.cx); - setHeight(monitor.size.cy); - m_window.setWidth(width()); - m_window.setHeight(height()); - - RECT oldRect; - GetWindowRect(m_windowHandle, &oldRect); - std::cout << "Old Window Position: (" << oldRect.left << ", " << oldRect.top << ")" << std::endl; - - float windowDpiScaleFactor = static_cast(GetDpiForWindow(m_windowHandle)) / 96.0f; - float targetMonitorDpiScaleFactor = monitor.scaleFactor; - std::cout << "Window DPI Scale Factor: " << windowDpiScaleFactor << std::endl; - std::cout << "Target Monitor DPI Scale Factor: " << targetMonitorDpiScaleFactor << std::endl; - - SetParent(m_windowHandle, m_windowHandleWorker); - RECT parentRect; - GetWindowRect(m_windowHandleWorker, &parentRect); - std::cout << "WorkerW Window Position: (" << parentRect.left << ", " << parentRect.top << ")" << std::endl; - - int newX = static_cast((oldRect.left - parentRect.left) * (windowDpiScaleFactor / targetMonitorDpiScaleFactor)); - int newY = static_cast((oldRect.top - parentRect.top) * (windowDpiScaleFactor / targetMonitorDpiScaleFactor)); - std::cout << "Calculated New Position: (" << newX << ", " << newY << ")" << std::endl; - - int newWidth = static_cast(monitor.size.cx * (windowDpiScaleFactor / targetMonitorDpiScaleFactor)); - int newHeight = static_cast(monitor.size.cy * (windowDpiScaleFactor / targetMonitorDpiScaleFactor)); - std::cout << "Calculated New Size: (" << newWidth << "x" << newHeight << ")" << std::endl; - - SetWindowPos(m_windowHandle, NULL, newX, newY, newWidth, newHeight, SWP_NOZORDER | SWP_NOACTIVATE); - - } - return; + WindowsIntegration windowsIntegration; + std::optional monitor = windowsIntegration.setupWallpaperForOneScreen(activeScreen,m_windowHandle,m_windowHandleWorker); + setWidth(monitor->size.cx); + setHeight(monitor->size.cy); + m_window.setWidth(width()); + m_window.setHeight(height()); } /*! @@ -282,43 +198,12 @@ void WinWindow::setupWallpaperForOneScreen(int activeScreen) */ void WinWindow::setupWallpaperForAllScreens() { - WinMonitorStats monitors; - QRect rect; - for (int i = 0; i < monitors.iMonitors.size(); i++) { - const int width = std::abs(monitors.rcMonitors[i].right - monitors.rcMonitors[i].left); - const int height = std::abs(monitors.rcMonitors[i].top - monitors.rcMonitors[i].bottom); - qInfo() << width << height; - rect.setWidth(rect.width() + width); - rect.setHeight(rect.height() + height); - } - int offsetX = 0; - int offsetY = 0; - for (int i = 0; i < monitors.iMonitors.size(); i++) { - const int x = monitors.rcMonitors[i].left; - const int y = monitors.rcMonitors[i].top; - qInfo() << x << y; - if (x < offsetX) { - offsetX = x; - } - if (y < offsetY) { - offsetY += y; - } - } - if (!SetWindowPos(m_windowHandle, nullptr, offsetX, offsetY, rect.width(), rect.height(), SWP_NOSIZE | SWP_NOMOVE)) { - qFatal("Could not set window pos: "); - } - if (!SetWindowPos(m_windowHandle, nullptr, offsetX, offsetY, rect.width(), rect.height(), SWP_NOSIZE | SWP_NOMOVE)) { - qFatal("Could not set window pos: "); - } - if (SetParent(m_windowHandle, m_windowHandleWorker) == nullptr) { - qFatal("Could not attach to parent window"); - } - qInfo() << rect.width() << rect.height() << offsetX << offsetY; - m_window.setHeight(rect.height()); - m_window.setWidth(rect.width()); - m_window.setY(offsetY); - m_window.setX(offsetX + 1920); - qInfo() << m_window.geometry(); + WindowsIntegration windowsIntegration; + WindowsIntegration::SpanResult span = windowsIntegration.setupWallpaperForAllScreens(m_windowHandle,m_windowHandleWorker); + setWidth(span.width); + setHeight(span.height); + m_window.setWidth(width()); + m_window.setHeight(height()); } /*! @@ -326,31 +211,13 @@ void WinWindow::setupWallpaperForAllScreens() */ void WinWindow::setupWallpaperForMultipleScreens(const QVector& activeScreensList) { - QRect rect; - QScreen* upperLeftScreen { nullptr }; - // Check for the upper left screen first so we get x and y positions - for (const int screen : activeScreensList) { - QScreen* screenTmp = QGuiApplication::screens().at(screen); - if (upperLeftScreen != nullptr) { - if (screenTmp->geometry().x() < upperLeftScreen->geometry().x() || screenTmp->geometry().y() < upperLeftScreen->geometry().y()) { - upperLeftScreen = screenTmp; - } - } else { - upperLeftScreen = screenTmp; - } - rect.setWidth(screenTmp->geometry().width() + rect.width()); - rect.setHeight(screenTmp->geometry().height() + rect.height()); - } - - rect.setX(upperLeftScreen->geometry().x()); - rect.setY(upperLeftScreen->geometry().y()); - - if (!SetWindowPos(m_windowHandle, nullptr, rect.x() + m_zeroPoint.x(), rect.y() + m_zeroPoint.y(), rect.width(), rect.height(), SWP_SHOWWINDOW)) { - qFatal("Could not set window pos: "); - } - if (SetParent(m_windowHandle, m_windowHandleWorker) == nullptr) { - qFatal("Could not attach to parent window"); - } + std::vector activeScreens(activeScreensList.begin(), activeScreensList.end()); + WindowsIntegration windowsIntegration; + WindowsIntegration::SpanResult span = windowsIntegration.setupWallpaperForMultipleScreens(activeScreens,m_windowHandle,m_windowHandleWorker); + setWidth(span.width); + setHeight(span.height); + m_window.setWidth(width()); + m_window.setHeight(height()); } bool WinWindow::searchWorkerWindowToParentTo() @@ -364,63 +231,6 @@ bool WinWindow::searchWorkerWindowToParentTo() return EnumWindows(SearchForWorkerWindow, reinterpret_cast(&m_windowHandleWorker)); } -/*! - \brief Returns scaling factor as reported by Windows. -*/ -float WinWindow::getScaling(const int monitorIndex) const -{ - // Get all monitors - int monitorCount = GetSystemMetrics(SM_CMONITORS); - - if (monitorIndex < 0 || monitorIndex >= monitorCount) { - // Invalid monitor index - return 1.0f; - } - - DISPLAY_DEVICE displayDevice; - ZeroMemory(&displayDevice, sizeof(displayDevice)); - displayDevice.cb = sizeof(displayDevice); - - // Enumerate through monitors until we find the one we're looking for - for (int i = 0; EnumDisplayDevices(NULL, i, &displayDevice, 0); i++) { - if (i == monitorIndex) { - DEVMODE devMode; - ZeroMemory(&devMode, sizeof(devMode)); - devMode.dmSize = sizeof(devMode); - - // Get settings for selected monitor - if (!EnumDisplaySettings(displayDevice.DeviceName, ENUM_CURRENT_SETTINGS, &devMode)) { - // Unable to get monitor settings - return 1.0f; - } - - // Get DPI for selected monitor - HMONITOR hMonitor = MonitorFromPoint({ devMode.dmPosition.x, devMode.dmPosition.y }, MONITOR_DEFAULTTONEAREST); - UINT dpiX = 0, dpiY = 0; - if (SUCCEEDED(GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY))) { - return (float)dpiX / 96.0f; // Standard DPI is 96 - } - } - } - - // If we reach here, it means we couldn't find the monitor with the given index or couldn't get the DPI. - return 1.0f; -} - -/*! - \brief Returns true of at least one monitor has active scaling enabled. -*/ -bool WinWindow::hasWindowScaling() const -{ - const auto screens = QGuiApplication::screens(); - for (int i = 0; i < screens.count(); i++) { - if (getScaling(i) != 1) { - return true; - } - } - return false; -} - /*! \brief Sets the size and the parent to the worker handle to be displayed behind the desktop icons. @@ -431,17 +241,6 @@ void WinWindow::configureWindowGeometry() qFatal("No worker window found"); } - RECT rect {}; - if (!GetWindowRect(m_windowHandleWorker, &rect)) { - qFatal("Unable to get WindoeRect from worker"); - } - - // Windows coordante system begins at 0x0 at the - // main monitors upper left and not at the most left top monitor. - // This can be easily read from the worker window. - // m_zeroPoint = { std::abs(rect.left), std::abs(rect.top) }; - // g_globalOffset = m_zeroPoint; - // WARNING: Setting Window flags must be called *here*! SetWindowLongPtr(m_windowHandle, GWL_EXSTYLE, WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR | WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT); SetWindowLongPtr(m_windowHandle, GWL_STYLE, WS_POPUP); @@ -455,75 +254,9 @@ void WinWindow::configureWindowGeometry() } else if (activeScreensList().length() > 1) { setupWallpaperForMultipleScreens(activeScreensList()); } - - m_window.show(); } -BOOL CALLBACK FindTheDesiredWnd(HWND hWnd, LPARAM lParam) -{ - DWORD dwStyle = (DWORD)GetWindowLong(hWnd, GWL_STYLE); - if ((dwStyle & WS_MAXIMIZE) != 0) { - *(reinterpret_cast(lParam)) = hWnd; - return false; // stop enumerating - } - return true; // keep enumerating -} - -BOOL CALLBACK GetMonitorByHandle(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) -{ - Q_UNUSED(hdcMonitor) - Q_UNUSED(lprcMonitor) - - auto info = (sEnumInfo*)dwData; - if (info->hMonitor == hMonitor) - return FALSE; - ++info->iIndex; - return TRUE; -} - -int GetMonitorIndex(HMONITOR hMonitor) -{ - sEnumInfo info; - info.hMonitor = hMonitor; - - if (EnumDisplayMonitors(NULL, NULL, GetMonitorByHandle, (LPARAM)&info)) - return -1; - - return info.iIndex; -} - -/*! - \brief This method is called via a fixed interval to detect if a window completely - covers a monitor. If then sets visualPaused for QML to pause the content. -*/ -void WinWindow::checkForFullScreenWindow() -{ - - HWND hFoundWnd = nullptr; - EnumWindows(&FindTheDesiredWnd, reinterpret_cast(&hFoundWnd)); - - // True if one window has WS_MAXIMIZE - if (hFoundWnd != nullptr) { - DWORD dwFlags = 0; - HMONITOR monitor = MonitorFromWindow(hFoundWnd, dwFlags); - HMONITOR wallpaper = MonitorFromWindow(m_windowHandle, dwFlags); - int monitorIndex = GetMonitorIndex(monitor); - int wallpaperIndex = GetMonitorIndex(wallpaper); - // qDebug() << monitorIndex << wallpaperIndex; - - // If the window that has WS_MAXIMIZE is at the same monitor as this wallpaper - if (monitorIndex == wallpaperIndex) { - - setVisualsPaused(true); - } else { - setVisualsPaused(false); - } - - } else { - setVisualsPaused(false); - } -} /*! \brief Custom exit method to force a redraw of the window so that the default desktop wallpaper can be seen agian. @@ -554,4 +287,10 @@ void WinWindow::clearComponentCache() m_window.engine()->clearComponentCache(); } +void WinWindow::checkForFullScreenWindow(){ + bool hasFullscreenWindow = WindowsIntegration().checkForFullScreenWindow(m_windowHandle); + + setVisualsPaused(hasFullscreenWindow); +} + #include "moc_winwindow.cpp" diff --git a/ScreenPlayWallpaper/src/winwindow.h b/ScreenPlayWallpaper/src/winwindow.h index d2b674a9..6e47a0b4 100644 --- a/ScreenPlayWallpaper/src/winwindow.h +++ b/ScreenPlayWallpaper/src/winwindow.h @@ -55,8 +55,6 @@ private: void setupWindowMouseHook(); bool searchWorkerWindowToParentTo(); void configureWindowGeometry(); - bool hasWindowScaling() const; - float getScaling(const int monitorIndex) const; private slots: void checkForFullScreenWindow();