diff --git a/src/x11/i3/ipc/CMakeLists.txt b/src/x11/i3/ipc/CMakeLists.txt index c228ae33..64388ea1 100644 --- a/src/x11/i3/ipc/CMakeLists.txt +++ b/src/x11/i3/ipc/CMakeLists.txt @@ -5,6 +5,8 @@ qt_add_library(quickshell-i3-ipc STATIC monitor.cpp controller.cpp listener.cpp + scroller.cpp + window.cpp ) qt_add_qml_module(quickshell-i3-ipc diff --git a/src/x11/i3/ipc/connection.cpp b/src/x11/i3/ipc/connection.cpp index b765ebc9..bcfd66ea 100644 --- a/src/x11/i3/ipc/connection.cpp +++ b/src/x11/i3/ipc/connection.cpp @@ -35,8 +35,8 @@ 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)) + if ((EventCode::Workspace <= raw && raw <= EventCode::Trails) + || (EventCode::RunCommand <= raw && raw <= EventCode::GetSpaces)) { return static_cast(raw); } else { @@ -51,6 +51,14 @@ QString I3IpcEvent::eventToString(EventCode event) { case EventCode::Subscribe: return "subscribe"; break; case EventCode::GetOutputs: return "get_outputs"; break; case EventCode::GetTree: return "get_tree"; break; + case EventCode::GetMarks: return "get_marks"; break; + case EventCode::GetVersion: return "get_version"; break; + case EventCode::GetBindingModes: return "get_binding_modes"; break; + case EventCode::GetBindingState: return "get_binding_state"; break; + case EventCode::GetInputs: return "get_inputs"; break; + case EventCode::GetScroller: return "get_scroller"; break; + case EventCode::GetTrails: return "get_trails"; break; + case EventCode::GetSpaces: return "get_spaces"; break; case EventCode::Output: return "output"; break; case EventCode::Workspace: return "workspace"; break; @@ -62,23 +70,39 @@ QString I3IpcEvent::eventToString(EventCode event) { case EventCode::Tick: return "tick"; break; case EventCode::BarStateUpdate: return "bar_state_update"; break; case EventCode::Input: return "input"; break; + case EventCode::Scroller: return "scroller"; break; + case EventCode::Trails: return "trails"; break; default: return "unknown"; break; } } I3Ipc::I3Ipc(const QList& events): mEvents(events) { - auto sock = qEnvironmentVariable("I3SOCK"); + auto sock = qEnvironmentVariable("SCROLLSOCK"); if (sock.isEmpty()) { - qCWarning(logI3Ipc) << "$I3SOCK is unset. Trying $SWAYSOCK."; + qCWarning(logI3Ipc) << "$SCROLLSOCK is unset. Trying $SWAYSOCK."; sock = qEnvironmentVariable("SWAYSOCK"); if (sock.isEmpty()) { - qCWarning(logI3Ipc) << "$SWAYSOCK and I3SOCK are unset. Cannot connect to socket."; - return; + qCWarning(logI3Ipc) << "$SCROLLSOCK and $SWAYSOCK are unset. Trying $I3SOCK."; + + sock = qEnvironmentVariable("I3SOCK"); + + if (sock.isEmpty()) { + qCWarning(logI3Ipc) << "$SCROLLSOCK, $SWAYSOCK and $I3SOCK are unset. Cannot connect to socket."; + return; + } else { + this->mCompositor = "i3"; + } + } else { + this->mCompositor = "sway"; } + } else { + this->mCompositor = "scroll"; + this->mEvents.append("scroller"); + this->mEvents.append("trails"); } this->mSocketPath = sock; @@ -215,4 +239,6 @@ void I3Ipc::eventSocketStateChanged(QLocalSocket::LocalSocketState state) { QString I3Ipc::socketPath() const { return this->mSocketPath; } +QString I3Ipc::compositor() const { return this->mCompositor; } + } // namespace qs::i3::ipc diff --git a/src/x11/i3/ipc/connection.hpp b/src/x11/i3/ipc/connection.hpp index 6100f7ed..d86c6328 100644 --- a/src/x11/i3/ipc/connection.hpp +++ b/src/x11/i3/ipc/connection.hpp @@ -19,6 +19,14 @@ enum EventCode { Subscribe = 2, GetOutputs = 3, GetTree = 4, + GetMarks = 5, + GetVersion = 7, + GetBindingModes = 8, + GetBindingState = 12, + GetInputs = 100, + GetScroller = 120, + GetTrails = 121, + GetSpaces = 122, Workspace = 0x80000000, Output = 0x80000001, @@ -30,6 +38,8 @@ enum EventCode { Tick = 0x80000007, BarStateUpdate = 0x80000014, Input = 0x80000015, + Scroller = 0x8000001e, + Trails = 0x8000001f, Unknown = 999, }; @@ -67,6 +77,7 @@ class I3Ipc: public QObject { explicit I3Ipc(const QList& events); [[nodiscard]] QString socketPath() const; + [[nodiscard]] QString compositor() const; void makeRequest(const QByteArray& request); void dispatch(const QString& payload); @@ -101,6 +112,7 @@ protected slots: private: QList mEvents; + QString mCompositor; }; } // namespace qs::i3::ipc diff --git a/src/x11/i3/ipc/controller.cpp b/src/x11/i3/ipc/controller.cpp index 1a08c635..492ee1f5 100644 --- a/src/x11/i3/ipc/controller.cpp +++ b/src/x11/i3/ipc/controller.cpp @@ -23,6 +23,8 @@ #include "connection.hpp" #include "monitor.hpp" #include "workspace.hpp" +#include "scroller.hpp" +#include "window.hpp" namespace qs::i3::ipc { @@ -31,7 +33,7 @@ QS_LOGGING_CATEGORY(logI3Ipc, "quickshell.I3.ipc", QtWarningMsg); QS_LOGGING_CATEGORY(logI3IpcEvents, "quickshell.I3.ipc.events", QtWarningMsg); } // namespace -I3IpcController::I3IpcController(): I3Ipc({"workspace", "output"}) { +I3IpcController::I3IpcController(): I3Ipc({"workspace", "output", "mode", "window"}) { // bind focused workspace to focused monitor's active workspace this->bFocusedWorkspace.setBinding([this]() -> I3Workspace* { if (!this->bFocusedMonitor) return nullptr; @@ -49,6 +51,13 @@ void I3IpcController::onConnected() { // detected on launch. this->refreshWorkspaces(); this->refreshMonitors(); + this->refreshBindingModes(); + this->bFocusedWindow = new I3Window(this); + if (this->compositor() == "scroll") { + this->bFocusedScroller = new I3Scroller(this); + this->refreshScroller(); + this->refreshTrails(); + } } void I3IpcController::setFocusedMonitor(I3Monitor* monitor) { @@ -198,6 +207,70 @@ void I3IpcController::handleGetOutputsEvent(I3IpcEvent* event) { } } +void I3IpcController::refreshBindingModes() { + this->makeRequest(I3Ipc::buildRequestMessage(EventCode::GetBindingState)); + this->makeRequest(I3Ipc::buildRequestMessage(EventCode::GetBindingModes)); +} + +void I3IpcController::handleGetBindingModesEvent(I3IpcEvent* event) { + auto data = event->mData; + auto modes = data.array(); + for (auto mode: modes) { + auto name = mode.toString(); + this->mBindingModes.push_back(name); + } +} + +void I3IpcController::handleGetBindingStateEvent(I3IpcEvent* event) { + auto data = event->mData; + this->bActiveBindingMode = data["name"].toString(); +} + +void I3IpcController::handleModeEvent(I3IpcEvent* event) { + auto data = event->mData; + this->bActiveBindingMode = data["change"].toString(); +} + +void I3IpcController::refreshScroller() { + if (this->compositor() == "scroll") { + this->makeRequest(I3Ipc::buildRequestMessage(EventCode::GetScroller)); + } +} + +void I3IpcController::handleGetScrollerEvent(I3IpcEvent* event) { + if (this->compositor() == "scroll") { + auto scroller = event->mData["scroller"].toObject(); + this->bFocusedScroller->updateFromObject(scroller.toVariantMap()); + } +} + +void I3IpcController::handleScrollerEvent(I3IpcEvent* event) { + this->handleGetScrollerEvent(event); +} + +void I3IpcController::handleWindowEvent(I3IpcEvent* event) { + this->bFocusedWindow->updateFromObject(event->mData.object().toVariantMap()); +} + +void I3IpcController::refreshTrails() { + if (this->compositor() == "scroll") { + this->makeRequest(I3Ipc::buildRequestMessage(EventCode::GetTrails)); + } +} + +void I3IpcController::handleGetTrailsEvent(I3IpcEvent* event) { + if (this->compositor() == "scroll") { + auto trails = event->mData["trails"]; + this->bNumberOfTrails = trails["length"].toInt(); + this->bActiveTrail = trails["active"].toInt(); + this->bActiveTrailLength = trails["trail_length"].toInt(); + } +} + +void I3IpcController::handleTrailsEvent(I3IpcEvent* event) { + this->handleGetTrailsEvent(event); +} + void I3IpcController::onEvent(I3IpcEvent* event) { switch (event->mCode) { case EventCode::Workspace: this->handleWorkspaceEvent(event); return; @@ -209,7 +282,15 @@ void I3IpcController::onEvent(I3IpcEvent* event) { 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::GetBindingModes: this->handleGetBindingModesEvent(event); return; + case EventCode::GetBindingState: this->handleGetBindingStateEvent(event); return; + case EventCode::GetScroller: this->handleGetScrollerEvent(event); return; + case EventCode::GetTrails: this->handleGetTrailsEvent(event); return; case EventCode::RunCommand: I3IpcController::handleRunCommand(event); return; + case EventCode::Mode: this->handleModeEvent(event); return; + case EventCode::Window: this->handleWindowEvent(event); return; + case EventCode::Scroller: this->handleScrollerEvent(event); return; + case EventCode::Trails: this->handleTrailsEvent(event); return; case EventCode::Unknown: qCWarning(logI3Ipc) << "Unknown event:" << event->type() << event->data(); return; @@ -359,6 +440,7 @@ I3Monitor* I3IpcController::findMonitorByName(const QString& name, bool createIf ObjectModel* I3IpcController::monitors() { return &this->mMonitors; } ObjectModel* I3IpcController::workspaces() { return &this->mWorkspaces; } +QVector* I3IpcController::bindingModes() { return &this->mBindingModes; } bool I3IpcController::compareWorkspaces(I3Workspace* a, I3Workspace* b) { return a->bindableNumber().value() > b->bindableNumber().value(); diff --git a/src/x11/i3/ipc/controller.hpp b/src/x11/i3/ipc/controller.hpp index 464f6f65..6ac77820 100644 --- a/src/x11/i3/ipc/controller.hpp +++ b/src/x11/i3/ipc/controller.hpp @@ -18,10 +18,14 @@ namespace qs::i3::ipc { class I3Workspace; class I3Monitor; +class I3Scroller; +class I3Window; } // namespace qs::i3::ipc Q_DECLARE_OPAQUE_POINTER(qs::i3::ipc::I3Workspace*); Q_DECLARE_OPAQUE_POINTER(qs::i3::ipc::I3Monitor*); +Q_DECLARE_OPAQUE_POINTER(qs::i3::ipc::I3Scroller*); +Q_DECLARE_OPAQUE_POINTER(qs::i3::ipc::I3Window*); namespace qs::i3::ipc { @@ -40,6 +44,9 @@ class I3IpcController: public I3Ipc { void refreshWorkspaces(); void refreshMonitors(); + void refreshBindingModes(); + void refreshScroller(); + void refreshTrails(); I3Monitor* monitorFor(QuickshellScreenInfo* screen); @@ -51,12 +58,43 @@ class I3IpcController: public I3Ipc { return &this->bFocusedWorkspace; }; + [[nodiscard]] QBindable bindableActiveBindingMode() const { + return &this->bActiveBindingMode; + }; + + [[nodiscard]] QBindable bindableFocusedScroller() const { + return &this->bFocusedScroller; + }; + + [[nodiscard]] QBindable bindableFocusedWindow() const { + return &this->bFocusedWindow; + }; + + [[nodiscard]] QBindable bindableNumberOfTrails() const { + return &this->bNumberOfTrails; + }; + + [[nodiscard]] QBindable bindableActiveTrail() const { + return &this->bActiveTrail; + }; + + [[nodiscard]] QBindable bindableActiveTrailLength() const { + return &this->bActiveTrailLength; + }; + [[nodiscard]] ObjectModel* monitors(); [[nodiscard]] ObjectModel* workspaces(); + [[nodiscard]] QVector* bindingModes(); signals: void focusedWorkspaceChanged(); void focusedMonitorChanged(); + void activeBindingModeChanged(); + void focusedScrollerChanged(); + void focusedWindowChanged(); + void numberOfTrailsChanged(); + void activeTrailChanged(); + void activeTrailLengthChanged(); private slots: void onFocusedMonitorDestroyed(); @@ -70,12 +108,22 @@ private slots: void handleWorkspaceEvent(I3IpcEvent* event); void handleGetWorkspacesEvent(I3IpcEvent* event); void handleGetOutputsEvent(I3IpcEvent* event); + void handleGetBindingModesEvent(I3IpcEvent* event); + void handleGetBindingStateEvent(I3IpcEvent* event); + void handleModeEvent(I3IpcEvent* event); + void handleGetScrollerEvent(I3IpcEvent* event); + void handleScrollerEvent(I3IpcEvent* event); + void handleGetTrailsEvent(I3IpcEvent* event); + void handleTrailsEvent(I3IpcEvent* event); + void handleWindowEvent(I3IpcEvent* event); static void handleRunCommand(I3IpcEvent* event); static bool compareWorkspaces(I3Workspace* a, I3Workspace* b); ObjectModel mMonitors {this}; ObjectModel mWorkspaces {this}; + QVector mBindingModes; + Q_OBJECT_BINDABLE_PROPERTY( I3IpcController, I3Monitor*, @@ -89,6 +137,48 @@ private slots: bFocusedWorkspace, &I3IpcController::focusedWorkspaceChanged ); + + Q_OBJECT_BINDABLE_PROPERTY( + I3IpcController, + QString, + bActiveBindingMode, + &I3IpcController::activeBindingModeChanged + ); + + Q_OBJECT_BINDABLE_PROPERTY( + I3IpcController, + I3Scroller*, + bFocusedScroller, + &I3IpcController::focusedScrollerChanged + ); + + Q_OBJECT_BINDABLE_PROPERTY( + I3IpcController, + I3Window*, + bFocusedWindow, + &I3IpcController::focusedWindowChanged + ); + + Q_OBJECT_BINDABLE_PROPERTY( + I3IpcController, + qint32, + bNumberOfTrails, + &I3IpcController::numberOfTrailsChanged + ); + + Q_OBJECT_BINDABLE_PROPERTY( + I3IpcController, + qint32, + bActiveTrail, + &I3IpcController::activeTrailChanged + ); + + Q_OBJECT_BINDABLE_PROPERTY( + I3IpcController, + qint32, + bActiveTrailLength, + &I3IpcController::activeTrailLengthChanged + ); }; } // namespace qs::i3::ipc diff --git a/src/x11/i3/ipc/qml.cpp b/src/x11/i3/ipc/qml.cpp index d835cbd5..e21bcb90 100644 --- a/src/x11/i3/ipc/qml.cpp +++ b/src/x11/i3/ipc/qml.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include "../../../core/model.hpp" #include "../../../core/qmlscreen.hpp" @@ -20,17 +22,30 @@ I3IpcQml::I3IpcQml() { QObject::connect(instance, &I3Ipc::connected, this, &I3IpcQml::connected); QObject::connect(instance, &I3IpcController::focusedWorkspaceChanged, this, &I3IpcQml::focusedWorkspaceChanged); QObject::connect(instance, &I3IpcController::focusedMonitorChanged, this, &I3IpcQml::focusedMonitorChanged); + QObject::connect(instance, &I3IpcController::activeBindingModeChanged, this, &I3IpcQml::activeBindingModeChanged); + QObject::connect(instance, &I3IpcController::focusedScrollerChanged, this, &I3IpcQml::focusedScrollerChanged); + QObject::connect(instance, &I3IpcController::focusedWindowChanged, this, &I3IpcQml::focusedWindowChanged); + QObject::connect(instance, &I3IpcController::numberOfTrailsChanged, this, &I3IpcQml::numberOfTrailsChanged); + QObject::connect(instance, &I3IpcController::activeTrailChanged, this, &I3IpcQml::activeTrailChanged); + QObject::connect(instance, &I3IpcController::activeTrailLengthChanged, this, &I3IpcQml::activeTrailLengthChanged); // clang-format on } void I3IpcQml::dispatch(const QString& request) { I3IpcController::instance()->dispatch(request); } void I3IpcQml::refreshMonitors() { I3IpcController::instance()->refreshMonitors(); } void I3IpcQml::refreshWorkspaces() { I3IpcController::instance()->refreshWorkspaces(); } +void I3IpcQml::refreshBindingModes() { I3IpcController::instance()->refreshBindingModes(); } +void I3IpcQml::refreshScroller() { I3IpcController::instance()->refreshScroller(); } +void I3IpcQml::refreshTrails() { I3IpcController::instance()->refreshTrails(); } QString I3IpcQml::socketPath() { return I3IpcController::instance()->socketPath(); } +QString I3IpcQml::compositor() { return I3IpcController::instance()->compositor(); } ObjectModel* I3IpcQml::monitors() { return I3IpcController::instance()->monitors(); } ObjectModel* I3IpcQml::workspaces() { return I3IpcController::instance()->workspaces(); } +QVector* I3IpcQml::bindingModes() { + return I3IpcController::instance()->bindingModes(); +} QBindable I3IpcQml::bindableFocusedWorkspace() { return I3IpcController::instance()->bindableFocusedWorkspace(); @@ -40,6 +55,30 @@ QBindable I3IpcQml::bindableFocusedMonitor() { return I3IpcController::instance()->bindableFocusedMonitor(); } +QBindable I3IpcQml::bindableActiveBindingMode() { + return I3IpcController::instance()->bindableActiveBindingMode(); +} + +QBindable I3IpcQml::bindableFocusedScroller() { + return I3IpcController::instance()->bindableFocusedScroller(); +} + +QBindable I3IpcQml::bindableFocusedWindow() { + return I3IpcController::instance()->bindableFocusedWindow(); +} + +QBindable I3IpcQml::bindableNumberOfTrails() { + return I3IpcController::instance()->bindableNumberOfTrails(); +} + +QBindable I3IpcQml::bindableActiveTrail() { + return I3IpcController::instance()->bindableActiveTrail(); +} + +QBindable I3IpcQml::bindableActiveTrailLength() { + return I3IpcController::instance()->bindableActiveTrailLength(); +} + I3Workspace* I3IpcQml::findWorkspaceByName(const QString& name) { return I3IpcController::instance()->findWorkspaceByName(name); } diff --git a/src/x11/i3/ipc/qml.hpp b/src/x11/i3/ipc/qml.hpp index 2e7c81c0..972c69c5 100644 --- a/src/x11/i3/ipc/qml.hpp +++ b/src/x11/i3/ipc/qml.hpp @@ -20,12 +20,20 @@ class I3IpcQml: public QObject { Q_PROPERTY(qs::i3::ipc::I3Workspace* focusedWorkspace READ default NOTIFY focusedWorkspaceChanged BINDABLE bindableFocusedWorkspace); Q_PROPERTY(qs::i3::ipc::I3Monitor* focusedMonitor READ default NOTIFY focusedMonitorChanged BINDABLE bindableFocusedMonitor); + Q_PROPERTY(QString activeBindingMode READ default NOTIFY activeBindingModeChanged BINDABLE bindableActiveBindingMode); + Q_PROPERTY(qs::i3::ipc::I3Scroller* focusedScroller READ default NOTIFY focusedScrollerChanged BINDABLE bindableFocusedScroller); + Q_PROPERTY(qs::i3::ipc::I3Window* focusedWindow READ default NOTIFY focusedWindowChanged BINDABLE bindableFocusedWindow); + Q_PROPERTY(qint32 numberOfTrails READ default NOTIFY numberOfTrailsChanged BINDABLE bindableNumberOfTrails); + Q_PROPERTY(qint32 activeTrail READ default NOTIFY activeTrailChanged BINDABLE bindableActiveTrail); + Q_PROPERTY(qint32 activeTrailLength READ default NOTIFY activeTrailLengthChanged BINDABLE bindableActiveTrailLength); /// All I3 monitors. QSDOC_TYPE_OVERRIDE(ObjectModel*); Q_PROPERTY(UntypedObjectModel* monitors READ monitors CONSTANT); /// All I3 workspaces. QSDOC_TYPE_OVERRIDE(ObjectModel*); Q_PROPERTY(UntypedObjectModel* workspaces READ workspaces CONSTANT); + /// All I3 binding modes. + Q_PROPERTY(QVector* bindingModes READ bindingModes CONSTANT); // clang-format on QML_NAMED_ELEMENT(I3); QML_SINGLETON; @@ -42,6 +50,15 @@ class I3IpcQml: public QObject { /// Refresh workspace information. Q_INVOKABLE static void refreshWorkspaces(); + /// Refresh binding modes information. + Q_INVOKABLE static void refreshBindingModes(); + + /// Refresh scroller information. + Q_INVOKABLE static void refreshScroller(); + + /// Refresh trails information. + Q_INVOKABLE static void refreshTrails(); + /// Find an I3Workspace using its name, returns null if the workspace doesn't exist. Q_INVOKABLE static I3Workspace* findWorkspaceByName(const QString& name); @@ -54,23 +71,53 @@ class I3IpcQml: public QObject { /// The path to the I3 or Sway socket currently being used [[nodiscard]] static QString socketPath(); + /// The compositor providing the IPC socket: i3, sway or scroll + [[nodiscard]] static QString compositor(); + /// All I3Monitors [[nodiscard]] static ObjectModel* monitors(); /// All I3Workspaces [[nodiscard]] static ObjectModel* workspaces(); + /// All I3 binding modes + [[nodiscard]] static QVector* bindingModes(); + /// The currently focused Workspace [[nodiscard]] static QBindable bindableFocusedWorkspace(); /// The currently focused Monitor [[nodiscard]] static QBindable bindableFocusedMonitor(); + /// The currently focused Monitor + [[nodiscard]] static QBindable bindableActiveBindingMode(); + + /// The currently focused Scroller + [[nodiscard]] static QBindable bindableFocusedScroller(); + + /// The currently focused Window + [[nodiscard]] static QBindable bindableFocusedWindow(); + + /// The current number of trails + [[nodiscard]] static QBindable bindableNumberOfTrails(); + + /// The current active trail index + [[nodiscard]] static QBindable bindableActiveTrail(); + + /// The length of the current active trail + [[nodiscard]] static QBindable bindableActiveTrailLength(); + signals: void rawEvent(I3IpcEvent* event); void connected(); void focusedWorkspaceChanged(); void focusedMonitorChanged(); + void activeBindingModeChanged(); + void focusedScrollerChanged(); + void focusedWindowChanged(); + void numberOfTrailsChanged(); + void activeTrailChanged(); + void activeTrailLengthChanged(); }; } // namespace qs::i3::ipc diff --git a/src/x11/i3/ipc/scroller.cpp b/src/x11/i3/ipc/scroller.cpp new file mode 100644 index 00000000..f24f5d86 --- /dev/null +++ b/src/x11/i3/ipc/scroller.cpp @@ -0,0 +1,64 @@ +#include "scroller.hpp" + +#include +#include +#include +#include +#include + +#include "controller.hpp" + +namespace qs::i3::ipc { + +I3Scroller::I3Scroller(I3IpcController* ipc): QObject(ipc), ipc(ipc) {} + +QVariantMap I3Scroller::lastIpcObject() const { return this->mLastIpcObject; } + +void I3Scroller::updateFromObject(const QVariantMap& obj) { + if (obj != this->mLastIpcObject) { + this->mLastIpcObject = obj; + emit this->lastIpcObjectChanged(); + } + + Qt::beginPropertyUpdateGroup(); + + auto workspace = obj.value("workspace").value(); + this->bWorkspace = this->ipc->findWorkspaceByName(workspace); + this->bOverview = obj.value("overview").value(); + this->bScaled = obj.value("scaled").value(); + this->bScale = obj.value("scale").value(); + this->bMode = obj.value("mode").value(); + this->bInsert = obj.value("insert").value(); + this->bFocus = obj.value("focus").value(); + this->bCenterHorizontal = obj.value("center_horizontal").value(); + this->bCenterVertical = obj.value("center_vertical").value(); + this->bReorder = obj.value("reorder").value(); + + Qt::endPropertyUpdateGroup(); +} + +void I3Scroller::setMode(const QString &mode) { + this->ipc->dispatch(QString("set_mode %1").arg(mode == "horizontal" ? "h" : "v")); +} + +void I3Scroller::setInsert(const QString &insert) { + this->ipc->dispatch(QString("set_mode %1").arg(insert)); +} + +void I3Scroller::setFocus(bool focus) { + this->ipc->dispatch(QString("set_mode %1").arg(focus ? "focus" : "nofocus")); +} + +void I3Scroller::setCenterHorizontal(bool center) { + this->ipc->dispatch(QString("set_mode %1").arg(center ? "center_horiz" : "nocenter_horiz")); +} + +void I3Scroller::setCenterVertical(bool center) { + this->ipc->dispatch(QString("set_mode %1").arg(center ? "center_vert" : "nocenter_vert")); +} + +void I3Scroller::setReorder(const QString &reorder) { + this->ipc->dispatch(QString("set_mode %1").arg(reorder == "auto" ? "reorder_auto" : "noreorder_auto")); +} + +} // namespace qs::i3::ipc diff --git a/src/x11/i3/ipc/scroller.hpp b/src/x11/i3/ipc/scroller.hpp new file mode 100644 index 00000000..9ebbbbf1 --- /dev/null +++ b/src/x11/i3/ipc/scroller.hpp @@ -0,0 +1,101 @@ +#pragma once + +#include +#include +#include + +#include "connection.hpp" +#include "controller.hpp" + +namespace qs::i3::ipc { + +class I3Workspace; + +///! Scroll scroller +class I3Scroller: public QObject { + Q_OBJECT; + // clang-format off + /// The workspace for this scroller + Q_PROPERTY(qs::i3::ipc::I3Workspace* workspace READ default NOTIFY workspaceChanged BINDABLE bindableWorkspace); + /// Overview state + Q_PROPERTY(bool overview READ default NOTIFY overviewChanged BINDABLE bindableOverview); + /// Workspace scaled state + Q_PROPERTY(bool scaled READ default NOTIFY scaledChanged BINDABLE bindableScaled); + /// Scale for the workspace, -1.0 if not scaled + Q_PROPERTY(double scale READ default NOTIFY scaleChanged BINDABLE bindableScale); + /// Current mode: "horizontal" or "vertical" + Q_PROPERTY(QString mode READ default WRITE setMode NOTIFY modeChanged BINDABLE bindableMode); + /// Current insert mode: "after", "before", "beginning" or "end" + Q_PROPERTY(QString insert READ default WRITE setInsert NOTIFY insertChanged BINDABLE bindableInsert); + /// Focus new windows? + Q_PROPERTY(bool focus READ default WRITE setFocus NOTIFY focusChanged BINDABLE bindableFocus); + /// Center Horizontal? + Q_PROPERTY(bool centerHorizontal READ default WRITE setCenterHorizontal NOTIFY centerHorizontalChanged BINDABLE bindableCenterHorizontal); + /// Center Vertical? + Q_PROPERTY(bool centerVertical READ default WRITE setCenterVertical NOTIFY centerVerticalChanged BINDABLE bindableCenterVertical); + /// Reorder mode: "auto" or "lazy" + Q_PROPERTY(QString reorder READ default WRITE setReorder NOTIFY reorderChanged BINDABLE bindableReorder); + /// Last JSON returned for this scroller, as a JavaScript object. + /// + /// This updates every time we receive a `scroller` event from i3/Sway/Scroll + Q_PROPERTY(QVariantMap lastIpcObject READ lastIpcObject NOTIFY lastIpcObjectChanged); + // clang-format on + QML_ELEMENT; + QML_UNCREATABLE("I3Scroller must be retrieved from the I3 object."); + +public: + I3Scroller(I3IpcController* ipc); + + [[nodiscard]] QBindable bindableWorkspace() { return &this->bWorkspace; } + [[nodiscard]] QBindable bindableOverview() { return &this->bOverview; } + [[nodiscard]] QBindable bindableScaled() { return &this->bScaled; } + [[nodiscard]] QBindable bindableScale() { return &this->bScale; } + [[nodiscard]] QBindable bindableMode() { return &this->bMode; } + [[nodiscard]] QBindable bindableInsert() { return &this->bInsert; } + [[nodiscard]] QBindable bindableFocus() { return &this->bFocus; } + [[nodiscard]] QBindable bindableCenterHorizontal() { return &this->bCenterHorizontal; } + [[nodiscard]] QBindable bindableCenterVertical() { return &this->bCenterVertical; } + [[nodiscard]] QBindable bindableReorder() { return &this->bReorder; } + [[nodiscard]] QVariantMap lastIpcObject() const; + + void updateFromObject(const QVariantMap& obj); + + void setMode(const QString &mode); + void setInsert(const QString &insert); + void setFocus(bool focus); + void setCenterHorizontal(bool center); + void setCenterVertical(bool center); + void setReorder(const QString &reorder); + +signals: + void workspaceChanged(); + void overviewChanged(); + void scaledChanged(); + void scaleChanged(); + void modeChanged(); + void insertChanged(); + void focusChanged(); + void centerHorizontalChanged(); + void centerVerticalChanged(); + void reorderChanged(); + void lastIpcObjectChanged(); + +private: + I3IpcController* ipc; + + QVariantMap mLastIpcObject; + + // clang-format off + Q_OBJECT_BINDABLE_PROPERTY(I3Scroller, I3Workspace*, bWorkspace, &I3Scroller::workspaceChanged); + Q_OBJECT_BINDABLE_PROPERTY(I3Scroller, bool, bOverview, &I3Scroller::overviewChanged); + Q_OBJECT_BINDABLE_PROPERTY(I3Scroller, bool, bScaled, &I3Scroller::scaledChanged); + Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(I3Scroller, double, bScale, -1.0, &I3Scroller::scaleChanged); + Q_OBJECT_BINDABLE_PROPERTY(I3Scroller, QString, bMode, &I3Scroller::modeChanged); + Q_OBJECT_BINDABLE_PROPERTY(I3Scroller, QString, bInsert, &I3Scroller::insertChanged); + Q_OBJECT_BINDABLE_PROPERTY(I3Scroller, bool, bFocus, &I3Scroller::focusChanged); + Q_OBJECT_BINDABLE_PROPERTY(I3Scroller, bool, bCenterHorizontal, &I3Scroller::centerHorizontalChanged); + Q_OBJECT_BINDABLE_PROPERTY(I3Scroller, bool, bCenterVertical, &I3Scroller::centerVerticalChanged); + Q_OBJECT_BINDABLE_PROPERTY(I3Scroller, QString, bReorder, &I3Scroller::reorderChanged); + // clang-format on +}; +} // namespace qs::i3::ipc diff --git a/src/x11/i3/ipc/window.cpp b/src/x11/i3/ipc/window.cpp new file mode 100644 index 00000000..a92614f7 --- /dev/null +++ b/src/x11/i3/ipc/window.cpp @@ -0,0 +1,59 @@ +#include "window.hpp" + +#include +#include +#include +#include +#include +#include + +#include "controller.hpp" + +namespace qs::i3::ipc { + +I3Window::I3Window(I3IpcController* ipc): QObject(ipc), ipc(ipc) {} + +QVariantMap I3Window::lastIpcObject() const { return this->mLastIpcObject; } + +void I3Window::updateFromObject(const QVariantMap& obj) { + if (obj != this->mLastIpcObject) { + this->mLastIpcObject = obj; + emit this->lastIpcObjectChanged(); + } + + auto container = obj.value("container").toJsonObject(); + auto change = obj.value("change").value(); + + Qt::beginPropertyUpdateGroup(); + + if (change == "focus") { + this->bId = container["id"].toInt(); + this->bTitle = container["name"].toString(); + auto marks = container["marks"]; + this->bMark = !(marks.isNull() || marks.toArray().empty()); + if (this->ipc->compositor() == "scroll") { + this->bTrailmark = container["trailmark"].toBool(); + } + } else if (change == "title") { + if (container["id"].toInt() == this->bId) { + this->bTitle = container["name"].toString(); + } + } else if (change == "close") { + if (container["id"].toInt() == this->bId) { + this->bTitle = QString(); + } + } else if (change == "mark") { + if (container["id"].toInt() == this->bId) { + auto marks = container["marks"]; + this->bMark = !(marks.isNull() || marks.toArray().empty()); + } + } else if (this->ipc->compositor() == "scroll" && change == "trailmark") { + if (container["id"].toInt() == this->bId) { + this->bTrailmark = container["trailmark"].toBool(); + } + } + + Qt::endPropertyUpdateGroup(); +} + +} // namespace qs::i3::ipc diff --git a/src/x11/i3/ipc/window.hpp b/src/x11/i3/ipc/window.hpp new file mode 100644 index 00000000..42c6a6a1 --- /dev/null +++ b/src/x11/i3/ipc/window.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include +#include +#include + +#include "controller.hpp" + +namespace qs::i3::ipc { + +///! I3/Sway focused window +class I3Window: public QObject { + Q_OBJECT; + // clang-format off + /// Id for the window + Q_PROPERTY(qint32 id READ default NOTIFY idChanged BINDABLE bindableId); + /// Title of the window + Q_PROPERTY(QString title READ default NOTIFY titleChanged BINDABLE bindableTitle); + /// Window has mark? + Q_PROPERTY(bool mark READ default NOTIFY markChanged BINDABLE bindableMark); + /// Window has trailmark? + Q_PROPERTY(bool trailmark READ default NOTIFY trailmarkChanged BINDABLE bindableTrailmark); + /// Last JSON returned for this scroller, as a JavaScript object. + /// + /// This updates every time we receive a `scroller` event from i3/Sway/Scroll + Q_PROPERTY(QVariantMap lastIpcObject READ lastIpcObject NOTIFY lastIpcObjectChanged); + // clang-format on + QML_ELEMENT; + QML_UNCREATABLE("I3Window must be retrieved from the I3 object."); + +public: + I3Window(I3IpcController* ipc); + + [[nodiscard]] QBindable bindableId() { return &this->bId; } + [[nodiscard]] QBindable bindableTitle() { return &this->bTitle; } + [[nodiscard]] QBindable bindableMark() { return &this->bMark; } + [[nodiscard]] QBindable bindableTrailmark() { return &this->bTrailmark; } + [[nodiscard]] QVariantMap lastIpcObject() const; + + void updateFromObject(const QVariantMap& obj); + +signals: + void idChanged(); + void titleChanged(); + void markChanged(); + void trailmarkChanged(); + void lastIpcObjectChanged(); + +private: + I3IpcController* ipc; + + QVariantMap mLastIpcObject; + + // clang-format off + Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(I3Window, qint32, bId, -1, &I3Window::idChanged); + Q_OBJECT_BINDABLE_PROPERTY(I3Window, QString, bTitle, &I3Window::titleChanged); + Q_OBJECT_BINDABLE_PROPERTY(I3Window, bool, bMark, &I3Window::markChanged); + Q_OBJECT_BINDABLE_PROPERTY(I3Window, bool, bTrailmark, &I3Window::trailmarkChanged); + // clang-format on +}; +} // namespace qs::i3::ipc