2020-02-10 14:59:51 +01:00
|
|
|
#include "pch.h"
|
|
|
|
|
#include "util.h"
|
|
|
|
|
|
2020-12-15 15:16:09 +03:00
|
|
|
#include <common/display/dpi_aware.h>
|
2022-03-10 09:59:31 -08:00
|
|
|
#include <common/logger/logger.h>
|
2020-12-15 15:16:09 +03:00
|
|
|
#include <common/utils/process_path.h>
|
|
|
|
|
#include <common/utils/window.h>
|
2022-02-14 18:22:05 +00:00
|
|
|
#include <common/utils/excluded_apps.h>
|
2020-02-10 14:59:51 +01:00
|
|
|
|
2020-08-25 18:55:29 +02:00
|
|
|
#include <array>
|
2020-08-21 12:53:03 +02:00
|
|
|
#include <complex>
|
2020-07-22 10:39:13 +02:00
|
|
|
|
2022-02-23 17:25:28 +03:00
|
|
|
#include <common/display/dpi_aware.h>
|
2020-06-17 11:55:14 +02:00
|
|
|
|
2022-02-23 17:25:28 +03:00
|
|
|
#include <FancyZonesLib/FancyZonesWindowProperties.h>
|
2020-03-24 18:50:26 +01:00
|
|
|
|
2020-08-24 19:38:15 +02:00
|
|
|
namespace FancyZonesUtils
|
2020-03-24 18:50:26 +01:00
|
|
|
{
|
2020-08-24 19:38:15 +02:00
|
|
|
typedef BOOL(WINAPI* GetDpiForMonitorInternalFunc)(HMONITOR, UINT, UINT*, UINT*);
|
2020-12-25 12:49:58 +03:00
|
|
|
|
2020-08-24 19:38:15 +02:00
|
|
|
UINT GetDpiForMonitor(HMONITOR monitor) noexcept
|
2020-03-24 18:50:26 +01:00
|
|
|
{
|
2020-08-24 19:38:15 +02:00
|
|
|
UINT dpi{};
|
|
|
|
|
if (wil::unique_hmodule user32{ LoadLibrary(L"user32.dll") })
|
2020-03-24 18:50:26 +01:00
|
|
|
{
|
2020-08-24 19:38:15 +02:00
|
|
|
if (auto func = reinterpret_cast<GetDpiForMonitorInternalFunc>(GetProcAddress(user32.get(), "GetDpiForMonitorInternal")))
|
2020-03-24 18:50:26 +01:00
|
|
|
{
|
2020-08-24 19:38:15 +02:00
|
|
|
func(monitor, 0, &dpi, &dpi);
|
2020-03-24 18:50:26 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-24 19:38:15 +02:00
|
|
|
if (dpi == 0)
|
|
|
|
|
{
|
|
|
|
|
if (wil::unique_hdc hdc{ GetDC(nullptr) })
|
|
|
|
|
{
|
|
|
|
|
dpi = GetDeviceCaps(hdc.get(), LOGPIXELSX);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-03-24 18:50:26 +01:00
|
|
|
|
2020-08-24 19:38:15 +02:00
|
|
|
return (dpi == 0) ? DPIAware::DEFAULT_DPI : dpi;
|
|
|
|
|
}
|
2020-03-24 18:50:26 +01:00
|
|
|
|
2020-08-24 19:38:15 +02:00
|
|
|
void OrderMonitors(std::vector<std::pair<HMONITOR, RECT>>& monitorInfo)
|
2020-03-24 18:50:26 +01:00
|
|
|
{
|
2020-08-24 19:38:15 +02:00
|
|
|
const size_t nMonitors = monitorInfo.size();
|
|
|
|
|
// blocking[i][j] - whether monitor i blocks monitor j in the ordering, i.e. monitor i should go before monitor j
|
|
|
|
|
std::vector<std::vector<bool>> blocking(nMonitors, std::vector<bool>(nMonitors, false));
|
|
|
|
|
|
|
|
|
|
// blockingCount[j] - the number of monitors which block monitor j
|
|
|
|
|
std::vector<size_t> blockingCount(nMonitors, 0);
|
2020-03-24 18:50:26 +01:00
|
|
|
|
|
|
|
|
for (size_t i = 0; i < nMonitors; i++)
|
|
|
|
|
{
|
2020-08-24 19:38:15 +02:00
|
|
|
RECT rectI = monitorInfo[i].second;
|
|
|
|
|
for (size_t j = 0; j < nMonitors; j++)
|
2020-03-24 18:50:26 +01:00
|
|
|
{
|
2020-08-24 19:38:15 +02:00
|
|
|
RECT rectJ = monitorInfo[j].second;
|
|
|
|
|
blocking[i][j] = rectI.top < rectJ.bottom && rectI.left < rectJ.right && i != j;
|
|
|
|
|
if (blocking[i][j])
|
|
|
|
|
{
|
|
|
|
|
blockingCount[j]++;
|
|
|
|
|
}
|
2020-03-24 18:50:26 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-24 19:38:15 +02:00
|
|
|
// used[i] - whether the sorting algorithm has used monitor i so far
|
|
|
|
|
std::vector<bool> used(nMonitors, false);
|
|
|
|
|
|
|
|
|
|
// the sorted sequence of monitors
|
|
|
|
|
std::vector<std::pair<HMONITOR, RECT>> sortedMonitorInfo;
|
|
|
|
|
|
|
|
|
|
for (size_t iteration = 0; iteration < nMonitors; iteration++)
|
2020-03-24 18:50:26 +01:00
|
|
|
{
|
2020-08-24 19:38:15 +02:00
|
|
|
// Indices of candidates to become the next monitor in the sequence
|
|
|
|
|
std::vector<size_t> candidates;
|
|
|
|
|
|
|
|
|
|
// First, find indices of all unblocked monitors
|
2020-03-24 18:50:26 +01:00
|
|
|
for (size_t i = 0; i < nMonitors; i++)
|
|
|
|
|
{
|
2020-08-24 19:38:15 +02:00
|
|
|
if (blockingCount[i] == 0 && !used[i])
|
2020-03-24 18:50:26 +01:00
|
|
|
{
|
|
|
|
|
candidates.push_back(i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-24 19:38:15 +02:00
|
|
|
// In the unlikely event that there are no unblocked monitors, declare all unused monitors as candidates.
|
|
|
|
|
if (candidates.empty())
|
|
|
|
|
{
|
|
|
|
|
for (size_t i = 0; i < nMonitors; i++)
|
|
|
|
|
{
|
|
|
|
|
if (!used[i])
|
|
|
|
|
{
|
|
|
|
|
candidates.push_back(i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-03-24 18:50:26 +01:00
|
|
|
|
2020-08-24 19:38:15 +02:00
|
|
|
// Pick the lexicographically smallest monitor as the next one
|
|
|
|
|
size_t smallest = candidates[0];
|
|
|
|
|
for (size_t j = 1; j < candidates.size(); j++)
|
2020-03-24 18:50:26 +01:00
|
|
|
{
|
2020-08-24 19:38:15 +02:00
|
|
|
size_t current = candidates[j];
|
|
|
|
|
|
|
|
|
|
// Compare (top, left) lexicographically
|
|
|
|
|
if (std::tie(monitorInfo[current].second.top, monitorInfo[current].second.left) <
|
|
|
|
|
std::tie(monitorInfo[smallest].second.top, monitorInfo[smallest].second.left))
|
|
|
|
|
{
|
|
|
|
|
smallest = current;
|
|
|
|
|
}
|
2020-03-24 18:50:26 +01:00
|
|
|
}
|
|
|
|
|
|
2020-08-24 19:38:15 +02:00
|
|
|
used[smallest] = true;
|
|
|
|
|
sortedMonitorInfo.push_back(monitorInfo[smallest]);
|
|
|
|
|
for (size_t i = 0; i < nMonitors; i++)
|
2020-03-24 18:50:26 +01:00
|
|
|
{
|
2020-08-24 19:38:15 +02:00
|
|
|
if (blocking[smallest][i])
|
|
|
|
|
{
|
|
|
|
|
blockingCount[i]--;
|
|
|
|
|
}
|
2020-03-24 18:50:26 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-24 19:38:15 +02:00
|
|
|
monitorInfo = std::move(sortedMonitorInfo);
|
|
|
|
|
}
|
2020-04-10 16:09:08 +02:00
|
|
|
|
2020-08-24 19:38:15 +02:00
|
|
|
bool IsValidGuid(const std::wstring& str)
|
|
|
|
|
{
|
|
|
|
|
GUID id;
|
|
|
|
|
return SUCCEEDED(CLSIDFromString(str.c_str(), &id));
|
|
|
|
|
}
|
2020-07-22 10:39:13 +02:00
|
|
|
|
2022-01-17 11:50:24 +03:00
|
|
|
std::optional<GUID> GuidFromString(const std::wstring& str) noexcept
|
|
|
|
|
{
|
|
|
|
|
GUID id;
|
|
|
|
|
if (SUCCEEDED(CLSIDFromString(str.c_str(), &id)))
|
|
|
|
|
{
|
|
|
|
|
return id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-07 13:18:52 +03:00
|
|
|
std::optional<std::wstring> GuidToString(const GUID& guid) noexcept
|
|
|
|
|
{
|
|
|
|
|
wil::unique_cotaskmem_string guidString;
|
|
|
|
|
if (SUCCEEDED(StringFromCLSID(guid, &guidString)))
|
|
|
|
|
{
|
|
|
|
|
return guidString.get();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-24 19:38:15 +02:00
|
|
|
size_t ChooseNextZoneByPosition(DWORD vkCode, RECT windowRect, const std::vector<RECT>& zoneRects) noexcept
|
|
|
|
|
{
|
|
|
|
|
using complex = std::complex<double>;
|
|
|
|
|
const size_t invalidResult = zoneRects.size();
|
|
|
|
|
const double inf = 1e100;
|
|
|
|
|
const double eccentricity = 2.0;
|
|
|
|
|
|
|
|
|
|
auto rectCenter = [](RECT rect) {
|
2020-12-15 15:16:09 +03:00
|
|
|
return complex{
|
2020-08-24 19:38:15 +02:00
|
|
|
0.5 * rect.left + 0.5 * rect.right,
|
|
|
|
|
0.5 * rect.top + 0.5 * rect.bottom
|
|
|
|
|
};
|
2020-08-21 12:53:03 +02:00
|
|
|
};
|
|
|
|
|
|
2020-08-24 19:38:15 +02:00
|
|
|
auto distance = [&](complex arrowDirection, complex zoneDirection) {
|
|
|
|
|
double result = inf;
|
2020-08-21 12:53:03 +02:00
|
|
|
|
2020-08-24 19:38:15 +02:00
|
|
|
try
|
2020-08-21 12:53:03 +02:00
|
|
|
{
|
2020-08-24 19:38:15 +02:00
|
|
|
double scalarProduct = (arrowDirection * conj(zoneDirection)).real();
|
|
|
|
|
if (scalarProduct <= 0.0)
|
|
|
|
|
{
|
|
|
|
|
return inf;
|
|
|
|
|
}
|
2020-08-21 12:53:03 +02:00
|
|
|
|
2020-08-24 19:38:15 +02:00
|
|
|
// no need to divide by abs(arrowDirection) because it's = 1
|
|
|
|
|
double cosAngle = scalarProduct / abs(zoneDirection);
|
|
|
|
|
double tanAngle = abs(tan(acos(cosAngle)));
|
2020-08-21 12:53:03 +02:00
|
|
|
|
2020-08-24 19:38:15 +02:00
|
|
|
if (tanAngle > 10)
|
|
|
|
|
{
|
|
|
|
|
// The angle is too wide
|
|
|
|
|
return inf;
|
|
|
|
|
}
|
2020-08-21 12:53:03 +02:00
|
|
|
|
2020-08-24 19:38:15 +02:00
|
|
|
// find the intersection with the ellipse with given eccentricity and major axis along arrowDirection
|
|
|
|
|
double intersectY = 2 * eccentricity / (1.0 + eccentricity * eccentricity * tanAngle * tanAngle);
|
|
|
|
|
double distanceEstimate = scalarProduct / intersectY;
|
2020-08-21 12:53:03 +02:00
|
|
|
|
2020-08-24 19:38:15 +02:00
|
|
|
if (std::isfinite(distanceEstimate))
|
|
|
|
|
{
|
|
|
|
|
result = distanceEstimate;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (...)
|
2020-08-21 12:53:03 +02:00
|
|
|
{
|
|
|
|
|
}
|
2020-08-24 19:38:15 +02:00
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
};
|
|
|
|
|
std::vector<std::pair<size_t, complex>> candidateCenters;
|
|
|
|
|
for (size_t i = 0; i < zoneRects.size(); i++)
|
2020-08-21 12:53:03 +02:00
|
|
|
{
|
2020-08-24 19:38:15 +02:00
|
|
|
auto center = rectCenter(zoneRects[i]);
|
|
|
|
|
|
|
|
|
|
// Offset the zone slightly, to differentiate in case there are overlapping zones
|
|
|
|
|
center += 0.001 * (i + 1);
|
|
|
|
|
|
|
|
|
|
candidateCenters.emplace_back(i, center);
|
2020-08-21 12:53:03 +02:00
|
|
|
}
|
|
|
|
|
|
2020-08-24 19:38:15 +02:00
|
|
|
complex directionVector, windowCenter = rectCenter(windowRect);
|
2020-08-21 12:53:03 +02:00
|
|
|
|
2020-08-24 19:38:15 +02:00
|
|
|
switch (vkCode)
|
|
|
|
|
{
|
|
|
|
|
case VK_UP:
|
|
|
|
|
directionVector = { 0.0, -1.0 };
|
|
|
|
|
break;
|
|
|
|
|
case VK_DOWN:
|
|
|
|
|
directionVector = { 0.0, 1.0 };
|
|
|
|
|
break;
|
|
|
|
|
case VK_LEFT:
|
|
|
|
|
directionVector = { -1.0, 0.0 };
|
|
|
|
|
break;
|
|
|
|
|
case VK_RIGHT:
|
|
|
|
|
directionVector = { 1.0, 0.0 };
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return invalidResult;
|
|
|
|
|
}
|
2020-08-21 12:53:03 +02:00
|
|
|
|
2020-08-24 19:38:15 +02:00
|
|
|
size_t closestIdx = invalidResult;
|
|
|
|
|
double smallestDistance = inf;
|
2020-08-21 12:53:03 +02:00
|
|
|
|
2020-08-24 19:38:15 +02:00
|
|
|
for (auto [zoneIdx, zoneCenter] : candidateCenters)
|
|
|
|
|
{
|
|
|
|
|
double dist = distance(directionVector, zoneCenter - windowCenter);
|
|
|
|
|
if (dist < smallestDistance)
|
|
|
|
|
{
|
|
|
|
|
smallestDistance = dist;
|
|
|
|
|
closestIdx = zoneIdx;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-08-21 12:53:03 +02:00
|
|
|
|
2020-08-24 19:38:15 +02:00
|
|
|
return closestIdx;
|
2020-08-21 12:53:03 +02:00
|
|
|
}
|
|
|
|
|
|
2021-11-11 19:23:22 +02:00
|
|
|
RECT PrepareRectForCycling(RECT windowRect, RECT workAreaRect, DWORD vkCode) noexcept
|
2020-08-21 12:53:03 +02:00
|
|
|
{
|
2020-08-24 19:38:15 +02:00
|
|
|
LONG deltaX = 0, deltaY = 0;
|
|
|
|
|
switch (vkCode)
|
2020-08-21 12:53:03 +02:00
|
|
|
{
|
2020-08-24 19:38:15 +02:00
|
|
|
case VK_UP:
|
2021-11-11 19:23:22 +02:00
|
|
|
deltaY = workAreaRect.bottom - workAreaRect.top;
|
2020-08-24 19:38:15 +02:00
|
|
|
break;
|
|
|
|
|
case VK_DOWN:
|
2021-11-11 19:23:22 +02:00
|
|
|
deltaY = workAreaRect.top - workAreaRect.bottom;
|
2020-08-24 19:38:15 +02:00
|
|
|
break;
|
|
|
|
|
case VK_LEFT:
|
2021-11-11 19:23:22 +02:00
|
|
|
deltaX = workAreaRect.right - workAreaRect.left;
|
2020-08-24 19:38:15 +02:00
|
|
|
break;
|
|
|
|
|
case VK_RIGHT:
|
2021-11-11 19:23:22 +02:00
|
|
|
deltaX = workAreaRect.left - workAreaRect.right;
|
2020-08-21 12:53:03 +02:00
|
|
|
}
|
|
|
|
|
|
2020-08-24 19:38:15 +02:00
|
|
|
windowRect.left += deltaX;
|
|
|
|
|
windowRect.right += deltaX;
|
|
|
|
|
windowRect.top += deltaY;
|
|
|
|
|
windowRect.bottom += deltaY;
|
2020-08-21 12:53:03 +02:00
|
|
|
|
2020-08-24 19:38:15 +02:00
|
|
|
return windowRect;
|
2020-08-21 12:53:03 +02:00
|
|
|
}
|
|
|
|
|
}
|