From cf3dfa2f142ff83f0be47bcafd80276f32fe6981 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 9 Jun 2026 09:49:02 +0300 Subject: [PATCH] Added external commands to list, activate and cycle windows. --- .../SourceFiles/core/external_control.cpp | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/Telegram/SourceFiles/core/external_control.cpp b/Telegram/SourceFiles/core/external_control.cpp index 83781289df..70f8d6054c 100644 --- a/Telegram/SourceFiles/core/external_control.cpp +++ b/Telegram/SourceFiles/core/external_control.cpp @@ -8,13 +8,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/external_control.h" #include "core/application.h" +#include "window/window_controller.h" +#include +#include +#include #include #include namespace Core { namespace { +struct WindowEntry { + not_null controller; + QWidget *window = nullptr; +}; + [[nodiscard]] QByteArray Pack(QJsonObject object) { return QJsonDocument(object).toJson(QJsonDocument::Compact); } @@ -26,6 +35,84 @@ namespace { return Pack(object); } +[[nodiscard]] std::vector CollectWindows() { + auto result = std::vector(); + for (const auto widget : QApplication::topLevelWidgets()) { + const auto controller = App().findWindow(widget); + if (!controller) { + continue; + } + auto already = false; + for (const auto &entry : result) { + if (entry.controller.get() == controller) { + already = true; + break; + } + } + if (!already) { + result.push_back({ controller, widget->window() }); + } + } + ranges::sort(result, [](const WindowEntry &a, const WindowEntry &b) { + if (a.controller->isPrimary() != b.controller->isPrimary()) { + return a.controller->isPrimary(); + } + return a.controller.get() < b.controller.get(); + }); + return result; +} + +[[nodiscard]] QByteArray HandleWindows() { + const auto active = App().activeWindow(); + auto list = QJsonArray(); + auto index = 0; + for (const auto &entry : CollectWindows()) { + auto object = QJsonObject(); + object.insert(u"id"_q, index++); + object.insert(u"title"_q, entry.window->windowTitle()); + object.insert(u"primary"_q, entry.controller->isPrimary()); + object.insert(u"active"_q, (entry.controller.get() == active)); + list.append(object); + } + auto object = QJsonObject(); + object.insert(u"ok"_q, true); + object.insert(u"windows"_q, list); + return Pack(object); +} + +[[nodiscard]] QByteArray HandleActivate(int index) { + const auto windows = CollectWindows(); + if (index < 0 || index >= int(windows.size())) { + return Error(u"no such window"_q); + } + windows[index].controller->activate(); + auto object = QJsonObject(); + object.insert(u"ok"_q, true); + object.insert(u"activated"_q, index); + return Pack(object); +} + +[[nodiscard]] QByteArray HandleCycle() { + const auto windows = CollectWindows(); + if (windows.empty()) { + return Error(u"no windows"_q); + } + const auto active = App().activeWindow(); + auto current = 0; + for (auto i = 0, count = int(windows.size()); i != count; ++i) { + if (windows[i].controller.get() == active) { + current = i; + break; + } + } + const auto next = (current + 1) % int(windows.size()); + windows[next].controller->activate(); + auto object = QJsonObject(); + object.insert(u"ok"_q, true); + object.insert(u"activated"_q, next); + return Pack(object); +} + } // namespace QByteArray HandleExternalControl(const QString &command) { @@ -36,6 +123,12 @@ QByteArray HandleExternalControl(const QString &command) { object.insert(u"ok"_q, true); object.insert(u"result"_q, u"pong"_q); return Pack(object); + } else if (command == u"windows"_q) { + return HandleWindows(); + } else if (command.startsWith(u"activate:"_q)) { + return HandleActivate(command.mid(9).toInt()); + } else if (command == u"cycle"_q) { + return HandleCycle(); } return Error(u"unknown control command"_q); }