5Q_LOGGING_CATEGORY(Qi3pcLogger,
"qi3pc", QtMsgType::QtDebugMsg)
16 m_barConfigs = std::make_pair(QJsonObject(), QDateTime::currentMSecsSinceEpoch());
18 throw std::invalid_argument(
"The provided server path must not be empty.");
44 auto [data, type] = m.value();
46 if (type & (1u << 31)) {
47 switch (
static_cast<IpcEvent>(type)) {
48 case IpcEvent::Workspace:
51 case IpcEvent::Output:
57 case IpcEvent::Window:
60 case IpcEvent::BarUpdate:
63 case IpcEvent::Binding:
66 case IpcEvent::Shutdown:
73 std::string log =
"Received event of unsupported type IpcType::%u";
74 qCWarning(Qi3pcLogger, log.c_str(), type, IpcType::Subscribe);
78 switch (
static_cast<IpcType>(type)) {
79 case IpcType::Subscribe:
86 std::string log =
"Received message IpcType::%u while expecting IpcEvent, IpcType::Subscribe (IpcType::%u),"
87 "or IpcType::Tick (IpcType::%u)";
88 qCWarning(Qi3pcLogger, log.c_str(), type, IpcType::Subscribe, IpcType::Tick);
102 auto [data, type] = m.value();
105 switch (
static_cast<IpcType>(type)) {
106 case IpcType::Command:
109 case IpcType::Workspaces:
112 case IpcType::Subscribe:
113 log =
"Received reply of type IpcType::Subscribe (IpcType::%u): unexpected.";
114 qCWarning(Qi3pcLogger, log.c_str(), IpcType::Subscribe);
116 case IpcType::Outputs:
125 case IpcType::BarConfig:
128 case IpcType::Version:
131 case IpcType::BindingModes:
134 case IpcType::Config:
138 log =
"Received reply of type IpcType::Tick (IpcType::%u): unexpected.";
139 qCWarning(Qi3pcLogger, log.c_str(), IpcType::Tick);
144 case IpcType::BindingState:
148 log =
"Received reply of unsupported type IpcType::%u.";
149 qCWarning(Qi3pcLogger, log.c_str(), type);
157 auto results = doc.array();
158 if (results.empty()) {
159 qCWarning(Qi3pcLogger) <<
"Received empty command reply:" << doc.toJson();
164 for (
auto jsonObject: std::as_const(results)) {
165 auto object = jsonObject.toObject();
166 if (!
object.contains(
"success")) {
167 qCWarning(Qi3pcLogger) <<
"Command reply does not contain success key. Skipping:" << object;
171 if (
auto success =
object[
"success"].toBool(); success) {
172 result.push_back({
true, {}});
184 m_workspaces = std::make_pair(doc.array(), QDateTime::currentMSecsSinceEpoch());
191 m_outputs = std::make_pair(doc.array(), QDateTime::currentMSecsSinceEpoch());
198 m_tree = std::make_pair(doc.object(), QDateTime::currentMSecsSinceEpoch());
205 m_marks = std::make_pair(doc.array(), QDateTime::currentMSecsSinceEpoch());
212 if (
const auto& ids = doc.array(); !ids.isEmpty()) {
213 for (
auto ref : ids) {
214 if (
auto id = ref.toString();
220 auto id = doc[
"id"].toString();
221 auto config = doc.object();
223 m_barConfigs->second = QDateTime::currentMSecsSinceEpoch();
231 m_version = std::make_pair(doc.object(), QDateTime::currentMSecsSinceEpoch());
238 m_bindingModes = std::make_pair(doc.array(), QDateTime::currentMSecsSinceEpoch());
245 m_config = std::make_pair(doc.object(), QDateTime::currentMSecsSinceEpoch());
252 emit
tickSent(doc[
"success"].toBool());
258 emit
synced(doc[
"success"].toBool());
264 m_bindingState = std::make_pair(doc[
"name"].toString(), QDateTime::currentMSecsSinceEpoch());
274 qCWarning(Qi3pcLogger) <<
"Received workspace event with unknown change string" << doc[
"change"].toString();
278 auto old = doc[
"old"].toObject();
279 auto current = doc[
"current"].toObject();
289 qCWarning(Qi3pcLogger) <<
"Received output event with unknown change string" << data[
"change"].toString();
299 emit
modeEvent(doc[
"change"].toString(), doc[
"pango_markup"].toBool());
308 qCWarning(Qi3pcLogger) <<
"Received window event with unknown change string" << doc[
"change"].toString();
312 emit
windowEvent(change, doc[
"container"].toObject());
327 qCWarning(Qi3pcLogger) <<
"Received binding event with unknown change string" << doc[
"change"].toString();
331 emit
bindingEvent(change, doc[
"binding"].toObject(), doc[
"mode"].toString());
340 qCWarning(Qi3pcLogger) <<
"Received shutdown event with unknown change string" << doc[
"change"].toString();
350 if(
auto first = doc[
"first"].toBool(); !first) {
351 emit
tickEvent(doc[
"payload"].toString());
360 }
else if (s ==
"init") {
362 }
else if (s ==
"empty") {
364 }
else if (s ==
"urgent") {
366 }
else if (s ==
"reload") {
368 }
else if (s ==
"rename") {
370 }
else if (s ==
"restored") {
372 }
else if (s ==
"move") {
384 }
else if (s ==
"close") {
386 }
else if (s ==
"focus") {
388 }
else if (s ==
"title") {
390 }
else if (s ==
"fullscreen_mode") {
392 }
else if (s ==
"move") {
394 }
else if (s ==
"floating") {
396 }
else if (s ==
"urgent") {
398 }
else if (s ==
"mark") {
408 if (s ==
"restart") {
410 }
else if (s ==
"exit") {
420 if (s ==
"unspecified") {
443 qCWarning(Qi3pcLogger) <<
"Insufficient data read for IPC magic string. Seeked"
449 qCWarning(Qi3pcLogger) <<
"Unexpected magic string in message. Expected"
455 if (
auto read_size = socket.read(
reinterpret_cast<char*
>(&size),
sizeof size);
456 read_size !=
sizeof size) {
457 qCWarning(Qi3pcLogger) <<
"Insufficient data read for message size. Seeked"
458 <<
sizeof size <<
"bytes. - Read" << read_size <<
"bytes.";
463 if (
auto read_size = socket.read(
reinterpret_cast<char*
>(&type),
sizeof type);
464 read_size !=
sizeof type) {
465 qCWarning(Qi3pcLogger) <<
"Insufficient data read for message type. Seeked"
466 <<
sizeof type <<
"bytes. - Read" << read_size <<
"bytes.";
470 QJsonParseError parseError;
471 auto payload = socket.read(size);
473 if (socket.bytesAvailable() > 0) {
474 emit socket.readyRead();
477 QJsonDocument data = QJsonDocument::fromJson(payload, &parseError);
478 if (parseError.error != QJsonParseError::NoError) {
479 qCWarning(Qi3pcLogger) <<
"Parsing message body failed - JSON parse error:"
480 << parseError.errorString() <<
"in:" << payload;
484 return std::make_pair(data, type);
490 if (
auto path = QProcessEnvironment::systemEnvironment().value(
"I3SOCK");
503 process.start(
"i3", QStringList(
"--get-socketpath"));
504 process.waitForReadyRead();
505 auto path = QString(process.readAllStandardOutput()).trimmed();
507 process.waitForFinished();
643 if (events.isEmpty()) {
644 qCWarning(Qi3pcLogger) <<
"Requested to subscibe to empty event list. Nothing to do.";
650 QJsonDocument(QJsonArray::fromStringList(events)).toJson(),
659 QDataStream stream(&message, QIODevice::WriteOnly);
663 qint32 size = payload.size();
664 stream.writeRawData(
reinterpret_cast<const char*
>(&size),
sizeof size);
665 stream.writeRawData(
reinterpret_cast<const char*
>(&type),
sizeof type);
668 stream.writeRawData(payload.constData(), size);
671 socket.write(message);
726 payload.append(
id.toStdString().c_str());
739 if(!json.contains(
"parse_error")) {
744 if (json.contains(name)) {
745 ret.*member = json[name].toString();
762 auto str = QString(
"{");
764 str += QString(name) +
":'" + this->*member +
"',";
ShutdownChange
Types of change a shutdown event can have.
void configUpdated(const qi3pc::DataObject &config)
Signal emitted when the (cached) config have been updated.
void treeUpdated(const qi3pc::DataObject &tree)
Signal emitted when the layout tree cache have been updated.
WindowChange
Types of change a window event can have.
void processOutputEvent(const QJsonDocument &doc)
Handle data received from an output event.
void barConfigUpdated(const QJsonObject &config)
Signal emitted when a specific bar's (cached) config have been updated. At this point the configurati...
void fetchConfig()
Signal to emit to trigger an update of the (cached) config.
void processBindingEvent(const QJsonDocument &doc)
Handle data received from a binding event.
void sendMessage(const QByteArray &payload=QByteArray())
Send a message with the specified type and payload to i3.
void fetchBarConfigs()
Signal to emit to update the list of bar configurations.
void fetchBindingModes()
Signal to emit to trigger an update of the (cached) list of modes.
std::vector< std::pair< bool, Error > > CommandResults
Pairs of qi3pc::Error and boolean.
std::optional< std::pair< QJsonDocument, quint32 > > Message
Optional pair of a JSON document with a qi3pc::IpcType received with a message or an event before it ...
void barUpdateEvent(const QJsonObject &doc)
Signal emitted when a bar's configuration have been updated.
QLocalSocket m_messageSocket
void workspacesUpdated(const qi3pc::DataArray &workspaces)
Signal emitted when the (cached) list of workspaces have been updated.
void synced(bool success)
Signal emitted when a sync message have been replied to by i3.
std::optional< ParseError > Error
Optional qi3pc::ParseError. The optional is empty when the error could not be parsed.
void tickSent(bool success)
Signal emitted when a tick have been processed by i3.
void processBindingModesReply(const QJsonDocument &doc)
Handle data received in a binding mode reply.
QString socketPath() const
Get the socket path selected at construction.
const DataArray & marks() const
Get the (cached) list of set marks.
void subscribed(bool success)
Signal emitted when a subscribe message have been replied to.
void processSyncReply(const QJsonDocument &doc)
Handle data received from a sync reply.
DataString m_bindingState
void fetchBarConfig(const QString &id)
Signal to emit to update the (cached) configuration of a certain bar.
void processBarUpdateEvent(const QJsonDocument &doc)
Handle data received from a bar update event.
void bindingStateUpdated(const qi3pc::DataString &state)
Signal emitted when the (cached) current binding state have been updated.
void outputEvent(qi3pc::OutputChange change)
Signal emitted when the output(s) change.
void processBarConfigReply(const QJsonDocument &doc)
Handle data received from a bar config reply.
void fetchTree()
Signal to emit to trigger an update of the (cached) layout tree.
void processReply()
Read data from the message socket.
void fetchOutputs()
Signal to emit to trigger an update of the (cached) outputs.
void versionUpdated(const qi3pc::DataObject &version)
Signal emitted when the (cached) i3 version have been updated.
void outputsUpdated(const qi3pc::DataArray &outputs)
Signal emitted when (cached) outputs have been updated.
bool connect()
Start listening to messages and events from the window manager.
void modeEvent(QString change, bool pango)
Signal emitted when the binding mode changes.
static ShutdownChange ShutdownChangeFromString(const QString &s)
Convert a string into a shutdown change object.
void processMarkReply(const QJsonDocument &doc)
Handle data received from a mark reply.
static constexpr auto IpcMagicLength
void fetchBindingState()
Request update of the (cached) binding state.
void windowEvent(qi3pc::WindowChange change, const QJsonObject &container)
Signal emitted when a window changes.
static WorkspaceChange WorkspaceChangeFromString(const QString &s)
Convert a string into a workspace change object.
QLocalSocket m_eventSocket
static BindingChange BindingChangeFromString(const QString &s)
Convert a string into a binding change object.
void processWindowEvent(const QJsonDocument &doc)
Handle data received from a window event.
void subscribe(const QStringList &events)
Subscribe to a list of events.
void fetchMarks()
Signal to emit to trigger an update of the (cached) list of marks.
void processVersionReply(const QJsonDocument &doc)
Handle data received from a version reply.
static WindowChange WindowChangeFromString(const QString &s)
Convert a string into a window change object.
const DataArray & workspaces() const
Get the list of (cached) workspaces.
void newBarConfig(const QString &id)
Signal emitted when a new bar config have been added to the cache.
void processWorkspaceReply(const QJsonDocument &doc)
Handle data received from a workspace reply.
void fetchVersion()
Signal to emit to trigger a cache update for the i3wm version.
bool disconnect()
Stop listening to messages and events from the window manager.
static QString FindSocketPath()
Find the path to the i3 ipc local unix socket.
void processConfigReply(const QJsonDocument &doc)
Handle data received from a config reply.
const DataString & bindingState() const
Get the (cached) binding state.
static void WritePayload(QLocalSocket &socket, const QByteArray &payload, IpcType type)
Send a message with the specified type and payload to i3 using the specified socket.
void processTickReply(const QJsonDocument &doc)
Handle data received from a tick reply.
void processTickEvent(const QJsonDocument &doc)
Handle data received from a tick event.
void processBindingStateReply(const QJsonDocument &doc)
Handle data received from a binding state event reply.
void sendTick(const QByteArray &payload=QByteArray())
Send a tick message with the spoecified payload.
IpcType
Types of message/replies the API send/expect to/from i3wm.
const DataArray & bindingModes() const
Get the (cached) list of binding modes.
static constexpr auto IpcMagicString
void processTreeReply(const QJsonDocument &doc)
Handle data received from a tree reply.
void processShutdownEvent(const QJsonDocument &doc)
Handle data received from a shutdowm event.
virtual ~qi3pc()
Simple destructor for the qi3pc class.
void tickEvent(const QString &payload)
Signal emitted when subscribing to tick events or when a tick message have been sent to the ipc conne...
BindingChange
Types of change a binding event can have.
void processModeEvent(const QJsonDocument &doc)
Handle data received from a mode event.
Message processMessage(QLocalSocket &socket)
Read one message using the socket parameter.
void marksUpdated(const qi3pc::DataArray &marks)
Signal emitted when the (cached) list of marks have been updated.
void commandRan(CommandResults result)
Signal emitted when a command have been ran by i3.
void processEvent()
Read data from the event socket.
OutputChange
Types of change an output event can have.
std::optional< std::pair< QJsonObject, qint64 > > DataObject
Optional pair of a JSON object with its last update time.
WorkspaceChange
Types of change a workspace event can have.
void processCommandReply(const QJsonDocument &doc)
Handle data received from a run command reply.
qi3pc(QObject *parent=nullptr)
Construct a qi3pc object.
void bindingEvent(qi3pc::BindingChange change, const QJsonObject &binding, const QString &mode)
Signal emitteed when a binding have been triggered to run a command.
const DataObject & tree() const
Get the (cached) i3 layout tree.
void fetchWorkspaces()
Signal to emit to trigger an update of the list of workspace cache.
void processOutputReply(const QJsonDocument &doc)
Handle data received from an output reply.
void shutdownEvent(qi3pc::ShutdownChange change)
Signal emitted when the ipc socket is about to shutdown.
const DataObject & config() const
Get the (cached) data read from the config file.
void workspaceEvent(qi3pc::WorkspaceChange change, const QJsonObject ¤t, const QJsonObject &old)
Signal emitted with a workspace event's data preprocessed.
std::optional< std::pair< QString, qint64 > > DataString
Optional pair of a string with its last update time.
static OutputChange OutputChangeFromString(const QString &s)
Convert a string into an output change object.
static QString FindSocketPathFromI3Binary()
Find the path to the i3 ipc local unix socket using the i3 binary.
void bindingModesUpdated(const qi3pc::DataArray &modes)
Signal emitted when the (cached) list of modes have been updated.
std::optional< std::pair< QJsonArray, qint64 > > DataArray
Optional pair of a JSON array with its last update time.
const DataObject & version() const
Get the (cached) i3 version object.
bool isConnected()
Check if the connection to the ipc socket is established.
const DataObject & barConfigs() const
Get the (cached) list of all bar configurations.
const DataArray & outputs() const
Get the (cached) list of outputs.
void processWorkspaceEvent(const QJsonDocument &doc)
Handle data received from a workspace event.
IpcEvent
Types of events offered by i3wm's IPC API.
The Err struct contains the attributes of a parsing error from i3wm when trying to run an unparsable ...
static constexpr auto _MEMBERS
Mapping of i3wm's parse error reply's attributes to members of ParseError.
QString errorPosition
The position where the error was detected in the input.
QString input
The command that failed to run.
QString toString() const
Convert to a json like string.
QString error
Human readble error message.
bool operator==(const ParseError &other) const
Memberwise comparison of this ParseError and another.
static std::optional< ParseError > FromJSON(const QJsonObject &json)
Build an optional ParseError from a QJsonObject.