forked from quickshell/quickshell
		
	i3/sway: add support for the I3 and Sway IPC
sway: add urgent and focused dispatchers to workspaces flake: add sway toggle WIP sway: add monitor status sway: handle multiple ipc events in one line sway: reuse socket connection for dispatches & better command type handling WIP sway: add associated monitor to a workspace i3/sway: update to allow for i3 compatibility i3/sway: manage setting the focused monitors i3/sway: fix multi monitor crash i3/sway: fix linting errors i3/sway: update nix package flag naming to i3 i3/sway: add documentation, fix module.md and impl monitorFor i3/sway: handle more workspace ipc events i3/sway: fix review i3/sway: fix crash due to newline breaking up an IPC message i3/sway: handle broken messages by forwarding to the next magic sequence i3/sway: break loop when buffer is empty i3/sway: fix monitor focus & focused monitor signal not being emitted i3/sway: use datastreams instead of qbytearrays for socket reading i3/sway: fix lint issues i3/sway: drop second socket connection, remove dispatch return value, recreate IPC connection on fatal error i3/sway: handle run_command responses i3/sway: remove reconnection on unknown event i3/sway: fix formatting, lint & avoid writing to socket if connection is not open
This commit is contained in:
		
							parent
							
								
									84ce47b6d3
								
							
						
					
					
						commit
						31adcaac76
					
				
					 15 changed files with 1252 additions and 1 deletions
				
			
		| 
						 | 
				
			
			@ -11,6 +11,10 @@ qt_add_qml_module(quickshell-x11
 | 
			
		|||
	DEPENDENCIES QtQuick
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
if(I3)
 | 
			
		||||
	add_subdirectory(i3)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
install_qml_module(quickshell-x11)
 | 
			
		||||
 | 
			
		||||
add_library(quickshell-x11-init OBJECT init.cpp)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										23
									
								
								src/x11/i3/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/x11/i3/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,23 @@
 | 
			
		|||
qt_add_library(quickshell-i3 STATIC)
 | 
			
		||||
 | 
			
		||||
target_link_libraries(quickshell-i3 PRIVATE ${QT_DEPS})
 | 
			
		||||
 | 
			
		||||
set(I3_MODULES)
 | 
			
		||||
 | 
			
		||||
if (I3_IPC)
 | 
			
		||||
	add_subdirectory(ipc)
 | 
			
		||||
	list(APPEND I3_MODULES Quickshell.I3._Ipc)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
qt_add_qml_module(quickshell-i3
 | 
			
		||||
	URI Quickshell.I3
 | 
			
		||||
	VERSION 0.1
 | 
			
		||||
	IMPORTS ${I3_MODULES}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
install_qml_module(quickshell-i3)
 | 
			
		||||
 | 
			
		||||
qs_pch(quickshell-i3)
 | 
			
		||||
qs_pch(quickshell-i3plugin)
 | 
			
		||||
 | 
			
		||||
target_link_libraries(quickshell PRIVATE quickshell-i3plugin)
 | 
			
		||||
							
								
								
									
										22
									
								
								src/x11/i3/ipc/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/x11/i3/ipc/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,22 @@
 | 
			
		|||
qt_add_library(quickshell-i3-ipc STATIC
 | 
			
		||||
	connection.cpp
 | 
			
		||||
	qml.cpp
 | 
			
		||||
	workspace.cpp
 | 
			
		||||
	monitor.cpp
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
qt_add_qml_module(quickshell-i3-ipc
 | 
			
		||||
	URI Quickshell.I3._Ipc
 | 
			
		||||
	VERSION 0.1
 | 
			
		||||
	DEPENDENCIES QtQml
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
qs_add_module_deps_light(quickshell-i3-ipc Quickshell)
 | 
			
		||||
 | 
			
		||||
install_qml_module(quickshell-i3-ipc)
 | 
			
		||||
 | 
			
		||||
target_link_libraries(quickshell-i3-ipc PRIVATE Qt::Quick)
 | 
			
		||||
 | 
			
		||||
qs_module_pch(quickshell-i3-ipc SET large)
 | 
			
		||||
 | 
			
		||||
target_link_libraries(quickshell PRIVATE quickshell-i3-ipcplugin)
 | 
			
		||||
							
								
								
									
										542
									
								
								src/x11/i3/ipc/connection.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										542
									
								
								src/x11/i3/ipc/connection.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,542 @@
 | 
			
		|||
#include <algorithm>
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <tuple>
 | 
			
		||||
 | 
			
		||||
#include <bit>
 | 
			
		||||
#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 <qnamespace.h>
 | 
			
		||||
#include <qobject.h>
 | 
			
		||||
#include <qsysinfo.h>
 | 
			
		||||
#include <qtenvironmentvariables.h>
 | 
			
		||||
#include <qtmetamacros.h>
 | 
			
		||||
#include <qtypes.h>
 | 
			
		||||
 | 
			
		||||
#include "../../../core/model.hpp"
 | 
			
		||||
#include "../../../core/qmlscreen.hpp"
 | 
			
		||||
#include "connection.hpp"
 | 
			
		||||
#include "monitor.hpp"
 | 
			
		||||
#include "workspace.hpp"
 | 
			
		||||
 | 
			
		||||
Q_LOGGING_CATEGORY(logI3Ipc, "quickshell.I3.ipc", QtWarningMsg);
 | 
			
		||||
Q_LOGGING_CATEGORY(logI3IpcEvents, "quickshell.I3.ipc.events", QtWarningMsg);
 | 
			
		||||
 | 
			
		||||
namespace qs::i3::ipc {
 | 
			
		||||
 | 
			
		||||
void I3Ipc::makeRequest(const QByteArray& request) {
 | 
			
		||||
	if (!this->valid) {
 | 
			
		||||
		qCWarning(logI3IpcEvents) << "IPC connection is not open, ignoring request.";
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	this->liveEventSocket.write(request);
 | 
			
		||||
	this->liveEventSocket.flush();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void I3Ipc::dispatch(const QString& payload) {
 | 
			
		||||
	auto message = I3Ipc::buildRequestMessage(EventCode::RunCommand, payload.toLocal8Bit());
 | 
			
		||||
 | 
			
		||||
	this->makeRequest(message);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QByteArray I3Ipc::buildRequestMessage(EventCode cmd, const QByteArray& payload) {
 | 
			
		||||
	auto payloadLength = static_cast<quint32>(payload.length());
 | 
			
		||||
 | 
			
		||||
	auto type = QByteArray(std::bit_cast<std::array<char, 4>>(cmd).data(), 4);
 | 
			
		||||
	auto len = QByteArray(std::bit_cast<std::array<char, 4>>(payloadLength).data(), 4);
 | 
			
		||||
 | 
			
		||||
	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->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() {
 | 
			
		||||
	auto payload = QByteArray(R"(["workspace","output"])");
 | 
			
		||||
	auto message = I3Ipc::buildRequestMessage(EventCode::Subscribe, payload);
 | 
			
		||||
 | 
			
		||||
	this->makeRequest(message);
 | 
			
		||||
 | 
			
		||||
	this->refreshWorkspaces();
 | 
			
		||||
	this->refreshMonitors();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void I3Ipc::eventSocketReady() {
 | 
			
		||||
	for (auto& [type, data]: this->parseResponse()) {
 | 
			
		||||
		this->event.mCode = type;
 | 
			
		||||
		this->event.mData = data;
 | 
			
		||||
 | 
			
		||||
		this->onEvent(&this->event);
 | 
			
		||||
		emit this->rawEvent(&this->event);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void I3Ipc::reconnectIPC() {
 | 
			
		||||
	qCWarning(logI3Ipc) << "Fatal IPC error occured, recreating connection";
 | 
			
		||||
	this->liveEventSocket.disconnectFromServer();
 | 
			
		||||
	this->liveEventSocket.connectToServer(this->mSocketPath);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QVector<Event> I3Ipc::parseResponse() {
 | 
			
		||||
	QVector<std::tuple<EventCode, QJsonDocument>> events;
 | 
			
		||||
	const int magicLen = 6;
 | 
			
		||||
 | 
			
		||||
	while (!this->liveEventSocketDs.atEnd()) {
 | 
			
		||||
		this->liveEventSocketDs.startTransaction();
 | 
			
		||||
		this->liveEventSocketDs.startTransaction();
 | 
			
		||||
 | 
			
		||||
		std::array<char, 6> buffer = {};
 | 
			
		||||
		qint32 size = 0;
 | 
			
		||||
		qint32 type = EventCode::Unknown;
 | 
			
		||||
 | 
			
		||||
		this->liveEventSocketDs.readRawData(buffer.data(), magicLen);
 | 
			
		||||
		this->liveEventSocketDs >> size;
 | 
			
		||||
		this->liveEventSocketDs >> type;
 | 
			
		||||
 | 
			
		||||
		if (!this->liveEventSocketDs.commitTransaction()) break;
 | 
			
		||||
 | 
			
		||||
		QByteArray payload(size, Qt::Uninitialized);
 | 
			
		||||
 | 
			
		||||
		this->liveEventSocketDs.readRawData(payload.data(), size);
 | 
			
		||||
 | 
			
		||||
		if (!this->liveEventSocketDs.commitTransaction()) break;
 | 
			
		||||
 | 
			
		||||
		if (strncmp(buffer.data(), MAGIC.data(), 6) != 0) {
 | 
			
		||||
			qCWarning(logI3Ipc) << "No magic sequence found in string.";
 | 
			
		||||
			this->reconnectIPC();
 | 
			
		||||
			break;
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		if (I3IpcEvent::intToEvent(type) == EventCode::Unknown) {
 | 
			
		||||
			qCWarning(logI3Ipc) << "Received unknown event";
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		QJsonParseError e;
 | 
			
		||||
 | 
			
		||||
		auto data = QJsonDocument::fromJson(payload, &e);
 | 
			
		||||
		if (e.error != QJsonParseError::NoError) {
 | 
			
		||||
			qCWarning(logI3Ipc) << "Invalid JSON value:" << e.errorString();
 | 
			
		||||
			break;
 | 
			
		||||
		} else {
 | 
			
		||||
			events.push_back(std::tuple(I3IpcEvent::intToEvent(type), data));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return events;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void I3Ipc::eventSocketError(QLocalSocket::LocalSocketError error) const {
 | 
			
		||||
	if (!this->valid) {
 | 
			
		||||
		qCWarning(logI3Ipc) << "Unable to connect to I3 socket:" << error;
 | 
			
		||||
	} else {
 | 
			
		||||
		qCWarning(logI3Ipc) << "I3 socket error:" << error;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void I3Ipc::eventSocketStateChanged(QLocalSocket::LocalSocketState state) {
 | 
			
		||||
	if (state == QLocalSocket::ConnectedState) {
 | 
			
		||||
		qCInfo(logI3Ipc) << "I3 event socket connected.";
 | 
			
		||||
		emit this->connected();
 | 
			
		||||
	} else if (state == QLocalSocket::UnconnectedState && this->valid) {
 | 
			
		||||
		qCWarning(logI3Ipc) << "I3 event socket disconnected.";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this->valid = state == QLocalSocket::ConnectedState;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QString I3Ipc::socketPath() const { return this->mSocketPath; }
 | 
			
		||||
I3Workspace* I3Ipc::focusedWorkspace() const { return this->mFocusedWorkspace; }
 | 
			
		||||
I3Monitor* I3Ipc::focusedMonitor() const { return this->mFocusedMonitor; }
 | 
			
		||||
 | 
			
		||||
void I3Ipc::setFocusedWorkspace(I3Workspace* workspace) {
 | 
			
		||||
	if (workspace == this->mFocusedWorkspace) return;
 | 
			
		||||
 | 
			
		||||
	if (this->mFocusedWorkspace != nullptr) {
 | 
			
		||||
		this->mFocusedWorkspace->setFocus(false);
 | 
			
		||||
		QObject::disconnect(this->mFocusedWorkspace, nullptr, this, nullptr);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this->mFocusedWorkspace = workspace;
 | 
			
		||||
 | 
			
		||||
	if (workspace != nullptr) {
 | 
			
		||||
		if (auto* monitor = this->mFocusedWorkspace->monitor()) {
 | 
			
		||||
			monitor->setFocusedWorkspace(this->mFocusedWorkspace);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		QObject::connect(workspace, &QObject::destroyed, this, &I3Ipc::onFocusedWorkspaceDestroyed);
 | 
			
		||||
		workspace->setFocus(true);
 | 
			
		||||
		this->setFocusedMonitor(workspace->monitor());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	emit this->focusedWorkspaceChanged();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void I3Ipc::setFocusedMonitor(I3Monitor* monitor) {
 | 
			
		||||
	if (monitor == this->mFocusedMonitor) return;
 | 
			
		||||
 | 
			
		||||
	if (this->mFocusedMonitor != nullptr) {
 | 
			
		||||
		this->mFocusedMonitor->setFocus(false);
 | 
			
		||||
		QObject::disconnect(this->mFocusedMonitor, nullptr, this, nullptr);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this->mFocusedMonitor = monitor;
 | 
			
		||||
 | 
			
		||||
	if (monitor != nullptr) {
 | 
			
		||||
		monitor->setFocus(true);
 | 
			
		||||
		QObject::connect(monitor, &QObject::destroyed, this, &I3Ipc::onFocusedMonitorDestroyed);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	emit this->focusedMonitorChanged();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void I3Ipc::onFocusedWorkspaceDestroyed() {
 | 
			
		||||
	this->mFocusedWorkspace = nullptr;
 | 
			
		||||
	emit this->focusedWorkspaceChanged();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void I3Ipc::onFocusedMonitorDestroyed() {
 | 
			
		||||
	this->mFocusedMonitor = nullptr;
 | 
			
		||||
	emit this->focusedMonitorChanged();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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::find_if(mList.begin(), mList.end(), [name](const I3Workspace* m) {
 | 
			
		||||
			return m->name() == name;
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		auto* workspace = workspaceIter == mList.end() ? nullptr : *workspaceIter;
 | 
			
		||||
		auto existed = workspace != nullptr;
 | 
			
		||||
 | 
			
		||||
		if (workspace == nullptr) {
 | 
			
		||||
			workspace = new I3Workspace(this);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		workspace->updateFromObject(object);
 | 
			
		||||
 | 
			
		||||
		if (workspace->focused()) {
 | 
			
		||||
			this->setFocusedWorkspace(workspace);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!existed) {
 | 
			
		||||
			this->mWorkspaces.insertObject(workspace);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		names.push_back(name);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	auto removedWorkspaces = QVector<I3Workspace*>();
 | 
			
		||||
 | 
			
		||||
	for (auto* workspace: mList) {
 | 
			
		||||
		if (!names.contains(workspace->name())) {
 | 
			
		||||
			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::find_if(mList.begin(), mList.end(), [name](const I3Monitor* m) {
 | 
			
		||||
			return m->name() == name;
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		auto* monitor = monitorIter == mList.end() ? nullptr : *monitorIter;
 | 
			
		||||
		auto existed = monitor != nullptr;
 | 
			
		||||
 | 
			
		||||
		if (monitor == nullptr) {
 | 
			
		||||
			monitor = new I3Monitor(this);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		monitor->updateFromObject(object);
 | 
			
		||||
 | 
			
		||||
		if (monitor->focused()) {
 | 
			
		||||
			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->name())) {
 | 
			
		||||
			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->handleWorkspaceEvent(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));
 | 
			
		||||
 | 
			
		||||
		if (workspace == nullptr) {
 | 
			
		||||
			workspace = new I3Workspace(this);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (workspaceData.isObject()) {
 | 
			
		||||
			workspace->updateFromObject(workspaceData.toObject().toVariantMap());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		this->mWorkspaces.insertObject(workspace);
 | 
			
		||||
		qCInfo(logI3Ipc) << "Added workspace" << workspace->name() << "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());
 | 
			
		||||
		this->setFocusedWorkspace(newWorkspace);
 | 
			
		||||
	} else if (change == "empty") {
 | 
			
		||||
		auto name = event->mData["current"]["name"].toString();
 | 
			
		||||
 | 
			
		||||
		auto* oldWorkspace = this->findWorkspaceByName(name);
 | 
			
		||||
 | 
			
		||||
		if (oldWorkspace != nullptr) {
 | 
			
		||||
			qCInfo(logI3Ipc) << "Deleting" << oldWorkspace->id() << name;
 | 
			
		||||
 | 
			
		||||
			if (this->mFocusedWorkspace == oldWorkspace) {
 | 
			
		||||
				this->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::find_if(list.begin(), list.end(), [id](const I3Workspace* m) { return m->id() == id; });
 | 
			
		||||
 | 
			
		||||
	return workspaceIter == list.end() ? nullptr : *workspaceIter;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
I3Workspace* I3Ipc::findWorkspaceByName(const QString& name) {
 | 
			
		||||
	auto list = this->mWorkspaces.valueList();
 | 
			
		||||
	auto workspaceIter = std::find_if(list.begin(), list.end(), [name](const I3Workspace* m) {
 | 
			
		||||
		return m->name() == name;
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	return workspaceIter == list.end() ? nullptr : *workspaceIter;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
I3Monitor* I3Ipc::findMonitorByName(const QString& name) {
 | 
			
		||||
	auto list = this->mMonitors.valueList();
 | 
			
		||||
	auto monitorIter = std::find_if(list.begin(), list.end(), [name](const I3Monitor* m) {
 | 
			
		||||
		return m->name() == name;
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	return monitorIter == list.end() ? nullptr : *monitorIter;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ObjectModel<I3Monitor>* I3Ipc::monitors() { return &this->mMonitors; }
 | 
			
		||||
ObjectModel<I3Workspace>* I3Ipc::workspaces() { return &this->mWorkspaces; }
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
	case EventCode::Unknown: return "unknown"; break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace qs::i3::ipc
 | 
			
		||||
							
								
								
									
										151
									
								
								src/x11/i3/ipc/connection.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								src/x11/i3/ipc/connection.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,151 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <qbytearrayview.h>
 | 
			
		||||
#include <qjsondocument.h>
 | 
			
		||||
#include <qjsonobject.h>
 | 
			
		||||
#include <qlocalsocket.h>
 | 
			
		||||
#include <qobject.h>
 | 
			
		||||
#include <qqml.h>
 | 
			
		||||
#include <qqmlintegration.h>
 | 
			
		||||
#include <qtmetamacros.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 {
 | 
			
		||||
 | 
			
		||||
constexpr std::string MAGIC = "i3-ipc";
 | 
			
		||||
 | 
			
		||||
enum EventCode {
 | 
			
		||||
	RunCommand = 0,
 | 
			
		||||
	GetWorkspaces = 1,
 | 
			
		||||
	Subscribe = 2,
 | 
			
		||||
	GetOutputs = 3,
 | 
			
		||||
	GetTree = 4,
 | 
			
		||||
 | 
			
		||||
	Workspace = 0x80000000,
 | 
			
		||||
	Output = 0x80000001,
 | 
			
		||||
	Mode = 0x80000002,
 | 
			
		||||
	Window = 0x80000003,
 | 
			
		||||
	BarconfigUpdate = 0x80000004,
 | 
			
		||||
	Binding = 0x80000005,
 | 
			
		||||
	Shutdown = 0x80000006,
 | 
			
		||||
	Tick = 0x80000007,
 | 
			
		||||
	BarStateUpdate = 0x80000014,
 | 
			
		||||
	Input = 0x80000015,
 | 
			
		||||
	Unknown = 999,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
using Event = std::tuple<EventCode, QJsonDocument>;
 | 
			
		||||
 | 
			
		||||
///! I3/Sway IPC Events
 | 
			
		||||
/// Emitted by @@I3.rawEvent(s)
 | 
			
		||||
class I3IpcEvent: public QObject {
 | 
			
		||||
	Q_OBJECT;
 | 
			
		||||
 | 
			
		||||
	/// The name of the event
 | 
			
		||||
	Q_PROPERTY(QString type READ type CONSTANT);
 | 
			
		||||
	/// The payload of the event in JSON format.
 | 
			
		||||
	Q_PROPERTY(QString data READ data CONSTANT);
 | 
			
		||||
 | 
			
		||||
	QML_NAMED_ELEMENT(I3Event);
 | 
			
		||||
	QML_UNCREATABLE("I3IpcEvents cannot be created.");
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	I3IpcEvent(QObject* parent): QObject(parent) {}
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] QString type() const;
 | 
			
		||||
	[[nodiscard]] QString data() const;
 | 
			
		||||
 | 
			
		||||
	EventCode mCode = EventCode::Unknown;
 | 
			
		||||
	QJsonDocument mData;
 | 
			
		||||
 | 
			
		||||
	static EventCode intToEvent(uint32_t raw);
 | 
			
		||||
	static QString eventToString(EventCode event);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class I3Ipc: public QObject {
 | 
			
		||||
	Q_OBJECT;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	static I3Ipc* instance();
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] QString socketPath() const;
 | 
			
		||||
 | 
			
		||||
	void makeRequest(const QByteArray& request);
 | 
			
		||||
	void dispatch(const QString& payload);
 | 
			
		||||
 | 
			
		||||
	static QByteArray buildRequestMessage(EventCode cmd, const QByteArray& payload = QByteArray());
 | 
			
		||||
 | 
			
		||||
	I3Workspace* findWorkspaceByName(const QString& name);
 | 
			
		||||
	I3Monitor* findMonitorByName(const QString& name);
 | 
			
		||||
	I3Workspace* findWorkspaceByID(qint32 id);
 | 
			
		||||
 | 
			
		||||
	void setFocusedWorkspace(I3Workspace* workspace);
 | 
			
		||||
	void setFocusedMonitor(I3Monitor* monitor);
 | 
			
		||||
 | 
			
		||||
	void refreshWorkspaces();
 | 
			
		||||
	void refreshMonitors();
 | 
			
		||||
 | 
			
		||||
	I3Monitor* monitorFor(QuickshellScreenInfo* screen);
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] I3Monitor* focusedMonitor() const;
 | 
			
		||||
	[[nodiscard]] I3Workspace* focusedWorkspace() const;
 | 
			
		||||
	[[nodiscard]] ObjectModel<I3Monitor>* monitors();
 | 
			
		||||
	[[nodiscard]] ObjectModel<I3Workspace>* workspaces();
 | 
			
		||||
signals:
 | 
			
		||||
	void connected();
 | 
			
		||||
	void rawEvent(I3IpcEvent* event);
 | 
			
		||||
	void focusedWorkspaceChanged();
 | 
			
		||||
	void focusedMonitorChanged();
 | 
			
		||||
 | 
			
		||||
private slots:
 | 
			
		||||
	void eventSocketError(QLocalSocket::LocalSocketError error) const;
 | 
			
		||||
	void eventSocketStateChanged(QLocalSocket::LocalSocketState state);
 | 
			
		||||
	void eventSocketReady();
 | 
			
		||||
	void subscribe();
 | 
			
		||||
 | 
			
		||||
	void onFocusedWorkspaceDestroyed();
 | 
			
		||||
	void onFocusedMonitorDestroyed();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	explicit I3Ipc();
 | 
			
		||||
 | 
			
		||||
	void onEvent(I3IpcEvent* event);
 | 
			
		||||
 | 
			
		||||
	void handleWorkspaceEvent(I3IpcEvent* event);
 | 
			
		||||
	void handleGetWorkspacesEvent(I3IpcEvent* event);
 | 
			
		||||
	void handleGetOutputsEvent(I3IpcEvent* event);
 | 
			
		||||
	static void handleRunCommand(I3IpcEvent* event);
 | 
			
		||||
 | 
			
		||||
	void reconnectIPC();
 | 
			
		||||
 | 
			
		||||
	QVector<std::tuple<EventCode, QJsonDocument>> parseResponse();
 | 
			
		||||
 | 
			
		||||
	QLocalSocket liveEventSocket;
 | 
			
		||||
	QDataStream liveEventSocketDs;
 | 
			
		||||
 | 
			
		||||
	QString mSocketPath;
 | 
			
		||||
 | 
			
		||||
	bool valid = false;
 | 
			
		||||
 | 
			
		||||
	ObjectModel<I3Monitor> mMonitors {this};
 | 
			
		||||
	ObjectModel<I3Workspace> mWorkspaces {this};
 | 
			
		||||
 | 
			
		||||
	I3IpcEvent event {this};
 | 
			
		||||
 | 
			
		||||
	I3Workspace* mFocusedWorkspace = nullptr;
 | 
			
		||||
	I3Monitor* mFocusedMonitor = nullptr;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace qs::i3::ipc
 | 
			
		||||
							
								
								
									
										111
									
								
								src/x11/i3/ipc/monitor.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								src/x11/i3/ipc/monitor.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,111 @@
 | 
			
		|||
#include "monitor.hpp"
 | 
			
		||||
 | 
			
		||||
#include <qcontainerfwd.h>
 | 
			
		||||
#include <qstring.h>
 | 
			
		||||
#include <qtmetamacros.h>
 | 
			
		||||
#include <qtypes.h>
 | 
			
		||||
 | 
			
		||||
#include "workspace.hpp"
 | 
			
		||||
 | 
			
		||||
namespace qs::i3::ipc {
 | 
			
		||||
 | 
			
		||||
qint32 I3Monitor::id() const { return this->mId; };
 | 
			
		||||
QString I3Monitor::name() const { return this->mName; };
 | 
			
		||||
bool I3Monitor::power() const { return this->mPower; };
 | 
			
		||||
I3Workspace* I3Monitor::focusedWorkspace() const { return this->mFocusedWorkspace; };
 | 
			
		||||
qint32 I3Monitor::x() const { return this->mX; };
 | 
			
		||||
qint32 I3Monitor::y() const { return this->mY; };
 | 
			
		||||
qint32 I3Monitor::width() const { return this->mWidth; };
 | 
			
		||||
qint32 I3Monitor::height() const { return this->mHeight; };
 | 
			
		||||
qreal I3Monitor::scale() const { return this->mScale; };
 | 
			
		||||
bool I3Monitor::focused() const { return this->mFocused; };
 | 
			
		||||
QVariantMap I3Monitor::lastIpcObject() const { return this->mLastIpcObject; };
 | 
			
		||||
 | 
			
		||||
void I3Monitor::updateFromObject(const QVariantMap& obj) {
 | 
			
		||||
	auto id = obj.value("id").value<qint32>();
 | 
			
		||||
	auto name = obj.value("name").value<QString>();
 | 
			
		||||
	auto power = obj.value("power").value<bool>();
 | 
			
		||||
	auto activeWorkspaceId = obj.value("current_workspace").value<QString>();
 | 
			
		||||
	auto rect = obj.value("rect").toMap();
 | 
			
		||||
	auto x = rect.value("x").value<qint32>();
 | 
			
		||||
	auto y = rect.value("y").value<qint32>();
 | 
			
		||||
	auto width = rect.value("width").value<qint32>();
 | 
			
		||||
	auto height = rect.value("height").value<qint32>();
 | 
			
		||||
	auto scale = obj.value("scale").value<qreal>();
 | 
			
		||||
	auto focused = obj.value("focused").value<bool>();
 | 
			
		||||
 | 
			
		||||
	if (id != this->mId) {
 | 
			
		||||
		this->mId = id;
 | 
			
		||||
		emit this->idChanged();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (name != this->mName) {
 | 
			
		||||
		this->mName = name;
 | 
			
		||||
		emit this->nameChanged();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (power != this->mPower) {
 | 
			
		||||
		this->mPower = power;
 | 
			
		||||
		this->powerChanged();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (activeWorkspaceId != this->mFocusedWorkspaceName) {
 | 
			
		||||
		auto* workspace = this->ipc->findWorkspaceByName(activeWorkspaceId);
 | 
			
		||||
		if (activeWorkspaceId.isEmpty() || workspace == nullptr) { // is null when output is disabled
 | 
			
		||||
			this->mFocusedWorkspace = nullptr;
 | 
			
		||||
			this->mFocusedWorkspaceName = "";
 | 
			
		||||
		} else {
 | 
			
		||||
			this->mFocusedWorkspaceName = activeWorkspaceId;
 | 
			
		||||
			this->mFocusedWorkspace = workspace;
 | 
			
		||||
		}
 | 
			
		||||
		emit this->focusedWorkspaceChanged();
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	if (x != this->mX) {
 | 
			
		||||
		this->mX = x;
 | 
			
		||||
		emit this->xChanged();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (y != this->mY) {
 | 
			
		||||
		this->mY = y;
 | 
			
		||||
		emit this->yChanged();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (width != this->mWidth) {
 | 
			
		||||
		this->mWidth = width;
 | 
			
		||||
		emit this->widthChanged();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (height != this->mHeight) {
 | 
			
		||||
		this->mHeight = height;
 | 
			
		||||
		emit this->heightChanged();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (scale != this->mScale) {
 | 
			
		||||
		this->mScale = scale;
 | 
			
		||||
		emit this->scaleChanged();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (focused != this->mFocused) {
 | 
			
		||||
		this->mFocused = focused;
 | 
			
		||||
		emit this->focusedChanged();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (obj != this->mLastIpcObject) {
 | 
			
		||||
		this->mLastIpcObject = obj;
 | 
			
		||||
		emit this->lastIpcObjectChanged();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void I3Monitor::setFocus(bool focused) {
 | 
			
		||||
	this->mFocused = focused;
 | 
			
		||||
	emit this->focusedChanged();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void I3Monitor::setFocusedWorkspace(I3Workspace* workspace) {
 | 
			
		||||
	this->mFocusedWorkspace = workspace;
 | 
			
		||||
	this->mFocusedWorkspaceName = workspace->name();
 | 
			
		||||
	emit this->focusedWorkspaceChanged();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace qs::i3::ipc
 | 
			
		||||
							
								
								
									
										100
									
								
								src/x11/i3/ipc/monitor.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								src/x11/i3/ipc/monitor.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,100 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <qobject.h>
 | 
			
		||||
 | 
			
		||||
#include "connection.hpp"
 | 
			
		||||
 | 
			
		||||
namespace qs::i3::ipc {
 | 
			
		||||
 | 
			
		||||
///! I3/Sway monitors
 | 
			
		||||
class I3Monitor: public QObject {
 | 
			
		||||
	Q_OBJECT;
 | 
			
		||||
 | 
			
		||||
	/// The ID of this monitor
 | 
			
		||||
	Q_PROPERTY(qint32 id READ id NOTIFY idChanged);
 | 
			
		||||
	/// The name of this monitor
 | 
			
		||||
	Q_PROPERTY(QString name READ name NOTIFY nameChanged);
 | 
			
		||||
	/// Wether this monitor is turned on or not
 | 
			
		||||
	Q_PROPERTY(bool power READ power NOTIFY powerChanged);
 | 
			
		||||
 | 
			
		||||
	/// The current workspace
 | 
			
		||||
	Q_PROPERTY(qs::i3::ipc::I3Workspace* focusedWorkspace READ focusedWorkspace NOTIFY
 | 
			
		||||
	               focusedWorkspaceChanged);
 | 
			
		||||
 | 
			
		||||
	/// The X coordinate of this monitor inside the monitor layout
 | 
			
		||||
	Q_PROPERTY(qint32 x READ x NOTIFY xChanged);
 | 
			
		||||
 | 
			
		||||
	/// The Y coordinate of this monitor inside the monitor layout
 | 
			
		||||
	Q_PROPERTY(qint32 y READ y NOTIFY yChanged);
 | 
			
		||||
 | 
			
		||||
	/// The width in pixels of this monitor
 | 
			
		||||
	Q_PROPERTY(qint32 width READ width NOTIFY widthChanged);
 | 
			
		||||
 | 
			
		||||
	/// The height in pixels of this monitor
 | 
			
		||||
	Q_PROPERTY(qint32 height READ height NOTIFY heightChanged);
 | 
			
		||||
 | 
			
		||||
	/// The scaling factor of this monitor, 1 means it runs at native resolution
 | 
			
		||||
	Q_PROPERTY(qreal scale READ scale NOTIFY scaleChanged);
 | 
			
		||||
 | 
			
		||||
	/// Whether this monitor is currently in focus
 | 
			
		||||
	Q_PROPERTY(bool focused READ focused NOTIFY focusedChanged);
 | 
			
		||||
 | 
			
		||||
	/// Last JSON returned for this monitor, as a JavaScript object.
 | 
			
		||||
	///
 | 
			
		||||
	/// This updates every time Quickshell receives an `output` event from i3/Sway
 | 
			
		||||
	Q_PROPERTY(QVariantMap lastIpcObject READ lastIpcObject NOTIFY lastIpcObjectChanged);
 | 
			
		||||
 | 
			
		||||
	QML_ELEMENT;
 | 
			
		||||
	QML_UNCREATABLE("I3Monitors must be retrieved from the I3Ipc object.");
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	explicit I3Monitor(I3Ipc* ipc): QObject(ipc), ipc(ipc) {}
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] qint32 id() const;
 | 
			
		||||
	[[nodiscard]] QString name() const;
 | 
			
		||||
	[[nodiscard]] bool power() const;
 | 
			
		||||
	[[nodiscard]] I3Workspace* focusedWorkspace() const;
 | 
			
		||||
	[[nodiscard]] qint32 x() const;
 | 
			
		||||
	[[nodiscard]] qint32 y() const;
 | 
			
		||||
	[[nodiscard]] qint32 width() const;
 | 
			
		||||
	[[nodiscard]] qint32 height() const;
 | 
			
		||||
	[[nodiscard]] qreal scale() const;
 | 
			
		||||
	[[nodiscard]] bool focused() const;
 | 
			
		||||
	[[nodiscard]] QVariantMap lastIpcObject() const;
 | 
			
		||||
 | 
			
		||||
	void updateFromObject(const QVariantMap& obj);
 | 
			
		||||
 | 
			
		||||
	void setFocusedWorkspace(I3Workspace* workspace);
 | 
			
		||||
	void setFocus(bool focus);
 | 
			
		||||
signals:
 | 
			
		||||
	void idChanged();
 | 
			
		||||
	void nameChanged();
 | 
			
		||||
	void powerChanged();
 | 
			
		||||
	void focusedWorkspaceChanged();
 | 
			
		||||
	void xChanged();
 | 
			
		||||
	void yChanged();
 | 
			
		||||
	void widthChanged();
 | 
			
		||||
	void heightChanged();
 | 
			
		||||
	void scaleChanged();
 | 
			
		||||
	void lastIpcObjectChanged();
 | 
			
		||||
	void focusedChanged();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	I3Ipc* ipc;
 | 
			
		||||
 | 
			
		||||
	qint32 mId = -1;
 | 
			
		||||
	QString mName;
 | 
			
		||||
	bool mPower = false;
 | 
			
		||||
	qint32 mX = 0;
 | 
			
		||||
	qint32 mY = 0;
 | 
			
		||||
	qint32 mWidth = 0;
 | 
			
		||||
	qint32 mHeight = 0;
 | 
			
		||||
	qreal mScale = 1;
 | 
			
		||||
	bool mFocused = false;
 | 
			
		||||
	QVariantMap mLastIpcObject;
 | 
			
		||||
 | 
			
		||||
	I3Workspace* mFocusedWorkspace = nullptr;
 | 
			
		||||
	QString mFocusedWorkspaceName; // use for faster change detection
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace qs::i3::ipc
 | 
			
		||||
							
								
								
									
										55
									
								
								src/x11/i3/ipc/qml.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/x11/i3/ipc/qml.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,55 @@
 | 
			
		|||
#include "qml.hpp"
 | 
			
		||||
 | 
			
		||||
#include <qobject.h>
 | 
			
		||||
#include <qstring.h>
 | 
			
		||||
 | 
			
		||||
#include "../../../core/model.hpp"
 | 
			
		||||
#include "../../../core/qmlscreen.hpp"
 | 
			
		||||
#include "connection.hpp"
 | 
			
		||||
#include "workspace.hpp"
 | 
			
		||||
 | 
			
		||||
namespace qs::i3::ipc {
 | 
			
		||||
 | 
			
		||||
I3IpcQml::I3IpcQml() {
 | 
			
		||||
	auto* instance = I3Ipc::instance();
 | 
			
		||||
 | 
			
		||||
	QObject::connect(instance, &I3Ipc::rawEvent, this, &I3IpcQml::rawEvent);
 | 
			
		||||
	QObject::connect(instance, &I3Ipc::connected, this, &I3IpcQml::connected);
 | 
			
		||||
	QObject::connect(
 | 
			
		||||
	    instance,
 | 
			
		||||
	    &I3Ipc::focusedWorkspaceChanged,
 | 
			
		||||
	    this,
 | 
			
		||||
	    &I3IpcQml::focusedWorkspaceChanged
 | 
			
		||||
	);
 | 
			
		||||
	QObject::connect(instance, &I3Ipc::focusedMonitorChanged, this, &I3IpcQml::focusedMonitorChanged);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void I3IpcQml::dispatch(const QString& request) { I3Ipc::instance()->dispatch(request); }
 | 
			
		||||
 | 
			
		||||
void I3IpcQml::refreshMonitors() { I3Ipc::instance()->refreshMonitors(); }
 | 
			
		||||
 | 
			
		||||
void I3IpcQml::refreshWorkspaces() { I3Ipc::instance()->refreshWorkspaces(); }
 | 
			
		||||
 | 
			
		||||
QString I3IpcQml::socketPath() { return I3Ipc::instance()->socketPath(); }
 | 
			
		||||
 | 
			
		||||
ObjectModel<I3Monitor>* I3IpcQml::monitors() { return I3Ipc::instance()->monitors(); }
 | 
			
		||||
 | 
			
		||||
ObjectModel<I3Workspace>* I3IpcQml::workspaces() { return I3Ipc::instance()->workspaces(); }
 | 
			
		||||
 | 
			
		||||
I3Workspace* I3IpcQml::focusedWorkspace() { return I3Ipc::instance()->focusedWorkspace(); }
 | 
			
		||||
 | 
			
		||||
I3Monitor* I3IpcQml::focusedMonitor() { return I3Ipc::instance()->focusedMonitor(); }
 | 
			
		||||
 | 
			
		||||
I3Workspace* I3IpcQml::findWorkspaceByName(const QString& name) {
 | 
			
		||||
	return I3Ipc::instance()->findWorkspaceByName(name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
I3Monitor* I3IpcQml::findMonitorByName(const QString& name) {
 | 
			
		||||
	return I3Ipc::instance()->findMonitorByName(name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
I3Monitor* I3IpcQml::monitorFor(QuickshellScreenInfo* screen) {
 | 
			
		||||
	return I3Ipc::instance()->monitorFor(screen);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace qs::i3::ipc
 | 
			
		||||
							
								
								
									
										74
									
								
								src/x11/i3/ipc/qml.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								src/x11/i3/ipc/qml.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,74 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <qjsonarray.h>
 | 
			
		||||
#include <qobject.h>
 | 
			
		||||
 | 
			
		||||
#include "../../../core/doc.hpp"
 | 
			
		||||
#include "../../../core/qmlscreen.hpp"
 | 
			
		||||
#include "connection.hpp"
 | 
			
		||||
 | 
			
		||||
namespace qs::i3::ipc {
 | 
			
		||||
 | 
			
		||||
///! I3/Sway IPC integration
 | 
			
		||||
class I3IpcQml: public QObject {
 | 
			
		||||
	Q_OBJECT;
 | 
			
		||||
	// clang-format off
 | 
			
		||||
	/// Path to the I3 socket
 | 
			
		||||
	Q_PROPERTY(QString socketPath READ socketPath CONSTANT);
 | 
			
		||||
 | 
			
		||||
	Q_PROPERTY(qs::i3::ipc::I3Workspace* focusedWorkspace READ focusedWorkspace NOTIFY focusedWorkspaceChanged);
 | 
			
		||||
	Q_PROPERTY(qs::i3::ipc::I3Monitor* focusedMonitor READ focusedMonitor NOTIFY focusedMonitorChanged);
 | 
			
		||||
	/// All I3 monitors.
 | 
			
		||||
	QSDOC_TYPE_OVERRIDE(ObjectModel<qs::i3::ipc::I3Monitor>*);
 | 
			
		||||
	Q_PROPERTY(UntypedObjectModel* monitors READ monitors CONSTANT);
 | 
			
		||||
	/// All I3 workspaces.
 | 
			
		||||
	QSDOC_TYPE_OVERRIDE(ObjectModel<qs::i3::ipc::I3Workspace>*);
 | 
			
		||||
	Q_PROPERTY(UntypedObjectModel* workspaces READ workspaces CONSTANT);
 | 
			
		||||
	// clang-format on
 | 
			
		||||
	QML_NAMED_ELEMENT(I3);
 | 
			
		||||
	QML_SINGLETON;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	explicit I3IpcQml();
 | 
			
		||||
 | 
			
		||||
	/// Execute an [I3/Sway command](https://i3wm.org/docs/userguide.html#list_of_commands)
 | 
			
		||||
	Q_INVOKABLE static void dispatch(const QString& request);
 | 
			
		||||
 | 
			
		||||
	/// Refresh monitor information.
 | 
			
		||||
	Q_INVOKABLE static void refreshMonitors();
 | 
			
		||||
 | 
			
		||||
	/// Refresh workspace information.
 | 
			
		||||
	Q_INVOKABLE static void refreshWorkspaces();
 | 
			
		||||
 | 
			
		||||
	/// Find an I3Workspace using its name, returns null if the workspace doesn't exist.
 | 
			
		||||
	Q_INVOKABLE static I3Workspace* findWorkspaceByName(const QString& name);
 | 
			
		||||
 | 
			
		||||
	/// Find an I3Monitor using its name, returns null if the monitor doesn't exist.
 | 
			
		||||
	Q_INVOKABLE static I3Monitor* findMonitorByName(const QString& name);
 | 
			
		||||
 | 
			
		||||
	/// Return the i3/Sway monitor associated with `screen`
 | 
			
		||||
	Q_INVOKABLE static I3Monitor* monitorFor(QuickshellScreenInfo* screen);
 | 
			
		||||
 | 
			
		||||
	/// The path to the I3 or Sway socket currently being used
 | 
			
		||||
	[[nodiscard]] static QString socketPath();
 | 
			
		||||
 | 
			
		||||
	/// All I3Monitors
 | 
			
		||||
	[[nodiscard]] static ObjectModel<I3Monitor>* monitors();
 | 
			
		||||
 | 
			
		||||
	/// All I3Workspaces
 | 
			
		||||
	[[nodiscard]] static ObjectModel<I3Workspace>* workspaces();
 | 
			
		||||
 | 
			
		||||
	/// The currently focused Workspace
 | 
			
		||||
	[[nodiscard]] static I3Workspace* focusedWorkspace();
 | 
			
		||||
 | 
			
		||||
	/// The currently focused Monitor
 | 
			
		||||
	[[nodiscard]] static I3Monitor* focusedMonitor();
 | 
			
		||||
 | 
			
		||||
signals:
 | 
			
		||||
	void rawEvent(I3IpcEvent* event);
 | 
			
		||||
	void connected();
 | 
			
		||||
	void focusedWorkspaceChanged();
 | 
			
		||||
	void focusedMonitorChanged();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace qs::i3::ipc
 | 
			
		||||
							
								
								
									
										73
									
								
								src/x11/i3/ipc/workspace.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/x11/i3/ipc/workspace.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,73 @@
 | 
			
		|||
#include "workspace.hpp"
 | 
			
		||||
 | 
			
		||||
#include <qcontainerfwd.h>
 | 
			
		||||
#include <qstring.h>
 | 
			
		||||
#include <qtmetamacros.h>
 | 
			
		||||
#include <qtypes.h>
 | 
			
		||||
 | 
			
		||||
#include "monitor.hpp"
 | 
			
		||||
 | 
			
		||||
namespace qs::i3::ipc {
 | 
			
		||||
 | 
			
		||||
qint32 I3Workspace ::id() const { return this->mId; }
 | 
			
		||||
QString I3Workspace::name() const { return this->mName; }
 | 
			
		||||
qint32 I3Workspace ::num() const { return this->mNum; }
 | 
			
		||||
bool I3Workspace ::urgent() const { return this->mUrgent; }
 | 
			
		||||
bool I3Workspace::focused() const { return this->mFocused; }
 | 
			
		||||
I3Monitor* I3Workspace::monitor() const { return this->mMonitor; }
 | 
			
		||||
QVariantMap I3Workspace::lastIpcObject() const { return this->mLastIpcObject; }
 | 
			
		||||
 | 
			
		||||
void I3Workspace::updateFromObject(const QVariantMap& obj) {
 | 
			
		||||
	auto id = obj.value("id").value<qint32>();
 | 
			
		||||
	auto name = obj.value("name").value<QString>();
 | 
			
		||||
	auto num = obj.value("num").value<qint32>();
 | 
			
		||||
	auto urgent = obj.value("urgent").value<bool>();
 | 
			
		||||
	auto focused = obj.value("focused").value<bool>();
 | 
			
		||||
	auto monitorName = obj.value("output").value<QString>();
 | 
			
		||||
 | 
			
		||||
	if (id != this->mId) {
 | 
			
		||||
		this->mId = id;
 | 
			
		||||
		emit this->idChanged();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (name != this->mName) {
 | 
			
		||||
		this->mName = name;
 | 
			
		||||
		emit this->nameChanged();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (num != this->mNum) {
 | 
			
		||||
		this->mNum = num;
 | 
			
		||||
		emit this->numChanged();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (urgent != this->mUrgent) {
 | 
			
		||||
		this->mUrgent = urgent;
 | 
			
		||||
		emit this->urgentChanged();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (focused != this->mFocused) {
 | 
			
		||||
		this->mFocused = focused;
 | 
			
		||||
		emit this->focusedChanged();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (obj != this->mLastIpcObject) {
 | 
			
		||||
		this->mLastIpcObject = obj;
 | 
			
		||||
		emit this->lastIpcObjectChanged();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (monitorName != this->mMonitorName) {
 | 
			
		||||
		auto* monitor = this->ipc->findMonitorByName(monitorName);
 | 
			
		||||
		if (monitorName.isEmpty() || monitor == nullptr) { // is null when output is disabled
 | 
			
		||||
			this->mMonitor = nullptr;
 | 
			
		||||
			this->mMonitorName = "";
 | 
			
		||||
		} else {
 | 
			
		||||
			this->mMonitorName = monitorName;
 | 
			
		||||
			this->mMonitor = monitor;
 | 
			
		||||
		}
 | 
			
		||||
		emit this->monitorChanged();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void I3Workspace::setFocus(bool focus) { this->mFocused = focus; }
 | 
			
		||||
 | 
			
		||||
} // namespace qs::i3::ipc
 | 
			
		||||
							
								
								
									
										73
									
								
								src/x11/i3/ipc/workspace.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/x11/i3/ipc/workspace.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,73 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "connection.hpp"
 | 
			
		||||
 | 
			
		||||
namespace qs::i3::ipc {
 | 
			
		||||
 | 
			
		||||
///! I3/Sway workspaces
 | 
			
		||||
class I3Workspace: public QObject {
 | 
			
		||||
	Q_OBJECT;
 | 
			
		||||
 | 
			
		||||
	/// The ID of this workspace, it is unique for i3/Sway launch
 | 
			
		||||
	Q_PROPERTY(qint32 id READ id NOTIFY idChanged);
 | 
			
		||||
 | 
			
		||||
	/// The name of this workspace
 | 
			
		||||
	Q_PROPERTY(QString name READ name NOTIFY nameChanged);
 | 
			
		||||
 | 
			
		||||
	/// The number of this workspace
 | 
			
		||||
	Q_PROPERTY(qint32 num READ num NOTIFY numChanged);
 | 
			
		||||
 | 
			
		||||
	/// If a window in this workspace has an urgent notification
 | 
			
		||||
	Q_PROPERTY(bool urgent READ urgent NOTIFY urgentChanged);
 | 
			
		||||
 | 
			
		||||
	/// If this workspace is the one currently in focus
 | 
			
		||||
	Q_PROPERTY(bool focused READ focused NOTIFY focusedChanged);
 | 
			
		||||
 | 
			
		||||
	/// The monitor this workspace is being displayed on
 | 
			
		||||
	Q_PROPERTY(qs::i3::ipc::I3Monitor* monitor READ monitor NOTIFY monitorChanged);
 | 
			
		||||
 | 
			
		||||
	/// Last JSON returned for this workspace, as a JavaScript object.
 | 
			
		||||
	///
 | 
			
		||||
	/// This updates every time we receive a `workspace` event from i3/Sway
 | 
			
		||||
	Q_PROPERTY(QVariantMap lastIpcObject READ lastIpcObject NOTIFY lastIpcObjectChanged);
 | 
			
		||||
 | 
			
		||||
	QML_ELEMENT;
 | 
			
		||||
	QML_UNCREATABLE("I3Workspaces must be retrieved from the I3 object.");
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	I3Workspace(qs::i3::ipc::I3Ipc* ipc): QObject(ipc), ipc(ipc) {}
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] qint32 id() const;
 | 
			
		||||
	[[nodiscard]] QString name() const;
 | 
			
		||||
	[[nodiscard]] qint32 num() const;
 | 
			
		||||
	[[nodiscard]] bool urgent() const;
 | 
			
		||||
	[[nodiscard]] bool focused() const;
 | 
			
		||||
	[[nodiscard]] I3Monitor* monitor() const;
 | 
			
		||||
	[[nodiscard]] QVariantMap lastIpcObject() const;
 | 
			
		||||
 | 
			
		||||
	void updateFromObject(const QVariantMap& obj);
 | 
			
		||||
	void setFocus(bool focus);
 | 
			
		||||
 | 
			
		||||
signals:
 | 
			
		||||
	void idChanged();
 | 
			
		||||
	void nameChanged();
 | 
			
		||||
	void urgentChanged();
 | 
			
		||||
	void focusedChanged();
 | 
			
		||||
	void numChanged();
 | 
			
		||||
	void monitorChanged();
 | 
			
		||||
	void lastIpcObjectChanged();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	I3Ipc* ipc;
 | 
			
		||||
 | 
			
		||||
	qint32 mId = -1;
 | 
			
		||||
	QString mName;
 | 
			
		||||
	qint32 mNum = -1;
 | 
			
		||||
	bool mFocused = false;
 | 
			
		||||
	bool mUrgent = false;
 | 
			
		||||
 | 
			
		||||
	QVariantMap mLastIpcObject;
 | 
			
		||||
	I3Monitor* mMonitor = nullptr;
 | 
			
		||||
	QString mMonitorName;
 | 
			
		||||
};
 | 
			
		||||
} // namespace qs::i3::ipc
 | 
			
		||||
							
								
								
									
										9
									
								
								src/x11/i3/module.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/x11/i3/module.md
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
name = "Quickshell.I3"
 | 
			
		||||
description = "I3 specific Quickshell types"
 | 
			
		||||
headers = [
 | 
			
		||||
	"ipc/connection.hpp",
 | 
			
		||||
	"ipc/qml.hpp",
 | 
			
		||||
	"ipc/workspace.hpp",
 | 
			
		||||
	"ipc/monitor.hpp",
 | 
			
		||||
]
 | 
			
		||||
-----
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue