i3/ipc: implement IPC listener to receive arbitrary events
This commit is contained in:
parent
fdbb86a06a
commit
ab494dd982
14 changed files with 693 additions and 484 deletions
|
|
@ -3,6 +3,8 @@ qt_add_library(quickshell-i3-ipc STATIC
|
||||||
qml.cpp
|
qml.cpp
|
||||||
workspace.cpp
|
workspace.cpp
|
||||||
monitor.cpp
|
monitor.cpp
|
||||||
|
controller.cpp
|
||||||
|
listener.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
qt_add_qml_module(quickshell-i3-ipc
|
qt_add_qml_module(quickshell-i3-ipc
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
#include <algorithm>
|
#include "connection.hpp"
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
@ -23,11 +23,6 @@
|
||||||
#include <qtypes.h>
|
#include <qtypes.h>
|
||||||
|
|
||||||
#include "../../../core/logcat.hpp"
|
#include "../../../core/logcat.hpp"
|
||||||
#include "../../../core/model.hpp"
|
|
||||||
#include "../../../core/qmlscreen.hpp"
|
|
||||||
#include "connection.hpp"
|
|
||||||
#include "monitor.hpp"
|
|
||||||
#include "workspace.hpp"
|
|
||||||
|
|
||||||
namespace qs::i3::ipc {
|
namespace qs::i3::ipc {
|
||||||
|
|
||||||
|
|
@ -36,6 +31,69 @@ QS_LOGGING_CATEGORY(logI3Ipc, "quickshell.I3.ipc", QtWarningMsg);
|
||||||
QS_LOGGING_CATEGORY(logI3IpcEvents, "quickshell.I3.ipc.events", QtWarningMsg);
|
QS_LOGGING_CATEGORY(logI3IpcEvents, "quickshell.I3.ipc.events", QtWarningMsg);
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
QString I3IpcEvent::type() const { return I3IpcEvent::eventToString(this->mCode); }
|
||||||
|
QString I3IpcEvent::data() const { return QString::fromUtf8(this->mData.toJson()); }
|
||||||
|
|
||||||
|
EventCode I3IpcEvent::intToEvent(quint32 raw) {
|
||||||
|
if ((EventCode::Workspace <= raw && raw <= EventCode::Input)
|
||||||
|
|| (EventCode::RunCommand <= raw && raw <= EventCode::GetTree))
|
||||||
|
{
|
||||||
|
return static_cast<EventCode>(raw);
|
||||||
|
} else {
|
||||||
|
return EventCode::Unknown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString I3IpcEvent::eventToString(EventCode event) {
|
||||||
|
switch (event) {
|
||||||
|
case EventCode::RunCommand: return "run_command"; break;
|
||||||
|
case EventCode::GetWorkspaces: return "get_workspaces"; break;
|
||||||
|
case EventCode::Subscribe: return "subscribe"; break;
|
||||||
|
case EventCode::GetOutputs: return "get_outputs"; break;
|
||||||
|
case EventCode::GetTree: return "get_tree"; break;
|
||||||
|
|
||||||
|
case EventCode::Output: return "output"; break;
|
||||||
|
case EventCode::Workspace: return "workspace"; break;
|
||||||
|
case EventCode::Mode: return "mode"; break;
|
||||||
|
case EventCode::Window: return "window"; break;
|
||||||
|
case EventCode::BarconfigUpdate: return "barconfig_update"; break;
|
||||||
|
case EventCode::Binding: return "binding"; break;
|
||||||
|
case EventCode::Shutdown: return "shutdown"; break;
|
||||||
|
case EventCode::Tick: return "tick"; break;
|
||||||
|
case EventCode::BarStateUpdate: return "bar_state_update"; break;
|
||||||
|
case EventCode::Input: return "input"; break;
|
||||||
|
|
||||||
|
default: return "unknown"; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
I3Ipc::I3Ipc(const QList<QString>& events): mEvents(events) {
|
||||||
|
auto sock = qEnvironmentVariable("I3SOCK");
|
||||||
|
|
||||||
|
if (sock.isEmpty()) {
|
||||||
|
qCWarning(logI3Ipc) << "$I3SOCK is unset. Trying $SWAYSOCK.";
|
||||||
|
|
||||||
|
sock = qEnvironmentVariable("SWAYSOCK");
|
||||||
|
|
||||||
|
if (sock.isEmpty()) {
|
||||||
|
qCWarning(logI3Ipc) << "$SWAYSOCK and I3SOCK are unset. Cannot connect to socket.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this->mSocketPath = sock;
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
QObject::connect(&this->liveEventSocket, &QLocalSocket::errorOccurred, this, &I3Ipc::eventSocketError);
|
||||||
|
QObject::connect(&this->liveEventSocket, &QLocalSocket::stateChanged, this, &I3Ipc::eventSocketStateChanged);
|
||||||
|
QObject::connect(&this->liveEventSocket, &QLocalSocket::readyRead, this, &I3Ipc::eventSocketReady);
|
||||||
|
QObject::connect(&this->liveEventSocket, &QLocalSocket::connected, this, &I3Ipc::subscribe);
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
this->liveEventSocketDs.setDevice(&this->liveEventSocket);
|
||||||
|
this->liveEventSocketDs.setByteOrder(static_cast<QDataStream::ByteOrder>(QSysInfo::ByteOrder));
|
||||||
|
}
|
||||||
|
|
||||||
void I3Ipc::makeRequest(const QByteArray& request) {
|
void I3Ipc::makeRequest(const QByteArray& request) {
|
||||||
if (!this->valid) {
|
if (!this->valid) {
|
||||||
qCWarning(logI3IpcEvents) << "IPC connection is not open, ignoring request.";
|
qCWarning(logI3IpcEvents) << "IPC connection is not open, ignoring request.";
|
||||||
|
|
@ -60,50 +118,13 @@ QByteArray I3Ipc::buildRequestMessage(EventCode cmd, const QByteArray& payload)
|
||||||
return MAGIC.data() + len + type + payload;
|
return MAGIC.data() + len + type + payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
I3Ipc::I3Ipc() {
|
|
||||||
auto sock = qEnvironmentVariable("I3SOCK");
|
|
||||||
|
|
||||||
if (sock.isEmpty()) {
|
|
||||||
qCWarning(logI3Ipc) << "$I3SOCK is unset. Trying $SWAYSOCK.";
|
|
||||||
|
|
||||||
sock = qEnvironmentVariable("SWAYSOCK");
|
|
||||||
|
|
||||||
if (sock.isEmpty()) {
|
|
||||||
qCWarning(logI3Ipc) << "$SWAYSOCK and I3SOCK are unset. Cannot connect to socket.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this->bFocusedWorkspace.setBinding([this]() -> I3Workspace* {
|
|
||||||
if (!this->bFocusedMonitor) return nullptr;
|
|
||||||
return this->bFocusedMonitor->bindableActiveWorkspace().value();
|
|
||||||
});
|
|
||||||
|
|
||||||
this->mSocketPath = sock;
|
|
||||||
|
|
||||||
// clang-format off
|
|
||||||
QObject::connect(&this->liveEventSocket, &QLocalSocket::errorOccurred, this, &I3Ipc::eventSocketError);
|
|
||||||
QObject::connect(&this->liveEventSocket, &QLocalSocket::stateChanged, this, &I3Ipc::eventSocketStateChanged);
|
|
||||||
QObject::connect(&this->liveEventSocket, &QLocalSocket::readyRead, this, &I3Ipc::eventSocketReady);
|
|
||||||
QObject::connect(&this->liveEventSocket, &QLocalSocket::connected, this, &I3Ipc::subscribe);
|
|
||||||
// clang-format on
|
|
||||||
|
|
||||||
this->liveEventSocketDs.setDevice(&this->liveEventSocket);
|
|
||||||
this->liveEventSocketDs.setByteOrder(static_cast<QDataStream::ByteOrder>(QSysInfo::ByteOrder));
|
|
||||||
|
|
||||||
this->liveEventSocket.connectToServer(this->mSocketPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
void I3Ipc::subscribe() {
|
void I3Ipc::subscribe() {
|
||||||
auto payload = QByteArray(R"(["workspace","output"])");
|
auto jsonArray = QJsonArray::fromStringList(this->mEvents);
|
||||||
|
auto jsonDoc = QJsonDocument(jsonArray);
|
||||||
|
auto payload = jsonDoc.toJson(QJsonDocument::Compact);
|
||||||
auto message = I3Ipc::buildRequestMessage(EventCode::Subscribe, payload);
|
auto message = I3Ipc::buildRequestMessage(EventCode::Subscribe, payload);
|
||||||
|
|
||||||
this->makeRequest(message);
|
this->makeRequest(message);
|
||||||
|
|
||||||
// Workspaces must be refreshed before monitors or no focus will be
|
|
||||||
// detected on launch.
|
|
||||||
this->refreshWorkspaces();
|
|
||||||
this->refreshMonitors();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void I3Ipc::eventSocketReady() {
|
void I3Ipc::eventSocketReady() {
|
||||||
|
|
@ -111,15 +132,16 @@ void I3Ipc::eventSocketReady() {
|
||||||
this->event.mCode = type;
|
this->event.mCode = type;
|
||||||
this->event.mData = data;
|
this->event.mData = data;
|
||||||
|
|
||||||
this->onEvent(&this->event);
|
|
||||||
emit this->rawEvent(&this->event);
|
emit this->rawEvent(&this->event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void I3Ipc::connect() { this->liveEventSocket.connectToServer(this->mSocketPath); }
|
||||||
|
|
||||||
void I3Ipc::reconnectIPC() {
|
void I3Ipc::reconnectIPC() {
|
||||||
qCWarning(logI3Ipc) << "Fatal IPC error occured, recreating connection";
|
qCWarning(logI3Ipc) << "Fatal IPC error occured, recreating connection";
|
||||||
this->liveEventSocket.disconnectFromServer();
|
this->liveEventSocket.disconnectFromServer();
|
||||||
this->liveEventSocket.connectToServer(this->mSocketPath);
|
this->connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<Event> I3Ipc::parseResponse() {
|
QVector<Event> I3Ipc::parseResponse() {
|
||||||
|
|
@ -193,347 +215,4 @@ void I3Ipc::eventSocketStateChanged(QLocalSocket::LocalSocketState state) {
|
||||||
|
|
||||||
QString I3Ipc::socketPath() const { return this->mSocketPath; }
|
QString I3Ipc::socketPath() const { return this->mSocketPath; }
|
||||||
|
|
||||||
void I3Ipc::setFocusedMonitor(I3Monitor* monitor) {
|
|
||||||
auto* oldMonitor = this->bFocusedMonitor.value();
|
|
||||||
if (monitor == oldMonitor) return;
|
|
||||||
|
|
||||||
if (oldMonitor != nullptr) {
|
|
||||||
QObject::disconnect(oldMonitor, nullptr, this, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (monitor != nullptr) {
|
|
||||||
QObject::connect(monitor, &QObject::destroyed, this, &I3Ipc::onFocusedMonitorDestroyed);
|
|
||||||
}
|
|
||||||
|
|
||||||
this->bFocusedMonitor = monitor;
|
|
||||||
}
|
|
||||||
|
|
||||||
void I3Ipc::onFocusedMonitorDestroyed() { this->bFocusedMonitor = nullptr; }
|
|
||||||
|
|
||||||
I3Ipc* I3Ipc::instance() {
|
|
||||||
static I3Ipc* instance = nullptr; // NOLINT
|
|
||||||
|
|
||||||
if (instance == nullptr) {
|
|
||||||
instance = new I3Ipc();
|
|
||||||
}
|
|
||||||
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
void I3Ipc::refreshWorkspaces() {
|
|
||||||
this->makeRequest(I3Ipc::buildRequestMessage(EventCode::GetWorkspaces));
|
|
||||||
}
|
|
||||||
|
|
||||||
void I3Ipc::handleGetWorkspacesEvent(I3IpcEvent* event) {
|
|
||||||
auto data = event->mData;
|
|
||||||
|
|
||||||
auto workspaces = data.array();
|
|
||||||
|
|
||||||
const auto& mList = this->mWorkspaces.valueList();
|
|
||||||
auto names = QVector<QString>();
|
|
||||||
|
|
||||||
qCDebug(logI3Ipc) << "There are" << workspaces.toVariantList().length() << "workspaces";
|
|
||||||
for (auto entry: workspaces) {
|
|
||||||
auto object = entry.toObject().toVariantMap();
|
|
||||||
auto name = object["name"].toString();
|
|
||||||
|
|
||||||
auto workspaceIter = std::ranges::find_if(mList, [name](I3Workspace* m) {
|
|
||||||
return m->bindableName().value() == name;
|
|
||||||
});
|
|
||||||
|
|
||||||
auto* workspace = workspaceIter == mList.end() ? nullptr : *workspaceIter;
|
|
||||||
auto existed = workspace != nullptr;
|
|
||||||
|
|
||||||
if (workspace == nullptr) {
|
|
||||||
workspace = new I3Workspace(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
workspace->updateFromObject(object);
|
|
||||||
|
|
||||||
if (!existed) {
|
|
||||||
this->mWorkspaces.insertObjectSorted(workspace, &I3Ipc::compareWorkspaces);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this->bFocusedWorkspace && object.value("focused").value<bool>()) {
|
|
||||||
this->bFocusedMonitor = workspace->bindableMonitor().value();
|
|
||||||
}
|
|
||||||
|
|
||||||
names.push_back(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto removedWorkspaces = QVector<I3Workspace*>();
|
|
||||||
|
|
||||||
for (auto* workspace: mList) {
|
|
||||||
if (!names.contains(workspace->bindableName().value())) {
|
|
||||||
removedWorkspaces.push_back(workspace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
qCDebug(logI3Ipc) << "Removing" << removedWorkspaces.length() << "deleted workspaces.";
|
|
||||||
|
|
||||||
for (auto* workspace: removedWorkspaces) {
|
|
||||||
this->mWorkspaces.removeObject(workspace);
|
|
||||||
delete workspace;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void I3Ipc::refreshMonitors() {
|
|
||||||
this->makeRequest(I3Ipc::buildRequestMessage(EventCode::GetOutputs));
|
|
||||||
}
|
|
||||||
|
|
||||||
void I3Ipc::handleGetOutputsEvent(I3IpcEvent* event) {
|
|
||||||
auto data = event->mData;
|
|
||||||
|
|
||||||
auto monitors = data.array();
|
|
||||||
const auto& mList = this->mMonitors.valueList();
|
|
||||||
auto names = QVector<QString>();
|
|
||||||
|
|
||||||
qCDebug(logI3Ipc) << "There are" << monitors.toVariantList().length() << "monitors";
|
|
||||||
|
|
||||||
for (auto elem: monitors) {
|
|
||||||
auto object = elem.toObject().toVariantMap();
|
|
||||||
auto name = object["name"].toString();
|
|
||||||
|
|
||||||
auto monitorIter = std::ranges::find_if(mList, [name](I3Monitor* m) {
|
|
||||||
return m->bindableName().value() == name;
|
|
||||||
});
|
|
||||||
|
|
||||||
auto* monitor = monitorIter == mList.end() ? nullptr : *monitorIter;
|
|
||||||
auto existed = monitor != nullptr;
|
|
||||||
|
|
||||||
if (monitor == nullptr) {
|
|
||||||
monitor = new I3Monitor(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
monitor->updateFromObject(object);
|
|
||||||
|
|
||||||
if (monitor->bindableFocused().value()) {
|
|
||||||
this->setFocusedMonitor(monitor);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!existed) {
|
|
||||||
this->mMonitors.insertObject(monitor);
|
|
||||||
}
|
|
||||||
|
|
||||||
names.push_back(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto removedMonitors = QVector<I3Monitor*>();
|
|
||||||
|
|
||||||
for (auto* monitor: mList) {
|
|
||||||
if (!names.contains(monitor->bindableName().value())) {
|
|
||||||
removedMonitors.push_back(monitor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
qCDebug(logI3Ipc) << "Removing" << removedMonitors.length() << "disconnected monitors.";
|
|
||||||
|
|
||||||
for (auto* monitor: removedMonitors) {
|
|
||||||
this->mMonitors.removeObject(monitor);
|
|
||||||
delete monitor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void I3Ipc::onEvent(I3IpcEvent* event) {
|
|
||||||
switch (event->mCode) {
|
|
||||||
case EventCode::Workspace: this->handleWorkspaceEvent(event); return;
|
|
||||||
case EventCode::Output:
|
|
||||||
/// I3 only sends an "unspecified" event, so we have to query the data changes ourselves
|
|
||||||
qCInfo(logI3Ipc) << "Refreshing Monitors...";
|
|
||||||
this->refreshMonitors();
|
|
||||||
return;
|
|
||||||
case EventCode::Subscribe: qCInfo(logI3Ipc) << "Connected to IPC"; return;
|
|
||||||
case EventCode::GetOutputs: this->handleGetOutputsEvent(event); return;
|
|
||||||
case EventCode::GetWorkspaces: this->handleGetWorkspacesEvent(event); return;
|
|
||||||
case EventCode::RunCommand: I3Ipc::handleRunCommand(event); return;
|
|
||||||
case EventCode::Unknown:
|
|
||||||
qCWarning(logI3Ipc) << "Unknown event:" << event->type() << event->data();
|
|
||||||
return;
|
|
||||||
default: qCWarning(logI3Ipc) << "Unhandled event:" << event->type();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void I3Ipc::handleRunCommand(I3IpcEvent* event) {
|
|
||||||
for (auto r: event->mData.array()) {
|
|
||||||
auto obj = r.toObject();
|
|
||||||
const bool success = obj["success"].toBool();
|
|
||||||
|
|
||||||
if (!success) {
|
|
||||||
const QString error = obj["error"].toString();
|
|
||||||
qCWarning(logI3Ipc) << "Error occured while running command:" << error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void I3Ipc::handleWorkspaceEvent(I3IpcEvent* event) {
|
|
||||||
// If a workspace doesn't exist, and is being switch to, no focus change event is emited,
|
|
||||||
// only the init one, which does not contain the previously focused workspace
|
|
||||||
auto change = event->mData["change"];
|
|
||||||
|
|
||||||
if (change == "init") {
|
|
||||||
qCInfo(logI3IpcEvents) << "New workspace has been created";
|
|
||||||
|
|
||||||
auto workspaceData = event->mData["current"];
|
|
||||||
|
|
||||||
auto* workspace = this->findWorkspaceByID(workspaceData["id"].toInt(-1));
|
|
||||||
auto existed = workspace != nullptr;
|
|
||||||
|
|
||||||
if (!existed) {
|
|
||||||
workspace = new I3Workspace(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (workspaceData.isObject()) {
|
|
||||||
workspace->updateFromObject(workspaceData.toObject().toVariantMap());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!existed) {
|
|
||||||
this->mWorkspaces.insertObjectSorted(workspace, &I3Ipc::compareWorkspaces);
|
|
||||||
qCInfo(logI3Ipc) << "Added workspace" << workspace->bindableName().value() << "to list";
|
|
||||||
}
|
|
||||||
} else if (change == "focus") {
|
|
||||||
auto oldData = event->mData["old"];
|
|
||||||
auto newData = event->mData["current"];
|
|
||||||
auto oldName = oldData["name"].toString();
|
|
||||||
auto newName = newData["name"].toString();
|
|
||||||
|
|
||||||
qCInfo(logI3IpcEvents) << "Focus changed: " << oldName << "->" << newName;
|
|
||||||
|
|
||||||
if (auto* oldWorkspace = this->findWorkspaceByName(oldName)) {
|
|
||||||
oldWorkspace->updateFromObject(oldData.toObject().toVariantMap());
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* newWorkspace = this->findWorkspaceByName(newName);
|
|
||||||
|
|
||||||
if (newWorkspace == nullptr) {
|
|
||||||
newWorkspace = new I3Workspace(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
newWorkspace->updateFromObject(newData.toObject().toVariantMap());
|
|
||||||
|
|
||||||
if (newWorkspace->bindableMonitor().value()) {
|
|
||||||
auto* monitor = newWorkspace->bindableMonitor().value();
|
|
||||||
monitor->setFocusedWorkspace(newWorkspace);
|
|
||||||
this->bFocusedMonitor = monitor;
|
|
||||||
}
|
|
||||||
} else if (change == "empty") {
|
|
||||||
auto name = event->mData["current"]["name"].toString();
|
|
||||||
|
|
||||||
auto* oldWorkspace = this->findWorkspaceByName(name);
|
|
||||||
|
|
||||||
if (oldWorkspace != nullptr) {
|
|
||||||
qCInfo(logI3Ipc) << "Deleting" << oldWorkspace->bindableId().value() << name;
|
|
||||||
|
|
||||||
if (this->bFocusedWorkspace == oldWorkspace) {
|
|
||||||
this->bFocusedMonitor->setFocusedWorkspace(nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
this->workspaces()->removeObject(oldWorkspace);
|
|
||||||
|
|
||||||
delete oldWorkspace;
|
|
||||||
} else {
|
|
||||||
qCInfo(logI3Ipc) << "Workspace" << name << "has already been deleted";
|
|
||||||
}
|
|
||||||
} else if (change == "move" || change == "rename" || change == "urgent") {
|
|
||||||
auto name = event->mData["current"]["name"].toString();
|
|
||||||
|
|
||||||
auto* workspace = this->findWorkspaceByName(name);
|
|
||||||
|
|
||||||
if (workspace != nullptr) {
|
|
||||||
auto data = event->mData["current"].toObject().toVariantMap();
|
|
||||||
|
|
||||||
workspace->updateFromObject(data);
|
|
||||||
} else {
|
|
||||||
qCWarning(logI3Ipc) << "Workspace" << name << "doesn't exist";
|
|
||||||
}
|
|
||||||
} else if (change == "reload") {
|
|
||||||
qCInfo(logI3Ipc) << "Refreshing Workspaces...";
|
|
||||||
this->refreshWorkspaces();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
I3Monitor* I3Ipc::monitorFor(QuickshellScreenInfo* screen) {
|
|
||||||
if (screen == nullptr) return nullptr;
|
|
||||||
|
|
||||||
return this->findMonitorByName(screen->name());
|
|
||||||
}
|
|
||||||
|
|
||||||
I3Workspace* I3Ipc::findWorkspaceByID(qint32 id) {
|
|
||||||
auto list = this->mWorkspaces.valueList();
|
|
||||||
auto workspaceIter =
|
|
||||||
std::ranges::find_if(list, [id](I3Workspace* m) { return m->bindableId().value() == id; });
|
|
||||||
|
|
||||||
return workspaceIter == list.end() ? nullptr : *workspaceIter;
|
|
||||||
}
|
|
||||||
|
|
||||||
I3Workspace* I3Ipc::findWorkspaceByName(const QString& name) {
|
|
||||||
auto list = this->mWorkspaces.valueList();
|
|
||||||
auto workspaceIter = std::ranges::find_if(list, [name](I3Workspace* m) {
|
|
||||||
return m->bindableName().value() == name;
|
|
||||||
});
|
|
||||||
|
|
||||||
return workspaceIter == list.end() ? nullptr : *workspaceIter;
|
|
||||||
}
|
|
||||||
|
|
||||||
I3Monitor* I3Ipc::findMonitorByName(const QString& name, bool createIfMissing) {
|
|
||||||
auto list = this->mMonitors.valueList();
|
|
||||||
auto monitorIter = std::ranges::find_if(list, [name](I3Monitor* m) {
|
|
||||||
return m->bindableName().value() == name;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (monitorIter != list.end()) {
|
|
||||||
return *monitorIter;
|
|
||||||
} else if (createIfMissing) {
|
|
||||||
qCDebug(logI3Ipc) << "Monitor" << name << "requested before creation, performing early init";
|
|
||||||
auto* monitor = new I3Monitor(this);
|
|
||||||
monitor->updateInitial(name);
|
|
||||||
this->mMonitors.insertObject(monitor);
|
|
||||||
return monitor;
|
|
||||||
} else {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjectModel<I3Monitor>* I3Ipc::monitors() { return &this->mMonitors; }
|
|
||||||
ObjectModel<I3Workspace>* I3Ipc::workspaces() { return &this->mWorkspaces; }
|
|
||||||
|
|
||||||
bool I3Ipc::compareWorkspaces(I3Workspace* a, I3Workspace* b) {
|
|
||||||
return a->bindableNumber().value() > b->bindableNumber().value();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString I3IpcEvent::type() const { return I3IpcEvent::eventToString(this->mCode); }
|
|
||||||
QString I3IpcEvent::data() const { return QString::fromUtf8(this->mData.toJson()); }
|
|
||||||
|
|
||||||
EventCode I3IpcEvent::intToEvent(quint32 raw) {
|
|
||||||
if ((EventCode::Workspace <= raw && raw <= EventCode::Input)
|
|
||||||
|| (EventCode::RunCommand <= raw && raw <= EventCode::GetTree))
|
|
||||||
{
|
|
||||||
return static_cast<EventCode>(raw);
|
|
||||||
} else {
|
|
||||||
return EventCode::Unknown;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString I3IpcEvent::eventToString(EventCode event) {
|
|
||||||
switch (event) {
|
|
||||||
case EventCode::RunCommand: return "run_command"; break;
|
|
||||||
case EventCode::GetWorkspaces: return "get_workspaces"; break;
|
|
||||||
case EventCode::Subscribe: return "subscribe"; break;
|
|
||||||
case EventCode::GetOutputs: return "get_outputs"; break;
|
|
||||||
case EventCode::GetTree: return "get_tree"; break;
|
|
||||||
|
|
||||||
case EventCode::Output: return "output"; break;
|
|
||||||
case EventCode::Workspace: return "workspace"; break;
|
|
||||||
case EventCode::Mode: return "mode"; break;
|
|
||||||
case EventCode::Window: return "window"; break;
|
|
||||||
case EventCode::BarconfigUpdate: return "barconfig_update"; break;
|
|
||||||
case EventCode::Binding: return "binding"; break;
|
|
||||||
case EventCode::Shutdown: return "shutdown"; break;
|
|
||||||
case EventCode::Tick: return "tick"; break;
|
|
||||||
case EventCode::BarStateUpdate: return "bar_state_update"; break;
|
|
||||||
case EventCode::Input: return "input"; break;
|
|
||||||
|
|
||||||
default: return "unknown"; break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace qs::i3::ipc
|
} // namespace qs::i3::ipc
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,14 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <qbytearrayview.h>
|
#include <qbytearrayview.h>
|
||||||
|
#include <qdatastream.h>
|
||||||
#include <qjsondocument.h>
|
#include <qjsondocument.h>
|
||||||
#include <qjsonobject.h>
|
|
||||||
#include <qlocalsocket.h>
|
#include <qlocalsocket.h>
|
||||||
#include <qobject.h>
|
#include <qobject.h>
|
||||||
#include <qproperty.h>
|
|
||||||
#include <qqml.h>
|
|
||||||
#include <qqmlintegration.h>
|
#include <qqmlintegration.h>
|
||||||
#include <qtmetamacros.h>
|
#include <qtmetamacros.h>
|
||||||
#include <qtypes.h>
|
#include <qtypes.h>
|
||||||
|
|
||||||
#include "../../../core/model.hpp"
|
|
||||||
#include "../../../core/qmlscreen.hpp"
|
|
||||||
|
|
||||||
namespace qs::i3::ipc {
|
|
||||||
|
|
||||||
class I3Workspace;
|
|
||||||
class I3Monitor;
|
|
||||||
} // namespace qs::i3::ipc
|
|
||||||
|
|
||||||
Q_DECLARE_OPAQUE_POINTER(qs::i3::ipc::I3Workspace*);
|
|
||||||
Q_DECLARE_OPAQUE_POINTER(qs::i3::ipc::I3Monitor*);
|
|
||||||
|
|
||||||
namespace qs::i3::ipc {
|
namespace qs::i3::ipc {
|
||||||
|
|
||||||
constexpr std::string MAGIC = "i3-ipc";
|
constexpr std::string MAGIC = "i3-ipc";
|
||||||
|
|
@ -54,9 +40,7 @@ using Event = std::tuple<EventCode, QJsonDocument>;
|
||||||
class I3IpcEvent: public QObject {
|
class I3IpcEvent: public QObject {
|
||||||
Q_OBJECT;
|
Q_OBJECT;
|
||||||
|
|
||||||
/// The name of the event
|
|
||||||
Q_PROPERTY(QString type READ type CONSTANT);
|
Q_PROPERTY(QString type READ type CONSTANT);
|
||||||
/// The payload of the event in JSON format.
|
|
||||||
Q_PROPERTY(QString data READ data CONSTANT);
|
Q_PROPERTY(QString data READ data CONSTANT);
|
||||||
|
|
||||||
QML_NAMED_ELEMENT(I3Event);
|
QML_NAMED_ELEMENT(I3Event);
|
||||||
|
|
@ -75,90 +59,48 @@ public:
|
||||||
static QString eventToString(EventCode event);
|
static QString eventToString(EventCode event);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Base class that manages the IPC socket, subscriptions and event reception.
|
||||||
class I3Ipc: public QObject {
|
class I3Ipc: public QObject {
|
||||||
Q_OBJECT;
|
Q_OBJECT;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static I3Ipc* instance();
|
explicit I3Ipc(const QList<QString>& events);
|
||||||
|
|
||||||
[[nodiscard]] QString socketPath() const;
|
[[nodiscard]] QString socketPath() const;
|
||||||
|
|
||||||
void makeRequest(const QByteArray& request);
|
void makeRequest(const QByteArray& request);
|
||||||
void dispatch(const QString& payload);
|
void dispatch(const QString& payload);
|
||||||
|
void connect();
|
||||||
|
|
||||||
static QByteArray buildRequestMessage(EventCode cmd, const QByteArray& payload = QByteArray());
|
[[nodiscard]] QByteArray static buildRequestMessage(
|
||||||
|
EventCode cmd,
|
||||||
I3Workspace* findWorkspaceByName(const QString& name);
|
const QByteArray& payload = QByteArray()
|
||||||
I3Monitor* findMonitorByName(const QString& name, bool createIfMissing = false);
|
);
|
||||||
I3Workspace* findWorkspaceByID(qint32 id);
|
|
||||||
|
|
||||||
void setFocusedMonitor(I3Monitor* monitor);
|
|
||||||
|
|
||||||
void refreshWorkspaces();
|
|
||||||
void refreshMonitors();
|
|
||||||
|
|
||||||
I3Monitor* monitorFor(QuickshellScreenInfo* screen);
|
|
||||||
|
|
||||||
[[nodiscard]] QBindable<I3Monitor*> bindableFocusedMonitor() const {
|
|
||||||
return &this->bFocusedMonitor;
|
|
||||||
};
|
|
||||||
|
|
||||||
[[nodiscard]] QBindable<I3Workspace*> bindableFocusedWorkspace() const {
|
|
||||||
return &this->bFocusedWorkspace;
|
|
||||||
};
|
|
||||||
|
|
||||||
[[nodiscard]] ObjectModel<I3Monitor>* monitors();
|
|
||||||
[[nodiscard]] ObjectModel<I3Workspace>* workspaces();
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void connected();
|
void connected();
|
||||||
void rawEvent(I3IpcEvent* event);
|
void rawEvent(I3IpcEvent* event);
|
||||||
void focusedWorkspaceChanged();
|
|
||||||
void focusedMonitorChanged();
|
|
||||||
|
|
||||||
private slots:
|
protected slots:
|
||||||
void eventSocketError(QLocalSocket::LocalSocketError error) const;
|
void eventSocketError(QLocalSocket::LocalSocketError error) const;
|
||||||
void eventSocketStateChanged(QLocalSocket::LocalSocketState state);
|
void eventSocketStateChanged(QLocalSocket::LocalSocketState state);
|
||||||
void eventSocketReady();
|
void eventSocketReady();
|
||||||
void subscribe();
|
void subscribe();
|
||||||
|
|
||||||
void onFocusedMonitorDestroyed();
|
protected:
|
||||||
|
|
||||||
private:
|
|
||||||
explicit I3Ipc();
|
|
||||||
|
|
||||||
void onEvent(I3IpcEvent* event);
|
|
||||||
|
|
||||||
void handleWorkspaceEvent(I3IpcEvent* event);
|
|
||||||
void handleGetWorkspacesEvent(I3IpcEvent* event);
|
|
||||||
void handleGetOutputsEvent(I3IpcEvent* event);
|
|
||||||
static void handleRunCommand(I3IpcEvent* event);
|
|
||||||
static bool compareWorkspaces(I3Workspace* a, I3Workspace* b);
|
|
||||||
|
|
||||||
void reconnectIPC();
|
void reconnectIPC();
|
||||||
|
|
||||||
QVector<std::tuple<EventCode, QJsonDocument>> parseResponse();
|
QVector<std::tuple<EventCode, QJsonDocument>> parseResponse();
|
||||||
|
|
||||||
QLocalSocket liveEventSocket;
|
QLocalSocket liveEventSocket;
|
||||||
QDataStream liveEventSocketDs;
|
QDataStream liveEventSocketDs;
|
||||||
|
|
||||||
QString mSocketPath;
|
QString mSocketPath;
|
||||||
|
|
||||||
bool valid = false;
|
bool valid = false;
|
||||||
|
|
||||||
ObjectModel<I3Monitor> mMonitors {this};
|
|
||||||
ObjectModel<I3Workspace> mWorkspaces {this};
|
|
||||||
|
|
||||||
I3IpcEvent event {this};
|
I3IpcEvent event {this};
|
||||||
|
|
||||||
Q_OBJECT_BINDABLE_PROPERTY(I3Ipc, I3Monitor*, bFocusedMonitor, &I3Ipc::focusedMonitorChanged);
|
private:
|
||||||
|
QList<QString> mEvents;
|
||||||
Q_OBJECT_BINDABLE_PROPERTY(
|
|
||||||
I3Ipc,
|
|
||||||
I3Workspace*,
|
|
||||||
bFocusedWorkspace,
|
|
||||||
&I3Ipc::focusedWorkspaceChanged
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace qs::i3::ipc
|
} // namespace qs::i3::ipc
|
||||||
|
|
|
||||||
367
src/x11/i3/ipc/controller.cpp
Normal file
367
src/x11/i3/ipc/controller.cpp
Normal file
|
|
@ -0,0 +1,367 @@
|
||||||
|
#include "controller.hpp"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include <qbytearray.h>
|
||||||
|
#include <qbytearrayview.h>
|
||||||
|
#include <qcontainerfwd.h>
|
||||||
|
#include <qdatastream.h>
|
||||||
|
#include <qjsonarray.h>
|
||||||
|
#include <qjsondocument.h>
|
||||||
|
#include <qjsonobject.h>
|
||||||
|
#include <qjsonvalue.h>
|
||||||
|
#include <qlocalsocket.h>
|
||||||
|
#include <qlogging.h>
|
||||||
|
#include <qloggingcategory.h>
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qsysinfo.h>
|
||||||
|
#include <qtenvironmentvariables.h>
|
||||||
|
#include <qtypes.h>
|
||||||
|
|
||||||
|
#include "../../../core/logcat.hpp"
|
||||||
|
#include "../../../core/model.hpp"
|
||||||
|
#include "../../../core/qmlscreen.hpp"
|
||||||
|
#include "connection.hpp"
|
||||||
|
#include "monitor.hpp"
|
||||||
|
#include "workspace.hpp"
|
||||||
|
|
||||||
|
namespace qs::i3::ipc {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
QS_LOGGING_CATEGORY(logI3Ipc, "quickshell.I3.ipc", QtWarningMsg);
|
||||||
|
QS_LOGGING_CATEGORY(logI3IpcEvents, "quickshell.I3.ipc.events", QtWarningMsg);
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
I3IpcController::I3IpcController(): I3Ipc({"workspace", "output"}) {
|
||||||
|
// bind focused workspace to focused monitor's active workspace
|
||||||
|
this->bFocusedWorkspace.setBinding([this]() -> I3Workspace* {
|
||||||
|
if (!this->bFocusedMonitor) return nullptr;
|
||||||
|
return this->bFocusedMonitor->bindableActiveWorkspace().value();
|
||||||
|
});
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
QObject::connect(this, &I3Ipc::rawEvent, this, &I3IpcController::onEvent);
|
||||||
|
QObject::connect(&this->liveEventSocket, &QLocalSocket::connected, this, &I3IpcController::onConnected);
|
||||||
|
// clang-format on
|
||||||
|
}
|
||||||
|
|
||||||
|
void I3IpcController::onConnected() {
|
||||||
|
// Workspaces must be refreshed before monitors or no focus will be
|
||||||
|
// detected on launch.
|
||||||
|
this->refreshWorkspaces();
|
||||||
|
this->refreshMonitors();
|
||||||
|
}
|
||||||
|
|
||||||
|
void I3IpcController::setFocusedMonitor(I3Monitor* monitor) {
|
||||||
|
auto* oldMonitor = this->bFocusedMonitor.value();
|
||||||
|
if (monitor == oldMonitor) return;
|
||||||
|
|
||||||
|
if (oldMonitor != nullptr) {
|
||||||
|
QObject::disconnect(oldMonitor, nullptr, this, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (monitor != nullptr) {
|
||||||
|
QObject::connect(
|
||||||
|
monitor,
|
||||||
|
&QObject::destroyed,
|
||||||
|
this,
|
||||||
|
&I3IpcController::onFocusedMonitorDestroyed
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->bFocusedMonitor = monitor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void I3IpcController::onFocusedMonitorDestroyed() { this->bFocusedMonitor = nullptr; }
|
||||||
|
|
||||||
|
I3IpcController* I3IpcController::instance() {
|
||||||
|
static I3IpcController* instance = nullptr; // NOLINT
|
||||||
|
|
||||||
|
if (instance == nullptr) {
|
||||||
|
instance = new I3IpcController();
|
||||||
|
instance->connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void I3IpcController::refreshWorkspaces() {
|
||||||
|
this->makeRequest(I3Ipc::buildRequestMessage(EventCode::GetWorkspaces));
|
||||||
|
}
|
||||||
|
|
||||||
|
void I3IpcController::handleGetWorkspacesEvent(I3IpcEvent* event) {
|
||||||
|
auto data = event->mData;
|
||||||
|
|
||||||
|
auto workspaces = data.array();
|
||||||
|
|
||||||
|
const auto& mList = this->mWorkspaces.valueList();
|
||||||
|
auto names = QVector<QString>();
|
||||||
|
|
||||||
|
qCDebug(logI3Ipc) << "There are" << workspaces.toVariantList().length() << "workspaces";
|
||||||
|
for (auto entry: workspaces) {
|
||||||
|
auto object = entry.toObject().toVariantMap();
|
||||||
|
auto name = object["name"].toString();
|
||||||
|
|
||||||
|
auto workspaceIter = std::ranges::find_if(mList, [name](I3Workspace* m) {
|
||||||
|
return m->bindableName().value() == name;
|
||||||
|
});
|
||||||
|
|
||||||
|
auto* workspace = workspaceIter == mList.end() ? nullptr : *workspaceIter;
|
||||||
|
auto existed = workspace != nullptr;
|
||||||
|
|
||||||
|
if (workspace == nullptr) {
|
||||||
|
workspace = new I3Workspace(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
workspace->updateFromObject(object);
|
||||||
|
|
||||||
|
if (!existed) {
|
||||||
|
this->mWorkspaces.insertObjectSorted(workspace, &I3IpcController::compareWorkspaces);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->bFocusedWorkspace && object.value("focused").value<bool>()) {
|
||||||
|
this->bFocusedMonitor = workspace->bindableMonitor().value();
|
||||||
|
}
|
||||||
|
|
||||||
|
names.push_back(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto removedWorkspaces = QVector<I3Workspace*>();
|
||||||
|
|
||||||
|
for (auto* workspace: mList) {
|
||||||
|
if (!names.contains(workspace->bindableName().value())) {
|
||||||
|
removedWorkspaces.push_back(workspace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qCDebug(logI3Ipc) << "Removing" << removedWorkspaces.length() << "deleted workspaces.";
|
||||||
|
|
||||||
|
for (auto* workspace: removedWorkspaces) {
|
||||||
|
this->mWorkspaces.removeObject(workspace);
|
||||||
|
delete workspace;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void I3IpcController::refreshMonitors() {
|
||||||
|
this->makeRequest(I3Ipc::buildRequestMessage(EventCode::GetOutputs));
|
||||||
|
}
|
||||||
|
|
||||||
|
void I3IpcController::handleGetOutputsEvent(I3IpcEvent* event) {
|
||||||
|
auto data = event->mData;
|
||||||
|
|
||||||
|
auto monitors = data.array();
|
||||||
|
const auto& mList = this->mMonitors.valueList();
|
||||||
|
auto names = QVector<QString>();
|
||||||
|
|
||||||
|
qCDebug(logI3Ipc) << "There are" << monitors.toVariantList().length() << "monitors";
|
||||||
|
|
||||||
|
for (auto elem: monitors) {
|
||||||
|
auto object = elem.toObject().toVariantMap();
|
||||||
|
auto name = object["name"].toString();
|
||||||
|
|
||||||
|
auto monitorIter = std::ranges::find_if(mList, [name](I3Monitor* m) {
|
||||||
|
return m->bindableName().value() == name;
|
||||||
|
});
|
||||||
|
|
||||||
|
auto* monitor = monitorIter == mList.end() ? nullptr : *monitorIter;
|
||||||
|
auto existed = monitor != nullptr;
|
||||||
|
|
||||||
|
if (monitor == nullptr) {
|
||||||
|
monitor = new I3Monitor(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
monitor->updateFromObject(object);
|
||||||
|
|
||||||
|
if (monitor->bindableFocused().value()) {
|
||||||
|
this->setFocusedMonitor(monitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!existed) {
|
||||||
|
this->mMonitors.insertObject(monitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
names.push_back(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto removedMonitors = QVector<I3Monitor*>();
|
||||||
|
|
||||||
|
for (auto* monitor: mList) {
|
||||||
|
if (!names.contains(monitor->bindableName().value())) {
|
||||||
|
removedMonitors.push_back(monitor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qCDebug(logI3Ipc) << "Removing" << removedMonitors.length() << "disconnected monitors.";
|
||||||
|
|
||||||
|
for (auto* monitor: removedMonitors) {
|
||||||
|
this->mMonitors.removeObject(monitor);
|
||||||
|
delete monitor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void I3IpcController::onEvent(I3IpcEvent* event) {
|
||||||
|
switch (event->mCode) {
|
||||||
|
case EventCode::Workspace: this->handleWorkspaceEvent(event); return;
|
||||||
|
case EventCode::Output:
|
||||||
|
/// I3 only sends an "unspecified" event, so we have to query the data changes ourselves
|
||||||
|
qCInfo(logI3Ipc) << "Refreshing Monitors...";
|
||||||
|
this->refreshMonitors();
|
||||||
|
return;
|
||||||
|
case EventCode::Subscribe: qCInfo(logI3Ipc) << "Connected to IPC"; return;
|
||||||
|
case EventCode::GetOutputs: this->handleGetOutputsEvent(event); return;
|
||||||
|
case EventCode::GetWorkspaces: this->handleGetWorkspacesEvent(event); return;
|
||||||
|
case EventCode::RunCommand: I3IpcController::handleRunCommand(event); return;
|
||||||
|
case EventCode::Unknown:
|
||||||
|
qCWarning(logI3Ipc) << "Unknown event:" << event->type() << event->data();
|
||||||
|
return;
|
||||||
|
default: qCWarning(logI3Ipc) << "Unhandled event:" << event->type();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void I3IpcController::handleRunCommand(I3IpcEvent* event) {
|
||||||
|
for (auto r: event->mData.array()) {
|
||||||
|
auto obj = r.toObject();
|
||||||
|
const bool success = obj["success"].toBool();
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
const QString error = obj["error"].toString();
|
||||||
|
qCWarning(logI3Ipc) << "Error occured while running command:" << error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void I3IpcController::handleWorkspaceEvent(I3IpcEvent* event) {
|
||||||
|
// If a workspace doesn't exist, and is being switch to, no focus change event is emited,
|
||||||
|
// only the init one, which does not contain the previously focused workspace
|
||||||
|
auto change = event->mData["change"];
|
||||||
|
|
||||||
|
if (change == "init") {
|
||||||
|
qCInfo(logI3IpcEvents) << "New workspace has been created";
|
||||||
|
|
||||||
|
auto workspaceData = event->mData["current"];
|
||||||
|
|
||||||
|
auto* workspace = this->findWorkspaceByID(workspaceData["id"].toInt(-1));
|
||||||
|
auto existed = workspace != nullptr;
|
||||||
|
|
||||||
|
if (!existed) {
|
||||||
|
workspace = new I3Workspace(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (workspaceData.isObject()) {
|
||||||
|
workspace->updateFromObject(workspaceData.toObject().toVariantMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!existed) {
|
||||||
|
this->mWorkspaces.insertObjectSorted(workspace, &I3IpcController::compareWorkspaces);
|
||||||
|
qCInfo(logI3Ipc) << "Added workspace" << workspace->bindableName().value() << "to list";
|
||||||
|
}
|
||||||
|
} else if (change == "focus") {
|
||||||
|
auto oldData = event->mData["old"];
|
||||||
|
auto newData = event->mData["current"];
|
||||||
|
auto oldName = oldData["name"].toString();
|
||||||
|
auto newName = newData["name"].toString();
|
||||||
|
|
||||||
|
qCInfo(logI3IpcEvents) << "Focus changed: " << oldName << "->" << newName;
|
||||||
|
|
||||||
|
if (auto* oldWorkspace = this->findWorkspaceByName(oldName)) {
|
||||||
|
oldWorkspace->updateFromObject(oldData.toObject().toVariantMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* newWorkspace = this->findWorkspaceByName(newName);
|
||||||
|
|
||||||
|
if (newWorkspace == nullptr) {
|
||||||
|
newWorkspace = new I3Workspace(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
newWorkspace->updateFromObject(newData.toObject().toVariantMap());
|
||||||
|
|
||||||
|
if (newWorkspace->bindableMonitor().value()) {
|
||||||
|
auto* monitor = newWorkspace->bindableMonitor().value();
|
||||||
|
monitor->setFocusedWorkspace(newWorkspace);
|
||||||
|
this->bFocusedMonitor = monitor;
|
||||||
|
}
|
||||||
|
} else if (change == "empty") {
|
||||||
|
auto name = event->mData["current"]["name"].toString();
|
||||||
|
|
||||||
|
auto* oldWorkspace = this->findWorkspaceByName(name);
|
||||||
|
|
||||||
|
if (oldWorkspace != nullptr) {
|
||||||
|
qCInfo(logI3Ipc) << "Deleting" << oldWorkspace->bindableId().value() << name;
|
||||||
|
|
||||||
|
if (this->bFocusedWorkspace == oldWorkspace) {
|
||||||
|
this->bFocusedMonitor->setFocusedWorkspace(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->workspaces()->removeObject(oldWorkspace);
|
||||||
|
|
||||||
|
delete oldWorkspace;
|
||||||
|
} else {
|
||||||
|
qCInfo(logI3Ipc) << "Workspace" << name << "has already been deleted";
|
||||||
|
}
|
||||||
|
} else if (change == "move" || change == "rename" || change == "urgent") {
|
||||||
|
auto name = event->mData["current"]["name"].toString();
|
||||||
|
|
||||||
|
auto* workspace = this->findWorkspaceByName(name);
|
||||||
|
|
||||||
|
if (workspace != nullptr) {
|
||||||
|
auto data = event->mData["current"].toObject().toVariantMap();
|
||||||
|
|
||||||
|
workspace->updateFromObject(data);
|
||||||
|
} else {
|
||||||
|
qCWarning(logI3Ipc) << "Workspace" << name << "doesn't exist";
|
||||||
|
}
|
||||||
|
} else if (change == "reload") {
|
||||||
|
qCInfo(logI3Ipc) << "Refreshing Workspaces...";
|
||||||
|
this->refreshWorkspaces();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
I3Monitor* I3IpcController::monitorFor(QuickshellScreenInfo* screen) {
|
||||||
|
if (screen == nullptr) return nullptr;
|
||||||
|
|
||||||
|
return this->findMonitorByName(screen->name());
|
||||||
|
}
|
||||||
|
|
||||||
|
I3Workspace* I3IpcController::findWorkspaceByID(qint32 id) {
|
||||||
|
auto list = this->mWorkspaces.valueList();
|
||||||
|
auto workspaceIter =
|
||||||
|
std::ranges::find_if(list, [id](I3Workspace* m) { return m->bindableId().value() == id; });
|
||||||
|
|
||||||
|
return workspaceIter == list.end() ? nullptr : *workspaceIter;
|
||||||
|
}
|
||||||
|
|
||||||
|
I3Workspace* I3IpcController::findWorkspaceByName(const QString& name) {
|
||||||
|
auto list = this->mWorkspaces.valueList();
|
||||||
|
auto workspaceIter = std::ranges::find_if(list, [name](I3Workspace* m) {
|
||||||
|
return m->bindableName().value() == name;
|
||||||
|
});
|
||||||
|
|
||||||
|
return workspaceIter == list.end() ? nullptr : *workspaceIter;
|
||||||
|
}
|
||||||
|
|
||||||
|
I3Monitor* I3IpcController::findMonitorByName(const QString& name, bool createIfMissing) {
|
||||||
|
auto list = this->mMonitors.valueList();
|
||||||
|
auto monitorIter = std::ranges::find_if(list, [name](I3Monitor* m) {
|
||||||
|
return m->bindableName().value() == name;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (monitorIter != list.end()) {
|
||||||
|
return *monitorIter;
|
||||||
|
} else if (createIfMissing) {
|
||||||
|
qCDebug(logI3Ipc) << "Monitor" << name << "requested before creation, performing early init";
|
||||||
|
auto* monitor = new I3Monitor(this);
|
||||||
|
monitor->updateInitial(name);
|
||||||
|
this->mMonitors.insertObject(monitor);
|
||||||
|
return monitor;
|
||||||
|
} else {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectModel<I3Monitor>* I3IpcController::monitors() { return &this->mMonitors; }
|
||||||
|
ObjectModel<I3Workspace>* I3IpcController::workspaces() { return &this->mWorkspaces; }
|
||||||
|
|
||||||
|
bool I3IpcController::compareWorkspaces(I3Workspace* a, I3Workspace* b) {
|
||||||
|
return a->bindableNumber().value() > b->bindableNumber().value();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace qs::i3::ipc
|
||||||
94
src/x11/i3/ipc/controller.hpp
Normal file
94
src/x11/i3/ipc/controller.hpp
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <qbytearrayview.h>
|
||||||
|
#include <qjsondocument.h>
|
||||||
|
#include <qjsonobject.h>
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qproperty.h>
|
||||||
|
#include <qqml.h>
|
||||||
|
#include <qqmlintegration.h>
|
||||||
|
#include <qtmetamacros.h>
|
||||||
|
#include <qtypes.h>
|
||||||
|
|
||||||
|
#include "../../../core/model.hpp"
|
||||||
|
#include "../../../core/qmlscreen.hpp"
|
||||||
|
#include "connection.hpp"
|
||||||
|
|
||||||
|
namespace qs::i3::ipc {
|
||||||
|
|
||||||
|
class I3Workspace;
|
||||||
|
class I3Monitor;
|
||||||
|
} // namespace qs::i3::ipc
|
||||||
|
|
||||||
|
Q_DECLARE_OPAQUE_POINTER(qs::i3::ipc::I3Workspace*);
|
||||||
|
Q_DECLARE_OPAQUE_POINTER(qs::i3::ipc::I3Monitor*);
|
||||||
|
|
||||||
|
namespace qs::i3::ipc {
|
||||||
|
|
||||||
|
/// I3/Sway IPC controller that manages workspaces and monitors
|
||||||
|
class I3IpcController: public I3Ipc {
|
||||||
|
Q_OBJECT;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static I3IpcController* instance();
|
||||||
|
|
||||||
|
I3Workspace* findWorkspaceByName(const QString& name);
|
||||||
|
I3Monitor* findMonitorByName(const QString& name, bool createIfMissing = false);
|
||||||
|
I3Workspace* findWorkspaceByID(qint32 id);
|
||||||
|
|
||||||
|
void setFocusedMonitor(I3Monitor* monitor);
|
||||||
|
|
||||||
|
void refreshWorkspaces();
|
||||||
|
void refreshMonitors();
|
||||||
|
|
||||||
|
I3Monitor* monitorFor(QuickshellScreenInfo* screen);
|
||||||
|
|
||||||
|
[[nodiscard]] QBindable<I3Monitor*> bindableFocusedMonitor() const {
|
||||||
|
return &this->bFocusedMonitor;
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] QBindable<I3Workspace*> bindableFocusedWorkspace() const {
|
||||||
|
return &this->bFocusedWorkspace;
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] ObjectModel<I3Monitor>* monitors();
|
||||||
|
[[nodiscard]] ObjectModel<I3Workspace>* workspaces();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void focusedWorkspaceChanged();
|
||||||
|
void focusedMonitorChanged();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onFocusedMonitorDestroyed();
|
||||||
|
|
||||||
|
void onEvent(I3IpcEvent* event);
|
||||||
|
void onConnected();
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit I3IpcController();
|
||||||
|
|
||||||
|
void handleWorkspaceEvent(I3IpcEvent* event);
|
||||||
|
void handleGetWorkspacesEvent(I3IpcEvent* event);
|
||||||
|
void handleGetOutputsEvent(I3IpcEvent* event);
|
||||||
|
static void handleRunCommand(I3IpcEvent* event);
|
||||||
|
static bool compareWorkspaces(I3Workspace* a, I3Workspace* b);
|
||||||
|
|
||||||
|
ObjectModel<I3Monitor> mMonitors {this};
|
||||||
|
ObjectModel<I3Workspace> mWorkspaces {this};
|
||||||
|
|
||||||
|
Q_OBJECT_BINDABLE_PROPERTY(
|
||||||
|
I3IpcController,
|
||||||
|
I3Monitor*,
|
||||||
|
bFocusedMonitor,
|
||||||
|
&I3IpcController::focusedMonitorChanged
|
||||||
|
);
|
||||||
|
|
||||||
|
Q_OBJECT_BINDABLE_PROPERTY(
|
||||||
|
I3IpcController,
|
||||||
|
I3Workspace*,
|
||||||
|
bFocusedWorkspace,
|
||||||
|
&I3IpcController::focusedWorkspaceChanged
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace qs::i3::ipc
|
||||||
49
src/x11/i3/ipc/listener.cpp
Normal file
49
src/x11/i3/ipc/listener.cpp
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
#include "listener.hpp"
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include <qbytearrayview.h>
|
||||||
|
#include <qcontainerfwd.h>
|
||||||
|
#include <qdatastream.h>
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qsysinfo.h>
|
||||||
|
#include <qtenvironmentvariables.h>
|
||||||
|
#include <qtmetamacros.h>
|
||||||
|
|
||||||
|
#include "connection.hpp"
|
||||||
|
|
||||||
|
namespace qs::i3::ipc {
|
||||||
|
|
||||||
|
I3IpcListener::~I3IpcListener() { this->freeI3Ipc(); }
|
||||||
|
|
||||||
|
void I3IpcListener::onPostReload() { this->startListening(); }
|
||||||
|
|
||||||
|
QList<QString> I3IpcListener::subscriptions() const { return this->mSubscriptions; }
|
||||||
|
void I3IpcListener::setSubscriptions(QList<QString> subscriptions) {
|
||||||
|
if (this->mSubscriptions == subscriptions) return;
|
||||||
|
this->mSubscriptions = std::move(subscriptions);
|
||||||
|
|
||||||
|
emit this->subscriptionsChanged();
|
||||||
|
this->startListening();
|
||||||
|
}
|
||||||
|
|
||||||
|
void I3IpcListener::startListening() {
|
||||||
|
this->freeI3Ipc();
|
||||||
|
if (this->mSubscriptions.isEmpty()) return;
|
||||||
|
|
||||||
|
this->i3Ipc = new I3Ipc(this->mSubscriptions);
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
QObject::connect(this->i3Ipc, &I3Ipc::rawEvent, this, &I3IpcListener::receiveEvent);
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
this->i3Ipc->connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void I3IpcListener::receiveEvent(I3IpcEvent* event) { emit this->ipcEvent(event); }
|
||||||
|
|
||||||
|
void I3IpcListener::freeI3Ipc() {
|
||||||
|
delete this->i3Ipc;
|
||||||
|
this->i3Ipc = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace qs::i3::ipc
|
||||||
68
src/x11/i3/ipc/listener.hpp
Normal file
68
src/x11/i3/ipc/listener.hpp
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <csignal> // NOLINT
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include <qdir.h>
|
||||||
|
#include <qhash.h>
|
||||||
|
#include <qlist.h>
|
||||||
|
#include <qlogging.h>
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qprocess.h>
|
||||||
|
#include <qproperty.h>
|
||||||
|
#include <qqmlinfo.h>
|
||||||
|
#include <qtmetamacros.h>
|
||||||
|
#include <qtypes.h>
|
||||||
|
#include <qvariant.h>
|
||||||
|
|
||||||
|
#include "../../../core/doc.hpp"
|
||||||
|
#include "../../../core/generation.hpp"
|
||||||
|
#include "../../../core/qmlglobal.hpp"
|
||||||
|
#include "../../../core/reload.hpp"
|
||||||
|
#include "connection.hpp"
|
||||||
|
|
||||||
|
namespace qs::i3::ipc {
|
||||||
|
|
||||||
|
///! I3/Sway IPC event listener
|
||||||
|
/// #### Example
|
||||||
|
/// ```qml
|
||||||
|
/// I3IpcListener {
|
||||||
|
/// subscriptions: ["input"]
|
||||||
|
/// onIpcEvent: function (event) {
|
||||||
|
/// handleInputEvent(event.data)
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
class I3IpcListener: public PostReloadHook {
|
||||||
|
Q_OBJECT;
|
||||||
|
// clang-format off
|
||||||
|
/// List of [I3/Sway events](https://man.archlinux.org/man/sway-ipc.7.en#EVENTS) to subscribe to.
|
||||||
|
Q_PROPERTY(QList<QString> subscriptions READ subscriptions WRITE setSubscriptions NOTIFY subscriptionsChanged);
|
||||||
|
// clang-format on
|
||||||
|
QML_ELEMENT;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit I3IpcListener(QObject* parent = nullptr): PostReloadHook(parent) {}
|
||||||
|
~I3IpcListener() override;
|
||||||
|
Q_DISABLE_COPY_MOVE(I3IpcListener);
|
||||||
|
|
||||||
|
void onPostReload() override;
|
||||||
|
|
||||||
|
[[nodiscard]] QList<QString> subscriptions() const;
|
||||||
|
void setSubscriptions(QList<QString> subscriptions);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void ipcEvent(I3IpcEvent* event);
|
||||||
|
void subscriptionsChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void startListening();
|
||||||
|
void receiveEvent(I3IpcEvent* event);
|
||||||
|
|
||||||
|
void freeI3Ipc();
|
||||||
|
|
||||||
|
QList<QString> mSubscriptions;
|
||||||
|
I3Ipc* i3Ipc = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace qs::i3::ipc
|
||||||
|
|
@ -7,12 +7,12 @@
|
||||||
#include <qtmetamacros.h>
|
#include <qtmetamacros.h>
|
||||||
#include <qtypes.h>
|
#include <qtypes.h>
|
||||||
|
|
||||||
#include "connection.hpp"
|
#include "controller.hpp"
|
||||||
#include "workspace.hpp"
|
#include "workspace.hpp"
|
||||||
|
|
||||||
namespace qs::i3::ipc {
|
namespace qs::i3::ipc {
|
||||||
|
|
||||||
I3Monitor::I3Monitor(I3Ipc* ipc): QObject(ipc), ipc(ipc) {
|
I3Monitor::I3Monitor(I3IpcController* ipc): QObject(ipc), ipc(ipc) {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
this->bFocused.setBinding([this]() { return this->ipc->bindableFocusedMonitor().value() == this; });
|
this->bFocused.setBinding([this]() { return this->ipc->bindableFocusedMonitor().value() == this; });
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
#include <qproperty.h>
|
#include <qproperty.h>
|
||||||
|
|
||||||
#include "connection.hpp"
|
#include "connection.hpp"
|
||||||
|
#include "controller.hpp"
|
||||||
|
|
||||||
namespace qs::i3::ipc {
|
namespace qs::i3::ipc {
|
||||||
|
|
||||||
|
|
@ -39,10 +40,10 @@ class I3Monitor: public QObject {
|
||||||
Q_PROPERTY(QVariantMap lastIpcObject READ lastIpcObject NOTIFY lastIpcObjectChanged);
|
Q_PROPERTY(QVariantMap lastIpcObject READ lastIpcObject NOTIFY lastIpcObjectChanged);
|
||||||
// clang-format on
|
// clang-format on
|
||||||
QML_ELEMENT;
|
QML_ELEMENT;
|
||||||
QML_UNCREATABLE("I3Monitors must be retrieved from the I3Ipc object.");
|
QML_UNCREATABLE("I3Monitors must be retrieved from the I3IpcController object.");
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit I3Monitor(I3Ipc* ipc);
|
explicit I3Monitor(I3IpcController* ipc);
|
||||||
|
|
||||||
[[nodiscard]] QBindable<qint32> bindableId() { return &this->bId; }
|
[[nodiscard]] QBindable<qint32> bindableId() { return &this->bId; }
|
||||||
[[nodiscard]] QBindable<QString> bindableName() { return &this->bName; }
|
[[nodiscard]] QBindable<QString> bindableName() { return &this->bName; }
|
||||||
|
|
@ -79,7 +80,7 @@ signals:
|
||||||
void focusedChanged();
|
void focusedChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
I3Ipc* ipc;
|
I3IpcController* ipc;
|
||||||
|
|
||||||
QVariantMap mLastIpcObject;
|
QVariantMap mLastIpcObject;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,46 +7,49 @@
|
||||||
#include "../../../core/model.hpp"
|
#include "../../../core/model.hpp"
|
||||||
#include "../../../core/qmlscreen.hpp"
|
#include "../../../core/qmlscreen.hpp"
|
||||||
#include "connection.hpp"
|
#include "connection.hpp"
|
||||||
|
#include "controller.hpp"
|
||||||
#include "workspace.hpp"
|
#include "workspace.hpp"
|
||||||
|
|
||||||
namespace qs::i3::ipc {
|
namespace qs::i3::ipc {
|
||||||
|
|
||||||
I3IpcQml::I3IpcQml() {
|
I3IpcQml::I3IpcQml() {
|
||||||
auto* instance = I3Ipc::instance();
|
auto* instance = I3IpcController::instance();
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
QObject::connect(instance, &I3Ipc::rawEvent, this, &I3IpcQml::rawEvent);
|
QObject::connect(instance, &I3Ipc::rawEvent, this, &I3IpcQml::rawEvent);
|
||||||
QObject::connect(instance, &I3Ipc::connected, this, &I3IpcQml::connected);
|
QObject::connect(instance, &I3Ipc::connected, this, &I3IpcQml::connected);
|
||||||
QObject::connect(instance, &I3Ipc::focusedWorkspaceChanged, this, &I3IpcQml::focusedWorkspaceChanged);
|
QObject::connect(instance, &I3IpcController::focusedWorkspaceChanged, this, &I3IpcQml::focusedWorkspaceChanged);
|
||||||
QObject::connect(instance, &I3Ipc::focusedMonitorChanged, this, &I3IpcQml::focusedMonitorChanged);
|
QObject::connect(instance, &I3IpcController::focusedMonitorChanged, this, &I3IpcQml::focusedMonitorChanged);
|
||||||
// clang-format on
|
// clang-format on
|
||||||
}
|
}
|
||||||
|
|
||||||
void I3IpcQml::dispatch(const QString& request) { I3Ipc::instance()->dispatch(request); }
|
void I3IpcQml::dispatch(const QString& request) { I3IpcController::instance()->dispatch(request); }
|
||||||
void I3IpcQml::refreshMonitors() { I3Ipc::instance()->refreshMonitors(); }
|
void I3IpcQml::refreshMonitors() { I3IpcController::instance()->refreshMonitors(); }
|
||||||
void I3IpcQml::refreshWorkspaces() { I3Ipc::instance()->refreshWorkspaces(); }
|
void I3IpcQml::refreshWorkspaces() { I3IpcController::instance()->refreshWorkspaces(); }
|
||||||
QString I3IpcQml::socketPath() { return I3Ipc::instance()->socketPath(); }
|
QString I3IpcQml::socketPath() { return I3IpcController::instance()->socketPath(); }
|
||||||
ObjectModel<I3Monitor>* I3IpcQml::monitors() { return I3Ipc::instance()->monitors(); }
|
ObjectModel<I3Monitor>* I3IpcQml::monitors() { return I3IpcController::instance()->monitors(); }
|
||||||
ObjectModel<I3Workspace>* I3IpcQml::workspaces() { return I3Ipc::instance()->workspaces(); }
|
ObjectModel<I3Workspace>* I3IpcQml::workspaces() {
|
||||||
|
return I3IpcController::instance()->workspaces();
|
||||||
|
}
|
||||||
|
|
||||||
QBindable<I3Workspace*> I3IpcQml::bindableFocusedWorkspace() {
|
QBindable<I3Workspace*> I3IpcQml::bindableFocusedWorkspace() {
|
||||||
return I3Ipc::instance()->bindableFocusedWorkspace();
|
return I3IpcController::instance()->bindableFocusedWorkspace();
|
||||||
}
|
}
|
||||||
|
|
||||||
QBindable<I3Monitor*> I3IpcQml::bindableFocusedMonitor() {
|
QBindable<I3Monitor*> I3IpcQml::bindableFocusedMonitor() {
|
||||||
return I3Ipc::instance()->bindableFocusedMonitor();
|
return I3IpcController::instance()->bindableFocusedMonitor();
|
||||||
}
|
}
|
||||||
|
|
||||||
I3Workspace* I3IpcQml::findWorkspaceByName(const QString& name) {
|
I3Workspace* I3IpcQml::findWorkspaceByName(const QString& name) {
|
||||||
return I3Ipc::instance()->findWorkspaceByName(name);
|
return I3IpcController::instance()->findWorkspaceByName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
I3Monitor* I3IpcQml::findMonitorByName(const QString& name) {
|
I3Monitor* I3IpcQml::findMonitorByName(const QString& name) {
|
||||||
return I3Ipc::instance()->findMonitorByName(name);
|
return I3IpcController::instance()->findMonitorByName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
I3Monitor* I3IpcQml::monitorFor(QuickshellScreenInfo* screen) {
|
I3Monitor* I3IpcQml::monitorFor(QuickshellScreenInfo* screen) {
|
||||||
return I3Ipc::instance()->monitorFor(screen);
|
return I3IpcController::instance()->monitorFor(screen);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace qs::i3::ipc
|
} // namespace qs::i3::ipc
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
#include "../../../core/doc.hpp"
|
#include "../../../core/doc.hpp"
|
||||||
#include "../../../core/qmlscreen.hpp"
|
#include "../../../core/qmlscreen.hpp"
|
||||||
#include "connection.hpp"
|
#include "connection.hpp"
|
||||||
|
#include "controller.hpp"
|
||||||
|
|
||||||
namespace qs::i3::ipc {
|
namespace qs::i3::ipc {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,12 @@
|
||||||
#include <qtmetamacros.h>
|
#include <qtmetamacros.h>
|
||||||
#include <qtypes.h>
|
#include <qtypes.h>
|
||||||
|
|
||||||
#include "connection.hpp"
|
#include "controller.hpp"
|
||||||
#include "monitor.hpp"
|
#include "monitor.hpp"
|
||||||
|
|
||||||
namespace qs::i3::ipc {
|
namespace qs::i3::ipc {
|
||||||
|
|
||||||
I3Workspace::I3Workspace(I3Ipc* ipc): QObject(ipc), ipc(ipc) {
|
I3Workspace::I3Workspace(I3IpcController* ipc): QObject(ipc), ipc(ipc) {
|
||||||
Qt::beginPropertyUpdateGroup();
|
Qt::beginPropertyUpdateGroup();
|
||||||
|
|
||||||
this->bActive.setBinding([this]() {
|
this->bActive.setBinding([this]() {
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
#include <qtypes.h>
|
#include <qtypes.h>
|
||||||
|
|
||||||
#include "connection.hpp"
|
#include "connection.hpp"
|
||||||
|
#include "controller.hpp"
|
||||||
|
|
||||||
namespace qs::i3::ipc {
|
namespace qs::i3::ipc {
|
||||||
|
|
||||||
|
|
@ -40,7 +41,7 @@ class I3Workspace: public QObject {
|
||||||
QML_UNCREATABLE("I3Workspaces must be retrieved from the I3 object.");
|
QML_UNCREATABLE("I3Workspaces must be retrieved from the I3 object.");
|
||||||
|
|
||||||
public:
|
public:
|
||||||
I3Workspace(I3Ipc* ipc);
|
I3Workspace(I3IpcController* ipc);
|
||||||
|
|
||||||
/// Activate the workspace.
|
/// Activate the workspace.
|
||||||
///
|
///
|
||||||
|
|
@ -72,7 +73,7 @@ signals:
|
||||||
void lastIpcObjectChanged();
|
void lastIpcObjectChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
I3Ipc* ipc;
|
I3IpcController* ipc;
|
||||||
|
|
||||||
QVariantMap mLastIpcObject;
|
QVariantMap mLastIpcObject;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,10 @@ name = "Quickshell.I3"
|
||||||
description = "I3 specific Quickshell types"
|
description = "I3 specific Quickshell types"
|
||||||
headers = [
|
headers = [
|
||||||
"ipc/connection.hpp",
|
"ipc/connection.hpp",
|
||||||
|
"ipc/controller.hpp",
|
||||||
"ipc/qml.hpp",
|
"ipc/qml.hpp",
|
||||||
"ipc/workspace.hpp",
|
"ipc/workspace.hpp",
|
||||||
"ipc/monitor.hpp",
|
"ipc/monitor.hpp",
|
||||||
|
"ipc/listener.hpp",
|
||||||
]
|
]
|
||||||
-----
|
-----
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue