mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-06-21 03:52:07 -05:00
Refactor window platform helper functions
This commit is contained in:
parent
ee1d0136d9
commit
930e8b38d4
|
|
@ -178,47 +178,19 @@ void AdvSceneSwitcher::on_ignoreWindows_currentRowChanged(int idx)
|
|||
}
|
||||
}
|
||||
|
||||
void checkWindowTitleSwitchDirect(WindowSwitch &s,
|
||||
std::string ¤tWindowTitle, bool &match,
|
||||
OBSWeakSource &scene,
|
||||
OBSWeakSource &transition)
|
||||
static bool windowInfoMatchesSwitch(const WindowInfo &info,
|
||||
const WindowSwitch &s)
|
||||
{
|
||||
bool focus = (!s.focus || s.window == currentWindowTitle);
|
||||
bool fullscreen = (!s.fullscreen || IsFullscreen(s.window));
|
||||
bool max = (!s.maximized || IsMaximized(s.window));
|
||||
|
||||
if (focus && fullscreen && max) {
|
||||
match = true;
|
||||
scene = s.getScene();
|
||||
transition = s.transition;
|
||||
if (s.focus && !info.focused) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void checkWindowTitleSwitchRegex(WindowSwitch &s,
|
||||
std::string ¤tWindowTitle,
|
||||
std::vector<std::string> windowList,
|
||||
bool &match, OBSWeakSource &scene,
|
||||
OBSWeakSource &transition)
|
||||
{
|
||||
for (auto &window : windowList) {
|
||||
try {
|
||||
std::regex expr(s.window);
|
||||
if (!std::regex_match(window, expr)) {
|
||||
continue;
|
||||
}
|
||||
} catch (const std::regex_error &) {
|
||||
}
|
||||
|
||||
bool focus = (!s.focus || window == currentWindowTitle);
|
||||
bool fullscreen = (!s.fullscreen || IsFullscreen(window));
|
||||
bool max = (!s.maximized || IsMaximized(window));
|
||||
|
||||
if (focus && fullscreen && max) {
|
||||
match = true;
|
||||
scene = s.getScene();
|
||||
transition = s.transition;
|
||||
}
|
||||
if (s.fullscreen && !info.fullscreen) {
|
||||
return false;
|
||||
}
|
||||
if (s.maximized && !info.maximized) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SwitcherData::checkWindowTitleSwitch(OBSWeakSource &scene,
|
||||
|
|
@ -228,23 +200,37 @@ bool SwitcherData::checkWindowTitleSwitch(OBSWeakSource &scene,
|
|||
return false;
|
||||
}
|
||||
|
||||
std::string currentWindowTitle = switcher->currentTitle;
|
||||
bool match = false;
|
||||
const auto windowList = GetWindowList();
|
||||
|
||||
WindowQueryOptions options;
|
||||
options.focus = true;
|
||||
options.fullscreen = true;
|
||||
options.maximized = true;
|
||||
const auto windows = GetWindows(options);
|
||||
|
||||
for (WindowSwitch &s : windowSwitches) {
|
||||
if (!s.initialized()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (std::find(windowList.begin(), windowList.end(), s.window) !=
|
||||
windowList.end()) {
|
||||
checkWindowTitleSwitchDirect(s, currentWindowTitle,
|
||||
match, scene, transition);
|
||||
} else {
|
||||
checkWindowTitleSwitchRegex(s, currentWindowTitle,
|
||||
windowList, match, scene,
|
||||
transition);
|
||||
for (const auto &info : windows) {
|
||||
bool titleMatch = false;
|
||||
try {
|
||||
std::regex expr(s.window);
|
||||
titleMatch = info.title == s.window ||
|
||||
std::regex_match(info.title, expr);
|
||||
} catch (const std::regex_error &) {
|
||||
titleMatch = info.title == s.window;
|
||||
}
|
||||
|
||||
if (!titleMatch || !windowInfoMatchesSwitch(info, s)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
match = true;
|
||||
scene = s.getScene();
|
||||
transition = s.transition;
|
||||
break;
|
||||
}
|
||||
|
||||
if (match) {
|
||||
|
|
|
|||
|
|
@ -236,17 +236,65 @@ std::string getWindowName(Window window)
|
|||
return windowTitle;
|
||||
}
|
||||
|
||||
std::vector<std::string> GetWindowList()
|
||||
std::vector<WindowInfo> GetWindows(const WindowQueryOptions &options)
|
||||
{
|
||||
std::vector<std::string> windows;
|
||||
for (auto window : getTopLevelWindows()) {
|
||||
const std::string foregroundTitle = GetCurrentWindowTitle();
|
||||
const auto topLevel = getTopLevelWindows();
|
||||
auto display = disp();
|
||||
|
||||
std::vector<WindowInfo> result;
|
||||
result.reserve(topLevel.size());
|
||||
|
||||
for (auto window : topLevel) {
|
||||
auto name = getWindowName(window);
|
||||
if (name.empty()) {
|
||||
continue;
|
||||
}
|
||||
windows.emplace_back(name);
|
||||
|
||||
WindowInfo info;
|
||||
info.title = name;
|
||||
|
||||
if (options.focus) {
|
||||
info.focused = (name == foregroundTitle);
|
||||
}
|
||||
|
||||
if (options.fullscreen || options.maximized) {
|
||||
QStringList states = getStates(window);
|
||||
if (options.fullscreen) {
|
||||
info.fullscreen = states.contains(
|
||||
"_NET_WM_STATE_FULLSCREEN");
|
||||
}
|
||||
if (options.maximized) {
|
||||
info.maximized =
|
||||
states.contains(
|
||||
"_NET_WM_STATE_MAXIMIZED_VERT") &&
|
||||
states.contains(
|
||||
"_NET_WM_STATE_MAXIMIZED_HORZ");
|
||||
}
|
||||
}
|
||||
|
||||
if (options.geometry && display) {
|
||||
XWindowAttributes attrs;
|
||||
if (XGetWindowAttributes(display, window, &attrs)) {
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
Window child;
|
||||
XTranslateCoordinates(
|
||||
display, window,
|
||||
DefaultRootWindow(display), 0, 0, &x,
|
||||
&y, &child);
|
||||
info.x = x;
|
||||
info.y = y;
|
||||
info.width = attrs.width;
|
||||
info.height = attrs.height;
|
||||
}
|
||||
}
|
||||
|
||||
// windowClass and text not implemented on Linux
|
||||
result.emplace_back(std::move(info));
|
||||
}
|
||||
return windows;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int getActiveWindow(Window *&window)
|
||||
|
|
@ -290,103 +338,6 @@ std::string GetCurrentWindowTitle()
|
|||
return name;
|
||||
}
|
||||
|
||||
bool windowStatesAreSet(const std::string &windowTitle,
|
||||
std::vector<QString> &expectedStates)
|
||||
{
|
||||
if (!ewmhIsSupported()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<Window> windows = getTopLevelWindows();
|
||||
for (auto &window : windows) {
|
||||
auto name = getWindowName(window);
|
||||
if (name.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool equals = windowTitle == name;
|
||||
bool matches = QString::fromStdString(name).contains(
|
||||
QRegularExpression(
|
||||
QString::fromStdString(windowTitle)));
|
||||
|
||||
if (!(equals || matches)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QStringList states = getStates(window);
|
||||
if (states.isEmpty()) {
|
||||
if (expectedStates.empty()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto &state : expectedStates) {
|
||||
if (!states.contains(state)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsMaximized(const std::string &title)
|
||||
{
|
||||
std::vector<QString> states;
|
||||
states.emplace_back("_NET_WM_STATE_MAXIMIZED_VERT");
|
||||
states.emplace_back("_NET_WM_STATE_MAXIMIZED_HORZ");
|
||||
return windowStatesAreSet(title, states);
|
||||
}
|
||||
|
||||
bool IsFullscreen(const std::string &title)
|
||||
{
|
||||
std::vector<QString> states;
|
||||
states.emplace_back("_NET_WM_STATE_FULLSCREEN");
|
||||
return windowStatesAreSet(title, states);
|
||||
}
|
||||
|
||||
std::optional<std::string> GetTextInWindow(const std::string &)
|
||||
{
|
||||
// Not implemented
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<WindowGeometry> GetWindowGeometry(const std::string &title)
|
||||
{
|
||||
auto display = disp();
|
||||
if (!display) {
|
||||
return {};
|
||||
}
|
||||
|
||||
for (auto window : getTopLevelWindows()) {
|
||||
if (getWindowName(window) != title) {
|
||||
continue;
|
||||
}
|
||||
|
||||
XWindowAttributes attrs;
|
||||
if (!XGetWindowAttributes(display, window, &attrs)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
Window child;
|
||||
XTranslateCoordinates(display, window,
|
||||
DefaultRootWindow(display), 0, 0, &x, &y,
|
||||
&child);
|
||||
|
||||
WindowGeometry geo;
|
||||
geo.x = x;
|
||||
geo.y = y;
|
||||
geo.width = attrs.width;
|
||||
geo.height = attrs.height;
|
||||
return geo;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static void getProcessListProcps(QStringList &processes)
|
||||
{
|
||||
#ifdef PROCPS_AVAILABLE
|
||||
|
|
|
|||
|
|
@ -14,41 +14,13 @@
|
|||
|
||||
namespace advss {
|
||||
|
||||
std::vector<std::string> GetWindowList()
|
||||
static std::string nsStringToStdString(NSString *str)
|
||||
{
|
||||
std::vector<std::string> windows;
|
||||
@autoreleasepool {
|
||||
CFArrayRef cfApps = CGWindowListCopyWindowInfo(
|
||||
kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
|
||||
NSMutableArray *apps = (__bridge NSMutableArray *)cfApps;
|
||||
for (NSDictionary *app in apps) {
|
||||
std::string name([[app objectForKey:@"kCGWindowName"]
|
||||
UTF8String],
|
||||
[[app objectForKey:@"kCGWindowName"]
|
||||
lengthOfBytesUsingEncoding:
|
||||
NSUTF8StringEncoding]);
|
||||
std::string owner(
|
||||
[[app objectForKey:@"kCGWindowOwnerName"]
|
||||
UTF8String],
|
||||
[[app objectForKey:@"kCGWindowOwnerName"]
|
||||
lengthOfBytesUsingEncoding:
|
||||
NSUTF8StringEncoding]);
|
||||
|
||||
if (!name.empty() &&
|
||||
find(windows.begin(), windows.end(), name) ==
|
||||
windows.end()) {
|
||||
windows.emplace_back(name);
|
||||
}
|
||||
if (!owner.empty() &&
|
||||
find(windows.begin(), windows.end(), owner) ==
|
||||
windows.end()) {
|
||||
windows.emplace_back(owner);
|
||||
}
|
||||
}
|
||||
apps = nil;
|
||||
CFRelease(cfApps);
|
||||
if (!str) {
|
||||
return "";
|
||||
}
|
||||
return windows;
|
||||
const char *utf8 = [str UTF8String];
|
||||
return utf8 ? utf8 : "";
|
||||
}
|
||||
|
||||
std::string GetCurrentWindowTitle()
|
||||
|
|
@ -148,54 +120,6 @@ bool isWindowMaximizedOnScreen(NSDictionary *app, NSScreen *screen)
|
|||
maximizedTolerance;
|
||||
}
|
||||
|
||||
bool nameMachesPattern(std::string windowName, std::string pattern)
|
||||
{
|
||||
return QString::fromStdString(windowName)
|
||||
.contains(QRegularExpression(QString::fromStdString(pattern)));
|
||||
}
|
||||
|
||||
bool IsMaximized(const std::string &title)
|
||||
{
|
||||
@autoreleasepool {
|
||||
NSArray *screens = [NSScreen screens];
|
||||
CFArrayRef cfApps = CGWindowListCopyWindowInfo(
|
||||
kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
|
||||
NSMutableArray *apps = (__bridge NSMutableArray *)cfApps;
|
||||
for (NSDictionary *app in apps) {
|
||||
std::string name([[app objectForKey:@"kCGWindowName"]
|
||||
UTF8String],
|
||||
[[app objectForKey:@"kCGWindowName"]
|
||||
lengthOfBytesUsingEncoding:
|
||||
NSUTF8StringEncoding]);
|
||||
std::string owner(
|
||||
[[app objectForKey:@"kCGWindowOwnerName"]
|
||||
UTF8String],
|
||||
[[app objectForKey:@"kCGWindowOwnerName"]
|
||||
lengthOfBytesUsingEncoding:
|
||||
NSUTF8StringEncoding]);
|
||||
|
||||
bool equals = (title == name || title == owner);
|
||||
bool matches = nameMachesPattern(name, title) ||
|
||||
nameMachesPattern(owner, title);
|
||||
if (equals || matches) {
|
||||
for (NSScreen *screen in screens) {
|
||||
if (isWindowOriginOnScreen(app,
|
||||
screen) &&
|
||||
isWindowMaximizedOnScreen(app,
|
||||
screen)) {
|
||||
apps = nil;
|
||||
CFRelease(cfApps);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
apps = nil;
|
||||
CFRelease(cfApps);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isWindowFullscreenOnScreen(NSDictionary *app, NSScreen *screen)
|
||||
{
|
||||
NSRect screenFrame = [screen frame];
|
||||
|
|
@ -207,52 +131,100 @@ bool isWindowFullscreenOnScreen(NSDictionary *app, NSScreen *screen)
|
|||
return NSEqualSizes(windowBounds.size, screenFrame.size);
|
||||
}
|
||||
|
||||
bool IsFullscreen(const std::string &title)
|
||||
std::vector<WindowInfo> GetWindows(const WindowQueryOptions &options)
|
||||
{
|
||||
std::vector<WindowInfo> result;
|
||||
const std::string foregroundTitle = GetCurrentWindowTitle();
|
||||
|
||||
@autoreleasepool {
|
||||
NSArray *screens = [NSScreen screens];
|
||||
|
||||
CFArrayRef cfApps = CGWindowListCopyWindowInfo(
|
||||
kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
|
||||
NSMutableArray *apps = (__bridge NSMutableArray *)cfApps;
|
||||
for (NSDictionary *app in apps) {
|
||||
std::string name([[app objectForKey:@"kCGWindowName"]
|
||||
UTF8String],
|
||||
[[app objectForKey:@"kCGWindowName"]
|
||||
lengthOfBytesUsingEncoding:
|
||||
NSUTF8StringEncoding]);
|
||||
std::string owner(
|
||||
[[app objectForKey:@"kCGWindowOwnerName"]
|
||||
UTF8String],
|
||||
[[app objectForKey:@"kCGWindowOwnerName"]
|
||||
lengthOfBytesUsingEncoding:
|
||||
NSUTF8StringEncoding]);
|
||||
|
||||
bool equals = (title == name || title == owner);
|
||||
bool matches = nameMachesPattern(name, title) ||
|
||||
nameMachesPattern(owner, title);
|
||||
if (equals || matches) {
|
||||
for (NSScreen *screen in screens) {
|
||||
if (isWindowOriginOnScreen(app, screen,
|
||||
true) &&
|
||||
isWindowFullscreenOnScreen(
|
||||
app, screen)) {
|
||||
apps = nil;
|
||||
CFRelease(cfApps);
|
||||
return true;
|
||||
// Track titles already added to avoid duplicates (name + owner)
|
||||
std::vector<std::string> seen;
|
||||
|
||||
for (NSDictionary *app in apps) {
|
||||
std::string name = nsStringToStdString(
|
||||
[app objectForKey:@"kCGWindowName"]);
|
||||
std::string owner = nsStringToStdString(
|
||||
[app objectForKey:@"kCGWindowOwnerName"]);
|
||||
|
||||
// Collect geometry once (used for multiple fields)
|
||||
NSRect bounds = NSZeroRect;
|
||||
if (options.geometry || options.fullscreen ||
|
||||
options.maximized) {
|
||||
CGRectMakeWithDictionaryRepresentation(
|
||||
(CFDictionaryRef)[app
|
||||
objectForKey:@"kCGWindowBounds"],
|
||||
&bounds);
|
||||
}
|
||||
|
||||
// Process both the window name and the owner name as
|
||||
// separate entries, matching the old GetWindowList() behaviour
|
||||
for (int pass = 0; pass < 2; ++pass) {
|
||||
const std::string &title = (pass == 0) ? name
|
||||
: owner;
|
||||
if (title.empty()) {
|
||||
continue;
|
||||
}
|
||||
if (std::find(seen.begin(), seen.end(),
|
||||
title) != seen.end()) {
|
||||
continue;
|
||||
}
|
||||
seen.emplace_back(title);
|
||||
|
||||
WindowInfo info;
|
||||
info.title = title;
|
||||
|
||||
if (options.focus) {
|
||||
info.focused =
|
||||
(title == foregroundTitle);
|
||||
}
|
||||
|
||||
if (options.geometry || options.fullscreen ||
|
||||
options.maximized) {
|
||||
info.x = (int)bounds.origin.x;
|
||||
info.y = (int)bounds.origin.y;
|
||||
info.width = (int)bounds.size.width;
|
||||
info.height = (int)bounds.size.height;
|
||||
}
|
||||
|
||||
if (options.fullscreen) {
|
||||
for (NSScreen *screen in screens) {
|
||||
if (isWindowOriginOnScreen(
|
||||
app, screen,
|
||||
true) &&
|
||||
isWindowFullscreenOnScreen(
|
||||
app, screen)) {
|
||||
info.fullscreen = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (options.maximized) {
|
||||
for (NSScreen *screen in screens) {
|
||||
if (isWindowOriginOnScreen(
|
||||
app, screen) &&
|
||||
isWindowMaximizedOnScreen(
|
||||
app, screen)) {
|
||||
info.maximized = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// windowClass and text not implemented on macOS
|
||||
result.emplace_back(std::move(info));
|
||||
}
|
||||
}
|
||||
apps = nil;
|
||||
CFRelease(cfApps);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<std::string> GetTextInWindow(const std::string &)
|
||||
{
|
||||
// Not implemented
|
||||
return {};
|
||||
return result;
|
||||
}
|
||||
|
||||
int SecondsSinceLastInput()
|
||||
|
|
@ -341,8 +313,7 @@ QStringList GetProcessPathsFromName(const QString &name)
|
|||
continue;
|
||||
}
|
||||
const char *nameStr = appName.UTF8String;
|
||||
if (!nameStr ||
|
||||
name != QString::fromUtf8(nameStr)) {
|
||||
if (!nameStr || name != QString::fromUtf8(nameStr)) {
|
||||
continue;
|
||||
}
|
||||
NSURL *url = app.executableURL;
|
||||
|
|
|
|||
|
|
@ -11,20 +11,31 @@ namespace advss {
|
|||
|
||||
enum class HotkeyType;
|
||||
|
||||
struct WindowGeometry {
|
||||
struct WindowQueryOptions {
|
||||
bool geometry = false;
|
||||
bool focus = false;
|
||||
bool fullscreen = false;
|
||||
bool maximized = false;
|
||||
bool windowClass = false; // Windows only, no-op on other platforms
|
||||
bool text = false; // Windows only, expensive, no-op on other platforms
|
||||
};
|
||||
|
||||
struct WindowInfo {
|
||||
std::string title;
|
||||
bool focused = false;
|
||||
bool fullscreen = false;
|
||||
bool maximized = false;
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
std::string windowClass;
|
||||
std::optional<std::string> text;
|
||||
};
|
||||
|
||||
EXPORT std::vector<std::string> GetWindowList();
|
||||
EXPORT std::vector<WindowInfo>
|
||||
GetWindows(const WindowQueryOptions &options = {});
|
||||
EXPORT std::string GetCurrentWindowTitle();
|
||||
EXPORT bool IsFullscreen(const std::string &title);
|
||||
EXPORT bool IsMaximized(const std::string &title);
|
||||
EXPORT std::optional<std::string> GetTextInWindow(const std::string &window);
|
||||
EXPORT std::optional<WindowGeometry>
|
||||
GetWindowGeometry(const std::string &title);
|
||||
EXPORT int SecondsSinceLastInput();
|
||||
EXPORT QStringList GetProcessList();
|
||||
EXPORT std::string GetForegroundProcessName();
|
||||
|
|
|
|||
|
|
@ -173,10 +173,10 @@ void PopulateTransitionSelection(QComboBox *sel, bool addCurrent, bool addAny,
|
|||
void PopulateWindowSelection(QComboBox *sel, bool addSelect)
|
||||
{
|
||||
|
||||
const auto windows = GetWindowList();
|
||||
const auto windows = GetWindows();
|
||||
|
||||
for (const std::string &window : windows) {
|
||||
sel->addItem(window.c_str());
|
||||
for (const auto &info : windows) {
|
||||
sel->addItem(info.title.c_str());
|
||||
}
|
||||
|
||||
sel->model()->sort(0);
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ const std::vector<std::string> getOBSWindows()
|
|||
return lastDoneHelper->windows;
|
||||
}
|
||||
|
||||
std::vector<std::string> GetWindowList()
|
||||
static std::vector<std::string> getAllWindowTitles()
|
||||
{
|
||||
std::vector<std::string> windows;
|
||||
EnumWindowsWithMetro(GetTitleCB, reinterpret_cast<LPARAM>(&windows));
|
||||
|
|
@ -155,6 +155,129 @@ std::vector<std::string> GetWindowList()
|
|||
return windows;
|
||||
}
|
||||
|
||||
static bool isWindowMaximized(HWND hwnd)
|
||||
{
|
||||
if (!hwnd || hwnd == GetDesktopWindow() || hwnd == GetShellWindow()) {
|
||||
return false;
|
||||
}
|
||||
if (IsZoomed(hwnd)) {
|
||||
return true;
|
||||
}
|
||||
RECT appBounds;
|
||||
MONITORINFO monitorInfo = {0};
|
||||
monitorInfo.cbSize = sizeof(MONITORINFO);
|
||||
GetMonitorInfo(MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST),
|
||||
&monitorInfo);
|
||||
GetWindowRect(hwnd, &appBounds);
|
||||
return monitorInfo.rcMonitor.bottom == appBounds.bottom &&
|
||||
monitorInfo.rcMonitor.top == appBounds.top &&
|
||||
monitorInfo.rcMonitor.left == appBounds.left &&
|
||||
monitorInfo.rcMonitor.right == appBounds.right;
|
||||
}
|
||||
|
||||
static bool isWindowFullscreen(HWND hwnd)
|
||||
{
|
||||
if (!hwnd || hwnd == GetDesktopWindow() || hwnd == GetShellWindow()) {
|
||||
return false;
|
||||
}
|
||||
RECT appBounds;
|
||||
MONITORINFO monitorInfo = {0};
|
||||
monitorInfo.cbSize = sizeof(MONITORINFO);
|
||||
GetMonitorInfo(MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST),
|
||||
&monitorInfo);
|
||||
GetWindowRect(hwnd, &appBounds);
|
||||
return monitorInfo.rcMonitor.bottom == appBounds.bottom &&
|
||||
monitorInfo.rcMonitor.top == appBounds.top &&
|
||||
monitorInfo.rcMonitor.left == appBounds.left &&
|
||||
monitorInfo.rcMonitor.right == appBounds.right;
|
||||
}
|
||||
|
||||
static std::string getWindowClass(HWND hwnd)
|
||||
{
|
||||
std::wstring wClass;
|
||||
wClass.resize(256);
|
||||
int len = GetClassNameW(hwnd, &wClass[0], (int)wClass.size());
|
||||
if (len <= 0) {
|
||||
return "";
|
||||
}
|
||||
wClass.resize(len);
|
||||
size_t utf8len = os_wcs_to_utf8(wClass.c_str(), 0, nullptr, 0);
|
||||
std::string result;
|
||||
result.resize(utf8len);
|
||||
os_wcs_to_utf8(wClass.c_str(), 0, &result[0], utf8len + 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
static HWND getHWNDfromTitle(const std::string &title);
|
||||
static std::optional<std::string> GetTextInWindow(const std::string &window);
|
||||
|
||||
std::vector<WindowInfo> GetWindows(const WindowQueryOptions &options)
|
||||
{
|
||||
const std::string foregroundTitle = GetCurrentWindowTitle();
|
||||
const auto titles = getAllWindowTitles();
|
||||
|
||||
std::vector<WindowInfo> result;
|
||||
result.reserve(titles.size());
|
||||
|
||||
for (const auto &title : titles) {
|
||||
WindowInfo info;
|
||||
info.title = title;
|
||||
|
||||
const bool needHwnd = options.geometry || options.fullscreen ||
|
||||
options.maximized ||
|
||||
options.windowClass || options.text ||
|
||||
options.focus;
|
||||
|
||||
if (options.focus) {
|
||||
info.focused = (title == foregroundTitle);
|
||||
}
|
||||
|
||||
if (!needHwnd || (!options.geometry && !options.fullscreen &&
|
||||
!options.maximized && !options.windowClass &&
|
||||
!options.text)) {
|
||||
result.emplace_back(std::move(info));
|
||||
continue;
|
||||
}
|
||||
|
||||
HWND hwnd = getHWNDfromTitle(title);
|
||||
if (!hwnd) {
|
||||
result.emplace_back(std::move(info));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (options.geometry || options.fullscreen ||
|
||||
options.maximized) {
|
||||
RECT rect;
|
||||
if (GetWindowRect(hwnd, &rect)) {
|
||||
info.x = rect.left;
|
||||
info.y = rect.top;
|
||||
info.width = rect.right - rect.left;
|
||||
info.height = rect.bottom - rect.top;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.maximized) {
|
||||
info.maximized = isWindowMaximized(hwnd);
|
||||
}
|
||||
|
||||
if (options.fullscreen) {
|
||||
info.fullscreen = isWindowFullscreen(hwnd);
|
||||
}
|
||||
|
||||
if (options.windowClass) {
|
||||
info.windowClass = getWindowClass(hwnd);
|
||||
}
|
||||
|
||||
if (options.text) {
|
||||
info.text = GetTextInWindow(title);
|
||||
}
|
||||
|
||||
result.emplace_back(std::move(info));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string GetCurrentWindowTitle()
|
||||
{
|
||||
HWND window = GetForegroundWindow();
|
||||
|
|
@ -193,36 +316,6 @@ static HWND getHWNDfromTitle(const std::string &title)
|
|||
return hwnd;
|
||||
}
|
||||
|
||||
bool IsMaximized(const std::string &title)
|
||||
{
|
||||
RECT appBounds;
|
||||
MONITORINFO monitorInfo = {0};
|
||||
HWND hwnd = NULL;
|
||||
|
||||
hwnd = getHWNDfromTitle(title);
|
||||
if (!hwnd) {
|
||||
return false;
|
||||
}
|
||||
|
||||
monitorInfo.cbSize = sizeof(MONITORINFO);
|
||||
GetMonitorInfo(MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST),
|
||||
&monitorInfo);
|
||||
|
||||
if (hwnd && hwnd != GetDesktopWindow() && hwnd != GetShellWindow()) {
|
||||
if (IsZoomed(hwnd)) {
|
||||
return true;
|
||||
}
|
||||
GetWindowRect(hwnd, &appBounds);
|
||||
if (monitorInfo.rcMonitor.bottom == appBounds.bottom &&
|
||||
monitorInfo.rcMonitor.top == appBounds.top &&
|
||||
monitorInfo.rcMonitor.left == appBounds.left &&
|
||||
monitorInfo.rcMonitor.right == appBounds.right) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static std::wstring GetControlText(HWND hwnd, IUIAutomationElement *element)
|
||||
{
|
||||
VARIANT var;
|
||||
|
|
@ -249,7 +342,7 @@ static std::wstring GetControlText(HWND hwnd, IUIAutomationElement *element)
|
|||
return text;
|
||||
}
|
||||
|
||||
std::optional<std::string> GetTextInWindow(const std::string &window)
|
||||
static std::optional<std::string> GetTextInWindow(const std::string &window)
|
||||
{
|
||||
HWND hwnd = getHWNDfromTitle(window);
|
||||
if (!hwnd) {
|
||||
|
|
@ -316,55 +409,6 @@ std::optional<std::string> GetTextInWindow(const std::string &window)
|
|||
return tmp;
|
||||
}
|
||||
|
||||
bool IsFullscreen(const std::string &title)
|
||||
{
|
||||
RECT appBounds;
|
||||
MONITORINFO monitorInfo = {0};
|
||||
|
||||
HWND hwnd = NULL;
|
||||
hwnd = getHWNDfromTitle(title);
|
||||
|
||||
if (!hwnd) {
|
||||
return false;
|
||||
}
|
||||
|
||||
monitorInfo.cbSize = sizeof(MONITORINFO);
|
||||
GetMonitorInfo(MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST),
|
||||
&monitorInfo);
|
||||
|
||||
if (hwnd && hwnd != GetDesktopWindow() && hwnd != GetShellWindow()) {
|
||||
GetWindowRect(hwnd, &appBounds);
|
||||
if (monitorInfo.rcMonitor.bottom == appBounds.bottom &&
|
||||
monitorInfo.rcMonitor.top == appBounds.top &&
|
||||
monitorInfo.rcMonitor.left == appBounds.left &&
|
||||
monitorInfo.rcMonitor.right == appBounds.right) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<WindowGeometry> GetWindowGeometry(const std::string &title)
|
||||
{
|
||||
HWND hwnd = getHWNDfromTitle(title);
|
||||
if (!hwnd) {
|
||||
return {};
|
||||
}
|
||||
|
||||
RECT rect;
|
||||
if (!GetWindowRect(hwnd, &rect)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
WindowGeometry geo;
|
||||
geo.x = rect.left;
|
||||
geo.y = rect.top;
|
||||
geo.width = rect.right - rect.left;
|
||||
geo.height = rect.bottom - rect.top;
|
||||
return geo;
|
||||
}
|
||||
|
||||
QStringList GetProcessList()
|
||||
{
|
||||
QStringList processes;
|
||||
|
|
|
|||
|
|
@ -42,19 +42,20 @@ void CloseWindow(const std::string &) {}
|
|||
|
||||
std::optional<std::string> MacroActionWindow::GetMatchingWindow() const
|
||||
{
|
||||
const auto windowList = GetWindowList();
|
||||
const auto windows = GetWindows();
|
||||
|
||||
if (!_regex.Enabled()) {
|
||||
if (std::find(windowList.begin(), windowList.end(),
|
||||
std::string(_window)) == windowList.end()) {
|
||||
return {};
|
||||
for (const auto &info : windows) {
|
||||
if (info.title == std::string(_window)) {
|
||||
return info.title;
|
||||
}
|
||||
}
|
||||
return _window;
|
||||
return {};
|
||||
}
|
||||
|
||||
for (const auto &window : windowList) {
|
||||
if (_regex.Matches(window, _window)) {
|
||||
return window;
|
||||
for (const auto &info : windows) {
|
||||
if (_regex.Matches(info.title, _window)) {
|
||||
return info.title;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,21 +14,6 @@ bool MacroConditionWindow::_registered = MacroConditionFactory::Register(
|
|||
{MacroConditionWindow::Create, MacroConditionWindowEdit::Create,
|
||||
"AdvSceneSwitcher.condition.window"});
|
||||
|
||||
static bool windowContainsText(const std::string &window,
|
||||
const std::string &matchText,
|
||||
const RegexConfig ®ex)
|
||||
{
|
||||
auto text = GetTextInWindow(window);
|
||||
if (!text.has_value()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (regex.Enabled()) {
|
||||
return regex.Matches(*text, matchText);
|
||||
}
|
||||
return text == matchText;
|
||||
}
|
||||
|
||||
void MacroConditionWindow::SetCheckText(bool value)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
|
|
@ -46,92 +31,76 @@ bool MacroConditionWindow::GetCheckText()
|
|||
}
|
||||
|
||||
bool MacroConditionWindow::WindowMatchesRequirements(
|
||||
const std::string &window) const
|
||||
const WindowInfo &info) const
|
||||
{
|
||||
const bool focusCheckOK =
|
||||
(!_focus || window == ForegroundWindowTitle());
|
||||
if (!focusCheckOK) {
|
||||
if (_focus && !info.focused) {
|
||||
return false;
|
||||
}
|
||||
const bool fullscreenCheckOK = (!_fullscreen || IsFullscreen(window));
|
||||
if (!fullscreenCheckOK) {
|
||||
if (_fullscreen && !info.fullscreen) {
|
||||
return false;
|
||||
}
|
||||
const bool maxCheckOK = (!_maximized || IsMaximized(window));
|
||||
if (!maxCheckOK) {
|
||||
if (_maximized && !info.maximized) {
|
||||
return false;
|
||||
}
|
||||
const bool textCheckOK =
|
||||
(!_checkText || windowContainsText(window, _text, _textRegex));
|
||||
if (!textCheckOK) {
|
||||
return false;
|
||||
if (_checkText) {
|
||||
if (!info.text.has_value()) {
|
||||
return false;
|
||||
}
|
||||
if (_textRegex.Enabled()) {
|
||||
if (!_textRegex.Matches(*info.text, _text)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (*info.text != std::string(_text)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionWindow::WindowMatches(
|
||||
const std::vector<std::string> &windowList)
|
||||
bool MacroConditionWindow::FindMatch(const std::vector<WindowInfo> &windows)
|
||||
{
|
||||
bool match = !_checkTitle ||
|
||||
std::find(windowList.begin(), windowList.end(),
|
||||
std::string(_window)) != windowList.end();
|
||||
match = match && WindowMatchesRequirements(_window);
|
||||
SetVariableValueBasedOnMatch(_window);
|
||||
return match;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
std::string GetWindowClassByWindowTitle(const std::string &window);
|
||||
#endif
|
||||
|
||||
bool MacroConditionWindow::WindowRegexMatches(
|
||||
const std::vector<std::string> &windowList)
|
||||
{
|
||||
// No need to test if checking for window title is required as if the
|
||||
// user has disabled window title matching the option will always be
|
||||
// enabled in the backend and use the regular expression ".*".
|
||||
|
||||
for (const auto &window : windowList) {
|
||||
if (_windowRegex.Matches(window, _window) &&
|
||||
WindowMatchesRequirements(window)) {
|
||||
SetVariableValueBasedOnMatch(window);
|
||||
return true;
|
||||
// When regex is enabled the title check is always active (the backend
|
||||
// uses ".*" when the user disables the title check), so we can use a
|
||||
// single predicate for both modes.
|
||||
for (const auto &info : windows) {
|
||||
const bool titleOK =
|
||||
_windowRegex.Enabled()
|
||||
? _windowRegex.Matches(info.title, _window)
|
||||
: (!_checkTitle ||
|
||||
info.title == std::string(_window));
|
||||
if (!titleOK || !WindowMatchesRequirements(info)) {
|
||||
continue;
|
||||
}
|
||||
SetVariableValueBasedOnMatch(&info);
|
||||
return true;
|
||||
}
|
||||
SetVariableValueBasedOnMatch("");
|
||||
SetVariableValueBasedOnMatch(nullptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
void MacroConditionWindow::SetVariableValueBasedOnMatch(
|
||||
const std::string &matchWindow)
|
||||
void MacroConditionWindow::SetVariableValueBasedOnMatch(const WindowInfo *info)
|
||||
{
|
||||
SetTempVarValue("window", matchWindow);
|
||||
|
||||
const auto geo = GetWindowGeometry(matchWindow);
|
||||
if (geo) {
|
||||
SetTempVarValue("windowX", std::to_string(geo->x));
|
||||
SetTempVarValue("windowY", std::to_string(geo->y));
|
||||
SetTempVarValue("windowWidth", std::to_string(geo->width));
|
||||
SetTempVarValue("windowHeight", std::to_string(geo->height));
|
||||
}
|
||||
|
||||
const std::string title = info ? info->title : "";
|
||||
SetTempVarValue("window", title);
|
||||
SetTempVarValue("windowX", info ? std::to_string(info->x) : "");
|
||||
SetTempVarValue("windowY", info ? std::to_string(info->y) : "");
|
||||
SetTempVarValue("windowWidth", info ? std::to_string(info->width) : "");
|
||||
SetTempVarValue("windowHeight",
|
||||
info ? std::to_string(info->height) : "");
|
||||
#ifdef _WIN32
|
||||
SetTempVarValue("windowClass",
|
||||
GetWindowClassByWindowTitle(matchWindow));
|
||||
SetTempVarValue("windowClass", info ? info->windowClass : "");
|
||||
if (_checkText) {
|
||||
const auto text = GetTextInWindow(matchWindow);
|
||||
if (text) {
|
||||
SetTempVarValue("windowText", *text);
|
||||
}
|
||||
SetTempVarValue("windowText",
|
||||
(info && info->text) ? *info->text : "");
|
||||
}
|
||||
#endif
|
||||
if (!IsReferencedInVars()) {
|
||||
return;
|
||||
}
|
||||
if (_checkText) {
|
||||
const auto text = GetTextInWindow(matchWindow);
|
||||
SetVariableValue(text.value_or(""));
|
||||
SetVariableValue((info && info->text) ? *info->text : "");
|
||||
} else {
|
||||
SetVariableValue(ForegroundWindowTitle());
|
||||
}
|
||||
|
|
@ -144,13 +113,18 @@ static bool foregroundWindowChanged()
|
|||
|
||||
bool MacroConditionWindow::CheckCondition()
|
||||
{
|
||||
const auto windowList = GetWindowList();
|
||||
bool match = false;
|
||||
if (_windowRegex.Enabled()) {
|
||||
match = WindowRegexMatches(windowList);
|
||||
} else {
|
||||
match = WindowMatches(windowList);
|
||||
}
|
||||
WindowQueryOptions options;
|
||||
options.focus = _focus;
|
||||
options.fullscreen = _fullscreen;
|
||||
options.maximized = _maximized;
|
||||
options.geometry = true;
|
||||
#ifdef _WIN32
|
||||
options.windowClass = true;
|
||||
options.text = _checkText;
|
||||
#endif
|
||||
|
||||
const auto windows = GetWindows(options);
|
||||
bool match = FindMatch(windows);
|
||||
match = match && (!_windowFocusChanged || foregroundWindowChanged());
|
||||
return match;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
#include "macro-condition-edit.hpp"
|
||||
#include "platform-funcs.hpp"
|
||||
#include "variable-text-edit.hpp"
|
||||
#include "regex-config.hpp"
|
||||
#include "window-selection.hpp"
|
||||
|
|
@ -37,10 +38,9 @@ public:
|
|||
RegexConfig _textRegex = RegexConfig::PartialMatchRegexConfig();
|
||||
|
||||
private:
|
||||
bool WindowMatchesRequirements(const std::string &window) const;
|
||||
bool WindowMatches(const std::vector<std::string> &windowList);
|
||||
bool WindowRegexMatches(const std::vector<std::string> &windowList);
|
||||
void SetVariableValueBasedOnMatch(const std::string &matchWindow);
|
||||
bool WindowMatchesRequirements(const WindowInfo &info) const;
|
||||
bool FindMatch(const std::vector<WindowInfo> &windows);
|
||||
void SetVariableValueBasedOnMatch(const WindowInfo *info);
|
||||
void SetupTempVars();
|
||||
|
||||
// For now only supported on Windows
|
||||
|
|
|
|||
|
|
@ -205,26 +205,6 @@ static std::vector<HWND> getHWNDfromTitle(const std::string &title)
|
|||
return hwnds;
|
||||
}
|
||||
|
||||
std::string GetWindowClassByWindowTitle(const std::string &window)
|
||||
{
|
||||
auto hwnds = getHWNDfromTitle(window);
|
||||
if (hwnds.empty()) {
|
||||
return "";
|
||||
}
|
||||
auto hwnd = hwnds.at(0);
|
||||
std::wstring wClass;
|
||||
wClass.resize(1024);
|
||||
if (!GetClassNameW(hwnd, &wClass[0], wClass.capacity())) {
|
||||
return "";
|
||||
}
|
||||
|
||||
size_t len = os_wcs_to_utf8(wClass.c_str(), 0, nullptr, 0);
|
||||
std::string className;
|
||||
className.resize(len);
|
||||
os_wcs_to_utf8(wClass.c_str(), 0, &className[0], len + 1);
|
||||
return className;
|
||||
}
|
||||
|
||||
void SetFocusWindow(const std::string &title)
|
||||
{
|
||||
auto hwnds = getHWNDfromTitle(title);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user