mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
scripted-widget: ipc call target now also support <target[:bar-name]> for complex setups
This commit is contained in:
+73
-30
@@ -29,6 +29,7 @@
|
||||
#include <cctype>
|
||||
#include <cmath>
|
||||
#include <linux/input-event-codes.h>
|
||||
#include <optional>
|
||||
#include <wayland-client-core.h>
|
||||
|
||||
namespace {
|
||||
@@ -137,7 +138,8 @@ namespace {
|
||||
}
|
||||
|
||||
constexpr Logger kLog("bar");
|
||||
constexpr std::string_view kScriptedWidgetIpcUsage = "scripted-widget <widget-name> <target> <event> [payload]";
|
||||
constexpr std::string_view kScriptedWidgetIpcUsage =
|
||||
"scripted-widget <widget-name> <target[:bar-name]> <event> [payload]";
|
||||
|
||||
struct ScriptedWidgetIpcCounts {
|
||||
int matched = 0;
|
||||
@@ -147,6 +149,17 @@ namespace {
|
||||
int failed = 0;
|
||||
};
|
||||
|
||||
struct ScriptedWidgetIpcTarget {
|
||||
std::string outputSelector;
|
||||
std::string barName;
|
||||
bool hasBarName = false;
|
||||
bool allOutputs = false;
|
||||
};
|
||||
|
||||
struct ScriptedWidgetIpcCandidate {
|
||||
ScriptedWidget* widget = nullptr;
|
||||
};
|
||||
|
||||
bool takeIpcWord(std::string_view& text, std::string& word) {
|
||||
text = StringUtils::trimLeftView(text);
|
||||
if (text.empty()) {
|
||||
@@ -161,6 +174,23 @@ namespace {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<ScriptedWidgetIpcTarget> parseScriptedWidgetIpcTarget(std::string_view raw) {
|
||||
ScriptedWidgetIpcTarget target;
|
||||
const std::size_t separator = raw.find(':');
|
||||
if (separator == std::string_view::npos) {
|
||||
target.outputSelector = std::string(raw);
|
||||
} else {
|
||||
target.outputSelector = std::string(raw.substr(0, separator));
|
||||
target.barName = std::string(raw.substr(separator + 1));
|
||||
target.hasBarName = true;
|
||||
}
|
||||
if (target.outputSelector.empty() || (target.hasBarName && target.barName.empty())) {
|
||||
return std::nullopt;
|
||||
}
|
||||
target.allOutputs = target.outputSelector == "all";
|
||||
return target;
|
||||
}
|
||||
|
||||
void recordScriptedWidgetIpcResult(ScriptedWidgetIpcCounts& counts, ScriptedWidget::IpcDispatchResult result) {
|
||||
++counts.matched;
|
||||
switch (result) {
|
||||
@@ -1933,56 +1963,55 @@ std::string Bar::dispatchScriptedWidgetIpc(std::string_view args) {
|
||||
|
||||
const std::string payload(StringUtils::trimLeftView(args));
|
||||
|
||||
auto dispatchWidget = [&](Widget* widget, ScriptedWidgetIpcCounts& counts) -> bool {
|
||||
const auto parsedTarget = parseScriptedWidgetIpcTarget(target);
|
||||
if (!parsedTarget.has_value()) {
|
||||
return std::string("error: usage: ") + std::string(kScriptedWidgetIpcUsage) + "\n";
|
||||
}
|
||||
|
||||
auto collectWidget = [&](Widget* widget, std::vector<ScriptedWidgetIpcCandidate>& candidates) {
|
||||
if (widget == nullptr || widget->configName() != std::string_view(widgetName)) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
auto* scripted = dynamic_cast<ScriptedWidget*>(widget);
|
||||
if (scripted == nullptr) {
|
||||
return false;
|
||||
if (scripted != nullptr) {
|
||||
candidates.push_back({.widget = scripted});
|
||||
}
|
||||
recordScriptedWidgetIpcResult(counts, scripted->dispatchIpcEvent(event, payload));
|
||||
return true;
|
||||
};
|
||||
|
||||
auto dispatchGroup = [&](std::vector<std::unique_ptr<Widget>>& widgets, ScriptedWidgetIpcCounts& counts,
|
||||
bool firstOnly) -> bool {
|
||||
auto collectGroup = [&](std::vector<std::unique_ptr<Widget>>& widgets,
|
||||
std::vector<ScriptedWidgetIpcCandidate>& candidates) {
|
||||
for (auto& widget : widgets) {
|
||||
if (dispatchWidget(widget.get(), counts) && firstOnly) {
|
||||
return true;
|
||||
}
|
||||
collectWidget(widget.get(), candidates);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
auto dispatchInstance = [&](BarInstance& instance, ScriptedWidgetIpcCounts& counts, bool firstOnly) -> bool {
|
||||
if (dispatchGroup(instance.startWidgets, counts, firstOnly)) {
|
||||
return true;
|
||||
auto collectInstance = [&](BarInstance& instance, std::vector<ScriptedWidgetIpcCandidate>& candidates) {
|
||||
if (parsedTarget->hasBarName && instance.barConfig.name != parsedTarget->barName) {
|
||||
return;
|
||||
}
|
||||
if (dispatchGroup(instance.centerWidgets, counts, firstOnly)) {
|
||||
return true;
|
||||
}
|
||||
return dispatchGroup(instance.endWidgets, counts, firstOnly);
|
||||
collectGroup(instance.startWidgets, candidates);
|
||||
collectGroup(instance.centerWidgets, candidates);
|
||||
collectGroup(instance.endWidgets, candidates);
|
||||
};
|
||||
|
||||
ScriptedWidgetIpcCounts counts;
|
||||
std::vector<ScriptedWidgetIpcCandidate> candidates;
|
||||
|
||||
if (target == "all") {
|
||||
if (parsedTarget->allOutputs) {
|
||||
for (const auto& instance : m_instances) {
|
||||
if (instance != nullptr) {
|
||||
(void)dispatchInstance(*instance, counts, false);
|
||||
collectInstance(*instance, candidates);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
wl_output* output = nullptr;
|
||||
|
||||
if (target == "focused") {
|
||||
if (parsedTarget->outputSelector == "focused") {
|
||||
output = m_platform != nullptr ? m_platform->preferredInteractiveOutput() : nullptr;
|
||||
} else {
|
||||
std::vector<wl_output*> matches;
|
||||
if (m_platform != nullptr) {
|
||||
for (const auto& candidate : m_platform->outputs()) {
|
||||
if (candidate.output != nullptr && outputMatchesSelector(target, candidate)) {
|
||||
if (candidate.output != nullptr && outputMatchesSelector(parsedTarget->outputSelector, candidate)) {
|
||||
matches.push_back(candidate.output);
|
||||
}
|
||||
}
|
||||
@@ -1997,13 +2026,30 @@ std::string Bar::dispatchScriptedWidgetIpc(std::string_view args) {
|
||||
|
||||
if (output != nullptr) {
|
||||
for (const auto& instance : m_instances) {
|
||||
if (instance != nullptr && instance->output == output && dispatchInstance(*instance, counts, true)) {
|
||||
break;
|
||||
if (instance != nullptr && instance->output == output) {
|
||||
collectInstance(*instance, candidates);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (candidates.empty()) {
|
||||
return "error: no scripted widget instance matched '" + widgetName + "' on target '" + target + "'\n";
|
||||
}
|
||||
|
||||
if (!parsedTarget->allOutputs && candidates.size() > 1) {
|
||||
return "error: target '" + target +
|
||||
"' matched multiple scripted widget instances; use '<target>:<bar-name>' "
|
||||
"or 'all'\n";
|
||||
}
|
||||
|
||||
ScriptedWidgetIpcCounts counts;
|
||||
for (const auto& candidate : candidates) {
|
||||
if (candidate.widget != nullptr) {
|
||||
recordScriptedWidgetIpcResult(counts, candidate.widget->dispatchIpcEvent(event, payload));
|
||||
}
|
||||
}
|
||||
|
||||
if (counts.failed > 0) {
|
||||
if (counts.handled > 0) {
|
||||
return "error: dispatched " + std::to_string(counts.handled) + ", failed " + std::to_string(counts.failed) + "\n";
|
||||
@@ -2013,9 +2059,6 @@ std::string Bar::dispatchScriptedWidgetIpc(std::string_view args) {
|
||||
if (counts.handled > 0) {
|
||||
return "ok: dispatched " + std::to_string(counts.handled) + "\n";
|
||||
}
|
||||
if (counts.matched == 0) {
|
||||
return "error: no scripted widget instance matched '" + widgetName + "' on target '" + target + "'\n";
|
||||
}
|
||||
if (counts.missingHost > 0) {
|
||||
return "error: matched scripted widget is not ready\n";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user