Skip to content

Commit 6e4abdd

Browse files
committed
i3/sway: use datastreams instead of qbytearrays for socket reading
1 parent 6ca870d commit 6e4abdd

File tree

6 files changed

+104
-79
lines changed

6 files changed

+104
-79
lines changed

src/x11/i3/ipc/connection.cpp

Lines changed: 63 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -31,19 +31,19 @@ Q_LOGGING_CATEGORY(logI3IpcEvents, "quickshell.I3.ipc.events", QtWarningMsg);
3131
namespace qs::i3::ipc {
3232

3333
QVector<Event> I3Ipc::makeRequest(const QByteArray& request) {
34+
3435
this->commandSocket.write(request);
3536
this->commandSocket.flush();
3637

3738
if (this->commandSocket.waitForReadyRead()) {
38-
auto dataRaw = this->commandSocket.readAll();
39-
return I3Ipc::parseResponse(dataRaw);
39+
return I3Ipc::parseResponse(this->commandSocketDs);
4040
} else {
4141
qCWarning(logI3Ipc) << "Timeout while waiting for read for" << request;
4242
return {};
4343
}
4444
}
4545

46-
void I3Ipc::dispatch(const QString& payload) {
46+
QJsonArray I3Ipc::dispatch(const QString& payload) {
4747
auto message = I3Ipc::buildRequestMessage(EventCode::RUN_COMMAND, payload.toLocal8Bit());
4848

4949
QVector<Event> res = this->makeRequest(message);
@@ -63,6 +63,8 @@ void I3Ipc::dispatch(const QString& payload) {
6363
<< jsonMessage["error"];
6464
}
6565
}
66+
67+
return data.array();
6668
}
6769

6870
QByteArray I3Ipc::buildRequestMessage(EventCode cmd, const QByteArray& payload) {
@@ -102,6 +104,7 @@ I3Ipc::I3Ipc() {
102104

103105
this->liveEventSocket.write(message);
104106
this->liveEventSocket.flush();
107+
emit this->connected();
105108
};
106109

107110
QObject::connect(
@@ -111,90 +114,70 @@ I3Ipc::I3Ipc() {
111114
liveIPCconnectedCallback
112115
);
113116

117+
this->liveEventSocketDs.setDevice(&this->liveEventSocket);
118+
this->commandSocketDs.setDevice(&this->commandSocket);
119+
this->liveEventSocketDs.setByteOrder(static_cast<QDataStream::ByteOrder>(QSysInfo::ByteOrder));
120+
this->commandSocketDs.setByteOrder(static_cast<QDataStream::ByteOrder>(QSysInfo::ByteOrder));
121+
114122
this->liveEventSocket.connectToServer(this->mSocketPath);
115123
this->commandSocket.connectToServer(this->mSocketPath);
116-
117124
this->refreshWorkspaces();
118125
this->refreshMonitors();
119126
}
120127

121128
void I3Ipc::eventSocketReady() {
122-
while (true) {
123-
auto rawEvent = this->liveEventSocket.readAll();
124-
if (rawEvent.isEmpty()) break;
125-
126-
for (auto& [type, data]: I3Ipc::parseResponse(rawEvent)) {
127-
this->event.mCode = type;
128-
this->event.mData = data;
129+
for (auto& [type, data]: I3Ipc::parseResponse(this->liveEventSocketDs)) {
130+
this->event.mCode = type;
131+
this->event.mData = data;
129132

130-
this->onEvent(&this->event);
131-
emit this->rawEvent(&this->event);
132-
}
133+
this->onEvent(&this->event);
134+
emit this->rawEvent(&this->event);
133135
}
134136
}
135137

136-
QVector<Event> I3Ipc::parseResponse(QByteArray rawEvent) {
138+
QVector<Event> I3Ipc::parseResponse(QDataStream& rawEvent) {
137139
QVector<std::tuple<EventCode, QJsonDocument>> events;
138140
const int magicLen = 6;
139-
const int header = 8 + magicLen;
140141

141-
while (!rawEvent.isEmpty()) {
142-
auto magicSeqInd = rawEvent.indexOf(MAGIC);
142+
while (!rawEvent.atEnd()) {
143+
rawEvent.startTransaction();
143144

144-
if (magicSeqInd < 0) {
145-
qCWarning(logI3Ipc) << "No magic sequence found in string.";
146-
break;
147-
};
148-
149-
if (magicSeqInd > 0) {
150-
rawEvent = rawEvent.sliced(magicSeqInd);
151-
}
145+
QByteArray buffer(6, 0);
146+
qint32 size = 0;
147+
qint32 type = EventCode::UNKNOWN;
152148

153-
auto eventLength = rawEvent.length();
149+
rawEvent.readRawData(buffer.data(), magicLen);
150+
rawEvent >> size;
151+
rawEvent >> type;
154152

155-
if (eventLength < header) {
156-
qCWarning(logI3Ipc) << "Event isn't long enough to hold the header data (14 bytes).";
153+
if (buffer != MAGIC.data()) {
154+
qCWarning(logI3Ipc) << "No magic sequence found in string." << buffer;
155+
rawEvent.rollbackTransaction();
157156
break;
158157
};
159158

160-
rawEvent = rawEvent.sliced(magicLen);
161-
162-
QDataStream ds(rawEvent);
163-
164-
ds.setByteOrder(static_cast<QDataStream::ByteOrder>(QSysInfo::ByteOrder));
165-
166-
qint32 size = 0;
167-
qint32 type = EventCode::UNKNOWN;
168-
169-
ds >> size;
170-
ds >> type;
171-
172159
if (I3IpcEvent::intToEvent(type) == EventCode::UNKNOWN) {
173-
qCWarning(logI3Ipc) << "Received unknown event" << rawEvent;
160+
qCWarning(logI3Ipc) << "Received unknown event";
161+
rawEvent.rollbackTransaction();
174162
break;
175163
}
176164

177-
auto maxPayloadLength = eventLength - header;
165+
QByteArray payload(size, Qt::Uninitialized);
178166

179-
if (maxPayloadLength < size) {
180-
qCWarning(logI3Ipc) << "Payload is smaller than length advertised in header, skipping... ("
181-
<< maxPayloadLength << "vs" << size << ")";
182-
break;
183-
}
184-
185-
rawEvent = rawEvent.sliced(8);
167+
rawEvent.readRawData(payload.data(), size);
186168

187169
QJsonParseError e;
188170

189-
auto data = QJsonDocument::fromJson(rawEvent.sliced(0, size), &e);
171+
auto data = QJsonDocument::fromJson(payload, &e);
190172

191173
if (e.error != QJsonParseError::NoError) {
192-
qCWarning(logI3Ipc) << "Invalid JSON value:" << e.errorString() << "\n\t" << rawEvent;
174+
qCWarning(logI3Ipc) << "Invalid JSON value:" << e.errorString();
175+
rawEvent.rollbackTransaction();
176+
break;
193177
} else {
178+
if (!rawEvent.commitTransaction()) break;
194179
events.push_back(std::tuple(I3IpcEvent::intToEvent(type), data));
195180
}
196-
197-
rawEvent = rawEvent.sliced(size);
198181
}
199182

200183
return events;
@@ -234,6 +217,10 @@ void I3Ipc::setFocusedWorkspace(I3Workspace* workspace) {
234217
this->mFocusedWorkspace = workspace;
235218

236219
if (workspace != nullptr) {
220+
if (auto* monitor = this->mFocusedWorkspace->monitor()) {
221+
monitor->setFocusedWorkspace(this->mFocusedWorkspace);
222+
}
223+
237224
QObject::connect(workspace, &QObject::destroyed, this, &I3Ipc::onFocusedWorkspaceDestroyed);
238225
workspace->setFocus(true);
239226
this->setFocusedMonitor(workspace->monitor());
@@ -428,28 +415,37 @@ void I3Ipc::handleWorkspaceEvent(I3IpcEvent* event) {
428415

429416
auto workspaceData = event->mData["current"];
430417

431-
auto* workspace = new I3Workspace(this);
418+
auto* workspace = this->findWorkspaceByID(workspaceData["id"].toInt(-1));
419+
420+
if (workspace == nullptr) {
421+
workspace = new I3Workspace(this);
422+
}
432423

433424
if (workspaceData.isObject()) {
434425
workspace->updateFromObject(workspaceData.toObject().toVariantMap());
435426
}
436427

437428
this->mWorkspaces.insertObject(workspace);
438429
qCInfo(logI3Ipc) << "Added workspace to list";
439-
440430
} else if (change == "focus") {
441431
auto oldData = event->mData["old"];
442432
auto newData = event->mData["current"];
443433
auto oldName = oldData["name"].toString();
444434
auto newName = newData["name"].toString();
445435

446436
qCInfo(logI3IpcEvents) << "Focus changed: " << oldName << "->" << newName;
447-
auto* oldWorkspace = this->findWorkspaceByName(oldName);
437+
438+
if (auto* oldWorkspace = this->findWorkspaceByName(oldName)) {
439+
oldWorkspace->updateFromObject(oldData.toObject().toVariantMap());
440+
}
441+
448442
auto* newWorkspace = this->findWorkspaceByName(newName);
449443

450-
oldWorkspace->updateFromObject(oldData.toObject().toVariantMap());
451-
newWorkspace->updateFromObject(newData.toObject().toVariantMap());
444+
if (newWorkspace == nullptr) {
445+
newWorkspace = new I3Workspace(this);
446+
}
452447

448+
newWorkspace->updateFromObject(newData.toObject().toVariantMap());
453449
this->setFocusedWorkspace(newWorkspace);
454450
} else if (change == "empty") {
455451
auto name = event->mData["current"]["name"].toString();
@@ -493,6 +489,14 @@ I3Monitor* I3Ipc::monitorFor(QuickshellScreenInfo* screen) {
493489
return this->findMonitorByName(screen->name());
494490
}
495491

492+
I3Workspace* I3Ipc::findWorkspaceByID(qint32 id) {
493+
auto list = this->mWorkspaces.valueList();
494+
auto workspaceIter =
495+
std::find_if(list.begin(), list.end(), [id](const I3Workspace* m) { return m->id() == id; });
496+
497+
return workspaceIter == list.end() ? nullptr : *workspaceIter;
498+
}
499+
496500
I3Workspace* I3Ipc::findWorkspaceByName(const QString& name) {
497501
auto list = this->mWorkspaces.valueList();
498502
auto workspaceIter = std::find_if(list.begin(), list.end(), [name](const I3Workspace* m) {

src/x11/i3/ipc/connection.hpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,13 @@ class I3Ipc: public QObject {
7575
[[nodiscard]] QString socketPath() const;
7676

7777
QVector<Event> makeRequest(const QByteArray& request);
78-
void dispatch(const QString& payload);
78+
QJsonArray dispatch(const QString& payload);
7979

8080
static QByteArray buildRequestMessage(EventCode cmd, const QByteArray& payload = QByteArray());
81+
8182
I3Workspace* findWorkspaceByName(const QString& name);
8283
I3Monitor* findMonitorByName(const QString& name);
84+
I3Workspace* findWorkspaceByID(qint32 id);
8385

8486
void setFocusedWorkspace(I3Workspace* workspace);
8587
void setFocusedMonitor(I3Monitor* monitor);
@@ -114,11 +116,16 @@ private slots:
114116

115117
void handleWorkspaceEvent(I3IpcEvent* event);
116118

117-
static QVector<std::tuple<EventCode, QJsonDocument>> parseResponse(QByteArray rawEvent);
119+
static QVector<std::tuple<EventCode, QJsonDocument>> parseResponse(QDataStream& rawEvent);
118120

119121
QLocalSocket liveEventSocket;
122+
QDataStream liveEventSocketDs;
123+
120124
QLocalSocket commandSocket;
125+
QDataStream commandSocketDs;
126+
121127
QString mSocketPath;
128+
122129
bool valid = false;
123130

124131
ObjectModel<I3Monitor> mMonitors {this};

src/x11/i3/ipc/monitor.cpp

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace qs::i3::ipc {
1212
qint32 I3Monitor::id() const { return this->mId; };
1313
QString I3Monitor::name() const { return this->mName; };
1414
bool I3Monitor::power() const { return this->mPower; };
15-
I3Workspace* I3Monitor::activeWorkspace() const { return this->mActiveWorkspace; };
15+
I3Workspace* I3Monitor::focusedWorkspace() const { return this->mFocusedWorkspace; };
1616
qint32 I3Monitor::x() const { return this->mX; };
1717
qint32 I3Monitor::y() const { return this->mY; };
1818
qint32 I3Monitor::width() const { return this->mWidth; };
@@ -49,16 +49,16 @@ void I3Monitor::updateFromObject(const QVariantMap& obj) {
4949
this->powerChanged();
5050
}
5151

52-
if (activeWorkspaceId != this->mActiveWorkspaceId) {
52+
if (activeWorkspaceId != this->mFocusedWorkspaceName) {
5353
auto* workspace = this->ipc->findWorkspaceByName(activeWorkspaceId);
5454
if (activeWorkspaceId.isEmpty() || workspace == nullptr) { // is null when output is disabled
55-
this->mActiveWorkspace = nullptr;
56-
this->mActiveWorkspaceId = "";
55+
this->mFocusedWorkspace = nullptr;
56+
this->mFocusedWorkspaceName = "";
5757
} else {
58-
this->mActiveWorkspaceId = activeWorkspaceId;
59-
this->mActiveWorkspace = workspace;
58+
this->mFocusedWorkspaceName = activeWorkspaceId;
59+
this->mFocusedWorkspace = workspace;
6060
}
61-
emit this->activeWorkspaceChanged();
61+
emit this->focusedWorkspaceChanged();
6262
};
6363

6464
if (x != this->mX) {
@@ -97,5 +97,15 @@ void I3Monitor::updateFromObject(const QVariantMap& obj) {
9797
}
9898
}
9999

100-
void I3Monitor::setFocus(bool focused) { this->mFocused = focused; }
100+
void I3Monitor::setFocus(bool focused) {
101+
this->mFocused = focused;
102+
emit this->focusedChanged();
103+
}
104+
105+
void I3Monitor::setFocusedWorkspace(I3Workspace* workspace) {
106+
this->mFocusedWorkspace = workspace;
107+
this->mFocusedWorkspaceName = workspace->name();
108+
emit this->focusedWorkspaceChanged();
109+
};
110+
101111
} // namespace qs::i3::ipc

src/x11/i3/ipc/monitor.hpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,9 @@ class I3Monitor: public QObject {
1717
/// Wether this monitor is turned on or not
1818
Q_PROPERTY(bool power READ power NOTIFY powerChanged);
1919

20-
/// The currently active workspace
21-
/// TODO: replace with a list of all workspaces
22-
Q_PROPERTY(qs::i3::ipc::I3Workspace* activeWorkspace READ activeWorkspace NOTIFY
23-
activeWorkspaceChanged);
20+
/// The current workspace
21+
Q_PROPERTY(qs::i3::ipc::I3Workspace* focusedWorkspace READ focusedWorkspace NOTIFY
22+
focusedWorkspaceChanged);
2423

2524
/// The X coordinate of this monitor inside the monitor layout
2625
Q_PROPERTY(qint32 x READ x NOTIFY xChanged);
@@ -54,7 +53,7 @@ class I3Monitor: public QObject {
5453
[[nodiscard]] qint32 id() const;
5554
[[nodiscard]] QString name() const;
5655
[[nodiscard]] bool power() const;
57-
[[nodiscard]] I3Workspace* activeWorkspace() const;
56+
[[nodiscard]] I3Workspace* focusedWorkspace() const;
5857
[[nodiscard]] qint32 x() const;
5958
[[nodiscard]] qint32 y() const;
6059
[[nodiscard]] qint32 width() const;
@@ -65,12 +64,13 @@ class I3Monitor: public QObject {
6564

6665
void updateFromObject(const QVariantMap& obj);
6766

67+
void setFocusedWorkspace(I3Workspace* workspace);
6868
void setFocus(bool focus);
6969
signals:
7070
void idChanged();
7171
void nameChanged();
7272
void powerChanged();
73-
void activeWorkspaceChanged();
73+
void focusedWorkspaceChanged();
7474
void xChanged();
7575
void yChanged();
7676
void widthChanged();
@@ -93,8 +93,8 @@ class I3Monitor: public QObject {
9393
bool mFocused = false;
9494
QVariantMap mLastIpcObject;
9595

96-
I3Workspace* mActiveWorkspace = nullptr;
97-
QString mActiveWorkspaceId; // use for faster change detection
96+
I3Workspace* mFocusedWorkspace = nullptr;
97+
QString mFocusedWorkspaceName; // use for faster change detection
9898
};
9999

100100
} // namespace qs::i3::ipc

src/x11/i3/ipc/qml.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "qml.hpp"
22

3+
#include <qjsonarray.h>
34
#include <qobject.h>
45
#include <qstring.h>
56

@@ -24,7 +25,9 @@ I3IpcQml::I3IpcQml() {
2425
QObject::connect(instance, &I3Ipc::focusedMonitorChanged, this, &I3IpcQml::focusedMonitorChanged);
2526
}
2627

27-
void I3IpcQml::dispatch(const QString& request) { I3Ipc::instance()->dispatch(request); }
28+
QJsonArray I3IpcQml::dispatch(const QString& request) {
29+
return I3Ipc::instance()->dispatch(request);
30+
}
2831

2932
void I3IpcQml::refreshMonitors() { I3Ipc::instance()->refreshMonitors(); }
3033

src/x11/i3/ipc/qml.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#pragma once
22

3+
#include <qjsonarray.h>
34
#include <qobject.h>
45

56
#include "../../../core/doc.hpp"
@@ -31,7 +32,7 @@ class I3IpcQml: public QObject {
3132
explicit I3IpcQml();
3233

3334
/// Execute an [I3/Sway command](https://i3wm.org/docs/userguide.html#list_of_commands)
34-
Q_INVOKABLE static void dispatch(const QString& request);
35+
Q_INVOKABLE static QJsonArray dispatch(const QString& request);
3536

3637
/// Refresh monitor information.
3738
Q_INVOKABLE static void refreshMonitors();

0 commit comments

Comments
 (0)