From 1ce332a91d4cd1eee85e3d242860a83aec9fb6d9 Mon Sep 17 00:00:00 2001 From: WarmUpTill <19472752+WarmUpTill@users.noreply.github.com> Date: Wed, 10 Jun 2026 20:22:02 +0200 Subject: [PATCH] Track all kWin wayland windows --- lib/linux/advanced-scene-switcher-nix.cpp | 34 +++++---- lib/linux/kwin-helpers.cpp | 92 ++++++++++++++++++----- lib/linux/kwin-helpers.h | 13 ++++ 3 files changed, 105 insertions(+), 34 deletions(-) diff --git a/lib/linux/advanced-scene-switcher-nix.cpp b/lib/linux/advanced-scene-switcher-nix.cpp index 46a2ef50..2862a38c 100644 --- a/lib/linux/advanced-scene-switcher-nix.cpp +++ b/lib/linux/advanced-scene-switcher-nix.cpp @@ -295,22 +295,26 @@ std::vector GetWindows(const WindowQueryOptions &options) } // When KWin compat is active, native Wayland windows are not tracked by - // X11 and therefore do not appear in the list above. If the currently - // focused window (reported via KWin DBus) is not already present, add it - // so that title and focus conditions can match native Wayland windows. - if (KWin && !foregroundTitle.empty()) { - bool found = false; - for (const auto &info : result) { - if (info.title == foregroundTitle) { - found = true; - break; + // X11 and therefore do not appear in the list above. Merge in any window + // reported by KWin that is not already present (matched by title) so that + // title and focus conditions work for native Wayland windows. + if (KWin) { + for (const auto &[pid, title] : + FocusNotifier::getWindowList()) { + bool found = false; + for (const auto &info : result) { + if (info.title == title) { + found = true; + break; + } + } + if (!found) { + WindowInfo waylandWindow; + waylandWindow.title = title; + waylandWindow.focused = + (title == foregroundTitle); + result.emplace_back(std::move(waylandWindow)); } - } - if (!found) { - WindowInfo waylandWindow; - waylandWindow.title = foregroundTitle; - waylandWindow.focused = true; - result.emplace_back(std::move(waylandWindow)); } } diff --git a/lib/linux/kwin-helpers.cpp b/lib/linux/kwin-helpers.cpp index 90c252e4..7d7efb68 100644 --- a/lib/linux/kwin-helpers.cpp +++ b/lib/linux/kwin-helpers.cpp @@ -12,35 +12,82 @@ namespace advss { +std::mutex FocusNotifier::_mutex; int FocusNotifier::activePID = -1; std::string FocusNotifier::activeTitle = {}; +std::map> FocusNotifier::_windows; int FocusNotifier::getActiveWindowPID() { + std::lock_guard lock(_mutex); return activePID; } std::string FocusNotifier::getActiveWindowTitle() { + std::lock_guard lock(_mutex); return activeTitle; } +std::vector> FocusNotifier::getWindowList() +{ + std::lock_guard lock(_mutex); + std::vector> result; + result.reserve(_windows.size()); + for (const auto &entry : _windows) { + result.emplace_back(entry.second); + } + return result; +} + void FocusNotifier::focusChanged(const int pid) { + std::lock_guard lock(_mutex); activePID = pid; } void FocusNotifier::focusTitle(const QString &title) { + std::lock_guard lock(_mutex); activeTitle = title.toStdString(); } +void FocusNotifier::focusUpdate(const int pid, const QString &title) +{ + std::lock_guard lock(_mutex); + activePID = pid; + activeTitle = title.toStdString(); +} + +void FocusNotifier::windowAdded(const QString &id, const int pid, + const QString &title) +{ + std::lock_guard lock(_mutex); + _windows[id.toStdString()] = {pid, title.toStdString()}; +} + +void FocusNotifier::windowRemoved(const QString &id) +{ + std::lock_guard lock(_mutex); + _windows.erase(id.toStdString()); +} + +void FocusNotifier::windowTitleChanged(const QString &id, const QString &title) +{ + std::lock_guard lock(_mutex); + auto it = _windows.find(id.toStdString()); + if (it != _windows.end()) { + it->second.second = title.toStdString(); + } +} + bool isKWinAvailable() { const QDBusConnectionInterface *interface = QDBusConnection::sessionBus().interface(); - if (!interface) + if (!interface) { return false; + } const QStringList services = interface->registeredServiceNames().value(); @@ -53,25 +100,32 @@ bool startKWinScript(QString &scriptObjectPath) "/tmp/AdvancedSceneSwitcher/KWinFocusNotifier.js"; const QString script = - R"(workspace.windowActivated.connect(function(client) { -if (!client) return; -if (!client.pid) return; -if (!client.caption) return; + R"(var adss = "com.github.AdvancedSceneSwitcher"; +var adssPath = "/com/github/AdvancedSceneSwitcher"; -callDBus( - "com.github.AdvancedSceneSwitcher", - "/com/github/AdvancedSceneSwitcher", - "com.github.AdvancedSceneSwitcher", - "focusChanged", - client.pid -); -callDBus( - "com.github.AdvancedSceneSwitcher", - "/com/github/AdvancedSceneSwitcher", - "com.github.AdvancedSceneSwitcher", - "focusTitle", - client.caption -); +function trackWindow(window) { + var id = window.internalId.toString(); + callDBus(adss, adssPath, adss, "windowAdded", id, window.pid, window.caption); + window.captionChanged.connect(function() { + callDBus(adss, adssPath, adss, "windowTitleChanged", id, window.caption); + }); +} + +var windows = workspace.windowList(); +for (var i = 0; i < windows.length; i++) { + trackWindow(windows[i]); +} + +workspace.windowAdded.connect(trackWindow); + +workspace.windowRemoved.connect(function(window) { + callDBus(adss, adssPath, adss, "windowRemoved", window.internalId.toString()); +}); + +workspace.windowActivated.connect(function(client) { + callDBus(adss, adssPath, adss, "focusUpdate", + client ? client.pid : 0, + client ? client.caption : ""); }))"; if (const QDir dir; !dir.mkpath(QFileInfo(scriptPath).absolutePath())) { diff --git a/lib/linux/kwin-helpers.h b/lib/linux/kwin-helpers.h index fbfcc438..5f023e53 100644 --- a/lib/linux/kwin-helpers.h +++ b/lib/linux/kwin-helpers.h @@ -2,7 +2,11 @@ #include #include +#include +#include #include +#include +#include namespace advss { @@ -10,18 +14,27 @@ class FocusNotifier final : public QObject { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "com.github.AdvancedSceneSwitcher") + static std::mutex _mutex; static int activePID; static std::string activeTitle; + // Maps KWin internal window ID to {pid, title} + static std::map> _windows; public: using QObject::QObject; static int getActiveWindowPID(); static std::string getActiveWindowTitle(); + static std::vector> getWindowList(); public slots: void focusChanged(const int pid); void focusTitle(const QString &title); + void focusUpdate(const int pid, const QString &title); + void windowAdded(const QString &id, const int pid, + const QString &title); + void windowRemoved(const QString &id); + void windowTitleChanged(const QString &id, const QString &title); }; bool isKWinAvailable();