diff --git a/lib/legacy/switch-window.cpp b/lib/legacy/switch-window.cpp index 8827ef70..09b97e34 100644 --- a/lib/legacy/switch-window.cpp +++ b/lib/legacy/switch-window.cpp @@ -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 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) { diff --git a/lib/linux/advanced-scene-switcher-nix.cpp b/lib/linux/advanced-scene-switcher-nix.cpp index b7afa73f..6d91c5d6 100644 --- a/lib/linux/advanced-scene-switcher-nix.cpp +++ b/lib/linux/advanced-scene-switcher-nix.cpp @@ -236,17 +236,65 @@ std::string getWindowName(Window window) return windowTitle; } -std::vector GetWindowList() +std::vector GetWindows(const WindowQueryOptions &options) { - std::vector windows; - for (auto window : getTopLevelWindows()) { + const std::string foregroundTitle = GetCurrentWindowTitle(); + const auto topLevel = getTopLevelWindows(); + auto display = disp(); + + std::vector 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 &expectedStates) -{ - if (!ewmhIsSupported()) { - return false; - } - - std::vector 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 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 states; - states.emplace_back("_NET_WM_STATE_FULLSCREEN"); - return windowStatesAreSet(title, states); -} - -std::optional GetTextInWindow(const std::string &) -{ - // Not implemented - return {}; -} - -std::optional 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 diff --git a/lib/osx/advanced-scene-switcher-osx.mm b/lib/osx/advanced-scene-switcher-osx.mm index 4f592041..7fb81e9a 100644 --- a/lib/osx/advanced-scene-switcher-osx.mm +++ b/lib/osx/advanced-scene-switcher-osx.mm @@ -14,41 +14,13 @@ namespace advss { -std::vector GetWindowList() +static std::string nsStringToStdString(NSString *str) { - std::vector 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 GetWindows(const WindowQueryOptions &options) { + std::vector 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 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 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; diff --git a/lib/platform-funcs.hpp b/lib/platform-funcs.hpp index 620d07f0..abb79455 100644 --- a/lib/platform-funcs.hpp +++ b/lib/platform-funcs.hpp @@ -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 text; }; -EXPORT std::vector GetWindowList(); +EXPORT std::vector +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 GetTextInWindow(const std::string &window); -EXPORT std::optional -GetWindowGeometry(const std::string &title); EXPORT int SecondsSinceLastInput(); EXPORT QStringList GetProcessList(); EXPORT std::string GetForegroundProcessName(); diff --git a/lib/utils/selection-helpers.cpp b/lib/utils/selection-helpers.cpp index b27de7eb..6001ebba 100644 --- a/lib/utils/selection-helpers.cpp +++ b/lib/utils/selection-helpers.cpp @@ -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); diff --git a/lib/win/advanced-scene-switcher-win.cpp b/lib/win/advanced-scene-switcher-win.cpp index 4359f70f..eb5ea85f 100644 --- a/lib/win/advanced-scene-switcher-win.cpp +++ b/lib/win/advanced-scene-switcher-win.cpp @@ -138,7 +138,7 @@ const std::vector getOBSWindows() return lastDoneHelper->windows; } -std::vector GetWindowList() +static std::vector getAllWindowTitles() { std::vector windows; EnumWindowsWithMetro(GetTitleCB, reinterpret_cast(&windows)); @@ -155,6 +155,129 @@ std::vector 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 GetTextInWindow(const std::string &window); + +std::vector GetWindows(const WindowQueryOptions &options) +{ + const std::string foregroundTitle = GetCurrentWindowTitle(); + const auto titles = getAllWindowTitles(); + + std::vector 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 GetTextInWindow(const std::string &window) +static std::optional GetTextInWindow(const std::string &window) { HWND hwnd = getHWNDfromTitle(window); if (!hwnd) { @@ -316,55 +409,6 @@ std::optional 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 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; diff --git a/plugins/base/macro-action-window.cpp b/plugins/base/macro-action-window.cpp index aa46abf9..42539050 100644 --- a/plugins/base/macro-action-window.cpp +++ b/plugins/base/macro-action-window.cpp @@ -42,19 +42,20 @@ void CloseWindow(const std::string &) {} std::optional 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; } } diff --git a/plugins/base/macro-condition-window.cpp b/plugins/base/macro-condition-window.cpp index 70fcbaa7..aa522faa 100644 --- a/plugins/base/macro-condition-window.cpp +++ b/plugins/base/macro-condition-window.cpp @@ -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 &windowList) +bool MacroConditionWindow::FindMatch(const std::vector &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 &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; } diff --git a/plugins/base/macro-condition-window.hpp b/plugins/base/macro-condition-window.hpp index 6eba3632..c00b4096 100644 --- a/plugins/base/macro-condition-window.hpp +++ b/plugins/base/macro-condition-window.hpp @@ -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 &windowList); - bool WindowRegexMatches(const std::vector &windowList); - void SetVariableValueBasedOnMatch(const std::string &matchWindow); + bool WindowMatchesRequirements(const WindowInfo &info) const; + bool FindMatch(const std::vector &windows); + void SetVariableValueBasedOnMatch(const WindowInfo *info); void SetupTempVars(); // For now only supported on Windows diff --git a/plugins/base/utils/windows/windows.cpp b/plugins/base/utils/windows/windows.cpp index 99333d67..b7699413 100644 --- a/plugins/base/utils/windows/windows.cpp +++ b/plugins/base/utils/windows/windows.cpp @@ -205,26 +205,6 @@ static std::vector 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);