From 1e06d76b21046feaa470505146d72311122fdd1d Mon Sep 17 00:00:00 2001
From: Ardox
Date: Fri, 14 Nov 2025 10:44:08 +0100
Subject: [PATCH 01/56] FHT: added new cpp modules
---
.../sleex/plugins/src/Sleex/services/fhtc.cpp | 143 ++++++++++++++++++
.../sleex/plugins/src/Sleex/services/fhtc.hpp | 57 +++++++
2 files changed, 200 insertions(+)
create mode 100644 src/share/sleex/plugins/src/Sleex/services/fhtc.cpp
create mode 100644 src/share/sleex/plugins/src/Sleex/services/fhtc.hpp
diff --git a/src/share/sleex/plugins/src/Sleex/services/fhtc.cpp b/src/share/sleex/plugins/src/Sleex/services/fhtc.cpp
new file mode 100644
index 00000000..d5c3ec98
--- /dev/null
+++ b/src/share/sleex/plugins/src/Sleex/services/fhtc.cpp
@@ -0,0 +1,143 @@
+#include "fhtc.hpp"
+#include
+#include
+#include
+
+FhtCompositor::FhtCompositor(QObject *parent)
+ : QObject(parent)
+{
+ QString socketPath = QProcessEnvironment::systemEnvironment().value("FHTC_SOCKET_PATH");
+ if (socketPath.isEmpty()) {
+ qWarning() << "FhtCompositor: FHTC_SOCKET_PATH not set.";
+ return;
+ }
+
+ m_socket = new QTcpSocket(this);
+ connect(m_socket, &QTcpSocket::readyRead, this, &FhtCompositor::onReadyRead);
+
+ m_socket->connectToHost(socketPath, 0, QIODevice::ReadWrite);
+}
+
+void FhtCompositor::onReadyRead()
+{
+ while (m_socket->canReadLine()) {
+ QByteArray line = m_socket->readLine().trimmed();
+ QJsonParseError err;
+ QJsonDocument doc = QJsonDocument::fromJson(line, &err);
+ if (err.error != QJsonParseError::NoError) {
+ qWarning() << "FhtCompositor: failed to parse event:" << line << err.errorString();
+ continue;
+ }
+
+ if (doc.isObject())
+ handleEvent(doc.object());
+ }
+}
+
+void FhtCompositor::handleEvent(const QJsonObject &event)
+{
+ QString type = event.value("event").toString();
+ QJsonValue data = event.value("data");
+
+ enum EventType {
+ Windows,
+ FocusedWindowChanged,
+ WindowClosed,
+ WindowChanged,
+ Workspaces,
+ ActiveWorkspaceChanged,
+ WorkspaceChanged,
+ WorkspaceRemoved,
+ Space,
+ Unknown
+ };
+
+ static const QHash eventMap = {
+ {"windows", Windows},
+ {"focused-window-changed", FocusedWindowChanged},
+ {"window-closed", WindowClosed},
+ {"window-changed", WindowChanged},
+ {"workspaces", Workspaces},
+ {"active-workspace-changed", ActiveWorkspaceChanged},
+ {"workspace-changed", WorkspaceChanged},
+ {"workspace-removed", WorkspaceRemoved},
+ {"space", Space}
+ };
+
+ switch (eventMap.value(type, Unknown)) {
+ case Windows:
+ m_windows = data.toObject().toVariantMap();
+ emit windowsChanged();
+ break;
+
+ case FocusedWindowChanged: {
+ int newId = data.toObject().value("id").toInt(-1);
+ if (newId == -1) {
+ m_focusedWindowId = -1;
+ m_focusedWindow = QVariant();
+ } else {
+ m_focusedWindowId = newId;
+ m_focusedWindow = m_windows.value(QString::number(newId));
+ }
+ emit focusedWindowIdChanged();
+ emit focusedWindowChanged();
+ break;
+ }
+
+ case WindowClosed: {
+ QString id = QString::number(data.toObject().value("id").toInt());
+ m_windows.remove(id);
+ emit windowsChanged();
+ break;
+ }
+
+ case WindowChanged: {
+ QJsonObject win = data.toObject();
+ m_windows[QString::number(win.value("id").toInt())] = win.toVariantMap();
+ emit windowsChanged();
+ break;
+ }
+
+ case Workspaces:
+ m_workspaces = data.toObject().toVariantMap();
+ emit workspacesChanged();
+ break;
+
+ case ActiveWorkspaceChanged: {
+ int newId = data.toObject().value("id").toInt(-1);
+ if (newId == -1) {
+ m_activeWorkspaceId = -1;
+ m_activeWorkspace = QVariant();
+ } else {
+ m_activeWorkspaceId = newId;
+ m_activeWorkspace = m_workspaces.value(QString::number(newId));
+ }
+ emit activeWorkspaceIdChanged();
+ emit activeWorkspaceChanged();
+ break;
+ }
+
+ case WorkspaceChanged: {
+ QJsonObject ws = data.toObject();
+ m_workspaces[QString::number(ws.value("id").toInt())] = ws.toVariantMap();
+ emit workspacesChanged();
+ break;
+ }
+
+ case WorkspaceRemoved: {
+ QString id = QString::number(data.toObject().value("id").toInt());
+ m_workspaces.remove(id);
+ emit workspacesChanged();
+ break;
+ }
+
+ case Space:
+ m_space = data.toVariant();
+ emit spaceChanged();
+ break;
+
+ case Unknown:
+ default:
+ break;
+ }
+}
diff --git a/src/share/sleex/plugins/src/Sleex/services/fhtc.hpp b/src/share/sleex/plugins/src/Sleex/services/fhtc.hpp
new file mode 100644
index 00000000..b64b9cf1
--- /dev/null
+++ b/src/share/sleex/plugins/src/Sleex/services/fhtc.hpp
@@ -0,0 +1,57 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+class FhtCompositor : public QObject {
+ Q_OBJECT
+ QML_SINGLETON
+ QML_ELEMENT
+
+ Q_PROPERTY(QVariantMap windows READ windows NOTIFY windowsChanged)
+ Q_PROPERTY(QVariantMap workspaces READ workspaces NOTIFY workspacesChanged)
+ Q_PROPERTY(QVariant space READ space NOTIFY spaceChanged)
+ Q_PROPERTY(int focusedWindowId READ focusedWindowId NOTIFY focusedWindowIdChanged)
+ Q_PROPERTY(QVariant focusedWindow READ focusedWindow NOTIFY focusedWindowChanged)
+ Q_PROPERTY(int activeWorkspaceId READ activeWorkspaceId NOTIFY activeWorkspaceIdChanged)
+ Q_PROPERTY(QVariant activeWorkspace READ activeWorkspace NOTIFY activeWorkspaceChanged)
+
+public:
+ explicit FhtCompositor(QObject *parent = nullptr);
+
+ QVariantMap windows() const { return m_windows; }
+ QVariantMap workspaces() const { return m_workspaces; }
+ QVariant space() const { return m_space; }
+ int focusedWindowId() const { return m_focusedWindowId; }
+ QVariant focusedWindow() const { return m_focusedWindow; }
+ int activeWorkspaceId() const { return m_activeWorkspaceId; }
+ QVariant activeWorkspace() const { return m_activeWorkspace; }
+
+signals:
+ void windowsChanged();
+ void workspacesChanged();
+ void spaceChanged();
+ void focusedWindowIdChanged();
+ void focusedWindowChanged();
+ void activeWorkspaceIdChanged();
+ void activeWorkspaceChanged();
+
+private slots:
+ void onReadyRead();
+ void handleEvent(const QJsonObject &event);
+
+private:
+ QTcpSocket *m_socket = nullptr;
+ QVariantMap m_windows;
+ QVariantMap m_workspaces;
+ QVariant m_space;
+ int m_focusedWindowId = -1;
+ QVariant m_focusedWindow;
+ int m_activeWorkspaceId = -1;
+ QVariant m_activeWorkspace;
+};
From a40b08f01ea39faab9c9d8d3c34fa2c73eb30219 Mon Sep 17 00:00:00 2001
From: Ardox
Date: Mon, 17 Nov 2025 14:15:13 +0100
Subject: [PATCH 02/56] Added new qml modules
Added a fhtc IPC and WORKSPACES entry for qml
---
.../plugins/src/Sleex/fhtc/CMakeLists.txt | 16 ++
.../sleex/plugins/src/Sleex/fhtc/ipc.cpp | 207 ++++++++++++++++++
.../sleex/plugins/src/Sleex/fhtc/ipc.hpp | 55 +++++
.../sleex/plugins/src/Sleex/fhtc/plugin.cpp | 14 ++
.../plugins/src/Sleex/fhtc/workspaces.cpp | 124 +++++++++++
.../plugins/src/Sleex/fhtc/workspaces.hpp | 51 +++++
6 files changed, 467 insertions(+)
create mode 100644 src/share/sleex/plugins/src/Sleex/fhtc/CMakeLists.txt
create mode 100644 src/share/sleex/plugins/src/Sleex/fhtc/ipc.cpp
create mode 100644 src/share/sleex/plugins/src/Sleex/fhtc/ipc.hpp
create mode 100644 src/share/sleex/plugins/src/Sleex/fhtc/plugin.cpp
create mode 100644 src/share/sleex/plugins/src/Sleex/fhtc/workspaces.cpp
create mode 100644 src/share/sleex/plugins/src/Sleex/fhtc/workspaces.hpp
diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/CMakeLists.txt b/src/share/sleex/plugins/src/Sleex/fhtc/CMakeLists.txt
new file mode 100644
index 00000000..abdfe1c0
--- /dev/null
+++ b/src/share/sleex/plugins/src/Sleex/fhtc/CMakeLists.txt
@@ -0,0 +1,16 @@
+
+qml_module(sleex-fhtc
+ URI Sleex.Fhtc
+ SOURCES
+ ipc.hpp ipc.cpp
+ workspaces.hpp workspaces.cpp
+ plugin.cpp
+)
+
+target_link_libraries(sleex-fhtc
+ PRIVATE
+ Qt6::Core
+ Qt6::Qml
+ Qt6::Network
+ Qt6::Json
+)
\ No newline at end of file
diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/ipc.cpp b/src/share/sleex/plugins/src/Sleex/fhtc/ipc.cpp
new file mode 100644
index 00000000..392e6c21
--- /dev/null
+++ b/src/share/sleex/plugins/src/Sleex/fhtc/ipc.cpp
@@ -0,0 +1,207 @@
+#include "ipc.hpp"
+#include
+#include
+#include
+#include
+#include
+
+Ipc::Ipc(QObject *parent)
+: QObject(parent),
+m_requestSocket(new QLocalSocket(this)),
+m_eventSocket(new QLocalSocket(this))
+{
+ m_socketPath = qEnvironmentVariable("FHTC_SOCKET_PATH");
+ if (m_socketPath.isEmpty()) {
+ emit error("The environment variable FHTC_SOCKET_PATH is not set.");
+ qWarning() << "Ipc: FHTC_SOCKET_PATH is not set.";
+ }
+
+ // Connections for the request socket
+ connect(m_requestSocket, &QLocalSocket::connected, this, &Ipc::onRequestConnected);
+ connect(m_requestSocket, &QLocalSocket::readyRead, this, &Ipc::onRequestReadyRead);
+ connect(m_requestSocket, &QLocalSocket::errorOccurred, this, &Ipc::onRequestError);
+ connect(m_requestSocket, &QLocalSocket::disconnected, this, &Ipc::onRequestDisconnected);
+
+ // Connections for the event socket
+ connect(m_eventSocket, &QLocalSocket::connected, this, &Ipc::onEventConnected);
+ connect(m_eventSocket, &QLocalSocket::readyRead, this, &Ipc::onEventReadyRead);
+ connect(m_eventSocket, &QLocalSocket::errorOccurred, this, &Ipc::onEventError);
+ connect(m_eventSocket, &QLocalSocket::disconnected, this, &Ipc::onEventDisconnected);
+}
+
+Ipc::~Ipc()
+{
+ // Sockets are automatically destroyed because 'this' is their parent
+}
+
+// --- Public slots ---
+
+void Ipc::subscribe()
+{
+ if (m_socketPath.isEmpty()) return;
+ if (m_eventSocket->state() == QLocalSocket::UnconnectedState) {
+ qDebug() << "Ipc: Connecting to the event socket...";
+ m_eventSocket->connectToServer(m_socketPath);
+ }
+}
+
+void Ipc::sendRequest(const QVariant &request)
+{
+ if (m_socketPath.isEmpty()) return;
+
+ m_pendingRequests.enqueue(request);
+ if (m_requestSocket->state() == QLocalSocket::UnconnectedState) {
+ qDebug() << "Ipc: Connecting to the request socket...";
+ m_requestSocket->connectToServer(m_socketPath);
+ } else if (m_requestSocket->state() == QLocalSocket::ConnectedState) {
+ // If already connected, send the pending request
+ writeJson(m_requestSocket, m_pendingRequests.dequeue());
+ }
+ // If 'Connecting', the request will be sent in onRequestConnected
+}
+
+void Ipc::sendAction(const QVariant &action)
+{
+ QVariantMap request;
+ request.insert("action", action);
+ sendRequest(request);
+}
+
+// --- Private slots: Request socket ---
+
+void Ipc::onRequestConnected()
+{
+ qDebug() << "Ipc: Request socket connected.";
+ // Send the first pending request (if any)
+ if (!m_pendingRequests.isEmpty()) {
+ writeJson(m_requestSocket, m_pendingRequests.dequeue());
+ }
+}
+
+void Ipc::onRequestReadyRead()
+{
+ m_requestBuffer.append(m_requestSocket->readAll());
+
+ // The fhtc IPC guarantees one response per request on this socket.
+ // We do not loop, we just wait for a complete line.
+ QVariant response = parseLine(m_requestBuffer);
+ if (!response.isNull()) {
+ emit requestResponse(response);
+
+ // If there are other requests, send them
+ if (!m_pendingRequests.isEmpty()) {
+ writeJson(m_requestSocket, m_pendingRequests.dequeue());
+ }
+ }
+}
+
+void Ipc::onRequestError(QLocalSocket::LocalSocketError socketError)
+{
+ Q_UNUSED(socketError);
+ QString msg = "Ipc (Request): " + m_requestSocket->errorString();
+ qWarning() << msg;
+ emit error(msg);
+ m_pendingRequests.clear(); // Clear pending requests
+}
+
+void Ipc::onRequestDisconnected()
+{
+ qDebug() << "Ipc: Request socket disconnected.";
+ if (!m_requestBuffer.isEmpty()) {
+ qWarning() << "Ipc (Request): DDisconnected with data in buffer:" << m_requestBuffer;
+ m_requestBuffer.clear();
+ }
+}
+
+// --- Private slots: Event socket ---
+
+void Ipc::onEventConnected()
+{
+ qDebug() << "Ipc: Event socket connected. Sending 'subscribe' request.";
+ emit subscribed();
+
+ // Send the subscribe request
+ QVariantMap subRequest;
+ subRequest.insert("subscribe", QVariant()); // "subscribe": null
+ writeJson(m_eventSocket, subRequest);
+}
+
+void Ipc::onEventReadyRead()
+{
+ m_eventBuffer.append(m_eventSocket->readAll());
+
+ // There may be multiple JSON events in the buffer
+ while (true) {
+ QVariant event = parseLine(m_eventBuffer);
+ if (event.isNull()) {
+ break; // Incomplete line, wait for more data
+ }
+ emit newEvent(event);
+ }
+}
+
+void Ipc::onEventError(QLocalSocket::LocalSocketError socketError)
+{
+ Q_UNUSED(socketError);
+ QString msg = "Ipc (Event): " + m_eventSocket->errorString();
+ qWarning() << msg;
+ emit error(msg);
+}
+
+void Ipc::onEventDisconnected()
+{
+ QString msg = "Ipc: Event socket disconnected. Attempting to reconnect in 5s...";
+ qWarning() << msg;
+ emit error(msg);
+
+ // Attempt automatic reconnection for the event socket
+ QTimer::singleShot(5000, this, &Ipc::subscribe);
+}
+
+// --- Utility functions ---
+void Ipc::writeJson(QLocalSocket *socket, const QVariant &data)
+{
+ if (socket->state() != QLocalSocket::ConnectedState) {
+ qWarning() << "Ipc: Attempt to write to a socket that is not connected.";
+ return;
+ }
+
+ QJsonDocument doc = QJsonDocument::fromVariant(data);
+ if (doc.isNull()) {
+ qWarning() << "Ipc: Failed to convert QVariant to JSON.";
+ return;
+ }
+
+ QByteArray json = doc.toJson(QJsonDocument::Compact);
+ json.append('\n'); // fhtc IPC is delimited by newlines
+
+ socket->write(json);
+}
+
+QVariant Ipc::parseLine(QByteArray &buffer)
+{
+ int newlineIdx = buffer.indexOf('\n');
+ if (newlineIdx == -1) {
+ return QVariant(); // Incomplete line
+ }
+
+ // Extract the line and remove it from the buffer
+ QByteArray line = buffer.left(newlineIdx);
+ buffer = buffer.mid(newlineIdx + 1);
+
+ if (line.isEmpty()) {
+ return QVariant(); // Empty line, ignore
+ }
+
+ QJsonParseError parseError;
+ QJsonDocument doc = QJsonDocument::fromJson(line, &parseError);
+
+ if (parseError.error != QJsonParseError::NoError) {
+ QString msg = "Ipc: JSON parse error: " + parseError.errorString() + " | Line: " + QString(line);
+ qWarning() << msg;
+ emit error(msg);
+ return QVariant();
+ }
+
+ return doc.toVariant();
+}
diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/ipc.hpp b/src/share/sleex/plugins/src/Sleex/fhtc/ipc.hpp
new file mode 100644
index 00000000..c1cdad4d
--- /dev/null
+++ b/src/share/sleex/plugins/src/Sleex/fhtc/ipc.hpp
@@ -0,0 +1,55 @@
+#ifndef IPC_H
+#define IPC_H
+
+#include
+#include
+#include
+#include
+#include
+
+class Ipc : public QObject
+{
+ Q_OBJECT
+ QML_NAMED_ELEMENT(Ipc)
+ QML_SINGLETON
+
+public:
+ explicit Ipc(QObject *parent = nullptr);
+ ~Ipc() override;
+
+signals:
+ void error(const QString &message);
+ void requestResponse(const QVariant &response);
+ void subscribed();
+ void newEvent(const QVariant &event);
+
+public slots:
+ void subscribe();
+ void sendRequest(const QVariant &request);
+ void sendAction(const QVariant &action);
+
+private slots:
+ void onRequestConnected();
+ void onRequestReadyRead();
+ void onRequestError(QLocalSocket::LocalSocketError socketError);
+ void onRequestDisconnected();
+
+ void onEventConnected();
+ void onEventReadyRead();
+ void onEventError(QLocalSocket::LocalSocketError socketError);
+ void onEventDisconnected();
+
+private:
+ void writeJson(QLocalSocket* socket, const QVariant &data);
+ QVariant parseLine(QByteArray &buffer);
+
+ QString m_socketPath;
+ QLocalSocket *m_requestSocket;
+ QLocalSocket *m_eventSocket;
+
+ QByteArray m_eventBuffer;
+ QByteArray m_requestBuffer;
+ QQueue m_pendingRequests;
+};
+
+#endif // IPC_H
\ No newline at end of file
diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/plugin.cpp b/src/share/sleex/plugins/src/Sleex/fhtc/plugin.cpp
new file mode 100644
index 00000000..aff6826b
--- /dev/null
+++ b/src/share/sleex/plugins/src/Sleex/fhtc/plugin.cpp
@@ -0,0 +1,14 @@
+#include
+
+class SleexFhtcPlugin : public QQmlExtensionPlugin {
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
+
+public:
+ void registerTypes(const char *uri) override {
+ // QML_ELEMENT and QML_SINGLETON macros handle registration automatically
+ Q_UNUSED(uri)
+ }
+};
+
+#include "plugin.moc"
\ No newline at end of file
diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.cpp b/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.cpp
new file mode 100644
index 00000000..977ddf48
--- /dev/null
+++ b/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.cpp
@@ -0,0 +1,124 @@
+#include "workspaces.hpp"
+#include
+#include
+
+Workspaces::Workspaces(QObject *parent)
+ : QObject(parent), m_ipc(nullptr), m_activeWorkspaceId(-1)
+{
+}
+
+void Workspaces::classBegin()
+{
+ // this "hook" is called by the QML engine after instantiation.
+ // It's the best moment to get the Ipc singleton instance.
+ QQmlEngine *engine = qmlEngine(this);
+ if (!engine) {
+ qWarning() << "Workspaces: Unable to find the QML engine.";
+ return;
+ }
+
+ // Qt 6.2+
+ m_ipc = engine->singletonInstance("Sleex.Fhtc", "Ipc");
+
+ if (!m_ipc) {
+ qWarning() << "Workspaces: Cannot find the 'Ipc' singleton.";
+ qWarning() << "Make sure 'Ipc' is imported and used in QML (e.g., Ipc.subscribe())";
+ return;
+ }
+
+ connect(m_ipc, &Ipc::newEvent, this, &Workspaces::handleNewEvent);
+ connect(m_ipc, &Ipc::requestResponse, this, &Workspaces::handleRequestResponse);
+ connect(m_ipc, &Ipc::subscribed, this, &Workspaces::requestInitialState);
+
+ // If Ipc is already 'subscribed', request the state
+ requestInitialState();
+}
+
+
+QVariantMap Workspaces::workspaces() const { return m_workspaces; }
+QVariantMap Workspaces::space() const { return m_space; }
+int Workspaces::activeWorkspaceId() const { return m_activeWorkspaceId; }
+
+QVariant Workspaces::activeWorkspace() const
+{
+ return m_workspaces.value(QString::number(m_activeWorkspaceId));
+}
+
+
+void Workspaces::requestInitialState()
+{
+ if (!m_ipc) return;
+
+ m_ipc->sendRequest(QVariantMap{{"workspaces", QVariant()}});
+ m_ipc->sendRequest(QVariantMap{{"space", QVariant()}});
+ m_ipc->sendRequest(QVariantMap{{"focused-workspace", QVariant()}});
+}
+
+void Workspaces::handleNewEvent(const QVariant &event)
+{
+ QVariantMap eventMap = event.toMap();
+ QString type = eventMap.value("event").toString();
+ if (type.isEmpty()) return;
+
+ QVariant data = eventMap.value("data");
+ bool changed = false;
+
+ if (type == "workspaces") {
+ m_workspaces = data.toMap();
+ emit workspacesChanged();
+ changed = true;
+ } else if (type == "workspace-changed") {
+ QVariantMap wsMap = data.toMap();
+ QString id = wsMap.value("id").toString();
+ m_workspaces.insert(id, wsMap);
+ emit workspacesChanged();
+ changed = true;
+ } else if (type == "workspace-removed") {
+ QString id = data.toMap().value("id").toString();
+ if (m_workspaces.remove(id)) {
+ emit workspacesChanged();
+ changed = true;
+ }
+ } else if (type == "active-workspace-changed") {
+ int id = data.toMap().value("id").toInt();
+ if (m_activeWorkspaceId != id) {
+ m_activeWorkspaceId = id;
+ emit activeWorkspaceChanged();
+ }
+ } else if (type == "space") {
+ m_space = data.toMap();
+ emit spaceChanged();
+ }
+
+ if (changed) {
+ emit activeWorkspaceChanged();
+ }
+}
+
+void Workspaces::handleRequestResponse(const QVariant &response)
+{
+ QVariantMap resMap = response.toMap();
+
+ if (resMap.contains("workspaces")) {
+ m_workspaces = resMap.value("workspaces").toMap();
+ emit workspacesChanged();
+ emit activeWorkspaceChanged();
+ }
+
+ if (resMap.contains("space")) {
+ m_space = resMap.value("space").toMap();
+ emit spaceChanged();
+ }
+
+ if (resMap.contains("workspace")) {
+ QVariant ws = resMap.value("workspace");
+ int newId = -1;
+ if (ws.isValid() && !ws.isNull()) {
+ newId = ws.toMap().value("id").toInt();
+ }
+ if (m_activeWorkspaceId != newId) {
+ m_activeWorkspaceId = newId;
+ emit activeWorkspaceChanged();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.hpp b/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.hpp
new file mode 100644
index 00000000..b3c280b8
--- /dev/null
+++ b/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.hpp
@@ -0,0 +1,51 @@
+#ifndef WORKSPACES_H
+#define WORKSPACES_H
+
+#include
+#include
+#include
+#include
+#include "ipc.hpp"
+
+class Workspaces : public QObject, public QQmlParserStatus
+{
+ Q_OBJECT
+ Q_INTERFACES(QQmlParserStatus)
+
+ QML_NAMED_ELEMENT(Workspaces)
+ QML_SINGLETON
+
+ Q_PROPERTY(QVariantMap workspaces READ workspaces NOTIFY workspacesChanged)
+ Q_PROPERTY(QVariantMap space READ space NOTIFY spaceChanged)
+ Q_PROPERTY(int activeWorkspaceId READ activeWorkspaceId NOTIFY activeWorkspaceChanged)
+ Q_PROPERTY(QVariant activeWorkspace READ activeWorkspace NOTIFY activeWorkspaceChanged)
+
+public:
+ explicit Workspaces(QObject *parent = nullptr);
+
+ void classBegin() override;
+ void componentComplete() override;
+
+ QVariantMap workspaces() const;
+ QVariantMap space() const;
+ int activeWorkspaceId() const;
+ QVariant activeWorkspace() const;
+
+signals:
+ void workspacesChanged();
+ void spaceChanged();
+ void activeWorkspaceChanged();
+
+private slots:
+ void handleNewEvent(const QVariant &event);
+ void handleRequestResponse(const QVariant &response);
+ void requestInitialState();
+
+private:
+ Ipc *m_ipc;
+ QVariantMap m_workspaces;
+ QVariantMap m_space;
+ int m_activeWorkspaceId;
+};
+
+#endif // WORKSPACES_H
\ No newline at end of file
From c35a9e0e799e2b5ef02b3afb049fd5abe9581cce Mon Sep 17 00:00:00 2001
From: Ardox
Date: Tue, 9 Dec 2025 14:39:31 +0100
Subject: [PATCH 03/56] FHTC: bar workspace widget handle
---
src/share/sleex/modules/bar/Workspaces.qml | 97 ++++++++------
src/share/sleex/services/Fhtc.qml | 148 +++++++++++++++++++++
2 files changed, 208 insertions(+), 37 deletions(-)
create mode 100644 src/share/sleex/services/Fhtc.qml
diff --git a/src/share/sleex/modules/bar/Workspaces.qml b/src/share/sleex/modules/bar/Workspaces.qml
index 5583bf93..50392dd8 100644
--- a/src/share/sleex/modules/bar/Workspaces.qml
+++ b/src/share/sleex/modules/bar/Workspaces.qml
@@ -8,7 +8,6 @@ import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Wayland
-import Quickshell.Hyprland
import Quickshell.Io
import Quickshell.Widgets
import Qt5Compat.GraphicalEffects
@@ -16,10 +15,27 @@ import Qt5Compat.GraphicalEffects
Item {
required property var bar
property bool borderless: Config.options.bar.borderless
- readonly property HyprlandMonitor monitor: Hyprland.monitorFor(bar.screen)
- readonly property Toplevel activeWindow: ToplevelManager.activeToplevel
+ readonly property var activeWindow: Fhtc.focusedWindow
+ readonly property string screenName: bar.screen?.name ?? ""
+
+ // Get workspaces for this screen only, sorted by ID
+ readonly property var screenWorkspaces: {
+ return Object.values(Fhtc.workspaces)
+ .filter(ws => ws.output === screenName)
+ .sort((a, b) => a.id - b.id);
+ }
+
+ // Active workspace index within this screen (0-based)
+ readonly property int activeWorkspaceIndex: {
+ if (!Fhtc.activeWorkspace) return -1;
+ if (Fhtc.activeWorkspace.output !== screenName) return -1;
+ // Find the index of the active workspace in our sorted screen workspaces
+ const idx = screenWorkspaces.findIndex(ws => ws.id === Fhtc.activeWorkspace.id);
+ // Return -1 if the workspace is beyond the shown limit
+ if (idx >= Config.options.bar.workspaces.shown) return -1;
+ return idx;
+ }
- readonly property int workspaceGroup: Math.floor((monitor.activeWorkspace?.id - 1) / Config.options.bar.workspaces.shown)
property list workspaceOccupied: []
property int widgetPadding: 0
property int horizontalPadding: 5
@@ -28,23 +44,31 @@ Item {
property real workspaceIconSizeShrinked: workspaceButtonWidth * 0.55
property real workspaceIconOpacityShrinked: 1
property real workspaceIconMarginShrinked: -4
- property int workspaceIndexInGroup: (monitor.activeWorkspace?.id - 1) % Config.options.bar.workspaces.shown
-
// Function to update workspaceOccupied
function updateWorkspaceOccupied() {
workspaceOccupied = Array.from({ length: Config.options.bar.workspaces.shown }, (_, i) => {
- return Hyprland.workspaces.values.some(ws => ws.id === workspaceGroup * Config.options.bar.workspaces.shown + i + 1);
+ // Get the workspace at this index for this screen
+ const ws = screenWorkspaces[i];
+ if (!ws) return false;
+ // Check if the workspace has any windows
+ return ws.windows && ws.windows.length > 0;
})
}
// Initialize workspaceOccupied when the component is created
Component.onCompleted: updateWorkspaceOccupied()
- // Listen for changes in Hyprland.workspaces.values
+ // Listen for changes in Fhtc.workspaces and windows
Connections {
- target: Hyprland.workspaces
- function onValuesChanged() {
+ target: Fhtc
+ function onWorkspacesChanged() {
+ updateWorkspaceOccupied();
+ }
+ function onWindowsChanged() {
+ updateWorkspaceOccupied();
+ }
+ function onActiveWorkspaceChanged() {
updateWorkspaceOccupied();
}
}
@@ -57,23 +81,13 @@ Item {
WheelHandler {
onWheel: (event) => {
if (event.angleDelta.y < 0)
- Hyprland.dispatch(`workspace +1`);
+ Fhtc.dispatch("focus-workspace", ["+1"]);
else if (event.angleDelta.y > 0)
- Hyprland.dispatch(`workspace -1`);
+ Fhtc.dispatch("focus-workspace", ["-1"]);
}
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
}
- MouseArea {
- anchors.fill: parent
- acceptedButtons: Qt.BackButton
- onPressed: (event) => {
- if (event.button === Qt.BackButton) {
- Hyprland.dispatch(`togglespecialworkspace`);
- }
- }
- }
-
Item {
anchors.fill: parent
anchors.leftMargin: horizontalPadding
@@ -96,8 +110,8 @@ Item {
implicitWidth: workspaceButtonWidth
implicitHeight: workspaceButtonWidth
radius: Appearance.rounding.full
- property var leftOccupied: (workspaceOccupied[index-1] && !(!activeWindow?.activated && monitor.activeWorkspace?.id === index))
- property var rightOccupied: (workspaceOccupied[index+1] && !(!activeWindow?.activated && monitor.activeWorkspace?.id === index+2))
+ property var leftOccupied: (workspaceOccupied[index-1])
+ property var rightOccupied: (workspaceOccupied[index+1])
property var radiusLeft: leftOccupied ? 0 : Appearance.rounding.full
property var radiusRight: rightOccupied ? 0 : Appearance.rounding.full
@@ -107,7 +121,7 @@ Item {
bottomRightRadius: radiusRight
color: ColorUtils.transparentize(Appearance.m3colors.m3secondaryContainer, 0.4)
- opacity: (workspaceOccupied[index] && !(!activeWindow?.activated && monitor.activeWorkspace?.id === index+1)) ? 1 : 0
+ opacity: (workspaceOccupied[index]) ? 1 : 0
Behavior on opacity {
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
@@ -129,6 +143,7 @@ Item {
// Active workspace
Rectangle {
z: 2
+ visible: activeWorkspaceIndex >= 0
// Make active ws indicator, which has a brighter color, smaller to look like it is of the same size as ws occupied highlight
property real activeWorkspaceMargin: 2
implicitHeight: workspaceButtonWidth - activeWorkspaceMargin * 2
@@ -136,8 +151,8 @@ Item {
color: Appearance.colors.colPrimary
anchors.verticalCenter: parent.verticalCenter
- property real idx1: workspaceIndexInGroup
- property real idx2: workspaceIndexInGroup
+ property real idx1: activeWorkspaceIndex >= 0 ? activeWorkspaceIndex : 0
+ property real idx2: activeWorkspaceIndex >= 0 ? activeWorkspaceIndex : 0
x: Math.min(idx1, idx2) * workspaceButtonWidth + activeWorkspaceMargin
implicitWidth: Math.abs(idx1 - idx2) * workspaceButtonWidth + workspaceButtonWidth - activeWorkspaceMargin * 2
@@ -172,24 +187,32 @@ Item {
Button {
id: button
- property int workspaceValue: workspaceGroup * Config.options.bar.workspaces.shown + index + 1
+ property var workspace: screenWorkspaces[index] ?? null
+ property int workspaceId: workspace?.id ?? -1
Layout.fillHeight: true
- onPressed: Hyprland.dispatch(`workspace ${workspaceValue}`)
+ onPressed: {
+ // Use index + 1 for focus-workspace command (1-based)
+ Fhtc.dispatch("focus-workspace", [(index + 1).toString()]);
+ }
width: workspaceButtonWidth
background: Item {
id: workspaceButtonBackground
implicitWidth: workspaceButtonWidth
implicitHeight: workspaceButtonWidth
+
+ // Get the biggest window from the workspace's window list
property var biggestWindow: {
- const windowsInThisWorkspace = HyprlandData.windowList.filter(w => w.workspace.id == button.workspaceValue)
+ if (!button.workspace || !button.workspace.windows || button.workspace.windows.length === 0) return null;
+ const windowIds = button.workspace.windows;
+ const windowsInThisWorkspace = windowIds.map(id => Fhtc.windows[id]).filter(w => w != null);
return windowsInThisWorkspace.reduce((maxWin, win) => {
const maxArea = (maxWin?.size?.[0] ?? 0) * (maxWin?.size?.[1] ?? 0)
const winArea = (win?.size?.[0] ?? 0) * (win?.size?.[1] ?? 0)
return winArea > maxArea ? win : maxWin
}, null)
}
- property var mainAppIconSource: Quickshell.iconPath(AppSearch.guessIcon(biggestWindow?.class), "image-missing")
+ property var mainAppIconSource: Quickshell.iconPath(AppSearch.guessIcon(biggestWindow?.["app-id"]), "image-missing")
StyledText { // Workspace number text
opacity: GlobalStates.workspaceShowNumbers
@@ -202,9 +225,9 @@ Item {
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font.pixelSize: Appearance.font.pixelSize.small - ((text.length - 1) * (text !== "10") * 2)
- text: `${button.workspaceValue}`
+ text: `${index + 1}`
elide: Text.ElideRight
- color: (monitor.activeWorkspace?.id == button.workspaceValue) ?
+ color: (activeWorkspaceIndex == index) ?
Appearance.m3colors.m3onPrimary :
(workspaceOccupied[index] ? Appearance.m3colors.m3onSecondaryContainer :
Appearance.colors.colOnLayer1Inactive)
@@ -224,9 +247,9 @@ Item {
width: workspaceButtonWidth * 0.18
height: width
radius: width / 2
- color: (monitor.activeWorkspace?.id == button.workspaceValue) ?
- Appearance.m3colors.m3onPrimary :
- (workspaceOccupied[index] ? Appearance.m3colors.m3onSecondaryContainer :
+ color: (activeWorkspaceIndex == index) ?
+ Appearance.m3colors.m3onPrimary :
+ (workspaceOccupied[index] ? Appearance.m3colors.m3onSecondaryContainer :
Appearance.colors.colOnLayer1Inactive)
Behavior on opacity {
@@ -240,7 +263,7 @@ Item {
opacity: !Config.options?.bar.workspaces.showAppIcons ? 0 :
(workspaceButtonBackground.biggestWindow && !GlobalStates.workspaceShowNumbers && Config.options?.bar.workspaces.showAppIcons) ?
1 : workspaceButtonBackground.biggestWindow ? workspaceIconOpacityShrinked : 0
- visible: opacity > 0
+ visible: opacity > 0
IconImage {
id: mainAppIcon
anchors.bottom: parent.bottom
diff --git a/src/share/sleex/services/Fhtc.qml b/src/share/sleex/services/Fhtc.qml
new file mode 100644
index 00000000..81bfe060
--- /dev/null
+++ b/src/share/sleex/services/Fhtc.qml
@@ -0,0 +1,148 @@
+pragma Singleton
+pragma ComponentBehavior: Bound
+
+import Quickshell
+import Quickshell.Io
+
+Singleton {
+ id: root
+
+ // The socket we connect to. This is the bridge between us and fht-compositor
+ readonly property string socketPath: Quickshell.env("FHTC_SOCKET_PATH")
+
+ // How things work here is that we store workspace and window data into separate maps,
+ // and actual data structures (like Monitors, or even Workspaces) store IDs. The stored
+ // maps are ID->Window/Workspace maps
+ //
+ // FIXME: Add typing to these.
+ property var workspaces: ({})
+ property var windows: ({})
+ property var space: ({})
+
+ // Focused window is the one that is currently receiving keyboard input. There can be multiple
+ // active windows at once, but only one at max focused window can exist at a time.
+ property int focusedWindowId: -1
+ property var focusedWindow: null
+ // The active workspace is the workspace that currently has the pointer on it. This usually means
+ // the displayed workspace on the focused monitor.
+ property int activeWorkspaceId: -1
+ property var activeWorkspace: null
+
+ Socket {
+ id: subscribeSocket
+ path: root.socketPath
+ connected: true
+
+ onConnectionStateChanged: {
+ if (connected)
+ // When we connect, start turn this socket immediatly into a subcribe
+ // socket. Allowing us to actually use it.
+ subscribeSocket.write('"subscribe"\n');
+ }
+
+ parser: SplitParser {
+ onRead: line => {
+ try {
+ root.handleEvent(JSON.parse(line));
+ } catch (err) {
+ console.warn("FhtCompositor: failed to parse event: ", line, err);
+ }
+ }
+ }
+ }
+
+ // The main event handler. The passed in `event` parameter is a fht-compositor-ipc::Event
+ // https://github.com/nferhat/fht-compositor/blob/ff3d9f3b6549b38e99755d022f5343fda3d6a971/fht-compositor-ipc/src/lib.rs#L131
+ function handleEvent(event) {
+ switch (event.event) {
+ case "windows":
+ // Update the window list. The passed in data is a HashMap
+ root.windows = event.data;
+ root.windowsChanged();
+ break;
+ case "focused-window-changed":
+ var newId = event.data.id;
+ if (newId == null) {
+ root.focusedWindowId = -1;
+ root.focusedWindow = null;
+ } else {
+ // FIXME: Maybe this event could be sent before a WindowChanged event, in this
+ // case this could lead to invalid state.
+ root.focusedWindowId = newId;
+ root.focusedWindow = root.windows[newId];
+ }
+
+ root.focusedWindowChanged();
+ root.focusedWindowIdChanged();
+
+ break;
+ case "window-closed":
+ // NOTE: the compositor will sent us a focused-window-changed event, so we don't
+ // have to update the focusedWindow here.
+ var id = event.data.id;
+ delete root.windows[id];
+
+ root.windowsChanged();
+
+ break;
+ case "window-changed":
+ // This event could either be an existing window changing, or a new window opening
+ const win = event.data;
+ root.windows[win.id] = win;
+
+ root.windowsChanged();
+
+ break;
+ case "workspaces":
+ // Update the workspace list. The passed in data is a HashMap
+ root.workspaces = event.data;
+ root.workspacesChanged();
+ break;
+ case "active-workspace-changed":
+ var newId = event.data.id;
+ if (newId == null) {
+ root.activeWorkspaceId = -1;
+ root.activeWorkspace = null;
+ } else {
+ // FIXME: Maybe this event could be sent before a WorkspaceChanged event, in this
+ // case this could lead to invalid state.
+ root.activeWorkspaceId = newId;
+ root.activeWorkspace = root.workspaces[newId];
+ }
+
+ root.activeWorkspaceChanged();
+ root.activeWorkspaceIdChanged();
+
+ break;
+ case "workspace-changed":
+ const ws = event.data;
+ root.workspaces[ws.id] = ws;
+ root.workspacesChanged();
+
+ break;
+ case "workspace-removed":
+ var id = event.data.id;
+ delete root.workspaces[id];
+
+ root.workspacesChanged();
+
+ break;
+ case "space":
+ root.space = event.data;
+ root.spaceChanged();
+ break;
+ default:
+ // console.warn("Unhandled fht-compositor event: ", event.event);
+ break;
+ }
+ }
+
+ function dispatch(command, args) {
+ var cmd = {
+ command: command,
+ args: args
+ };
+
+ subscribeSocket.write(JSON.stringify(cmd) + "\n");
+ }
+}
From a60ffd66dc78f09ef341e85838592d408136814e Mon Sep 17 00:00:00 2001
From: Ardox
Date: Tue, 9 Dec 2025 15:00:03 +0100
Subject: [PATCH 04/56] FHTC: bar workspace action handle
---
src/share/sleex/modules/bar/Workspaces.qml | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/src/share/sleex/modules/bar/Workspaces.qml b/src/share/sleex/modules/bar/Workspaces.qml
index 50392dd8..f7d89c0a 100644
--- a/src/share/sleex/modules/bar/Workspaces.qml
+++ b/src/share/sleex/modules/bar/Workspaces.qml
@@ -81,9 +81,9 @@ Item {
WheelHandler {
onWheel: (event) => {
if (event.angleDelta.y < 0)
- Fhtc.dispatch("focus-workspace", ["+1"]);
+ Quickshell.execDetached(["fht-compositor", "ipc", "action", "focus-next-workspace"]);
else if (event.angleDelta.y > 0)
- Fhtc.dispatch("focus-workspace", ["-1"]);
+ Quickshell.execDetached(["fht-compositor", "ipc", "action", "focus-previous-workspace"]);
}
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
}
@@ -191,8 +191,9 @@ Item {
property int workspaceId: workspace?.id ?? -1
Layout.fillHeight: true
onPressed: {
- // Use index + 1 for focus-workspace command (1-based)
- Fhtc.dispatch("focus-workspace", [(index + 1).toString()]);
+ if (button.workspaceId >= 0) {
+ Quickshell.execDetached(["fht-compositor", "ipc", "action", "focus-workspace", `${button.workspaceId}`]);
+ }
}
width: workspaceButtonWidth
From 42bff99683a33556d71490b6ffb4b37fa76fc849 Mon Sep 17 00:00:00 2001
From: Ardox
Date: Tue, 9 Dec 2025 15:00:23 +0100
Subject: [PATCH 05/56] FHTC: dock visibility handle
---
src/share/sleex/modules/dock/Dock.qml | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/share/sleex/modules/dock/Dock.qml b/src/share/sleex/modules/dock/Dock.qml
index d96b82eb..78a4f234 100644
--- a/src/share/sleex/modules/dock/Dock.qml
+++ b/src/share/sleex/modules/dock/Dock.qml
@@ -10,7 +10,6 @@ import Quickshell.Io
import Quickshell
import Quickshell.Widgets
import Quickshell.Wayland
-import Quickshell.Hyprland
Scope { // Scope
id: root
@@ -27,7 +26,7 @@ Scope { // Scope
property bool reveal: root.pinned
|| (Config.options?.dock.hoverToReveal && dockMouseArea.containsMouse)
|| dockApps.requestDockShow
- || (!ToplevelManager.activeToplevel?.activated)
+ || (ToplevelManager.toplevels?.length === 0)
anchors {
bottom: true
From 07627068ff98eaedf204a91326efa22cebb7d546 Mon Sep 17 00:00:00 2001
From: Ardox
Date: Wed, 4 Feb 2026 12:59:14 +0100
Subject: [PATCH 06/56] Link fhtc c++ classes
---
src/share/sleex/plugins/src/Sleex/CMakeLists.txt | 1 +
src/share/sleex/plugins/src/Sleex/fhtc/CMakeLists.txt | 1 -
2 files changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/share/sleex/plugins/src/Sleex/CMakeLists.txt b/src/share/sleex/plugins/src/Sleex/CMakeLists.txt
index 2efdd4b1..cb070815 100644
--- a/src/share/sleex/plugins/src/Sleex/CMakeLists.txt
+++ b/src/share/sleex/plugins/src/Sleex/CMakeLists.txt
@@ -34,3 +34,4 @@ function(qml_module arg_TARGET)
endfunction()
add_subdirectory(services)
+add_subdirectory(fhtc)
\ No newline at end of file
diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/CMakeLists.txt b/src/share/sleex/plugins/src/Sleex/fhtc/CMakeLists.txt
index abdfe1c0..64aad482 100644
--- a/src/share/sleex/plugins/src/Sleex/fhtc/CMakeLists.txt
+++ b/src/share/sleex/plugins/src/Sleex/fhtc/CMakeLists.txt
@@ -12,5 +12,4 @@ target_link_libraries(sleex-fhtc
Qt6::Core
Qt6::Qml
Qt6::Network
- Qt6::Json
)
\ No newline at end of file
From e48511e16b9b4d2426c792f969622fa368006feb Mon Sep 17 00:00:00 2001
From: Ardox
Date: Wed, 4 Feb 2026 13:34:28 +0100
Subject: [PATCH 07/56] Impl ActiveWindow with FHTC
---
src/share/sleex/modules/bar/ActiveWindow.qml | 6 +++---
src/share/sleex/services/Fhtc.qml | 3 +++
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/src/share/sleex/modules/bar/ActiveWindow.qml b/src/share/sleex/modules/bar/ActiveWindow.qml
index 8262dbfb..f55a1569 100644
--- a/src/share/sleex/modules/bar/ActiveWindow.qml
+++ b/src/share/sleex/modules/bar/ActiveWindow.qml
@@ -1,5 +1,6 @@
import qs.modules.common
import qs.modules.common.widgets
+import qs.services
import QtQuick
import QtQuick.Layouts
import Quickshell.Wayland
@@ -9,7 +10,6 @@ Item {
id: root
required property var bar
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(bar.screen)
- readonly property Toplevel activeWindow: ToplevelManager.activeToplevel
implicitWidth: colLayout.implicitWidth
@@ -26,7 +26,7 @@ Item {
font.pixelSize: Appearance.font.pixelSize.smaller
color: Appearance.colors.colSubtext
elide: Text.ElideRight
- text: root.activeWindow?.activated ? root.activeWindow?.appId : qsTr("Desktop")
+ text: Fhtc.activeWindowAppId !== "" ? Fhtc.activeWindowAppId : qsTr("Desktop")
}
StyledText {
@@ -34,7 +34,7 @@ Item {
font.pixelSize: Appearance.font.pixelSize.small
color: Appearance.colors.colOnLayer0
elide: Text.ElideRight
- text: root.activeWindow?.activated ? root.activeWindow?.title : `${qsTr("Workspace")} ${monitor.activeWorkspace?.id}`
+ text: Fhtc.activeWindowTitle !== "" ? Fhtc.activeWindowTitle : `${qsTr("Workspace")} ${Fhtc.activeWorkspace.id + 1}`
}
}
diff --git a/src/share/sleex/services/Fhtc.qml b/src/share/sleex/services/Fhtc.qml
index 81bfe060..191f13d7 100644
--- a/src/share/sleex/services/Fhtc.qml
+++ b/src/share/sleex/services/Fhtc.qml
@@ -28,6 +28,9 @@ Singleton {
property int activeWorkspaceId: -1
property var activeWorkspace: null
+ property string activeWindowTitle: focusedWindow != null ? focusedWindow.title : ""
+ property string activeWindowAppId: focusedWindow != null ? focusedWindow["app-id"] : ""
+
Socket {
id: subscribeSocket
path: root.socketPath
From 8759246d8b29935ca28a261578b2d453cf494fb0 Mon Sep 17 00:00:00 2001
From: Ardox
Date: Wed, 4 Feb 2026 13:49:58 +0100
Subject: [PATCH 08/56] Update active ws id
---
src/share/sleex/modules/bar/ActiveWindow.qml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/share/sleex/modules/bar/ActiveWindow.qml b/src/share/sleex/modules/bar/ActiveWindow.qml
index f55a1569..a2c93376 100644
--- a/src/share/sleex/modules/bar/ActiveWindow.qml
+++ b/src/share/sleex/modules/bar/ActiveWindow.qml
@@ -34,7 +34,7 @@ Item {
font.pixelSize: Appearance.font.pixelSize.small
color: Appearance.colors.colOnLayer0
elide: Text.ElideRight
- text: Fhtc.activeWindowTitle !== "" ? Fhtc.activeWindowTitle : `${qsTr("Workspace")} ${Fhtc.activeWorkspace.id + 1}`
+ text: Fhtc.activeWindowTitle !== "" ? Fhtc.activeWindowTitle : `${qsTr("Workspace")} ${Fhtc.activeWorkspaceId + 1}`
}
}
From 52c85fe6dcb48382e72ad80278f075bc485e1af8 Mon Sep 17 00:00:00 2001
From: Ardox
Date: Wed, 4 Feb 2026 13:50:11 +0100
Subject: [PATCH 09/56] Comment out every GlobalShortcut definitions
---
src/share/sleex/GlobalStates.qml | 40 ++--
.../sleex/modules/cheatsheet/Cheatsheet.qml | 52 +++---
.../sleex/modules/dashboard/Dashboard.qml | 42 ++---
.../OnScreenDisplayBrightness.qml | 32 ++--
.../onScreenDisplay/OnScreenDisplayVolume.qml | 32 ++--
src/share/sleex/modules/overview/Overview.qml | 176 +++++++++---------
src/share/sleex/modules/session/Session.qml | 34 ++--
.../sleex/modules/sidebarLeft/SidebarLeft.qml | 70 +++----
.../wallpaperSelector/WallpaperSelector.qml | 48 ++---
src/share/sleex/services/Brightness.qml | 22 +--
src/share/sleex/shell.qml | 2 +-
11 files changed, 275 insertions(+), 275 deletions(-)
diff --git a/src/share/sleex/GlobalStates.qml b/src/share/sleex/GlobalStates.qml
index 7651be22..66a6a56e 100644
--- a/src/share/sleex/GlobalStates.qml
+++ b/src/share/sleex/GlobalStates.qml
@@ -41,18 +41,18 @@ Singleton {
}
}
- GlobalShortcut {
- name: "workspaceNumber"
- description: qsTr("Hold to show workspace numbers, release to show icons")
-
- onPressed: {
- workspaceShowNumbersTimer.start()
- }
- onReleased: {
- workspaceShowNumbersTimer.stop()
- workspaceShowNumbers = false
- }
- }
+ // GlobalShortcut {
+ // name: "workspaceNumber"
+ // description: qsTr("Hold to show workspace numbers, release to show icons")
+
+ // onPressed: {
+ // workspaceShowNumbersTimer.start()
+ // }
+ // onReleased: {
+ // workspaceShowNumbersTimer.stop()
+ // workspaceShowNumbers = false
+ // }
+ // }
IpcHandler {
target: "zoom"
@@ -74,13 +74,13 @@ Singleton {
root.screenLocked = true;
}
}
- GlobalShortcut {
- name: "lockScreen"
- description: qsTr("Lock screen (obviously)")
-
- onPressed: {
- root.screenLocked = true;
- }
- }
+ // GlobalShortcut {
+ // name: "lockScreen"
+ // description: qsTr("Lock screen (obviously)")
+
+ // onPressed: {
+ // root.screenLocked = true;
+ // }
+ // }
}
diff --git a/src/share/sleex/modules/cheatsheet/Cheatsheet.qml b/src/share/sleex/modules/cheatsheet/Cheatsheet.qml
index 2146fb23..3f7fcfc4 100644
--- a/src/share/sleex/modules/cheatsheet/Cheatsheet.qml
+++ b/src/share/sleex/modules/cheatsheet/Cheatsheet.qml
@@ -135,31 +135,31 @@ Scope { // Scope
}
}
- GlobalShortcut {
- name: "cheatsheetToggle"
- description: qsTr("Toggles cheatsheet on press")
-
- onPressed: {
- cheatsheetLoader.active = !cheatsheetLoader.active;
- }
- }
-
- GlobalShortcut {
- name: "cheatsheetOpen"
- description: qsTr("Opens cheatsheet on press")
-
- onPressed: {
- cheatsheetLoader.active = true;
- }
- }
-
- GlobalShortcut {
- name: "cheatsheetClose"
- description: qsTr("Closes cheatsheet on press")
-
- onPressed: {
- cheatsheetLoader.active = false;
- }
- }
+ // GlobalShortcut {
+ // name: "cheatsheetToggle"
+ // description: qsTr("Toggles cheatsheet on press")
+
+ // onPressed: {
+ // cheatsheetLoader.active = !cheatsheetLoader.active;
+ // }
+ // }
+
+ // GlobalShortcut {
+ // name: "cheatsheetOpen"
+ // description: qsTr("Opens cheatsheet on press")
+
+ // onPressed: {
+ // cheatsheetLoader.active = true;
+ // }
+ // }
+
+ // GlobalShortcut {
+ // name: "cheatsheetClose"
+ // description: qsTr("Closes cheatsheet on press")
+
+ // onPressed: {
+ // cheatsheetLoader.active = false;
+ // }
+ // }
}
diff --git a/src/share/sleex/modules/dashboard/Dashboard.qml b/src/share/sleex/modules/dashboard/Dashboard.qml
index 8df2cc8a..519fa347 100644
--- a/src/share/sleex/modules/dashboard/Dashboard.qml
+++ b/src/share/sleex/modules/dashboard/Dashboard.qml
@@ -208,25 +208,25 @@ Scope {
}
}
- GlobalShortcut {
- name: "dashboardToggle"
- description: qsTr("Toggles dashboard on press")
- onPressed: {
- GlobalStates.dashboardOpen = !GlobalStates.dashboardOpen;
- if(GlobalStates.dashboardOpen) Notifications.timeoutAll();
- }
- }
- GlobalShortcut {
- name: "dashboardOpen"
- description: qsTr("Opens dashboard on press")
- onPressed: {
- GlobalStates.dashboardOpen = true;
- Notifications.timeoutAll();
- }
- }
- GlobalShortcut {
- name: "dashboardClose"
- description: qsTr("Closes dashboard on press")
- onPressed: { GlobalStates.dashboardOpen = false; }
- }
+ // GlobalShortcut {
+ // name: "dashboardToggle"
+ // description: qsTr("Toggles dashboard on press")
+ // onPressed: {
+ // GlobalStates.dashboardOpen = !GlobalStates.dashboardOpen;
+ // if(GlobalStates.dashboardOpen) Notifications.timeoutAll();
+ // }
+ // }
+ // GlobalShortcut {
+ // name: "dashboardOpen"
+ // description: qsTr("Opens dashboard on press")
+ // onPressed: {
+ // GlobalStates.dashboardOpen = true;
+ // Notifications.timeoutAll();
+ // }
+ // }
+ // GlobalShortcut {
+ // name: "dashboardClose"
+ // description: qsTr("Closes dashboard on press")
+ // onPressed: { GlobalStates.dashboardOpen = false; }
+ // }
}
diff --git a/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayBrightness.qml b/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayBrightness.qml
index af6115cb..f99b8f24 100644
--- a/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayBrightness.qml
+++ b/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayBrightness.qml
@@ -132,21 +132,21 @@ Scope {
}
}
- GlobalShortcut {
- name: "osdBrightnessTrigger"
- description: qsTr("Triggers brightness OSD on press")
-
- onPressed: {
- root.triggerOsd()
- }
- }
- GlobalShortcut {
- name: "osdBrightnessHide"
- description: qsTr("Hides brightness OSD on press")
-
- onPressed: {
- root.showOsdValues = false
- }
- }
+ // GlobalShortcut {
+ // name: "osdBrightnessTrigger"
+ // description: qsTr("Triggers brightness OSD on press")
+
+ // onPressed: {
+ // root.triggerOsd()
+ // }
+ // }
+ // GlobalShortcut {
+ // name: "osdBrightnessHide"
+ // description: qsTr("Hides brightness OSD on press")
+
+ // onPressed: {
+ // root.showOsdValues = false
+ // }
+ // }
}
\ No newline at end of file
diff --git a/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayVolume.qml b/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayVolume.qml
index 5598f2ef..ac40827f 100644
--- a/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayVolume.qml
+++ b/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayVolume.qml
@@ -184,21 +184,21 @@ Scope {
showOsdValues = !showOsdValues
}
}
- GlobalShortcut {
- name: "osdVolumeTrigger"
- description: qsTr("Triggers volume OSD on press")
-
- onPressed: {
- root.triggerOsd()
- }
- }
- GlobalShortcut {
- name: "osdVolumeHide"
- description: qsTr("Hides volume OSD on press")
-
- onPressed: {
- root.showOsdValues = false
- }
- }
+ // GlobalShortcut {
+ // name: "osdVolumeTrigger"
+ // description: qsTr("Triggers volume OSD on press")
+
+ // onPressed: {
+ // root.triggerOsd()
+ // }
+ // }
+ // GlobalShortcut {
+ // name: "osdVolumeHide"
+ // description: qsTr("Hides volume OSD on press")
+
+ // onPressed: {
+ // root.showOsdValues = false
+ // }
+ // }
}
\ No newline at end of file
diff --git a/src/share/sleex/modules/overview/Overview.qml b/src/share/sleex/modules/overview/Overview.qml
index 5687357e..54a5ddca 100644
--- a/src/share/sleex/modules/overview/Overview.qml
+++ b/src/share/sleex/modules/overview/Overview.qml
@@ -98,7 +98,7 @@ Scope {
}
Keys.onPressed: (event) => {
- if (event.key === Qt.Key_Escape) {
+ if (eƧvent.key === Qt.Key_Escape) {
GlobalStates.overviewOpen = false;
} else if (event.key === Qt.Key_Left) {
if (!root.searchingText) Hyprland.dispatch("workspace -1");
@@ -150,92 +150,92 @@ Scope {
}
}
- GlobalShortcut {
- name: "overviewToggle"
- description: qsTr("Toggles overview on press")
-
- onPressed: {
- GlobalStates.overviewOpen = !GlobalStates.overviewOpen
- }
- }
- GlobalShortcut {
- name: "overviewClose"
- description: qsTr("Closes overview")
-
- onPressed: {
- GlobalStates.overviewOpen = false
- }
- }
- GlobalShortcut {
- name: "overviewToggleRelease"
- description: qsTr("Toggles overview on release")
-
- onPressed: {
- GlobalStates.superReleaseMightTrigger = true
- }
-
- onReleased: {
- if (!GlobalStates.superReleaseMightTrigger) {
- GlobalStates.superReleaseMightTrigger = true
- return
- }
- GlobalStates.overviewOpen = !GlobalStates.overviewOpen
- }
- }
- GlobalShortcut {
- name: "overviewToggleReleaseInterrupt"
- description: qsTr("Interrupts possibility of overview being toggled on release. ") +
- qsTr("This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. ") +
- qsTr("To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything.")
-
- onPressed: {
- GlobalStates.superReleaseMightTrigger = false
- }
- }
- GlobalShortcut {
- name: "overviewClipboardToggle"
- description: qsTr("Toggle clipboard query on overview widget")
-
- onPressed: {
- if (GlobalStates.overviewOpen && overviewScope.dontAutoCancelSearch) {
- GlobalStates.overviewOpen = false;
- return;
- }
- for (let i = 0; i < overviewVariants.instances.length; i++) {
- let panelWindow = overviewVariants.instances[i];
- if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
- overviewScope.dontAutoCancelSearch = true;
- panelWindow.setSearchingText(
- Config.options.search.prefix.clipboard
- );
- GlobalStates.overviewOpen = true;
- return
- }
- }
- }
- }
-
- GlobalShortcut {
- name: "overviewEmojiToggle"
- description: qsTr("Toggle emoji query on overview widget")
-
- onPressed: {
- if (GlobalStates.overviewOpen && overviewScope.dontAutoCancelSearch) {
- GlobalStates.overviewOpen = false;
- return;
- }
- for (let i = 0; i < overviewVariants.instances.length; i++) {
- let panelWindow = overviewVariants.instances[i];
- if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
- overviewScope.dontAutoCancelSearch = true;
- panelWindow.setSearchingText(
- Config.options.search.prefix.emojis
- );
- GlobalStates.overviewOpen = true;
- return
- }
- }
- }
- }
+ // GlobalShortcut {
+ // name: "overviewToggle"
+ // description: qsTr("Toggles overview on press")
+
+ // onPressed: {
+ // GlobalStates.overviewOpen = !GlobalStates.overviewOpen
+ // }
+ // }
+ // GlobalShortcut {
+ // name: "overviewClose"
+ // description: qsTr("Closes overview")
+
+ // onPressed: {
+ // GlobalStates.overviewOpen = false
+ // }
+ // }
+ // GlobalShortcut {
+ // name: "overviewToggleRelease"
+ // description: qsTr("Toggles overview on release")
+
+ // onPressed: {
+ // GlobalStates.superReleaseMightTrigger = true
+ // }
+
+ // onReleased: {
+ // if (!GlobalStates.superReleaseMightTrigger) {
+ // GlobalStates.superReleaseMightTrigger = true
+ // return
+ // }
+ // GlobalStates.overviewOpen = !GlobalStates.overviewOpen
+ // }
+ // }
+ // GlobalShortcut {
+ // name: "overviewToggleReleaseInterrupt"
+ // description: qsTr("Interrupts possibility of overview being toggled on release. ") +
+ // qsTr("This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. ") +
+ // qsTr("To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything.")
+
+ // onPressed: {
+ // GlobalStates.superReleaseMightTrigger = false
+ // }
+ // }
+ // GlobalShortcut {
+ // name: "overviewClipboardToggle"
+ // description: qsTr("Toggle clipboard query on overview widget")
+
+ // onPressed: {
+ // if (GlobalStates.overviewOpen && overviewScope.dontAutoCancelSearch) {
+ // GlobalStates.overviewOpen = false;
+ // return;
+ // }
+ // for (let i = 0; i < overviewVariants.instances.length; i++) {
+ // let panelWindow = overviewVariants.instances[i];
+ // if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
+ // overviewScope.dontAutoCancelSearch = true;
+ // panelWindow.setSearchingText(
+ // Config.options.search.prefix.clipboard
+ // );
+ // GlobalStates.overviewOpen = true;
+ // return
+ // }
+ // }
+ // }
+ // }
+
+ // GlobalShortcut {
+ // name: "overviewEmojiToggle"
+ // description: qsTr("Toggle emoji query on overview widget")
+
+ // onPressed: {
+ // if (GlobalStates.overviewOpen && overviewScope.dontAutoCancelSearch) {
+ // GlobalStates.overviewOpen = false;
+ // return;
+ // }
+ // for (let i = 0; i < overviewVariants.instances.length; i++) {
+ // let panelWindow = overviewVariants.instances[i];
+ // if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
+ // overviewScope.dontAutoCancelSearch = true;
+ // panelWindow.setSearchingText(
+ // Config.options.search.prefix.emojis
+ // );
+ // GlobalStates.overviewOpen = true;
+ // return
+ // }
+ // }
+ // }
+ // }
}
diff --git a/src/share/sleex/modules/session/Session.qml b/src/share/sleex/modules/session/Session.qml
index 9987efca..4dcccd5c 100644
--- a/src/share/sleex/modules/session/Session.qml
+++ b/src/share/sleex/modules/session/Session.qml
@@ -207,22 +207,22 @@ Scope {
}
}
- GlobalShortcut {
- name: "sessionToggle"
- description: qsTr("Toggles session screen on press")
-
- onPressed: {
- sessionLoader.active = !sessionLoader.active;
- }
- }
-
- GlobalShortcut {
- name: "sessionOpen"
- description: qsTr("Opens session screen on press")
-
- onPressed: {
- sessionLoader.active = true;
- }
- }
+ // GlobalShortcut {
+ // name: "sessionToggle"
+ // description: qsTr("Toggles session screen on press")
+
+ // onPressed: {
+ // sessionLoader.active = !sessionLoader.active;
+ // }
+ // }
+
+ // GlobalShortcut {
+ // name: "sessionOpen"
+ // description: qsTr("Opens session screen on press")
+
+ // onPressed: {
+ // sessionLoader.active = true;
+ // }
+ // }
}
diff --git a/src/share/sleex/modules/sidebarLeft/SidebarLeft.qml b/src/share/sleex/modules/sidebarLeft/SidebarLeft.qml
index fbdfe662..abbd7757 100644
--- a/src/share/sleex/modules/sidebarLeft/SidebarLeft.qml
+++ b/src/share/sleex/modules/sidebarLeft/SidebarLeft.qml
@@ -164,40 +164,40 @@ Scope { // Scope
}
}
- GlobalShortcut {
- name: "sidebarLeftToggle"
- description: qsTr("Toggles left sidebar on press")
-
- onPressed: {
- GlobalStates.sidebarLeftOpen = !GlobalStates.sidebarLeftOpen;
- }
- }
-
- GlobalShortcut {
- name: "sidebarLeftOpen"
- description: qsTr("Opens left sidebar on press")
-
- onPressed: {
- GlobalStates.sidebarLeftOpen = true;
- }
- }
-
- GlobalShortcut {
- name: "sidebarLeftClose"
- description: qsTr("Closes left sidebar on press")
-
- onPressed: {
- GlobalStates.sidebarLeftOpen = false;
- }
- }
-
- GlobalShortcut {
- name: "sidebarLeftToggleDetach"
- description: qsTr("Detach left sidebar into a window/Attach it back")
-
- onPressed: {
- root.detach = !root.detach;
- }
- }
+ // GlobalShortcut {
+ // name: "sidebarLeftToggle"
+ // description: qsTr("Toggles left sidebar on press")
+
+ // onPressed: {
+ // GlobalStates.sidebarLeftOpen = !GlobalStates.sidebarLeftOpen;
+ // }
+ // }
+
+ // GlobalShortcut {
+ // name: "sidebarLeftOpen"
+ // description: qsTr("Opens left sidebar on press")
+
+ // onPressed: {
+ // GlobalStates.sidebarLeftOpen = true;
+ // }
+ // }
+
+ // GlobalShortcut {
+ // name: "sidebarLeftClose"
+ // description: qsTr("Closes left sidebar on press")
+
+ // onPressed: {
+ // GlobalStates.sidebarLeftOpen = false;
+ // }
+ // }
+
+ // GlobalShortcut {
+ // name: "sidebarLeftToggleDetach"
+ // description: qsTr("Detach left sidebar into a window/Attach it back")
+
+ // onPressed: {
+ // root.detach = !root.detach;
+ // }
+ // }
}
diff --git a/src/share/sleex/modules/wallpaperSelector/WallpaperSelector.qml b/src/share/sleex/modules/wallpaperSelector/WallpaperSelector.qml
index 71b0d59c..3ce7d89c 100644
--- a/src/share/sleex/modules/wallpaperSelector/WallpaperSelector.qml
+++ b/src/share/sleex/modules/wallpaperSelector/WallpaperSelector.qml
@@ -173,29 +173,29 @@ Scope {
}
}
- GlobalShortcut {
- name: "wppselectorToggle"
- description: qsTr("Toggles wallpaper selector on press")
-
- onPressed: {
- GlobalStates.wppselectorOpen = !GlobalStates.wppselectorOpen;
- }
- }
- GlobalShortcut {
- name: "wppselectorOpen"
- description: qsTr("Opens wallpaper selector on press")
-
- onPressed: {
- GlobalStates.wppselectorOpen = true;
- }
- }
- GlobalShortcut {
- name: "wppselectorClose"
- description: qsTr("Closes wallpaper selector on press")
-
- onPressed: {
- GlobalStates.wppselectorOpen = false;
- }
- }
+ // GlobalShortcut {
+ // name: "wppselectorToggle"
+ // description: qsTr("Toggles wallpaper selector on press")
+
+ // onPressed: {
+ // GlobalStates.wppselectorOpen = !GlobalStates.wppselectorOpen;
+ // }
+ // }
+ // GlobalShortcut {
+ // name: "wppselectorOpen"
+ // description: qsTr("Opens wallpaper selector on press")
+
+ // onPressed: {
+ // GlobalStates.wppselectorOpen = true;
+ // }
+ // }
+ // GlobalShortcut {
+ // name: "wppselectorClose"
+ // description: qsTr("Closes wallpaper selector on press")
+
+ // onPressed: {
+ // GlobalStates.wppselectorOpen = false;
+ // }
+ // }
}
\ No newline at end of file
diff --git a/src/share/sleex/services/Brightness.qml b/src/share/sleex/services/Brightness.qml
index 552b2f88..9b8c4fa4 100644
--- a/src/share/sleex/services/Brightness.qml
+++ b/src/share/sleex/services/Brightness.qml
@@ -145,15 +145,15 @@ Singleton {
}
}
- GlobalShortcut {
- name: "brightnessIncrease"
- description: qsTr("Increase brightness")
- onPressed: root.increaseBrightness()
- }
-
- GlobalShortcut {
- name: "brightnessDecrease"
- description: qsTr("Decrease brightness")
- onPressed: root.decreaseBrightness()
- }
+ // GlobalShortcut {
+ // name: "brightnessIncrease"
+ // description: qsTr("Increase brightness")
+ // onPressed: root.increaseBrightness()
+ // }
+
+ // GlobalShortcut {
+ // name: "brightnessDecrease"
+ // description: qsTr("Decrease brightness")
+ // onPressed: root.decreaseBrightness()
+ // }
}
diff --git a/src/share/sleex/shell.qml b/src/share/sleex/shell.qml
index c1eb2c14..eff13b2e 100644
--- a/src/share/sleex/shell.qml
+++ b/src/share/sleex/shell.qml
@@ -54,7 +54,7 @@ ShellRoot {
FirstRunExperience.load()
Cliphist.refresh()
Idle.init();
- nightLight.load()
+ NightLight.load()
}
LazyLoader { active: enableBar; component: Bar {} }
From d49ccdd85b6d26336e465d2d43970f3ff8911fcf Mon Sep 17 00:00:00 2001
From: Ardox
Date: Wed, 4 Feb 2026 14:10:49 +0100
Subject: [PATCH 10/56] Using C++ plugins for workspace and window dispatchers
and vars
---
src/share/sleex/modules/bar/ActiveWindow.qml | 5 +-
src/share/sleex/modules/bar/Workspaces.qml | 1 +
.../plugins/src/Sleex/fhtc/workspaces.cpp | 190 ++++++++----------
.../plugins/src/Sleex/fhtc/workspaces.hpp | 57 +++---
src/share/sleex/services/Fhtc.qml | 151 --------------
5 files changed, 126 insertions(+), 278 deletions(-)
delete mode 100644 src/share/sleex/services/Fhtc.qml
diff --git a/src/share/sleex/modules/bar/ActiveWindow.qml b/src/share/sleex/modules/bar/ActiveWindow.qml
index a2c93376..db358966 100644
--- a/src/share/sleex/modules/bar/ActiveWindow.qml
+++ b/src/share/sleex/modules/bar/ActiveWindow.qml
@@ -5,6 +5,7 @@ import QtQuick
import QtQuick.Layouts
import Quickshell.Wayland
import Quickshell.Hyprland
+import Sleex.Fhtc
Item {
id: root
@@ -26,7 +27,7 @@ Item {
font.pixelSize: Appearance.font.pixelSize.smaller
color: Appearance.colors.colSubtext
elide: Text.ElideRight
- text: Fhtc.activeWindowAppId !== "" ? Fhtc.activeWindowAppId : qsTr("Desktop")
+ text: Fhtc.focusedWindow['app-id'] !== "" ? Fhtc.focusedWindow['app-id'] : qsTr("Desktop")
}
StyledText {
@@ -34,7 +35,7 @@ Item {
font.pixelSize: Appearance.font.pixelSize.small
color: Appearance.colors.colOnLayer0
elide: Text.ElideRight
- text: Fhtc.activeWindowTitle !== "" ? Fhtc.activeWindowTitle : `${qsTr("Workspace")} ${Fhtc.activeWorkspaceId + 1}`
+ text: Fhtc.focusedWindow.title !== "" ? Fhtc.focusedWindow.title : `${qsTr("Workspace")} ${Fhtc.activeWorkspaceId + 1}`
}
}
diff --git a/src/share/sleex/modules/bar/Workspaces.qml b/src/share/sleex/modules/bar/Workspaces.qml
index f7d89c0a..ee1632cd 100644
--- a/src/share/sleex/modules/bar/Workspaces.qml
+++ b/src/share/sleex/modules/bar/Workspaces.qml
@@ -11,6 +11,7 @@ import Quickshell.Wayland
import Quickshell.Io
import Quickshell.Widgets
import Qt5Compat.GraphicalEffects
+import Sleex.Fhtc
Item {
required property var bar
diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.cpp b/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.cpp
index 977ddf48..325f790b 100644
--- a/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.cpp
+++ b/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.cpp
@@ -1,124 +1,112 @@
#include "workspaces.hpp"
-#include
-#include
+#include
-Workspaces::Workspaces(QObject *parent)
- : QObject(parent), m_ipc(nullptr), m_activeWorkspaceId(-1)
+Workspaces::Workspaces(QObject *parent) : QObject(parent)
{
-}
-
-void Workspaces::classBegin()
-{
- // this "hook" is called by the QML engine after instantiation.
- // It's the best moment to get the Ipc singleton instance.
- QQmlEngine *engine = qmlEngine(this);
- if (!engine) {
- qWarning() << "Workspaces: Unable to find the QML engine.";
- return;
- }
-
- // Qt 6.2+
- m_ipc = engine->singletonInstance("Sleex.Fhtc", "Ipc");
-
- if (!m_ipc) {
- qWarning() << "Workspaces: Cannot find the 'Ipc' singleton.";
- qWarning() << "Make sure 'Ipc' is imported and used in QML (e.g., Ipc.subscribe())";
- return;
- }
-
- connect(m_ipc, &Ipc::newEvent, this, &Workspaces::handleNewEvent);
- connect(m_ipc, &Ipc::requestResponse, this, &Workspaces::handleRequestResponse);
- connect(m_ipc, &Ipc::subscribed, this, &Workspaces::requestInitialState);
+ m_ipc = new Ipc(this);
- // If Ipc is already 'subscribed', request the state
- requestInitialState();
-}
-
-
-QVariantMap Workspaces::workspaces() const { return m_workspaces; }
-QVariantMap Workspaces::space() const { return m_space; }
-int Workspaces::activeWorkspaceId() const { return m_activeWorkspaceId; }
-
-QVariant Workspaces::activeWorkspace() const
-{
- return m_workspaces.value(QString::number(m_activeWorkspaceId));
+ connect(m_ipc, &Ipc::newEvent, this, &Workspaces::handleEvent);
+
+ m_ipc->subscribe();
}
-
-void Workspaces::requestInitialState()
+void Workspaces::dispatch(const QString &command, const QVariant &args)
{
- if (!m_ipc) return;
+ QVariantMap cmd;
+ cmd["command"] = command;
+ cmd["args"] = args;
- m_ipc->sendRequest(QVariantMap{{"workspaces", QVariant()}});
- m_ipc->sendRequest(QVariantMap{{"space", QVariant()}});
- m_ipc->sendRequest(QVariantMap{{"focused-workspace", QVariant()}});
+ // Envoi via le socket de requête géré par Ipc
+ m_ipc->sendRequest(cmd);
}
-void Workspaces::handleNewEvent(const QVariant &event)
+void Workspaces::handleEvent(const QVariant &eventVar)
{
- QVariantMap eventMap = event.toMap();
- QString type = eventMap.value("event").toString();
- if (type.isEmpty()) return;
-
- QVariant data = eventMap.value("data");
- bool changed = false;
-
- if (type == "workspaces") {
- m_workspaces = data.toMap();
- emit workspacesChanged();
- changed = true;
- } else if (type == "workspace-changed") {
- QVariantMap wsMap = data.toMap();
- QString id = wsMap.value("id").toString();
- m_workspaces.insert(id, wsMap);
- emit workspacesChanged();
- changed = true;
- } else if (type == "workspace-removed") {
- QString id = data.toMap().value("id").toString();
- if (m_workspaces.remove(id)) {
- emit workspacesChanged();
- changed = true;
+ // Conversion de l'ƩvƩnement reƧu (QVariant/JSON)
+ QVariantMap event = eventVar.toMap();
+ QString type = event.value("event").toString();
+ QVariant data = event.value("data");
+
+ if (type == "windows") {
+ m_windows = data.toMap();
+ emit windowsChanged();
+ }
+ else if (type == "focused-window-changed") {
+ QVariantMap dataMap = data.toMap();
+ // VƩrification si l'ID est null/undefined
+ QVariant idVar = dataMap.value("id");
+
+ if (idVar.isNull() || !idVar.isValid()) {
+ m_focusedWindowId = -1;
+ m_focusedWindow = QVariant();
+ } else {
+ m_focusedWindowId = idVar.toInt();
+ m_focusedWindow = m_windows.value(QString::number(m_focusedWindowId));
}
- } else if (type == "active-workspace-changed") {
+ emit focusedWindowIdChanged();
+ emit focusedWindowChanged();
+ }
+ else if (type == "window-closed") {
int id = data.toMap().value("id").toInt();
- if (m_activeWorkspaceId != id) {
- m_activeWorkspaceId = id;
+ m_windows.remove(QString::number(id));
+ emit windowsChanged();
+ }
+ else if (type == "window-changed") {
+ QVariantMap win = data.toMap();
+ int id = win.value("id").toInt();
+
+ m_windows.insert(QString::number(id), win);
+ emit windowsChanged();
+
+ // Si c'est la fenêtre active qui a changé, on met à jour focusedWindow
+ if (id == m_focusedWindowId) {
+ m_focusedWindow = win;
+ emit focusedWindowChanged();
+ }
+ }
+ else if (type == "workspaces") {
+ m_workspaces = data.toMap();
+ emit workspacesChanged();
+
+ // Rafraîchir l'activeWorkspace au cas où ses données auraient changé
+ if (m_activeWorkspaceId != -1) {
+ m_activeWorkspace = m_workspaces.value(QString::number(m_activeWorkspaceId));
emit activeWorkspaceChanged();
}
- } else if (type == "space") {
- m_space = data.toMap();
- emit spaceChanged();
}
-
- if (changed) {
+ else if (type == "active-workspace-changed") {
+ QVariantMap dataMap = data.toMap();
+ QVariant idVar = dataMap.value("id");
+
+ if (idVar.isNull() || !idVar.isValid()) {
+ m_activeWorkspaceId = -1;
+ m_activeWorkspace = QVariant();
+ } else {
+ m_activeWorkspaceId = idVar.toInt();
+ m_activeWorkspace = m_workspaces.value(QString::number(m_activeWorkspaceId));
+ }
+ emit activeWorkspaceIdChanged();
emit activeWorkspaceChanged();
}
-}
-
-void Workspaces::handleRequestResponse(const QVariant &response)
-{
- QVariantMap resMap = response.toMap();
-
- if (resMap.contains("workspaces")) {
- m_workspaces = resMap.value("workspaces").toMap();
+ else if (type == "workspace-changed") {
+ QVariantMap ws = data.toMap();
+ int id = ws.value("id").toInt();
+
+ m_workspaces.insert(QString::number(id), ws);
emit workspacesChanged();
- emit activeWorkspaceChanged();
- }
-
- if (resMap.contains("space")) {
- m_space = resMap.value("space").toMap();
- emit spaceChanged();
- }
-
- if (resMap.contains("workspace")) {
- QVariant ws = resMap.value("workspace");
- int newId = -1;
- if (ws.isValid() && !ws.isNull()) {
- newId = ws.toMap().value("id").toInt();
- }
- if (m_activeWorkspaceId != newId) {
- m_activeWorkspaceId = newId;
+
+ if (id == m_activeWorkspaceId) {
+ m_activeWorkspace = ws;
emit activeWorkspaceChanged();
}
}
+ else if (type == "workspace-removed") {
+ int id = data.toMap().value("id").toInt();
+ m_workspaces.remove(QString::number(id));
+ emit workspacesChanged();
+ }
+ else if (type == "space") {
+ m_space = data;
+ emit spaceChanged();
+ }
}
\ No newline at end of file
diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.hpp b/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.hpp
index b3c280b8..0d4460a7 100644
--- a/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.hpp
+++ b/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.hpp
@@ -1,51 +1,60 @@
-#ifndef WORKSPACES_H
-#define WORKSPACES_H
+#pragma once
#include
-#include
-#include
+#include
#include
+#include
#include "ipc.hpp"
-class Workspaces : public QObject, public QQmlParserStatus
+class Workspaces : public QObject
{
Q_OBJECT
- Q_INTERFACES(QQmlParserStatus)
-
- QML_NAMED_ELEMENT(Workspaces)
+ QML_NAMED_ELEMENT(Fhtc)
QML_SINGLETON
+ Q_PROPERTY(QVariantMap windows READ windows NOTIFY windowsChanged)
Q_PROPERTY(QVariantMap workspaces READ workspaces NOTIFY workspacesChanged)
- Q_PROPERTY(QVariantMap space READ space NOTIFY spaceChanged)
- Q_PROPERTY(int activeWorkspaceId READ activeWorkspaceId NOTIFY activeWorkspaceChanged)
+ Q_PROPERTY(QVariant space READ space NOTIFY spaceChanged)
+
+ Q_PROPERTY(int focusedWindowId READ focusedWindowId NOTIFY focusedWindowIdChanged)
+ Q_PROPERTY(QVariant focusedWindow READ focusedWindow NOTIFY focusedWindowChanged)
+
+ Q_PROPERTY(int activeWorkspaceId READ activeWorkspaceId NOTIFY activeWorkspaceIdChanged)
Q_PROPERTY(QVariant activeWorkspace READ activeWorkspace NOTIFY activeWorkspaceChanged)
public:
explicit Workspaces(QObject *parent = nullptr);
- void classBegin() override;
- void componentComplete() override;
+ QVariantMap windows() const { return m_windows; }
+ QVariantMap workspaces() const { return m_workspaces; }
+ QVariant space() const { return m_space; }
+ int focusedWindowId() const { return m_focusedWindowId; }
+ QVariant focusedWindow() const { return m_focusedWindow; }
+ int activeWorkspaceId() const { return m_activeWorkspaceId; }
+ QVariant activeWorkspace() const { return m_activeWorkspace; }
- QVariantMap workspaces() const;
- QVariantMap space() const;
- int activeWorkspaceId() const;
- QVariant activeWorkspace() const;
+ Q_INVOKABLE void dispatch(const QString &command, const QVariant &args);
signals:
+ void windowsChanged();
void workspacesChanged();
void spaceChanged();
+ void focusedWindowIdChanged();
+ void focusedWindowChanged();
+ void activeWorkspaceIdChanged();
void activeWorkspaceChanged();
private slots:
- void handleNewEvent(const QVariant &event);
- void handleRequestResponse(const QVariant &response);
- void requestInitialState();
+ void handleEvent(const QVariant &eventVar);
private:
Ipc *m_ipc;
- QVariantMap m_workspaces;
- QVariantMap m_space;
- int m_activeWorkspaceId;
-};
-#endif // WORKSPACES_H
\ No newline at end of file
+ QVariantMap m_windows;
+ QVariantMap m_workspaces;
+ QVariant m_space;
+ int m_focusedWindowId = -1;
+ QVariant m_focusedWindow;
+ int m_activeWorkspaceId = -1;
+ QVariant m_activeWorkspace;
+};
\ No newline at end of file
diff --git a/src/share/sleex/services/Fhtc.qml b/src/share/sleex/services/Fhtc.qml
deleted file mode 100644
index 191f13d7..00000000
--- a/src/share/sleex/services/Fhtc.qml
+++ /dev/null
@@ -1,151 +0,0 @@
-pragma Singleton
-pragma ComponentBehavior: Bound
-
-import Quickshell
-import Quickshell.Io
-
-Singleton {
- id: root
-
- // The socket we connect to. This is the bridge between us and fht-compositor
- readonly property string socketPath: Quickshell.env("FHTC_SOCKET_PATH")
-
- // How things work here is that we store workspace and window data into separate maps,
- // and actual data structures (like Monitors, or even Workspaces) store IDs. The stored
- // maps are ID->Window/Workspace maps
- //
- // FIXME: Add typing to these.
- property var workspaces: ({})
- property var windows: ({})
- property var space: ({})
-
- // Focused window is the one that is currently receiving keyboard input. There can be multiple
- // active windows at once, but only one at max focused window can exist at a time.
- property int focusedWindowId: -1
- property var focusedWindow: null
- // The active workspace is the workspace that currently has the pointer on it. This usually means
- // the displayed workspace on the focused monitor.
- property int activeWorkspaceId: -1
- property var activeWorkspace: null
-
- property string activeWindowTitle: focusedWindow != null ? focusedWindow.title : ""
- property string activeWindowAppId: focusedWindow != null ? focusedWindow["app-id"] : ""
-
- Socket {
- id: subscribeSocket
- path: root.socketPath
- connected: true
-
- onConnectionStateChanged: {
- if (connected)
- // When we connect, start turn this socket immediatly into a subcribe
- // socket. Allowing us to actually use it.
- subscribeSocket.write('"subscribe"\n');
- }
-
- parser: SplitParser {
- onRead: line => {
- try {
- root.handleEvent(JSON.parse(line));
- } catch (err) {
- console.warn("FhtCompositor: failed to parse event: ", line, err);
- }
- }
- }
- }
-
- // The main event handler. The passed in `event` parameter is a fht-compositor-ipc::Event
- // https://github.com/nferhat/fht-compositor/blob/ff3d9f3b6549b38e99755d022f5343fda3d6a971/fht-compositor-ipc/src/lib.rs#L131
- function handleEvent(event) {
- switch (event.event) {
- case "windows":
- // Update the window list. The passed in data is a HashMap
- root.windows = event.data;
- root.windowsChanged();
- break;
- case "focused-window-changed":
- var newId = event.data.id;
- if (newId == null) {
- root.focusedWindowId = -1;
- root.focusedWindow = null;
- } else {
- // FIXME: Maybe this event could be sent before a WindowChanged event, in this
- // case this could lead to invalid state.
- root.focusedWindowId = newId;
- root.focusedWindow = root.windows[newId];
- }
-
- root.focusedWindowChanged();
- root.focusedWindowIdChanged();
-
- break;
- case "window-closed":
- // NOTE: the compositor will sent us a focused-window-changed event, so we don't
- // have to update the focusedWindow here.
- var id = event.data.id;
- delete root.windows[id];
-
- root.windowsChanged();
-
- break;
- case "window-changed":
- // This event could either be an existing window changing, or a new window opening
- const win = event.data;
- root.windows[win.id] = win;
-
- root.windowsChanged();
-
- break;
- case "workspaces":
- // Update the workspace list. The passed in data is a HashMap
- root.workspaces = event.data;
- root.workspacesChanged();
- break;
- case "active-workspace-changed":
- var newId = event.data.id;
- if (newId == null) {
- root.activeWorkspaceId = -1;
- root.activeWorkspace = null;
- } else {
- // FIXME: Maybe this event could be sent before a WorkspaceChanged event, in this
- // case this could lead to invalid state.
- root.activeWorkspaceId = newId;
- root.activeWorkspace = root.workspaces[newId];
- }
-
- root.activeWorkspaceChanged();
- root.activeWorkspaceIdChanged();
-
- break;
- case "workspace-changed":
- const ws = event.data;
- root.workspaces[ws.id] = ws;
- root.workspacesChanged();
-
- break;
- case "workspace-removed":
- var id = event.data.id;
- delete root.workspaces[id];
-
- root.workspacesChanged();
-
- break;
- case "space":
- root.space = event.data;
- root.spaceChanged();
- break;
- default:
- // console.warn("Unhandled fht-compositor event: ", event.event);
- break;
- }
- }
-
- function dispatch(command, args) {
- var cmd = {
- command: command,
- args: args
- };
-
- subscribeSocket.write(JSON.stringify(cmd) + "\n");
- }
-}
From 434c971b129a3cf6f5f1f5d1c866f8a7ba07dfcd Mon Sep 17 00:00:00 2001
From: Ardox
Date: Wed, 4 Feb 2026 14:11:29 +0100
Subject: [PATCH 11/56] removed FR comments
---
src/share/sleex/plugins/src/Sleex/fhtc/workspaces.cpp | 5 -----
1 file changed, 5 deletions(-)
diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.cpp b/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.cpp
index 325f790b..752eddc3 100644
--- a/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.cpp
+++ b/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.cpp
@@ -16,13 +16,11 @@ void Workspaces::dispatch(const QString &command, const QVariant &args)
cmd["command"] = command;
cmd["args"] = args;
- // Envoi via le socket de requête géré par Ipc
m_ipc->sendRequest(cmd);
}
void Workspaces::handleEvent(const QVariant &eventVar)
{
- // Conversion de l'ƩvƩnement reƧu (QVariant/JSON)
QVariantMap event = eventVar.toMap();
QString type = event.value("event").toString();
QVariant data = event.value("data");
@@ -33,7 +31,6 @@ void Workspaces::handleEvent(const QVariant &eventVar)
}
else if (type == "focused-window-changed") {
QVariantMap dataMap = data.toMap();
- // VƩrification si l'ID est null/undefined
QVariant idVar = dataMap.value("id");
if (idVar.isNull() || !idVar.isValid()) {
@@ -58,7 +55,6 @@ void Workspaces::handleEvent(const QVariant &eventVar)
m_windows.insert(QString::number(id), win);
emit windowsChanged();
- // Si c'est la fenêtre active qui a changé, on met à jour focusedWindow
if (id == m_focusedWindowId) {
m_focusedWindow = win;
emit focusedWindowChanged();
@@ -68,7 +64,6 @@ void Workspaces::handleEvent(const QVariant &eventVar)
m_workspaces = data.toMap();
emit workspacesChanged();
- // Rafraîchir l'activeWorkspace au cas où ses données auraient changé
if (m_activeWorkspaceId != -1) {
m_activeWorkspace = m_workspaces.value(QString::number(m_activeWorkspaceId));
emit activeWorkspaceChanged();
From 8eaf9523432efdd3025aa6a87dad9b3f8ab2dd2b Mon Sep 17 00:00:00 2001
From: Ardox
Date: Wed, 4 Feb 2026 14:19:44 +0100
Subject: [PATCH 12/56] Fix app title handling
---
src/share/sleex/modules/bar/ActiveWindow.qml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/share/sleex/modules/bar/ActiveWindow.qml b/src/share/sleex/modules/bar/ActiveWindow.qml
index db358966..1cd325db 100644
--- a/src/share/sleex/modules/bar/ActiveWindow.qml
+++ b/src/share/sleex/modules/bar/ActiveWindow.qml
@@ -27,7 +27,7 @@ Item {
font.pixelSize: Appearance.font.pixelSize.smaller
color: Appearance.colors.colSubtext
elide: Text.ElideRight
- text: Fhtc.focusedWindow['app-id'] !== "" ? Fhtc.focusedWindow['app-id'] : qsTr("Desktop")
+ text: Fhtc.focusedWindow !== undefined ? Fhtc.focusedWindow['app-id'] : qsTr("Desktop")
}
StyledText {
@@ -35,7 +35,7 @@ Item {
font.pixelSize: Appearance.font.pixelSize.small
color: Appearance.colors.colOnLayer0
elide: Text.ElideRight
- text: Fhtc.focusedWindow.title !== "" ? Fhtc.focusedWindow.title : `${qsTr("Workspace")} ${Fhtc.activeWorkspaceId + 1}`
+ text: Fhtc.focusedWindow !== undefined ? Fhtc.focusedWindow.title : `${qsTr("Workspace")} ${Fhtc.activeWorkspaceId + 1}`
}
}
From 8b2419c710356181a01e657b83ef9e23d5776967 Mon Sep 17 00:00:00 2001
From: Ardox
Date: Thu, 5 Feb 2026 09:27:44 +0100
Subject: [PATCH 13/56] Removed old FHTC services
---
.../sleex/plugins/src/Sleex/services/fhtc.cpp | 143 ------------------
.../sleex/plugins/src/Sleex/services/fhtc.hpp | 57 -------
2 files changed, 200 deletions(-)
delete mode 100644 src/share/sleex/plugins/src/Sleex/services/fhtc.cpp
delete mode 100644 src/share/sleex/plugins/src/Sleex/services/fhtc.hpp
diff --git a/src/share/sleex/plugins/src/Sleex/services/fhtc.cpp b/src/share/sleex/plugins/src/Sleex/services/fhtc.cpp
deleted file mode 100644
index d5c3ec98..00000000
--- a/src/share/sleex/plugins/src/Sleex/services/fhtc.cpp
+++ /dev/null
@@ -1,143 +0,0 @@
-#include "fhtc.hpp"
-#include
-#include
-#include
-
-FhtCompositor::FhtCompositor(QObject *parent)
- : QObject(parent)
-{
- QString socketPath = QProcessEnvironment::systemEnvironment().value("FHTC_SOCKET_PATH");
- if (socketPath.isEmpty()) {
- qWarning() << "FhtCompositor: FHTC_SOCKET_PATH not set.";
- return;
- }
-
- m_socket = new QTcpSocket(this);
- connect(m_socket, &QTcpSocket::readyRead, this, &FhtCompositor::onReadyRead);
-
- m_socket->connectToHost(socketPath, 0, QIODevice::ReadWrite);
-}
-
-void FhtCompositor::onReadyRead()
-{
- while (m_socket->canReadLine()) {
- QByteArray line = m_socket->readLine().trimmed();
- QJsonParseError err;
- QJsonDocument doc = QJsonDocument::fromJson(line, &err);
- if (err.error != QJsonParseError::NoError) {
- qWarning() << "FhtCompositor: failed to parse event:" << line << err.errorString();
- continue;
- }
-
- if (doc.isObject())
- handleEvent(doc.object());
- }
-}
-
-void FhtCompositor::handleEvent(const QJsonObject &event)
-{
- QString type = event.value("event").toString();
- QJsonValue data = event.value("data");
-
- enum EventType {
- Windows,
- FocusedWindowChanged,
- WindowClosed,
- WindowChanged,
- Workspaces,
- ActiveWorkspaceChanged,
- WorkspaceChanged,
- WorkspaceRemoved,
- Space,
- Unknown
- };
-
- static const QHash eventMap = {
- {"windows", Windows},
- {"focused-window-changed", FocusedWindowChanged},
- {"window-closed", WindowClosed},
- {"window-changed", WindowChanged},
- {"workspaces", Workspaces},
- {"active-workspace-changed", ActiveWorkspaceChanged},
- {"workspace-changed", WorkspaceChanged},
- {"workspace-removed", WorkspaceRemoved},
- {"space", Space}
- };
-
- switch (eventMap.value(type, Unknown)) {
- case Windows:
- m_windows = data.toObject().toVariantMap();
- emit windowsChanged();
- break;
-
- case FocusedWindowChanged: {
- int newId = data.toObject().value("id").toInt(-1);
- if (newId == -1) {
- m_focusedWindowId = -1;
- m_focusedWindow = QVariant();
- } else {
- m_focusedWindowId = newId;
- m_focusedWindow = m_windows.value(QString::number(newId));
- }
- emit focusedWindowIdChanged();
- emit focusedWindowChanged();
- break;
- }
-
- case WindowClosed: {
- QString id = QString::number(data.toObject().value("id").toInt());
- m_windows.remove(id);
- emit windowsChanged();
- break;
- }
-
- case WindowChanged: {
- QJsonObject win = data.toObject();
- m_windows[QString::number(win.value("id").toInt())] = win.toVariantMap();
- emit windowsChanged();
- break;
- }
-
- case Workspaces:
- m_workspaces = data.toObject().toVariantMap();
- emit workspacesChanged();
- break;
-
- case ActiveWorkspaceChanged: {
- int newId = data.toObject().value("id").toInt(-1);
- if (newId == -1) {
- m_activeWorkspaceId = -1;
- m_activeWorkspace = QVariant();
- } else {
- m_activeWorkspaceId = newId;
- m_activeWorkspace = m_workspaces.value(QString::number(newId));
- }
- emit activeWorkspaceIdChanged();
- emit activeWorkspaceChanged();
- break;
- }
-
- case WorkspaceChanged: {
- QJsonObject ws = data.toObject();
- m_workspaces[QString::number(ws.value("id").toInt())] = ws.toVariantMap();
- emit workspacesChanged();
- break;
- }
-
- case WorkspaceRemoved: {
- QString id = QString::number(data.toObject().value("id").toInt());
- m_workspaces.remove(id);
- emit workspacesChanged();
- break;
- }
-
- case Space:
- m_space = data.toVariant();
- emit spaceChanged();
- break;
-
- case Unknown:
- default:
- break;
- }
-}
diff --git a/src/share/sleex/plugins/src/Sleex/services/fhtc.hpp b/src/share/sleex/plugins/src/Sleex/services/fhtc.hpp
deleted file mode 100644
index b64b9cf1..00000000
--- a/src/share/sleex/plugins/src/Sleex/services/fhtc.hpp
+++ /dev/null
@@ -1,57 +0,0 @@
-#pragma once
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-class FhtCompositor : public QObject {
- Q_OBJECT
- QML_SINGLETON
- QML_ELEMENT
-
- Q_PROPERTY(QVariantMap windows READ windows NOTIFY windowsChanged)
- Q_PROPERTY(QVariantMap workspaces READ workspaces NOTIFY workspacesChanged)
- Q_PROPERTY(QVariant space READ space NOTIFY spaceChanged)
- Q_PROPERTY(int focusedWindowId READ focusedWindowId NOTIFY focusedWindowIdChanged)
- Q_PROPERTY(QVariant focusedWindow READ focusedWindow NOTIFY focusedWindowChanged)
- Q_PROPERTY(int activeWorkspaceId READ activeWorkspaceId NOTIFY activeWorkspaceIdChanged)
- Q_PROPERTY(QVariant activeWorkspace READ activeWorkspace NOTIFY activeWorkspaceChanged)
-
-public:
- explicit FhtCompositor(QObject *parent = nullptr);
-
- QVariantMap windows() const { return m_windows; }
- QVariantMap workspaces() const { return m_workspaces; }
- QVariant space() const { return m_space; }
- int focusedWindowId() const { return m_focusedWindowId; }
- QVariant focusedWindow() const { return m_focusedWindow; }
- int activeWorkspaceId() const { return m_activeWorkspaceId; }
- QVariant activeWorkspace() const { return m_activeWorkspace; }
-
-signals:
- void windowsChanged();
- void workspacesChanged();
- void spaceChanged();
- void focusedWindowIdChanged();
- void focusedWindowChanged();
- void activeWorkspaceIdChanged();
- void activeWorkspaceChanged();
-
-private slots:
- void onReadyRead();
- void handleEvent(const QJsonObject &event);
-
-private:
- QTcpSocket *m_socket = nullptr;
- QVariantMap m_windows;
- QVariantMap m_workspaces;
- QVariant m_space;
- int m_focusedWindowId = -1;
- QVariant m_focusedWindow;
- int m_activeWorkspaceId = -1;
- QVariant m_activeWorkspace;
-};
From 1c561d394a76b0fac70ce2250d27cf50256eb33f Mon Sep 17 00:00:00 2001
From: Ardox
Date: Thu, 5 Feb 2026 10:54:39 +0100
Subject: [PATCH 14/56] Added monitors FHTC binding
---
.../plugins/src/Sleex/fhtc/CMakeLists.txt | 1 +
.../sleex/plugins/src/Sleex/fhtc/monitors.cpp | 46 +++++++++++++++++++
.../sleex/plugins/src/Sleex/fhtc/monitors.hpp | 40 ++++++++++++++++
3 files changed, 87 insertions(+)
create mode 100644 src/share/sleex/plugins/src/Sleex/fhtc/monitors.cpp
create mode 100644 src/share/sleex/plugins/src/Sleex/fhtc/monitors.hpp
diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/CMakeLists.txt b/src/share/sleex/plugins/src/Sleex/fhtc/CMakeLists.txt
index 64aad482..50cb6f15 100644
--- a/src/share/sleex/plugins/src/Sleex/fhtc/CMakeLists.txt
+++ b/src/share/sleex/plugins/src/Sleex/fhtc/CMakeLists.txt
@@ -4,6 +4,7 @@ qml_module(sleex-fhtc
SOURCES
ipc.hpp ipc.cpp
workspaces.hpp workspaces.cpp
+ monitors.hpp monitors.cpp
plugin.cpp
)
diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/monitors.cpp b/src/share/sleex/plugins/src/Sleex/fhtc/monitors.cpp
new file mode 100644
index 00000000..05db8968
--- /dev/null
+++ b/src/share/sleex/plugins/src/Sleex/fhtc/monitors.cpp
@@ -0,0 +1,46 @@
+#include "monitors.hpp"
+#include
+
+Monitors::Monitors(QObject *parent) : QObject(parent)
+{
+ m_ipc = new Ipc(this);
+
+ connect(m_ipc, &Ipc::newEvent, this, &Monitors::handleEvent);
+
+ m_ipc->subscribe();
+}
+
+void Monitors::handleEvent(const QVariant &eventVar)
+{
+ QVariantMap event = eventVar.toMap();
+ QString type = event.value("event").toString();
+ QVariant data = event.value("data");
+
+ if (type == "space") {
+ QVariant data = event.value("data");
+ QVariantMap monitorsMap = data.toMap().value("monitors").toMap();
+
+ m_monitors = monitorsMap;
+
+ bool activeFound = false;
+ QMapIterator i(m_monitors);
+ while (i.hasNext()) {
+ i.next();
+ QVariantMap monitor = i.value().toMap();
+ if (monitor.value("active").toBool()) {
+ m_activeMonitor = monitor;
+ m_activeMonitorName = monitor.value("output").toString();
+ activeFound = true;
+ break;
+ }
+ }
+
+ if (!activeFound) {
+ m_activeMonitor = QVariant();
+ }
+
+ emit monitorsChanged();
+ emit activeMonitorChanged();
+ emit activeMonitorNameChanged();
+ }
+}
\ No newline at end of file
diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/monitors.hpp b/src/share/sleex/plugins/src/Sleex/fhtc/monitors.hpp
new file mode 100644
index 00000000..fd0a3fc6
--- /dev/null
+++ b/src/share/sleex/plugins/src/Sleex/fhtc/monitors.hpp
@@ -0,0 +1,40 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include "ipc.hpp"
+
+class Monitors : public QObject
+{
+ Q_OBJECT
+ QML_NAMED_ELEMENT(FhtcMonitors)
+ QML_SINGLETON
+
+ Q_PROPERTY(QVariantMap monitors READ monitors NOTIFY monitorsChanged)
+ Q_PROPERTY(QVariant activeMonitor READ activeMonitor NOTIFY activeMonitorChanged)
+ Q_PROPERTY(QString activeMonitorName READ activeMonitorName NOTIFY activeMonitorNameChanged)
+
+public:
+ explicit Monitors(QObject *parent = nullptr);
+
+ QVariantMap monitors() const { return m_monitors; }
+ QVariant activeMonitor() const { return m_activeMonitor; }
+ QString activeMonitorName() const { return m_activeMonitorName; }
+
+signals:
+ void monitorsChanged();
+ void activeMonitorChanged();
+ void activeMonitorNameChanged();
+
+private slots:
+ void handleEvent(const QVariant &eventVar);
+
+private:
+ Ipc *m_ipc;
+
+ QVariantMap m_monitors;
+ QVariant m_activeMonitor;
+ QString m_activeMonitorName;
+};
\ No newline at end of file
From 5679c726ae59d11963e56293a6ee0d659d91adad Mon Sep 17 00:00:00 2001
From: Ardox
Date: Thu, 5 Feb 2026 10:54:57 +0100
Subject: [PATCH 15/56] Renamed workspaces QML name
---
src/share/sleex/plugins/src/Sleex/fhtc/workspaces.hpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.hpp b/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.hpp
index 0d4460a7..19ab562f 100644
--- a/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.hpp
+++ b/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.hpp
@@ -9,7 +9,7 @@
class Workspaces : public QObject
{
Q_OBJECT
- QML_NAMED_ELEMENT(Fhtc)
+ QML_NAMED_ELEMENT(FhtcWorkspaces)
QML_SINGLETON
Q_PROPERTY(QVariantMap windows READ windows NOTIFY windowsChanged)
From d57a147fe34dc84a94abc64ca1c6c810e72de243 Mon Sep 17 00:00:00 2001
From: Ardox
Date: Thu, 5 Feb 2026 10:58:21 +0100
Subject: [PATCH 16/56] Using new fhtc module name
---
src/share/sleex/modules/bar/ActiveWindow.qml | 4 ++--
src/share/sleex/modules/bar/Workspaces.qml | 14 +++++++-------
2 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/src/share/sleex/modules/bar/ActiveWindow.qml b/src/share/sleex/modules/bar/ActiveWindow.qml
index 1cd325db..036123e1 100644
--- a/src/share/sleex/modules/bar/ActiveWindow.qml
+++ b/src/share/sleex/modules/bar/ActiveWindow.qml
@@ -27,7 +27,7 @@ Item {
font.pixelSize: Appearance.font.pixelSize.smaller
color: Appearance.colors.colSubtext
elide: Text.ElideRight
- text: Fhtc.focusedWindow !== undefined ? Fhtc.focusedWindow['app-id'] : qsTr("Desktop")
+ text: FhtcWorkspaces.focusedWindow !== undefined ? FhtcWorkspaces.focusedWindow['app-id'] : qsTr("Desktop")
}
StyledText {
@@ -35,7 +35,7 @@ Item {
font.pixelSize: Appearance.font.pixelSize.small
color: Appearance.colors.colOnLayer0
elide: Text.ElideRight
- text: Fhtc.focusedWindow !== undefined ? Fhtc.focusedWindow.title : `${qsTr("Workspace")} ${Fhtc.activeWorkspaceId + 1}`
+ text: FhtcWorkspaces.focusedWindow !== undefined ? FhtcWorkspaces.focusedWindow.title : `${qsTr("Workspace")} ${FhtcWorkspaces.activeWorkspaceId + 1}`
}
}
diff --git a/src/share/sleex/modules/bar/Workspaces.qml b/src/share/sleex/modules/bar/Workspaces.qml
index ee1632cd..12525684 100644
--- a/src/share/sleex/modules/bar/Workspaces.qml
+++ b/src/share/sleex/modules/bar/Workspaces.qml
@@ -16,22 +16,22 @@ import Sleex.Fhtc
Item {
required property var bar
property bool borderless: Config.options.bar.borderless
- readonly property var activeWindow: Fhtc.focusedWindow
+ readonly property var activeWindow: FhtcWorkspaces.focusedWindow
readonly property string screenName: bar.screen?.name ?? ""
// Get workspaces for this screen only, sorted by ID
readonly property var screenWorkspaces: {
- return Object.values(Fhtc.workspaces)
+ return Object.values(FhtcWorkspaces.workspaces)
.filter(ws => ws.output === screenName)
.sort((a, b) => a.id - b.id);
}
// Active workspace index within this screen (0-based)
readonly property int activeWorkspaceIndex: {
- if (!Fhtc.activeWorkspace) return -1;
- if (Fhtc.activeWorkspace.output !== screenName) return -1;
+ if (!FhtcWorkspaces.activeWorkspace) return -1;
+ if (FhtcWorkspaces.activeWorkspace.output !== screenName) return -1;
// Find the index of the active workspace in our sorted screen workspaces
- const idx = screenWorkspaces.findIndex(ws => ws.id === Fhtc.activeWorkspace.id);
+ const idx = screenWorkspaces.findIndex(ws => ws.id === FhtcWorkspaces.activeWorkspace.id);
// Return -1 if the workspace is beyond the shown limit
if (idx >= Config.options.bar.workspaces.shown) return -1;
return idx;
@@ -62,7 +62,7 @@ Item {
// Listen for changes in Fhtc.workspaces and windows
Connections {
- target: Fhtc
+ target: FhtcWorkspaces
function onWorkspacesChanged() {
updateWorkspaceOccupied();
}
@@ -207,7 +207,7 @@ Item {
property var biggestWindow: {
if (!button.workspace || !button.workspace.windows || button.workspace.windows.length === 0) return null;
const windowIds = button.workspace.windows;
- const windowsInThisWorkspace = windowIds.map(id => Fhtc.windows[id]).filter(w => w != null);
+ const windowsInThisWorkspace = windowIds.map(id => FhtcWorkspaces.windows[id]).filter(w => w != null);
return windowsInThisWorkspace.reduce((maxWin, win) => {
const maxArea = (maxWin?.size?.[0] ?? 0) * (maxWin?.size?.[1] ?? 0)
const winArea = (win?.size?.[0] ?? 0) * (win?.size?.[1] ?? 0)
From dad80b85b32f488fb0542b5ba0449d45d5ac942a Mon Sep 17 00:00:00 2001
From: Ardox
Date: Thu, 5 Feb 2026 11:43:02 +0100
Subject: [PATCH 17/56] Cleaned workspaces module
---
.../sleex/plugins/src/Sleex/fhtc/workspaces.cpp | 13 -------------
.../sleex/plugins/src/Sleex/fhtc/workspaces.hpp | 6 ------
2 files changed, 19 deletions(-)
diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.cpp b/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.cpp
index 752eddc3..145f2d5a 100644
--- a/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.cpp
+++ b/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.cpp
@@ -10,15 +10,6 @@ Workspaces::Workspaces(QObject *parent) : QObject(parent)
m_ipc->subscribe();
}
-void Workspaces::dispatch(const QString &command, const QVariant &args)
-{
- QVariantMap cmd;
- cmd["command"] = command;
- cmd["args"] = args;
-
- m_ipc->sendRequest(cmd);
-}
-
void Workspaces::handleEvent(const QVariant &eventVar)
{
QVariantMap event = eventVar.toMap();
@@ -100,8 +91,4 @@ void Workspaces::handleEvent(const QVariant &eventVar)
m_workspaces.remove(QString::number(id));
emit workspacesChanged();
}
- else if (type == "space") {
- m_space = data;
- emit spaceChanged();
- }
}
\ No newline at end of file
diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.hpp b/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.hpp
index 19ab562f..72fa77bc 100644
--- a/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.hpp
+++ b/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.hpp
@@ -14,7 +14,6 @@ class Workspaces : public QObject
Q_PROPERTY(QVariantMap windows READ windows NOTIFY windowsChanged)
Q_PROPERTY(QVariantMap workspaces READ workspaces NOTIFY workspacesChanged)
- Q_PROPERTY(QVariant space READ space NOTIFY spaceChanged)
Q_PROPERTY(int focusedWindowId READ focusedWindowId NOTIFY focusedWindowIdChanged)
Q_PROPERTY(QVariant focusedWindow READ focusedWindow NOTIFY focusedWindowChanged)
@@ -27,18 +26,14 @@ class Workspaces : public QObject
QVariantMap windows() const { return m_windows; }
QVariantMap workspaces() const { return m_workspaces; }
- QVariant space() const { return m_space; }
int focusedWindowId() const { return m_focusedWindowId; }
QVariant focusedWindow() const { return m_focusedWindow; }
int activeWorkspaceId() const { return m_activeWorkspaceId; }
QVariant activeWorkspace() const { return m_activeWorkspace; }
- Q_INVOKABLE void dispatch(const QString &command, const QVariant &args);
-
signals:
void windowsChanged();
void workspacesChanged();
- void spaceChanged();
void focusedWindowIdChanged();
void focusedWindowChanged();
void activeWorkspaceIdChanged();
@@ -52,7 +47,6 @@ private slots:
QVariantMap m_windows;
QVariantMap m_workspaces;
- QVariant m_space;
int m_focusedWindowId = -1;
QVariant m_focusedWindow;
int m_activeWorkspaceId = -1;
From 87ffb7da4d76982be098153df9af9b2b53a7755d Mon Sep 17 00:00:00 2001
From: Ardox
Date: Thu, 5 Feb 2026 11:48:04 +0100
Subject: [PATCH 18/56] [WIP OVERVIEW] fixed monitor selection logic
---
src/share/sleex/modules/overview/Overview.qml | 9 +++++----
.../sleex/modules/overview/OverviewWidget.qml | 11 ++++++-----
src/share/sleex/test.qml | 15 +++++++++++++++
3 files changed, 26 insertions(+), 9 deletions(-)
create mode 100644 src/share/sleex/test.qml
diff --git a/src/share/sleex/modules/overview/Overview.qml b/src/share/sleex/modules/overview/Overview.qml
index 54a5ddca..79ae9144 100644
--- a/src/share/sleex/modules/overview/Overview.qml
+++ b/src/share/sleex/modules/overview/Overview.qml
@@ -9,6 +9,7 @@ import Quickshell
import Quickshell.Io
import Quickshell.Wayland
import Quickshell.Hyprland
+import Sleex.Fhtc
Scope {
id: overviewScope
@@ -20,14 +21,14 @@ Scope {
id: root
required property var modelData
property string searchingText: ""
- readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.screen)
- property bool monitorIsFocused: (Hyprland.focusedMonitor?.id == monitor.id)
+ required property ShellScreen screen
+ property bool monitorIsFocused: (FhtcMonitors.activeMonitorName === screen.name)
screen: modelData
visible: GlobalStates.overviewOpen && monitorIsFocused
WlrLayershell.namespace: "quickshell:overview"
WlrLayershell.layer: WlrLayer.Overlay
- // WlrLayershell.keyboardFocus: GlobalStates.overviewOpen ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
+ WlrLayershell.keyboardFocus: GlobalStates.overviewOpen ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
color: "transparent"
mask: Region {
@@ -98,7 +99,7 @@ Scope {
}
Keys.onPressed: (event) => {
- if (eƧvent.key === Qt.Key_Escape) {
+ if (event.key === Qt.Key_Escape) {
GlobalStates.overviewOpen = false;
} else if (event.key === Qt.Key_Left) {
if (!root.searchingText) Hyprland.dispatch("workspace -1");
diff --git a/src/share/sleex/modules/overview/OverviewWidget.qml b/src/share/sleex/modules/overview/OverviewWidget.qml
index 0b8d73ca..debb4957 100644
--- a/src/share/sleex/modules/overview/OverviewWidget.qml
+++ b/src/share/sleex/modules/overview/OverviewWidget.qml
@@ -11,15 +11,16 @@ import Quickshell.Io
import Quickshell.Widgets
import Quickshell.Wayland
import Quickshell.Hyprland
+import Sleex.Fhtc
Item {
id: root
required property var panelWindow
- readonly property HyprlandMonitor monitor: Hyprland.monitorFor(panelWindow.screen)
+ readonly property var monitor: FhtcMonitors.activeMonitor
readonly property var toplevels: ToplevelManager.toplevels
readonly property int workspacesShown: Config.options.overview.numOfRows * Config.options.overview.numOfCols
- readonly property int workspaceGroup: Math.floor((monitor.activeWorkspace?.id - 1) / workspacesShown)
- property bool monitorIsFocused: (Hyprland.focusedMonitor?.id == monitor.id)
+ readonly property int workspaceGroup: Math.floor((monitor["active-workspace-idx"] - 1) / workspacesShown)
+ property bool monitorIsFocused: (FhtcMonitors.activeMonitorName === screen.name)
property var windows: HyprlandData.windowList
property var windowByAddress: HyprlandData.windowByAddress
property var windowAddresses: HyprlandData.addresses
@@ -113,7 +114,7 @@ Item {
if (root.draggingTargetWorkspace === -1) {
// Hyprland.dispatch(`exec qs ipc call overview close`)
GlobalStates.overviewOpen = false
- Hyprland.dispatch(`workspace ${workspaceValue}`)
+ Fhtc.dispatch(`workspace ${workspaceValue}`)
}
}
}
@@ -213,7 +214,7 @@ Item {
window.Drag.active = false
root.draggingFromWorkspace = -1
if (targetWorkspace !== -1 && targetWorkspace !== windowData?.workspace.id) {
- Hyprland.dispatch(`movetoworkspacesilent ${targetWorkspace}, address:${window.windowData?.address}`)
+ Fhtc.dispatch(`movetoworkspacesilent ${targetWorkspace}, address:${window.windowData?.address}`)
updateWindowPosition.restart()
}
else {
diff --git a/src/share/sleex/test.qml b/src/share/sleex/test.qml
new file mode 100644
index 00000000..ac3e9e20
--- /dev/null
+++ b/src/share/sleex/test.qml
@@ -0,0 +1,15 @@
+import Sleex.Fhtc
+import QtQuick
+
+Item {
+ Connections {
+ target: FhtcMonitors
+
+ function onMonitorsChanged() {
+ // console.log("UPDATE REĆU :", JSON.stringify(FhtcMonitors.activeMonitor));
+ console.log(FhtcMonitors.activeMonitorName);
+ console.log(JSON.stringify(FhtcMonitors.activeMonitor));
+ console.log(JSON.stringify(FhtcMonitors.monitors));
+ }
+ }
+}
\ No newline at end of file
From d64c5e36e7d350c86921be96b872eacd6a62567a Mon Sep 17 00:00:00 2001
From: Ardox
Date: Thu, 5 Feb 2026 11:48:22 +0100
Subject: [PATCH 19/56] Removed test file
---
src/share/sleex/test.qml | 15 ---------------
1 file changed, 15 deletions(-)
delete mode 100644 src/share/sleex/test.qml
diff --git a/src/share/sleex/test.qml b/src/share/sleex/test.qml
deleted file mode 100644
index ac3e9e20..00000000
--- a/src/share/sleex/test.qml
+++ /dev/null
@@ -1,15 +0,0 @@
-import Sleex.Fhtc
-import QtQuick
-
-Item {
- Connections {
- target: FhtcMonitors
-
- function onMonitorsChanged() {
- // console.log("UPDATE REĆU :", JSON.stringify(FhtcMonitors.activeMonitor));
- console.log(FhtcMonitors.activeMonitorName);
- console.log(JSON.stringify(FhtcMonitors.activeMonitor));
- console.log(JSON.stringify(FhtcMonitors.monitors));
- }
- }
-}
\ No newline at end of file
From 4b263b46339a222fbbb721c3b6b78ee2ef2fff4d Mon Sep 17 00:00:00 2001
From: Ardox
Date: Thu, 5 Feb 2026 14:36:42 +0100
Subject: [PATCH 20/56] Made the dispatch work
---
src/share/sleex/modules/bar/Workspaces.qml | 11 +++++-----
.../sleex/plugins/src/Sleex/fhtc/ipc.cpp | 21 +++++++++++++++----
.../sleex/plugins/src/Sleex/fhtc/ipc.hpp | 3 ++-
3 files changed, 25 insertions(+), 10 deletions(-)
diff --git a/src/share/sleex/modules/bar/Workspaces.qml b/src/share/sleex/modules/bar/Workspaces.qml
index 12525684..b32115f2 100644
--- a/src/share/sleex/modules/bar/Workspaces.qml
+++ b/src/share/sleex/modules/bar/Workspaces.qml
@@ -81,10 +81,11 @@ Item {
// Scroll to switch workspaces
WheelHandler {
onWheel: (event) => {
- if (event.angleDelta.y < 0)
- Quickshell.execDetached(["fht-compositor", "ipc", "action", "focus-next-workspace"]);
- else if (event.angleDelta.y > 0)
- Quickshell.execDetached(["fht-compositor", "ipc", "action", "focus-previous-workspace"]);
+ if (event.angleDelta.y < 0) {
+ FhtcIpc.dispatch("focus-next-workspace", { "output": null });
+ } else if (event.angleDelta.y > 0) {
+ FhtcIpc.dispatch("focus-previous-workspace", { "output": null });
+ }
}
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
}
@@ -193,7 +194,7 @@ Item {
Layout.fillHeight: true
onPressed: {
if (button.workspaceId >= 0) {
- Quickshell.execDetached(["fht-compositor", "ipc", "action", "focus-workspace", `${button.workspaceId}`]);
+ FhtcIpc.dispatch("focus-workspace-by-index", { "workspace_idx": button.workspaceId });
}
}
width: workspaceButtonWidth
diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/ipc.cpp b/src/share/sleex/plugins/src/Sleex/fhtc/ipc.cpp
index 392e6c21..a2b4454f 100644
--- a/src/share/sleex/plugins/src/Sleex/fhtc/ipc.cpp
+++ b/src/share/sleex/plugins/src/Sleex/fhtc/ipc.cpp
@@ -34,7 +34,6 @@ Ipc::~Ipc()
// Sockets are automatically destroyed because 'this' is their parent
}
-// --- Public slots ---
void Ipc::subscribe()
{
@@ -45,10 +44,24 @@ void Ipc::subscribe()
}
}
+void Ipc::dispatch(const QString &name, const QVariantMap &args)
+{
+ if (!args.isEmpty() || name.contains("workspace")) {
+ QVariantMap actionBody;
+ actionBody.insert(name, args);
+ sendAction(actionBody);
+ }
+ else {
+ sendAction(name);
+ }
+}
+
void Ipc::sendRequest(const QVariant &request)
{
if (m_socketPath.isEmpty()) return;
+ // qDebug() << "Ipc: Queuing request:" << request;
+
m_pendingRequests.enqueue(request);
if (m_requestSocket->state() == QLocalSocket::UnconnectedState) {
qDebug() << "Ipc: Connecting to the request socket...";
@@ -67,7 +80,6 @@ void Ipc::sendAction(const QVariant &action)
sendRequest(request);
}
-// --- Private slots: Request socket ---
void Ipc::onRequestConnected()
{
@@ -86,6 +98,8 @@ void Ipc::onRequestReadyRead()
// We do not loop, we just wait for a complete line.
QVariant response = parseLine(m_requestBuffer);
if (!response.isNull()) {
+ // qDebug() << "Ipc: Received response:" << response;
+
emit requestResponse(response);
// If there are other requests, send them
@@ -108,12 +122,11 @@ void Ipc::onRequestDisconnected()
{
qDebug() << "Ipc: Request socket disconnected.";
if (!m_requestBuffer.isEmpty()) {
- qWarning() << "Ipc (Request): DDisconnected with data in buffer:" << m_requestBuffer;
+ qWarning() << "Ipc (Request): Disconnected with data in buffer:" << m_requestBuffer;
m_requestBuffer.clear();
}
}
-// --- Private slots: Event socket ---
void Ipc::onEventConnected()
{
diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/ipc.hpp b/src/share/sleex/plugins/src/Sleex/fhtc/ipc.hpp
index c1cdad4d..ca5abb6f 100644
--- a/src/share/sleex/plugins/src/Sleex/fhtc/ipc.hpp
+++ b/src/share/sleex/plugins/src/Sleex/fhtc/ipc.hpp
@@ -10,7 +10,7 @@
class Ipc : public QObject
{
Q_OBJECT
- QML_NAMED_ELEMENT(Ipc)
+ QML_NAMED_ELEMENT(FhtcIpc)
QML_SINGLETON
public:
@@ -27,6 +27,7 @@ public slots:
void subscribe();
void sendRequest(const QVariant &request);
void sendAction(const QVariant &action);
+ void dispatch(const QString &name, const QVariantMap &args = {});
private slots:
void onRequestConnected();
From 5afc5c9ba925db2ca0f3f7509eb7987748d315c9 Mon Sep 17 00:00:00 2001
From: Ardox
Date: Thu, 5 Feb 2026 14:39:26 +0100
Subject: [PATCH 21/56] ws: remove unnecessary output parameter
---
src/share/sleex/modules/bar/Workspaces.qml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/share/sleex/modules/bar/Workspaces.qml b/src/share/sleex/modules/bar/Workspaces.qml
index b32115f2..621902a1 100644
--- a/src/share/sleex/modules/bar/Workspaces.qml
+++ b/src/share/sleex/modules/bar/Workspaces.qml
@@ -82,9 +82,9 @@ Item {
WheelHandler {
onWheel: (event) => {
if (event.angleDelta.y < 0) {
- FhtcIpc.dispatch("focus-next-workspace", { "output": null });
+ FhtcIpc.dispatch("focus-next-workspace", {});
} else if (event.angleDelta.y > 0) {
- FhtcIpc.dispatch("focus-previous-workspace", { "output": null });
+ FhtcIpc.dispatch("focus-previous-workspace", {});
}
}
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
From 65e69a2bf903e7e537c57cace54c8d2d90d6285d Mon Sep 17 00:00:00 2001
From: Ardox
Date: Sun, 15 Mar 2026 10:13:07 +0100
Subject: [PATCH 22/56] Uncomment shortcuts
---
src/share/sleex/GlobalStates.qml | 40 ++--
.../sleex/modules/cheatsheet/Cheatsheet.qml | 43 +++--
.../sleex/modules/dashboard/Dashboard.qml | 42 ++---
.../OnScreenDisplayBrightness.qml | 31 ++--
.../onScreenDisplay/OnScreenDisplayVolume.qml | 31 ++--
src/share/sleex/modules/overview/Overview.qml | 173 +++++++++---------
src/share/sleex/modules/session/Session.qml | 29 ++-
.../sleex/modules/sidebarLeft/SidebarLeft.qml | 69 ++++---
.../wallpaperSelector/WallpaperSelector.qml | 47 +++--
src/share/sleex/services/Brightness.qml | 22 +--
10 files changed, 260 insertions(+), 267 deletions(-)
diff --git a/src/share/sleex/GlobalStates.qml b/src/share/sleex/GlobalStates.qml
index b09b7500..68c1a47a 100644
--- a/src/share/sleex/GlobalStates.qml
+++ b/src/share/sleex/GlobalStates.qml
@@ -41,18 +41,18 @@ Singleton {
}
}
- // GlobalShortcut {
- // name: "workspaceNumber"
- // description: qsTr("Hold to show workspace numbers, release to show icons")
-
- // onPressed: {
- // workspaceShowNumbersTimer.start()
- // }
- // onReleased: {
- // workspaceShowNumbersTimer.stop()
- // workspaceShowNumbers = false
- // }
- // }
+ GlobalShortcut {
+ name: "workspaceNumber"
+ description: qsTr("Hold to show workspace numbers, release to show icons")
+
+ onPressed: {
+ workspaceShowNumbersTimer.start()
+ }
+ onReleased: {
+ workspaceShowNumbersTimer.stop()
+ workspaceShowNumbers = false
+ }
+ }
IpcHandler {
target: "zoom"
@@ -74,14 +74,14 @@ Singleton {
root.screenLocked = true;
}
}
- // GlobalShortcut {
- // name: "lockScreen"
- // description: qsTr("Lock screen (obviously)")
-
- // onPressed: {
- // root.screenLocked = true;
- // }
- // }
+ GlobalShortcut {
+ name: "lockScreen"
+ description: qsTr("Lock screen (obviously)")
+
+ onPressed: {
+ root.screenLocked = true;
+ }
+ }
IpcHandler {
target: "background"
diff --git a/src/share/sleex/modules/cheatsheet/Cheatsheet.qml b/src/share/sleex/modules/cheatsheet/Cheatsheet.qml
index 3f7fcfc4..4ae33395 100644
--- a/src/share/sleex/modules/cheatsheet/Cheatsheet.qml
+++ b/src/share/sleex/modules/cheatsheet/Cheatsheet.qml
@@ -135,31 +135,30 @@ Scope { // Scope
}
}
- // GlobalShortcut {
- // name: "cheatsheetToggle"
- // description: qsTr("Toggles cheatsheet on press")
+ GlobalShortcut {
+ name: "cheatsheetToggle"
+ description: qsTr("Toggles cheatsheet on press")
- // onPressed: {
- // cheatsheetLoader.active = !cheatsheetLoader.active;
- // }
- // }
-
- // GlobalShortcut {
- // name: "cheatsheetOpen"
- // description: qsTr("Opens cheatsheet on press")
+ onPressed: {
+ cheatsheetLoader.active = !cheatsheetLoader.active;
+ }
+ }
- // onPressed: {
- // cheatsheetLoader.active = true;
- // }
- // }
+ GlobalShortcut {
+ name: "cheatsheetOpen"
+ description: qsTr("Opens cheatsheet on press")
- // GlobalShortcut {
- // name: "cheatsheetClose"
- // description: qsTr("Closes cheatsheet on press")
+ onPressed: {
+ cheatsheetLoader.active = true;
+ }
+ }
- // onPressed: {
- // cheatsheetLoader.active = false;
- // }
- // }
+ GlobalShortcut {
+ name: "cheatsheetClose"
+ description: qsTr("Closes cheatsheet on press")
+ onPressed: {
+ cheatsheetLoader.active = false;
+ }
+ }
}
diff --git a/src/share/sleex/modules/dashboard/Dashboard.qml b/src/share/sleex/modules/dashboard/Dashboard.qml
index 519fa347..8df2cc8a 100644
--- a/src/share/sleex/modules/dashboard/Dashboard.qml
+++ b/src/share/sleex/modules/dashboard/Dashboard.qml
@@ -208,25 +208,25 @@ Scope {
}
}
- // GlobalShortcut {
- // name: "dashboardToggle"
- // description: qsTr("Toggles dashboard on press")
- // onPressed: {
- // GlobalStates.dashboardOpen = !GlobalStates.dashboardOpen;
- // if(GlobalStates.dashboardOpen) Notifications.timeoutAll();
- // }
- // }
- // GlobalShortcut {
- // name: "dashboardOpen"
- // description: qsTr("Opens dashboard on press")
- // onPressed: {
- // GlobalStates.dashboardOpen = true;
- // Notifications.timeoutAll();
- // }
- // }
- // GlobalShortcut {
- // name: "dashboardClose"
- // description: qsTr("Closes dashboard on press")
- // onPressed: { GlobalStates.dashboardOpen = false; }
- // }
+ GlobalShortcut {
+ name: "dashboardToggle"
+ description: qsTr("Toggles dashboard on press")
+ onPressed: {
+ GlobalStates.dashboardOpen = !GlobalStates.dashboardOpen;
+ if(GlobalStates.dashboardOpen) Notifications.timeoutAll();
+ }
+ }
+ GlobalShortcut {
+ name: "dashboardOpen"
+ description: qsTr("Opens dashboard on press")
+ onPressed: {
+ GlobalStates.dashboardOpen = true;
+ Notifications.timeoutAll();
+ }
+ }
+ GlobalShortcut {
+ name: "dashboardClose"
+ description: qsTr("Closes dashboard on press")
+ onPressed: { GlobalStates.dashboardOpen = false; }
+ }
}
diff --git a/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayBrightness.qml b/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayBrightness.qml
index f99b8f24..94d7018b 100644
--- a/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayBrightness.qml
+++ b/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayBrightness.qml
@@ -132,21 +132,20 @@ Scope {
}
}
- // GlobalShortcut {
- // name: "osdBrightnessTrigger"
- // description: qsTr("Triggers brightness OSD on press")
-
- // onPressed: {
- // root.triggerOsd()
- // }
- // }
- // GlobalShortcut {
- // name: "osdBrightnessHide"
- // description: qsTr("Hides brightness OSD on press")
-
- // onPressed: {
- // root.showOsdValues = false
- // }
- // }
+ GlobalShortcut {
+ name: "osdBrightnessTrigger"
+ description: qsTr("Triggers brightness OSD on press")
+ onPressed: {
+ root.triggerOsd()
+ }
+ }
+ GlobalShortcut {
+ name: "osdBrightnessHide"
+ description: qsTr("Hides brightness OSD on press")
+
+ onPressed: {
+ root.showOsdValues = false
+ }
+ }
}
\ No newline at end of file
diff --git a/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayVolume.qml b/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayVolume.qml
index ac40827f..8c47e0c5 100644
--- a/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayVolume.qml
+++ b/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayVolume.qml
@@ -184,21 +184,20 @@ Scope {
showOsdValues = !showOsdValues
}
}
- // GlobalShortcut {
- // name: "osdVolumeTrigger"
- // description: qsTr("Triggers volume OSD on press")
-
- // onPressed: {
- // root.triggerOsd()
- // }
- // }
- // GlobalShortcut {
- // name: "osdVolumeHide"
- // description: qsTr("Hides volume OSD on press")
-
- // onPressed: {
- // root.showOsdValues = false
- // }
- // }
+ GlobalShortcut {
+ name: "osdVolumeTrigger"
+ description: qsTr("Triggers volume OSD on press")
+ onPressed: {
+ root.triggerOsd()
+ }
+ }
+ GlobalShortcut {
+ name: "osdVolumeHide"
+ description: qsTr("Hides volume OSD on press")
+
+ onPressed: {
+ root.showOsdValues = false
+ }
+ }
}
\ No newline at end of file
diff --git a/src/share/sleex/modules/overview/Overview.qml b/src/share/sleex/modules/overview/Overview.qml
index 79ae9144..c86a3e09 100644
--- a/src/share/sleex/modules/overview/Overview.qml
+++ b/src/share/sleex/modules/overview/Overview.qml
@@ -151,92 +151,91 @@ Scope {
}
}
- // GlobalShortcut {
- // name: "overviewToggle"
- // description: qsTr("Toggles overview on press")
-
- // onPressed: {
- // GlobalStates.overviewOpen = !GlobalStates.overviewOpen
- // }
- // }
- // GlobalShortcut {
- // name: "overviewClose"
- // description: qsTr("Closes overview")
-
- // onPressed: {
- // GlobalStates.overviewOpen = false
- // }
- // }
- // GlobalShortcut {
- // name: "overviewToggleRelease"
- // description: qsTr("Toggles overview on release")
-
- // onPressed: {
- // GlobalStates.superReleaseMightTrigger = true
- // }
-
- // onReleased: {
- // if (!GlobalStates.superReleaseMightTrigger) {
- // GlobalStates.superReleaseMightTrigger = true
- // return
- // }
- // GlobalStates.overviewOpen = !GlobalStates.overviewOpen
- // }
- // }
- // GlobalShortcut {
- // name: "overviewToggleReleaseInterrupt"
- // description: qsTr("Interrupts possibility of overview being toggled on release. ") +
- // qsTr("This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. ") +
- // qsTr("To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything.")
-
- // onPressed: {
- // GlobalStates.superReleaseMightTrigger = false
- // }
- // }
- // GlobalShortcut {
- // name: "overviewClipboardToggle"
- // description: qsTr("Toggle clipboard query on overview widget")
-
- // onPressed: {
- // if (GlobalStates.overviewOpen && overviewScope.dontAutoCancelSearch) {
- // GlobalStates.overviewOpen = false;
- // return;
- // }
- // for (let i = 0; i < overviewVariants.instances.length; i++) {
- // let panelWindow = overviewVariants.instances[i];
- // if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
- // overviewScope.dontAutoCancelSearch = true;
- // panelWindow.setSearchingText(
- // Config.options.search.prefix.clipboard
- // );
- // GlobalStates.overviewOpen = true;
- // return
- // }
- // }
- // }
- // }
-
- // GlobalShortcut {
- // name: "overviewEmojiToggle"
- // description: qsTr("Toggle emoji query on overview widget")
-
- // onPressed: {
- // if (GlobalStates.overviewOpen && overviewScope.dontAutoCancelSearch) {
- // GlobalStates.overviewOpen = false;
- // return;
- // }
- // for (let i = 0; i < overviewVariants.instances.length; i++) {
- // let panelWindow = overviewVariants.instances[i];
- // if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
- // overviewScope.dontAutoCancelSearch = true;
- // panelWindow.setSearchingText(
- // Config.options.search.prefix.emojis
- // );
- // GlobalStates.overviewOpen = true;
- // return
- // }
- // }
- // }
- // }
+ GlobalShortcut {
+ name: "overviewToggle"
+ description: qsTr("Toggles overview on press")
+ onPressed: {
+ GlobalStates.overviewOpen = !GlobalStates.overviewOpen
+ }
+ }
+ GlobalShortcut {
+ name: "overviewClose"
+ description: qsTr("Closes overview")
+
+ onPressed: {
+ GlobalStates.overviewOpen = false
+ }
+ }
+ GlobalShortcut {
+ name: "overviewToggleRelease"
+ description: qsTr("Toggles overview on release")
+
+ onPressed: {
+ GlobalStates.superReleaseMightTrigger = true
+ }
+
+ onReleased: {
+ if (!GlobalStates.superReleaseMightTrigger) {
+ GlobalStates.superReleaseMightTrigger = true
+ return
+ }
+ GlobalStates.overviewOpen = !GlobalStates.overviewOpen
+ }
+ }
+ GlobalShortcut {
+ name: "overviewToggleReleaseInterrupt"
+ description: qsTr("Interrupts possibility of overview being toggled on release. ") +
+ qsTr("This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. ") +
+ qsTr("To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything.")
+
+ onPressed: {
+ GlobalStates.superReleaseMightTrigger = false
+ }
+ }
+ GlobalShortcut {
+ name: "overviewClipboardToggle"
+ description: qsTr("Toggle clipboard query on overview widget")
+
+ onPressed: {
+ if (GlobalStates.overviewOpen && overviewScope.dontAutoCancelSearch) {
+ GlobalStates.overviewOpen = false;
+ return;
+ }
+ for (let i = 0; i < overviewVariants.instances.length; i++) {
+ let panelWindow = overviewVariants.instances[i];
+ if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
+ overviewScope.dontAutoCancelSearch = true;
+ panelWindow.setSearchingText(
+ Config.options.search.prefix.clipboard
+ );
+ GlobalStates.overviewOpen = true;
+ return
+ }
+ }
+ }
+ }
+
+ GlobalShortcut {
+ name: "overviewEmojiToggle"
+ description: qsTr("Toggle emoji query on overview widget")
+
+ onPressed: {
+ if (GlobalStates.overviewOpen && overviewScope.dontAutoCancelSearch) {
+ GlobalStates.overviewOpen = false;
+ return;
+ }
+ for (let i = 0; i < overviewVariants.instances.length; i++) {
+ let panelWindow = overviewVariants.instances[i];
+ if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
+ overviewScope.dontAutoCancelSearch = true;
+ panelWindow.setSearchingText(
+ Config.options.search.prefix.emojis
+ );
+ GlobalStates.overviewOpen = true;
+ return
+ }
+ }
+ }
+ }
}
diff --git a/src/share/sleex/modules/session/Session.qml b/src/share/sleex/modules/session/Session.qml
index 4dcccd5c..b52d47ec 100644
--- a/src/share/sleex/modules/session/Session.qml
+++ b/src/share/sleex/modules/session/Session.qml
@@ -207,22 +207,21 @@ Scope {
}
}
- // GlobalShortcut {
- // name: "sessionToggle"
- // description: qsTr("Toggles session screen on press")
+ GlobalShortcut {
+ name: "sessionToggle"
+ description: qsTr("Toggles session screen on press")
- // onPressed: {
- // sessionLoader.active = !sessionLoader.active;
- // }
- // }
-
- // GlobalShortcut {
- // name: "sessionOpen"
- // description: qsTr("Opens session screen on press")
+ onPressed: {
+ sessionLoader.active = !sessionLoader.active;
+ }
+ }
- // onPressed: {
- // sessionLoader.active = true;
- // }
- // }
+ GlobalShortcut {
+ name: "sessionOpen"
+ description: qsTr("Opens session screen on press")
+ onPressed: {
+ sessionLoader.active = true;
+ }
+ }
}
diff --git a/src/share/sleex/modules/sidebarLeft/SidebarLeft.qml b/src/share/sleex/modules/sidebarLeft/SidebarLeft.qml
index abbd7757..268851bb 100644
--- a/src/share/sleex/modules/sidebarLeft/SidebarLeft.qml
+++ b/src/share/sleex/modules/sidebarLeft/SidebarLeft.qml
@@ -164,40 +164,39 @@ Scope { // Scope
}
}
- // GlobalShortcut {
- // name: "sidebarLeftToggle"
- // description: qsTr("Toggles left sidebar on press")
-
- // onPressed: {
- // GlobalStates.sidebarLeftOpen = !GlobalStates.sidebarLeftOpen;
- // }
- // }
-
- // GlobalShortcut {
- // name: "sidebarLeftOpen"
- // description: qsTr("Opens left sidebar on press")
-
- // onPressed: {
- // GlobalStates.sidebarLeftOpen = true;
- // }
- // }
-
- // GlobalShortcut {
- // name: "sidebarLeftClose"
- // description: qsTr("Closes left sidebar on press")
-
- // onPressed: {
- // GlobalStates.sidebarLeftOpen = false;
- // }
- // }
-
- // GlobalShortcut {
- // name: "sidebarLeftToggleDetach"
- // description: qsTr("Detach left sidebar into a window/Attach it back")
-
- // onPressed: {
- // root.detach = !root.detach;
- // }
- // }
+ GlobalShortcut {
+ name: "sidebarLeftToggle"
+ description: qsTr("Toggles left sidebar on press")
+ onPressed: {
+ GlobalStates.sidebarLeftOpen = !GlobalStates.sidebarLeftOpen;
+ }
+ }
+
+ GlobalShortcut {
+ name: "sidebarLeftOpen"
+ description: qsTr("Opens left sidebar on press")
+
+ onPressed: {
+ GlobalStates.sidebarLeftOpen = true;
+ }
+ }
+
+ GlobalShortcut {
+ name: "sidebarLeftClose"
+ description: qsTr("Closes left sidebar on press")
+
+ onPressed: {
+ GlobalStates.sidebarLeftOpen = false;
+ }
+ }
+
+ GlobalShortcut {
+ name: "sidebarLeftToggleDetach"
+ description: qsTr("Detach left sidebar into a window/Attach it back")
+
+ onPressed: {
+ root.detach = !root.detach;
+ }
+ }
}
diff --git a/src/share/sleex/modules/wallpaperSelector/WallpaperSelector.qml b/src/share/sleex/modules/wallpaperSelector/WallpaperSelector.qml
index f1f3a992..22f501e3 100644
--- a/src/share/sleex/modules/wallpaperSelector/WallpaperSelector.qml
+++ b/src/share/sleex/modules/wallpaperSelector/WallpaperSelector.qml
@@ -169,29 +169,28 @@ Scope {
}
}
- // GlobalShortcut {
- // name: "wppselectorToggle"
- // description: qsTr("Toggles wallpaper selector on press")
-
- // onPressed: {
- // GlobalStates.wppselectorOpen = !GlobalStates.wppselectorOpen;
- // }
- // }
- // GlobalShortcut {
- // name: "wppselectorOpen"
- // description: qsTr("Opens wallpaper selector on press")
-
- // onPressed: {
- // GlobalStates.wppselectorOpen = true;
- // }
- // }
- // GlobalShortcut {
- // name: "wppselectorClose"
- // description: qsTr("Closes wallpaper selector on press")
-
- // onPressed: {
- // GlobalStates.wppselectorOpen = false;
- // }
- // }
+ GlobalShortcut {
+ name: "wppselectorToggle"
+ description: qsTr("Toggles wallpaper selector on press")
+ onPressed: {
+ GlobalStates.wppselectorOpen = !GlobalStates.wppselectorOpen;
+ }
+ }
+ GlobalShortcut {
+ name: "wppselectorOpen"
+ description: qsTr("Opens wallpaper selector on press")
+
+ onPressed: {
+ GlobalStates.wppselectorOpen = true;
+ }
+ }
+ GlobalShortcut {
+ name: "wppselectorClose"
+ description: qsTr("Closes wallpaper selector on press")
+
+ onPressed: {
+ GlobalStates.wppselectorOpen = false;
+ }
+ }
}
diff --git a/src/share/sleex/services/Brightness.qml b/src/share/sleex/services/Brightness.qml
index 9b8c4fa4..552b2f88 100644
--- a/src/share/sleex/services/Brightness.qml
+++ b/src/share/sleex/services/Brightness.qml
@@ -145,15 +145,15 @@ Singleton {
}
}
- // GlobalShortcut {
- // name: "brightnessIncrease"
- // description: qsTr("Increase brightness")
- // onPressed: root.increaseBrightness()
- // }
-
- // GlobalShortcut {
- // name: "brightnessDecrease"
- // description: qsTr("Decrease brightness")
- // onPressed: root.decreaseBrightness()
- // }
+ GlobalShortcut {
+ name: "brightnessIncrease"
+ description: qsTr("Increase brightness")
+ onPressed: root.increaseBrightness()
+ }
+
+ GlobalShortcut {
+ name: "brightnessDecrease"
+ description: qsTr("Decrease brightness")
+ onPressed: root.decreaseBrightness()
+ }
}
From bb1d90db14961117c2ae0c4f8173dcb6842538a0 Mon Sep 17 00:00:00 2001
From: Ardox
Date: Sun, 15 Mar 2026 10:59:06 +0100
Subject: [PATCH 23/56] Added .vscode to gitignore
---
.gitignore | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/.gitignore b/.gitignore
index 50b4af4b..e4e69e73 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
*.tar.zst
-pkg/
\ No newline at end of file
+pkg/
+.vscode/
\ No newline at end of file
From 670df702987abf3baedb453f6f59e30e7e93a7a5 Mon Sep 17 00:00:00 2001
From: Ardox
Date: Sun, 15 Mar 2026 10:59:22 +0100
Subject: [PATCH 24/56] Fixed workspace icons
---
src/share/sleex/modules/bar/Workspaces.qml | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/share/sleex/modules/bar/Workspaces.qml b/src/share/sleex/modules/bar/Workspaces.qml
index 97c30715..7c7eab43 100644
--- a/src/share/sleex/modules/bar/Workspaces.qml
+++ b/src/share/sleex/modules/bar/Workspaces.qml
@@ -220,12 +220,12 @@ Item {
return winArea > maxArea ? win : maxWin
}, null)
}
- property var mainAppIconSource: Quickshell.iconPath(AppSearch.guessIcon(biggestWindow?.class), "image-missing")
+ property var mainAppIconSource: Quickshell.iconPath(AppSearch.guessIcon(biggestWindow?.["app-id"]), "image-missing")
property string materialIconName: {
if (!biggestWindow) return ""
- const winClass = biggestWindow.class.toLowerCase()
+ const winClass = biggestWindow?.["app-id"]
const map = {
"language": ["firefox", "chromium", "google-chrome", "brave", "edge", "vivaldi", "qutebrowser", "librewolf", "zen-browser"],
"terminal": ["foot", "kitty", "alacritty", "wezterm", "gnome-terminal", "konsole", "xfce4-terminal", "xterm"],
@@ -336,8 +336,8 @@ Item {
(workspaceButtonWidth - workspaceIconSize) / 2 - 2 : workspaceIconMarginShrinked
anchors.rightMargin: (!GlobalStates.workspaceShowNumbers) ?
(workspaceButtonWidth - workspaceIconSize) / 2 : workspaceIconMarginShrinked
- color: (monitor.activeWorkspace?.id == button.workspaceValue) ?
- Appearance.m3colors.m3onPrimary : Appearance.m3colors.m3onSecondaryContainer
+ color: (activeWorkspaceIndex == index) ?
+ Appearance.m3colors.m3onPrimary : Appearance.colors.colOnSecondaryContainer
Behavior on opacity {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
From dcec76361f9c0ec2a00f8a29ad2d85fc473194ef Mon Sep 17 00:00:00 2001
From: Ardox
Date: Sun, 15 Mar 2026 11:03:33 +0100
Subject: [PATCH 25/56] Remove unused properties from Workspaces.qml
---
src/share/sleex/modules/bar/Workspaces.qml | 4 ----
1 file changed, 4 deletions(-)
diff --git a/src/share/sleex/modules/bar/Workspaces.qml b/src/share/sleex/modules/bar/Workspaces.qml
index 7c7eab43..bc923bb8 100644
--- a/src/share/sleex/modules/bar/Workspaces.qml
+++ b/src/share/sleex/modules/bar/Workspaces.qml
@@ -16,8 +16,6 @@ import Sleex.Fhtc
Item {
id: root
required property var bar
- property bool borderless: Config.options.bar.borderless
- readonly property var activeWindow: FhtcWorkspaces.focusedWindow
readonly property string screenName: bar.screen?.name ?? ""
// Get workspaces for this screen only, sorted by ID
@@ -39,14 +37,12 @@ Item {
}
property list workspaceOccupied: []
- property int widgetPadding: 0
property int horizontalPadding: 5
property int workspaceButtonWidth: 30
property real workspaceIconSize: workspaceButtonWidth * 0.6
property real workspaceIconSizeShrinked: workspaceButtonWidth * 0.55
property real workspaceIconOpacityShrinked: 1
property real workspaceIconMarginShrinked: -4
- property int workspaceIndexInGroup: (monitor.activeWorkspace?.id - 1) % Config.options.bar.workspaces.shown
property bool useMaterialIcons: Config.options.bar.workspaces.useMaterialIcons
From 321661b2bee5ff1534e88bd903fa1d5ea839c57a Mon Sep 17 00:00:00 2001
From: Ardox
Date: Sun, 15 Mar 2026 11:15:38 +0100
Subject: [PATCH 26/56] Update focusedScreen property to use
FhtcMonitors.activeMonitorName
---
src/share/sleex/modules/session/Session.qml | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/share/sleex/modules/session/Session.qml b/src/share/sleex/modules/session/Session.qml
index b52d47ec..9d7a50bf 100644
--- a/src/share/sleex/modules/session/Session.qml
+++ b/src/share/sleex/modules/session/Session.qml
@@ -10,10 +10,11 @@ import Quickshell.Io
import Quickshell.Widgets
import Quickshell.Wayland
import Quickshell.Hyprland
+import Sleex.Fhtc
Scope {
id: root
- property var focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name)
+ property var focusedScreen: Quickshell.screens.find(s => s.name === FhtcMonitors.activeMonitorName)
Loader {
id: sessionLoader
From 8bf1717b98977951ce30f09fd67a1d9c1bc4b3ca Mon Sep 17 00:00:00 2001
From: Ardox
Date: Sun, 15 Mar 2026 13:17:30 +0100
Subject: [PATCH 27/56] Overview: made it work for FHTC
---
src/share/sleex/modules/overview/Overview.qml | 4 +-
.../sleex/modules/overview/OverviewWidget.qml | 86 ++++++++-----------
.../sleex/modules/overview/OverviewWindow.qml | 10 +--
3 files changed, 42 insertions(+), 58 deletions(-)
diff --git a/src/share/sleex/modules/overview/Overview.qml b/src/share/sleex/modules/overview/Overview.qml
index c86a3e09..ca55ecc1 100644
--- a/src/share/sleex/modules/overview/Overview.qml
+++ b/src/share/sleex/modules/overview/Overview.qml
@@ -204,7 +204,7 @@ Scope {
}
for (let i = 0; i < overviewVariants.instances.length; i++) {
let panelWindow = overviewVariants.instances[i];
- if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
+ if (panelWindow.modelData.name == FhtcMonitors.activeMonitorName) {
overviewScope.dontAutoCancelSearch = true;
panelWindow.setSearchingText(
Config.options.search.prefix.clipboard
@@ -227,7 +227,7 @@ Scope {
}
for (let i = 0; i < overviewVariants.instances.length; i++) {
let panelWindow = overviewVariants.instances[i];
- if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
+ if (panelWindow.modelData.name == FhtcMonitors.activeMonitorName) {
overviewScope.dontAutoCancelSearch = true;
panelWindow.setSearchingText(
Config.options.search.prefix.emojis
diff --git a/src/share/sleex/modules/overview/OverviewWidget.qml b/src/share/sleex/modules/overview/OverviewWidget.qml
index debb4957..6894298a 100644
--- a/src/share/sleex/modules/overview/OverviewWidget.qml
+++ b/src/share/sleex/modules/overview/OverviewWidget.qml
@@ -17,26 +17,20 @@ Item {
id: root
required property var panelWindow
readonly property var monitor: FhtcMonitors.activeMonitor
- readonly property var toplevels: ToplevelManager.toplevels
+ // readonly property var toplevels: ToplevelManager.toplevels
readonly property int workspacesShown: Config.options.overview.numOfRows * Config.options.overview.numOfCols
- readonly property int workspaceGroup: Math.floor((monitor["active-workspace-idx"] - 1) / workspacesShown)
+ readonly property int workspaceGroup: Math.floor(((monitor["active-workspace-idx"] ?? 0)) / workspacesShown)
property bool monitorIsFocused: (FhtcMonitors.activeMonitorName === screen.name)
- property var windows: HyprlandData.windowList
- property var windowByAddress: HyprlandData.windowByAddress
- property var windowAddresses: HyprlandData.addresses
- property var monitorData: HyprlandData.monitors.find(m => m.id === root.monitor.id)
+ readonly property var focusedScreen: Quickshell.screens.find(s => s.name === FhtcMonitors.activeMonitorName)
property real scale: Config.options.overview.scale
property color activeBorderColor: Appearance.colors.colSecondary
- property real workspaceImplicitWidth: (monitorData?.transform % 2 === 1) ?
- ((monitor.height - monitorData?.reserved[0] - monitorData?.reserved[2]) * root.scale / monitor.scale) :
- ((monitor.width - monitorData?.reserved[0] - monitorData?.reserved[2]) * root.scale / monitor.scale)
- property real workspaceImplicitHeight: (monitorData?.transform % 2 === 1) ?
- ((monitor.width - monitorData?.reserved[1] - monitorData?.reserved[3]) * root.scale / monitor.scale) :
- ((monitor.height - monitorData?.reserved[1] - monitorData?.reserved[3]) * root.scale / monitor.scale)
+ property real workspaceImplicitWidth: (focusedScreen?.width ?? 0) * root.scale
+ property real workspaceImplicitHeight: ((focusedScreen?.height ?? 0) - Appearance.sizes.barHeight) * root.scale
property real workspaceNumberMargin: 80
- property real workspaceNumberSize: Math.min(workspaceImplicitHeight, workspaceImplicitWidth) * monitor.scale
+ property real workspaceNumberSize: Math.min(workspaceImplicitHeight, workspaceImplicitWidth) * (panelWindow.screen.devicePixelRatio ?? 1)
+
property int workspaceZ: 0
property int windowZ: 1
property int windowDraggingZ: 99999
@@ -83,7 +77,7 @@ Item {
Rectangle { // Workspace
id: workspace
property int colIndex: index
- property int workspaceValue: root.workspaceGroup * workspacesShown + rowIndex * Config.options.overview.numOfCols + colIndex + 1
+ property int workspaceValue: root.workspaceGroup * workspacesShown + rowIndex * Config.options.overview.numOfCols + colIndex
property color defaultWorkspaceColor: Appearance.colors.colLayer1 // TODO: reconsider this color for a cleaner look
property color hoveredWorkspaceColor: ColorUtils.mix(defaultWorkspaceColor, Appearance.colors.colLayer1Hover, 0.1)
property color hoveredBorderColor: Appearance.colors.colLayer2Hover
@@ -112,9 +106,8 @@ Item {
acceptedButtons: Qt.LeftButton
onClicked: {
if (root.draggingTargetWorkspace === -1) {
- // Hyprland.dispatch(`exec qs ipc call overview close`)
GlobalStates.overviewOpen = false
- Fhtc.dispatch(`workspace ${workspaceValue}`)
+ FhtcIpc.dispatch("focus-workspace-by-index", { "workspace_idx": workspaceValue })
}
}
}
@@ -147,36 +140,29 @@ Item {
Repeater { // Window repeater
model: ScriptModel {
values: {
- // console.log(JSON.stringify(ToplevelManager.toplevels.values.map(t => t), null, 2))
- return ToplevelManager.toplevels.values.filter((toplevel) => {
- const address = `0x${toplevel.HyprlandToplevel.address}`
- // console.log(`Checking window with address: ${address}`)
- var win = windowByAddress[address]
- return (root.workspaceGroup * root.workspacesShown < win?.workspace?.id && win?.workspace?.id <= (root.workspaceGroup + 1) * root.workspacesShown)
+ return Object.values(FhtcWorkspaces.windows).filter((win) => {
+ const wsId = win?.["workspace-id"] ?? -1
+ return wsId >= root.workspaceGroup * root.workspacesShown &&
+ wsId < (root.workspaceGroup + 1) * root.workspacesShown
})
}
}
delegate: OverviewWindow {
id: window
required property var modelData
- property var address: `0x${modelData.HyprlandToplevel.address}`
- windowData: windowByAddress[address]
- toplevel: modelData
- monitorData: root.monitorData
+ windowData: modelData
+ // toplevel: not yet available with fhtc compositor
+ monitorData: root.focusedScreen
scale: root.scale
availableWorkspaceWidth: root.workspaceImplicitWidth
availableWorkspaceHeight: root.workspaceImplicitHeight
- property int monitorId: windowData?.monitor
- property var monitor: HyprlandData.monitors[monitorId]
-
property bool atInitPosition: (initX == x && initY == y)
- restrictToWorkspace: Drag.active || atInitPosition
- property int workspaceColIndex: (windowData?.workspace.id - 1) % Config.options.overview.numOfCols
- property int workspaceRowIndex: Math.floor((windowData?.workspace.id - 1) % root.workspacesShown / Config.options.overview.numOfCols)
- xOffset: (root.workspaceImplicitWidth + workspaceSpacing) * workspaceColIndex - (monitor?.x * root.scale)
- yOffset: (root.workspaceImplicitHeight + workspaceSpacing) * workspaceRowIndex - (monitor?.y * root.scale)
+ property int workspaceColIndex: (modelData?.["workspace-id"] ?? 0) % Config.options.overview.numOfCols
+ property int workspaceRowIndex: Math.floor((modelData?.["workspace-id"] ?? 0) % root.workspacesShown / Config.options.overview.numOfCols)
+ xOffset: (root.workspaceImplicitWidth + workspaceSpacing) * workspaceColIndex - ((root.focusedScreen?.x ?? 0) * root.scale)
+ yOffset: (root.workspaceImplicitHeight + workspaceSpacing) * workspaceRowIndex - ((root.focusedScreen?.y ?? 0) * root.scale)
Timer {
id: updateWindowPosition
@@ -184,12 +170,13 @@ Item {
repeat: false
running: false
onTriggered: {
- window.x = Math.round(Math.max((windowData?.at[0] - monitorData?.reserved[0]) * root.scale, 0) + xOffset)
- window.y = Math.round(Math.max((windowData?.at[1] - monitorData?.reserved[1]) * root.scale, 0) + yOffset)
- // console.log(`[OverviewWindow] Updated position for window ${windowData?.address} to (${window.x}, ${window.y})`)
+ window.x = Math.round(Math.max(modelData?.location[0] * root.scale, 0) + xOffset)
+ window.y = Math.round(Math.max((modelData?.location[1] - Appearance.sizes.barHeight) * root.scale, 0) + yOffset)
}
}
+ Component.onCompleted: updateWindowPosition.restart()
+
z: atInitPosition ? root.windowZ : root.windowDraggingZ
Drag.hotSpot.x: targetWindowWidth / 2
Drag.hotSpot.y: targetWindowHeight / 2
@@ -202,35 +189,32 @@ Item {
acceptedButtons: Qt.LeftButton | Qt.MiddleButton
drag.target: parent
onPressed: {
- root.draggingFromWorkspace = windowData?.workspace.id
+ root.draggingFromWorkspace = modelData?.["workspace-id"]
window.pressed = true
window.Drag.active = true
window.Drag.source = window
- // console.log(`[OverviewWindow] Dragging window ${windowData?.address} from position (${window.x}, ${window.y})`)
}
onReleased: {
const targetWorkspace = root.draggingTargetWorkspace
window.pressed = false
window.Drag.active = false
root.draggingFromWorkspace = -1
- if (targetWorkspace !== -1 && targetWorkspace !== windowData?.workspace.id) {
- Fhtc.dispatch(`movetoworkspacesilent ${targetWorkspace}, address:${window.windowData?.address}`)
+ if (targetWorkspace !== -1 && targetWorkspace !== modelData?.["workspace-id"]) {
+ FhtcIpc.dispatch("send-window-to-workspace", { "window-id": modelData?.id, "workspace-id": targetWorkspace })
updateWindowPosition.restart()
- }
- else {
+ } else {
window.x = window.initX
window.y = window.initY
}
}
onClicked: (event) => {
- if (!windowData) return;
-
+ if (!modelData) return;
if (event.button === Qt.LeftButton) {
GlobalStates.overviewOpen = false
- Hyprland.dispatch(`focuswindow address:${windowData.address}`)
+ FhtcIpc.dispatch("focus-window", { "window-id": modelData.id })
event.accepted = true
} else if (event.button === Qt.MiddleButton) {
- Hyprland.dispatch(`closewindow address:${windowData.address}`)
+ FhtcIpc.dispatch("close-window", { "window-id": modelData.id })
event.accepted = true
}
}
@@ -238,7 +222,7 @@ Item {
StyledToolTip {
extraVisibleCondition: false
alternativeVisibleCondition: dragArea.containsMouse && !window.Drag.active
- text: `${windowData.title}\n[${windowData.class}] ${windowData.xwayland ? "[XWayland] " : ""}\n`
+ text: `${modelData.title}\n[${modelData["app-id"]}]\n`
}
}
}
@@ -246,9 +230,9 @@ Item {
Rectangle { // Focused workspace indicator
id: focusedWorkspaceIndicator
- property int activeWorkspaceInGroup: monitor.activeWorkspace?.id - (root.workspaceGroup * root.workspacesShown)
- property int activeWorkspaceRowIndex: Math.floor((activeWorkspaceInGroup - 1) / Config.options.overview.numOfCols)
- property int activeWorkspaceColIndex: (activeWorkspaceInGroup - 1) % Config.options.overview.numOfCols
+ property int activeWorkspaceInGroup: (monitor["active-workspace-idx"] ?? 0) - (root.workspaceGroup * root.workspacesShown)
+ property int activeWorkspaceRowIndex: Math.floor(activeWorkspaceInGroup / Config.options.overview.numOfCols)
+ property int activeWorkspaceColIndex: activeWorkspaceInGroup % Config.options.overview.numOfCols
x: (root.workspaceImplicitWidth + workspaceSpacing) * activeWorkspaceColIndex
y: (root.workspaceImplicitHeight + workspaceSpacing) * activeWorkspaceRowIndex
z: root.windowZ
diff --git a/src/share/sleex/modules/overview/OverviewWindow.qml b/src/share/sleex/modules/overview/OverviewWindow.qml
index e75a6487..c6410a68 100644
--- a/src/share/sleex/modules/overview/OverviewWindow.qml
+++ b/src/share/sleex/modules/overview/OverviewWindow.qml
@@ -22,8 +22,8 @@ Item { // Window
property var availableWorkspaceWidth
property var availableWorkspaceHeight
property bool restrictToWorkspace: true
- property real initX: Math.max((windowData?.at[0] - monitorData?.reserved[0]) * root.scale, 0) + xOffset
- property real initY: Math.max((windowData?.at[1] - monitorData?.reserved[1]) * root.scale, 0) + yOffset
+ property real initX: Math.max(windowData?.location[0] * root.scale, 0) + xOffset
+ property real initY: Math.max((windowData?.location[1] - Appearance.sizes.barHeight) * root.scale, 0) + yOffset
property real xOffset: 0
property real yOffset: 0
@@ -35,10 +35,10 @@ Item { // Window
property var iconToWindowRatio: 0.35
property var xwaylandIndicatorToIconRatio: 0.35
property var iconToWindowRatioCompact: 0.6
- property var iconPath: Quickshell.iconPath(AppSearch.guessIcon(windowData?.class), "image-missing")
+ property var iconPath: Quickshell.iconPath(AppSearch.guessIcon(windowData?.["app-id"]), "image-missing")
property bool compactMode: Appearance.font.pixelSize.smaller * 4 > targetWindowHeight || Appearance.font.pixelSize.smaller * 4 > targetWindowWidth
- property bool indicateXWayland: (Config.options.overview.showXwaylandIndicator && windowData?.xwayland) ?? false
+ // property bool indicateXWayland: (Config.options.overview.showXwaylandIndicator && windowData?.xwayland) ?? false
x: initX
y: initY
@@ -70,7 +70,7 @@ Item { // Window
ScreencopyView {
id: windowPreview
anchors.fill: parent
- captureSource: GlobalStates.overviewOpen ? root.toplevel : null
+ captureSource: null // GlobalStates.overviewOpen ? root.toplevel : null
live: true
Rectangle {
From 52864b08ad84feb2bf0ef227b30cf8cd4be40644 Mon Sep 17 00:00:00 2001
From: Ardox
Date: Fri, 10 Apr 2026 13:43:29 +0200
Subject: [PATCH 28/56] Add experimental extcaptureitem component
---
.../plugins/src/Sleex/widgets/CMakeLists.txt | 60 ++
.../Sleex/widgets/ContributionCalendar.cpp | 0
.../Sleex/widgets/ContributionCalendar.hpp | 0
.../src/Sleex/widgets/ExtCaptureItem.cpp | 575 ++++++++++++++++++
.../src/Sleex/widgets/ExtCaptureItem.hpp | 189 ++++++
.../plugins/src/Sleex/widgets/GbmBuffer.cpp | 98 +++
.../plugins/src/Sleex/widgets/GbmBuffer.hpp | 61 ++
.../plugins/src/Sleex/widgets/plugin.cpp | 2 +-
8 files changed, 984 insertions(+), 1 deletion(-)
mode change 100644 => 100755 src/share/sleex/plugins/src/Sleex/widgets/ContributionCalendar.cpp
mode change 100644 => 100755 src/share/sleex/plugins/src/Sleex/widgets/ContributionCalendar.hpp
create mode 100644 src/share/sleex/plugins/src/Sleex/widgets/ExtCaptureItem.cpp
create mode 100644 src/share/sleex/plugins/src/Sleex/widgets/ExtCaptureItem.hpp
create mode 100644 src/share/sleex/plugins/src/Sleex/widgets/GbmBuffer.cpp
create mode 100644 src/share/sleex/plugins/src/Sleex/widgets/GbmBuffer.hpp
mode change 100644 => 100755 src/share/sleex/plugins/src/Sleex/widgets/plugin.cpp
diff --git a/src/share/sleex/plugins/src/Sleex/widgets/CMakeLists.txt b/src/share/sleex/plugins/src/Sleex/widgets/CMakeLists.txt
index b0931ed5..eac681a7 100755
--- a/src/share/sleex/plugins/src/Sleex/widgets/CMakeLists.txt
+++ b/src/share/sleex/plugins/src/Sleex/widgets/CMakeLists.txt
@@ -1,13 +1,65 @@
+enable_language(C)
+
pkg_check_modules(GLIB REQUIRED glib-2.0 gobject-2.0 gio-2.0)
+find_package(Qt6 REQUIRED COMPONENTS GuiPrivate)
+
+pkg_check_modules(WAYLAND_CLIENT REQUIRED IMPORTED_TARGET wayland-client)
+pkg_check_modules(WAYLAND_PROTOS REQUIRED IMPORTED_TARGET wayland-protocols)
+pkg_check_modules(GBM REQUIRED IMPORTED_TARGET gbm)
+pkg_check_modules(DRM REQUIRED IMPORTED_TARGET libdrm)
+pkg_check_modules(EGL REQUIRED IMPORTED_TARGET egl)
+pkg_check_modules(GLESv2 REQUIRED IMPORTED_TARGET glesv2)
+
+find_program(WAYLAND_SCANNER wayland-scanner REQUIRED)
+
+pkg_get_variable(WL_PROTO_DIR wayland-protocols pkgdatadir)
+set(PROTO_DIR "${CMAKE_SOURCE_DIR}/protocols")
+
+set(WAYLAND_PROTO_SOURCES "")
+
+macro(wayland_add_protocol_sources _xml)
+ get_filename_component(_base "${_xml}" NAME_WE)
+ set(_header "${CMAKE_CURRENT_BINARY_DIR}/${_base}-client-protocol.h")
+ set(_source "${CMAKE_CURRENT_BINARY_DIR}/${_base}-client-protocol.c")
+
+ add_custom_command(
+ OUTPUT "${_header}"
+ COMMAND "${WAYLAND_SCANNER}" client-header "${_xml}" "${_header}"
+ DEPENDS "${_xml}"
+ VERBATIM
+ )
+ add_custom_command(
+ OUTPUT "${_source}"
+ COMMAND "${WAYLAND_SCANNER}" private-code "${_xml}" "${_source}"
+ DEPENDS "${_xml}"
+ VERBATIM
+ )
+ list(APPEND WAYLAND_PROTO_SOURCES "${_header}" "${_source}")
+endmacro()
+
+wayland_add_protocol_sources("${WL_PROTO_DIR}/staging/ext-image-copy-capture/ext-image-copy-capture-v1.xml")
+wayland_add_protocol_sources("${WL_PROTO_DIR}/staging/ext-image-capture-source/ext-image-capture-source-v1.xml")
+wayland_add_protocol_sources("${WL_PROTO_DIR}/staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-v1.xml")
+wayland_add_protocol_sources("${WL_PROTO_DIR}/unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml")
+
+add_library(sleex_wayland_protocols STATIC ${WAYLAND_PROTO_SOURCES})
+target_link_libraries(sleex_wayland_protocols PUBLIC PkgConfig::WAYLAND_CLIENT)
+target_include_directories(sleex_wayland_protocols PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
+set_target_properties(sleex_wayland_protocols PROPERTIES POSITION_INDEPENDENT_CODE ON)
+
qml_module(sleex-widgets
URI Sleex.Widgets
SOURCES
ContributionCalendar.cpp ContributionCalendar.hpp
+ ExtCaptureItem.cpp ExtCaptureItem.hpp
+ GbmBuffer.cpp GbmBuffer.hpp
plugin.cpp
)
target_include_directories(sleex-widgets PRIVATE
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}
${LIBNM_INCLUDE_DIRS}
${GLIB_INCLUDE_DIRS}
)
@@ -15,6 +67,7 @@ target_include_directories(sleex-widgets PRIVATE
target_link_libraries(sleex-widgets PRIVATE
${LIBNM_LIBRARIES}
${GLIB_LIBRARIES}
+ sleex_wayland_protocols
)
target_compile_options(sleex-widgets PRIVATE
@@ -25,6 +78,13 @@ target_compile_options(sleex-widgets PRIVATE
target_link_libraries(sleex-widgets
PRIVATE
Qt6::Core
+ Qt6::Gui
+ Qt6::GuiPrivate
Qt6::Qml
Qt6::Quick
+ PkgConfig::WAYLAND_CLIENT
+ PkgConfig::GBM
+ PkgConfig::DRM
+ PkgConfig::EGL
+ PkgConfig::GLESv2
)
\ No newline at end of file
diff --git a/src/share/sleex/plugins/src/Sleex/widgets/ContributionCalendar.cpp b/src/share/sleex/plugins/src/Sleex/widgets/ContributionCalendar.cpp
old mode 100644
new mode 100755
diff --git a/src/share/sleex/plugins/src/Sleex/widgets/ContributionCalendar.hpp b/src/share/sleex/plugins/src/Sleex/widgets/ContributionCalendar.hpp
old mode 100644
new mode 100755
diff --git a/src/share/sleex/plugins/src/Sleex/widgets/ExtCaptureItem.cpp b/src/share/sleex/plugins/src/Sleex/widgets/ExtCaptureItem.cpp
new file mode 100644
index 00000000..8c40b4b8
--- /dev/null
+++ b/src/share/sleex/plugins/src/Sleex/widgets/ExtCaptureItem.cpp
@@ -0,0 +1,575 @@
+#include "ExtCaptureItem.hpp"
+#include "GbmBuffer.hpp"
+
+#include "ext-image-copy-capture-v1-client-protocol.h"
+#include "ext-image-capture-source-v1-client-protocol.h"
+#include "ext-foreign-toplevel-list-v1-client-protocol.h"
+#include "linux-dmabuf-unstable-v1-client-protocol.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+static const wl_registry_listener s_registryListener = {
+ .global = &ExtCaptureItem::onRegistryGlobal,
+ .global_remove = &ExtCaptureItem::onRegistryGlobalRemove,
+};
+
+static const ext_foreign_toplevel_list_v1_listener s_toplevelListListener = {
+ .toplevel = &ExtCaptureItem::onToplevelListToplevel,
+ .finished = &ExtCaptureItem::onToplevelListFinished,
+};
+
+static const ext_foreign_toplevel_handle_v1_listener s_toplevelHandleListener = {
+ .closed = &ExtCaptureItem::onToplevelClosed,
+ .done = &ExtCaptureItem::onToplevelDone,
+ .title = &ExtCaptureItem::onToplevelTitle,
+ .app_id = &ExtCaptureItem::onToplevelAppId,
+ .identifier = &ExtCaptureItem::onToplevelIdentifier,
+};
+
+static const ext_image_copy_capture_session_v1_listener s_sessionListener = {
+ .buffer_size = &ExtCaptureItem::onSessionBufferSize,
+ .shm_format = &ExtCaptureItem::onSessionShmFormat,
+ .dmabuf_device = &ExtCaptureItem::onSessionDmabufDevice,
+ .dmabuf_format = &ExtCaptureItem::onSessionDmabufFormat,
+ .done = &ExtCaptureItem::onSessionDone,
+ .stopped = &ExtCaptureItem::onSessionStopped,
+};
+
+static const ext_image_copy_capture_frame_v1_listener s_frameListener = {
+ .transform = &ExtCaptureItem::onFrameTransform,
+ .damage = &ExtCaptureItem::onFrameDamage,
+ .presentation_time = &ExtCaptureItem::onFramePresentationTime,
+ .ready = &ExtCaptureItem::onFrameReady,
+ .failed = &ExtCaptureItem::onFrameFailed,
+};
+
+ExtCaptureItem::ExtCaptureItem(QQuickItem* parent) : QQuickItem(parent)
+{
+ setFlag(ItemHasContents, true);
+}
+
+ExtCaptureItem::~ExtCaptureItem()
+{
+ stopCapture();
+
+ // Destroy protocol objects
+ if (m_toplevelList) ext_foreign_toplevel_list_v1_destroy(m_toplevelList);
+ if (m_sourceManager) ext_foreign_toplevel_image_capture_source_manager_v1_destroy(m_sourceManager);
+ if (m_captureManager) ext_image_copy_capture_manager_v1_destroy(m_captureManager);
+ if (m_dmabuf) zwp_linux_dmabuf_v1_destroy(m_dmabuf);
+
+ // release GBM buffers before the device
+ for (auto& buf : m_buffers) buf.reset();
+
+ if (m_gbmDev) gbm_device_destroy(m_gbmDev);
+ if (m_drmFd >= 0) close(m_drmFd);
+}
+
+void ExtCaptureItem::componentComplete()
+{
+ QQuickItem::componentComplete();
+ initWayland();
+ initGbm();
+ if (m_active) tryStartCapture();
+}
+
+void ExtCaptureItem::setActive(bool active)
+{
+ if (m_active == active) return;
+ m_active = active;
+ emit activeChanged();
+
+ if (m_waylandReady) {
+ if (active) tryStartCapture();
+ else stopCapture();
+ }
+}
+
+void ExtCaptureItem::initWayland()
+{
+ // QPlatformNativeInterface is available on all Qt 5/6 Wayland QPA backends
+ // without pulling in any QtWaylandClient private headers.
+ QPlatformNativeInterface* ni = qGuiApp->platformNativeInterface();
+ if (!ni) {
+ qWarning() << "[ExtCapture] Not running on a Wayland QPA backend";
+ return;
+ }
+ m_display = static_cast(
+ ni->nativeResourceForIntegration(QByteArrayLiteral("wl_display")));
+ if (!m_display) {
+ qWarning() << "[ExtCapture] wl_display not available via QPlatformNativeInterface";
+ return;
+ }
+
+ // registry
+ m_registry = wl_display_get_registry(m_display);
+ wl_registry_add_listener(m_registry, &s_registryListener, this);
+
+ // Blocking roundtrip so that all globals are bound synchronously before
+ // we proceed. A production implementation may prefer a deferred approach.
+ wl_display_roundtrip(m_display);
+ wl_display_roundtrip(m_display); // second roundtrip picks up any late events
+
+ m_waylandReady = true;
+}
+
+// Sometimes, even I don't understand my own code
+void ExtCaptureItem::onRegistryGlobal(void* data, wl_registry* registry, uint32_t name, const char* iface, uint32_t version)
+{
+ auto* self = static_cast(data);
+
+ if (strcmp(iface, ext_image_copy_capture_manager_v1_interface.name) == 0) {
+ self->m_captureManager = static_cast(wl_registry_bind(registry, name, &ext_image_copy_capture_manager_v1_interface, qMin(version, 1u)));
+ }
+ else if (strcmp(iface, ext_foreign_toplevel_image_capture_source_manager_v1_interface.name) == 0) {
+ self->m_sourceManager = static_cast( wl_registry_bind(registry, name, &ext_foreign_toplevel_image_capture_source_manager_v1_interface, qMin(version, 1u)));
+ }
+ else if (strcmp(iface, ext_foreign_toplevel_list_v1_interface.name) == 0) {
+ self->m_toplevelList = static_cast(wl_registry_bind(registry, name, &ext_foreign_toplevel_list_v1_interface, qMin(version, 1u)));
+ ext_foreign_toplevel_list_v1_add_listener(self->m_toplevelList, &s_toplevelListListener, self);
+ }
+ else if (strcmp(iface, zwp_linux_dmabuf_v1_interface.name) == 0) {
+ if (version >= 3) {
+ self->m_dmabuf = static_cast(wl_registry_bind(registry, name, &zwp_linux_dmabuf_v1_interface, qMin(version, 4u)));
+ }
+ }
+}
+
+void ExtCaptureItem::onRegistryGlobalRemove(void* /*data*/, wl_registry*, uint32_t /*name*/) {}
+
+
+void ExtCaptureItem::onToplevelListToplevel(void* data, ext_foreign_toplevel_list_v1*, ext_foreign_toplevel_handle_v1* handle)
+{
+ auto* self = static_cast(data);
+ ToplevelInfo info;
+ info.handle = handle;
+ self->m_pendingToplevels.append(info);
+ ext_foreign_toplevel_handle_v1_add_listener(handle, &s_toplevelHandleListener, self);
+}
+
+void ExtCaptureItem::onToplevelListFinished(void* data, ext_foreign_toplevel_list_v1*) {
+ // The list won't send more toplevels. We're watching ongoing events via
+ // onToplevelDone / onToplevelClosed.
+ Q_UNUSED(data)
+}
+
+void ExtCaptureItem::onToplevelAppId(void* data, ext_foreign_toplevel_handle_v1* handle, const char* app_id)
+{
+ auto* self = static_cast(data);
+ for (auto& t : self->m_pendingToplevels) {
+ if (t.handle == handle) {
+ t.appId = QString::fromUtf8(app_id);
+ return;
+ }
+ }
+}
+
+void ExtCaptureItem::onToplevelTitle(void* data, ext_foreign_toplevel_handle_v1* handle, const char* title)
+{
+ auto* self = static_cast(data);
+ for (auto& t : self->m_pendingToplevels) {
+ if (t.handle == handle) {
+ t.title = QString::fromUtf8(title);
+ emit self->windowsChanged();
+ return;
+ }
+ }
+}
+
+void ExtCaptureItem::onToplevelIdentifier(void* data, ext_foreign_toplevel_handle_v1* handle, const char* identifier)
+{
+ auto* self = static_cast(data);
+ for (auto& t : self->m_pendingToplevels) {
+ if (t.handle == handle) {
+ t.identifier = QString::fromUtf8(identifier);
+ emit self->windowsChanged();
+ return;
+ }
+ }
+}
+
+void ExtCaptureItem::setWindowId(const QString& id)
+{
+ if (m_windowId == id) return;
+ m_windowId = id;
+ emit windowIdChanged();
+
+ if (m_waylandReady) {
+ stopCapture();
+ m_targetHandle = nullptr;
+
+ for (const auto& t : m_pendingToplevels) {
+ if (t.identifier == m_windowId) {
+ m_targetHandle = t.handle;
+ break;
+ }
+ }
+
+ if (m_active && m_targetHandle) tryStartCapture();
+ }
+}
+
+void ExtCaptureItem::onToplevelDone(void* data, ext_foreign_toplevel_handle_v1* handle)
+{
+ auto* self = static_cast(data);
+ for (auto& t : self->m_pendingToplevels) {
+ if (t.handle == handle && !t.matched) {
+ if (!self->m_windowId.isEmpty() && t.identifier == self->m_windowId) {
+ t.matched = true;
+ self->m_targetHandle = handle;
+ if (self->m_active && self->m_waylandReady)
+ self->tryStartCapture();
+ }
+ return;
+ }
+ }
+}
+
+void ExtCaptureItem::onToplevelClosed(void* data, ext_foreign_toplevel_handle_v1* handle)
+{
+ auto* self = static_cast(data);
+ if (self->m_targetHandle == handle) {
+ self->stopCapture();
+ self->m_targetHandle = nullptr;
+ }
+ // Remove from pending list
+ for (int i = self->m_pendingToplevels.size() - 1; i >= 0; --i) {
+ if (self->m_pendingToplevels[i].handle == handle) {
+ self->m_pendingToplevels.removeAt(i);
+ }
+ }
+}
+
+void ExtCaptureItem::tryStartCapture()
+{
+ if (!m_captureManager || !m_sourceManager || !m_targetHandle) return;
+ if (m_session) return; // already running
+
+ m_source = ext_foreign_toplevel_image_capture_source_manager_v1_create_source(m_sourceManager, m_targetHandle);
+
+ m_session = ext_image_copy_capture_manager_v1_create_session(m_captureManager, m_source, 0u);
+
+ ext_image_copy_capture_session_v1_add_listener(m_session, &s_sessionListener, this);
+
+ // The session will fire buffer_size + dmabuf_format* + done events in a batch. Flush and dispatch.
+ wl_display_flush(m_display);
+}
+
+void ExtCaptureItem::stopCapture()
+{
+ if (m_frame) {
+ ext_image_copy_capture_frame_v1_destroy(m_frame);
+ m_frame = nullptr;
+ }
+ if (m_session) {
+ ext_image_copy_capture_session_v1_destroy(m_session);
+ m_session = nullptr;
+ }
+ if (m_source) {
+ ext_image_capture_source_v1_destroy(m_source);
+ m_source = nullptr;
+ }
+ m_formatsReceived = false;
+
+ if (m_running) {
+ m_running = false;
+ emit runningChanged();
+ }
+}
+
+void ExtCaptureItem::onSessionBufferSize(void* data, ext_image_copy_capture_session_v1*, uint32_t w, uint32_t h)
+{
+ auto* self = static_cast(data);
+ self->m_captureSize = QSize(static_cast(w), static_cast(h));
+}
+
+void ExtCaptureItem::onSessionShmFormat(void*, ext_image_copy_capture_session_v1*, uint32_t) {
+ // We only use DMA-BUF. ignore SHM formats.
+}
+
+void ExtCaptureItem::onSessionDmabufDevice(void*, ext_image_copy_capture_session_v1*, wl_array* /*device*/) {
+ // The device hint is optional; we already opened the render node.
+}
+
+void ExtCaptureItem::onSessionDmabufFormat(void* data, ext_image_copy_capture_session_v1*, uint32_t fmt, wl_array* modifiers)
+{
+ auto* self = static_cast(data);
+ const auto* modifierValues = static_cast(modifiers->data);
+ const int modifierCount = static_cast(modifiers->size / sizeof(uint64_t));
+ for (int i = 0; i < modifierCount; ++i) {
+ self->m_dmabufFormats.append({ fmt, modifierValues[i] });
+ }
+}
+
+void ExtCaptureItem::onSessionDone(void* data, ext_image_copy_capture_session_v1*)
+{
+ // All constraints received. Allocate buffers and request the first frame.
+ auto* self = static_cast(data);
+ self->m_formatsReceived = true;
+ self->allocateBuffers();
+ self->requestNextFrame();
+}
+
+void ExtCaptureItem::onSessionStopped(void* data, ext_image_copy_capture_session_v1*)
+{
+ auto* self = static_cast(data);
+ qWarning() << "[ExtCapture] Session stopped by compositor";
+ self->stopCapture();
+}
+
+void ExtCaptureItem::initGbm()
+{
+ // Open the first available render node
+ for (int i = 128; i < 138 && m_drmFd < 0; ++i) {
+ QString path = QStringLiteral("/dev/dri/renderD%1").arg(i);
+ m_drmFd = open(path.toLocal8Bit().constData(), O_RDWR | O_CLOEXEC);
+ }
+ if (m_drmFd < 0) {
+ qWarning() << "[ExtCapture] Failed to open DRM render node";
+ return;
+ }
+ m_gbmDev = gbm_create_device(m_drmFd);
+ if (!m_gbmDev) {
+ qWarning() << "[ExtCapture] gbm_create_device failed";
+ close(m_drmFd);
+ m_drmFd = -1;
+ }
+}
+
+void ExtCaptureItem::allocateBuffers()
+{
+ if (!m_gbmDev || m_captureSize.isEmpty()) return;
+
+ uint32_t fmt = DRM_FORMAT_XRGB8888;
+ uint64_t modifier = DRM_FORMAT_MOD_LINEAR;
+
+ for (const auto& fi : m_dmabufFormats) {
+ if (fi.format == DRM_FORMAT_XRGB8888) {
+ fmt = fi.format;
+ modifier = fi.modifier;
+ break;
+ }
+ }
+
+ for (int i = 0; i < POOL; ++i) {
+ auto buf = std::make_unique();
+ if (!buf->allocate(m_gbmDev,
+ m_captureSize.width(),
+ m_captureSize.height(),
+ fmt, modifier)) {
+ qWarning() << "[ExtCapture] Failed to allocate GBM buffer" << i;
+ return;
+ }
+ createWaylandBuffer(buf.get());
+ m_buffers[i] = std::move(buf);
+ }
+}
+
+void ExtCaptureItem::createWaylandBuffer(GbmBuffer* buf)
+{
+ if (!m_dmabuf) return;
+
+ zwp_linux_buffer_params_v1* params = zwp_linux_dmabuf_v1_create_params(m_dmabuf);
+
+ // Add the single plane
+ zwp_linux_buffer_params_v1_add(params,
+ buf->fd(),
+ 0, // plane index
+ buf->offset(),
+ buf->stride(),
+ static_cast(buf->modifier() >> 32),
+ static_cast(buf->modifier() & 0xFFFFFFFF)
+ );
+
+ wl_buffer* wlBuf = zwp_linux_buffer_params_v1_create_immed(params, buf->width(), buf->height(), buf->format(), 0 /* flags */);
+
+ zwp_linux_buffer_params_v1_destroy(params);
+ buf->setWaylandBuffer(wlBuf);
+}
+
+GbmBuffer* ExtCaptureItem::acquireCaptureBuffer()
+{
+ // Return a buffer that Qt is not currently reading from.
+ for (int i = 0; i < POOL; ++i) {
+ if (m_buffers[i] && !m_buffers[i]->inUse) {
+ m_captureIdx = i;
+ return m_buffers[i].get();
+ }
+ }
+ return nullptr;
+}
+
+void ExtCaptureItem::requestNextFrame() {
+ if (!m_session || m_frame || !m_formatsReceived) return;
+
+ int target = -1;
+ for (int i = 0; i < POOL; ++i) {
+ if (!m_buffers[i]->inUse && !m_buffers[i]->ready && !m_buffers[i]->isCapturing) {
+ target = i;
+ break;
+ }
+ }
+
+ if (target == -1) return;
+
+ m_captureIdx = target;
+ m_buffers[target]->isCapturing = true;
+
+ m_frame = ext_image_copy_capture_session_v1_create_frame(m_session);
+ ext_image_copy_capture_frame_v1_add_listener(m_frame, &s_frameListener, this);
+ ext_image_copy_capture_frame_v1_attach_buffer(m_frame, m_buffers[target]->waylandBuffer());
+ ext_image_copy_capture_frame_v1_capture(m_frame);
+ wl_display_flush(m_display);
+}
+
+void ExtCaptureItem::onFrameTransform(void*, ext_image_copy_capture_frame_v1*, uint32_t) {}
+
+void ExtCaptureItem::onFrameDamage(void*, ext_image_copy_capture_frame_v1*, int32_t, int32_t, int32_t, int32_t) {}
+
+void ExtCaptureItem::onFramePresentationTime(void*, ext_image_copy_capture_frame_v1*, uint32_t, uint32_t, uint32_t) {}
+
+void ExtCaptureItem::onFrameReady(void* data, ext_image_copy_capture_frame_v1* frame) {
+ auto* self = static_cast(data);
+ int idx = self->m_captureIdx;
+
+ ext_image_copy_capture_frame_v1_destroy(frame);
+ self->m_frame = nullptr;
+
+ self->m_buffers[idx]->isCapturing = false;
+ self->m_buffers[idx]->ready = true;
+
+ {
+ QMutexLocker lk(&self->m_sgMutex);
+ self->m_pendingIdx = idx;
+ }
+
+ self->update();
+}
+
+void ExtCaptureItem::onFrameFailed(void* data, ext_image_copy_capture_frame_v1* frame, uint32_t reason)
+{
+ auto* self = static_cast(data);
+ qWarning() << "[ExtCapture] Frame capture failed, reason:" << reason;
+ ext_image_copy_capture_frame_v1_destroy(frame);
+ self->m_frame = nullptr;
+ // Retry after a brief pause (avoid tight loop on persistent error)
+ QTimer::singleShot(200, self, &ExtCaptureItem::requestNextFrame);
+}
+
+QSGNode* ExtCaptureItem::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*)
+{
+ auto* node = static_cast(oldNode);
+ int readyIdx = -1;
+
+ {
+ QMutexLocker lk(&m_sgMutex);
+ readyIdx = m_pendingIdx;
+ m_pendingBuffer = nullptr;
+ }
+
+ if (m_displayIdx >= 0) {
+ m_buffers[m_displayIdx]->inUse = false;
+ }
+
+ if (readyIdx != -1) {
+ m_displayIdx = readyIdx;
+ m_buffers[m_displayIdx]->ready = false;
+ m_buffers[m_displayIdx]->inUse = true;
+ }
+
+ if (!m_frame && m_active) {
+ QMetaObject::invokeMethod(this, &ExtCaptureItem::requestNextFrame, Qt::QueuedConnection);
+ }
+
+ if (m_displayIdx == -1) return node;
+
+ GbmBuffer* buf = m_buffers[m_displayIdx].get();
+ if (!buf->qtTexture()) {
+ if (!m_eglCreateImage) {
+ QPlatformNativeInterface* ni = qGuiApp->platformNativeInterface();
+ m_eglDisplay = static_cast(ni->nativeResourceForIntegration(QByteArrayLiteral("egldisplay")));
+ if (m_eglDisplay == EGL_NO_DISPLAY) m_eglDisplay = eglGetCurrentDisplay();
+
+ m_eglCreateImage = reinterpret_cast(eglGetProcAddress("eglCreateImageKHR"));
+ m_eglDestroyImage = reinterpret_cast(eglGetProcAddress("eglDestroyImageKHR"));
+ m_glEGLImageTarget = reinterpret_cast(eglGetProcAddress("glEGLImageTargetTexture2DOES"));
+ }
+
+ auto fnCreateImage = reinterpret_cast(m_eglCreateImage);
+ auto fnEGLTarget = reinterpret_cast(m_glEGLImageTarget);
+
+ EGLint attribs[] = {
+ EGL_WIDTH, buf->width(),
+ EGL_HEIGHT, buf->height(),
+ EGL_LINUX_DRM_FOURCC_EXT, static_cast(buf->format()),
+ EGL_DMA_BUF_PLANE0_FD_EXT, buf->fd(),
+ EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0,
+ EGL_DMA_BUF_PLANE0_PITCH_EXT, static_cast(buf->stride()),
+ EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, static_cast(buf->modifier() & 0xFFFFFFFF),
+ EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, static_cast(buf->modifier() >> 32),
+ EGL_NONE
+ };
+
+ buf->eglImage = fnCreateImage(m_eglDisplay, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, attribs);
+
+ if (buf->eglImage == EGL_NO_IMAGE_KHR) {
+ qWarning() << "[ExtCapture] eglCreateImageKHR failed:" << eglGetError();
+ return node;
+ }
+
+ GLuint texId = 0;
+ glGenTextures(1, &texId);
+ glBindTexture(GL_TEXTURE_2D, texId);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ fnEGLTarget(GL_TEXTURE_2D, static_cast(buf->eglImage));
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ // On crƩe la texture Qt qui "enveloppe" notre texture native
+ // On ne la dƩtruira que lors de la destruction du GbmBuffer
+ QSGTexture* sgTex = QNativeInterface::QSGOpenGLTexture::fromNative(
+ texId,
+ window(),
+ QSize(buf->width(), buf->height()),
+ QQuickWindow::TextureHasAlphaChannel);
+
+ buf->setQtTexture(sgTex);
+ }
+
+ if (!node) node = new QSGSimpleTextureNode;
+ node->setTexture(buf->qtTexture());
+ node->setRect(boundingRect());
+ return node;
+}
+
+QVariantList ExtCaptureItem::windows() const
+{
+ QVariantList list;
+ for (const auto& t : m_pendingToplevels) {
+ if (t.identifier.isEmpty()) continue;
+ QVariantMap map;
+ map["id"] = t.identifier;
+ map["appId"] = t.appId;
+ map["title"] = t.title.isEmpty() ? t.appId : t.title;
+ list << map;
+ }
+ return list;
+}
\ No newline at end of file
diff --git a/src/share/sleex/plugins/src/Sleex/widgets/ExtCaptureItem.hpp b/src/share/sleex/plugins/src/Sleex/widgets/ExtCaptureItem.hpp
new file mode 100644
index 00000000..0f6228d4
--- /dev/null
+++ b/src/share/sleex/plugins/src/Sleex/widgets/ExtCaptureItem.hpp
@@ -0,0 +1,189 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+struct ext_image_copy_capture_manager_v1;
+struct ext_image_copy_capture_session_v1;
+struct ext_image_copy_capture_frame_v1;
+struct ext_image_capture_source_v1;
+struct ext_image_copy_capture_source_v1;
+struct ext_foreign_toplevel_list_v1;
+struct ext_foreign_toplevel_handle_v1;
+struct ext_foreign_toplevel_image_capture_source_manager_v1;
+struct zwp_linux_dmabuf_v1;
+struct zwp_linux_buffer_params_v1;
+
+class GbmBuffer;
+class QSGTexture;
+
+// ---------------------------------------------------------------------------
+// ExtCaptureItem
+//
+// QML usage:
+// import Sleex.ExtCapture
+//
+// ExtCaptureItem {
+// anchors.fill: parent
+// appId: "firefox"
+// active: true
+// }
+// ---------------------------------------------------------------------------
+class ExtCaptureItem : public QQuickItem
+{
+ Q_OBJECT
+ QML_ELEMENT
+
+ // The identifier of the window to capture, typically in the form "appId:instanceId". Updated when the session is active.
+ Q_PROPERTY(QString windowId READ windowId WRITE setWindowId NOTIFY windowIdChanged)
+ // App title
+ Q_PROPERTY(QVariantList windows READ windows NOTIFY windowsChanged)
+ // Set to true to start capturing.
+ Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged)
+ // Read-only: true once the session is live and frames are flowing.
+ Q_PROPERTY(bool running READ running NOTIFY runningChanged)
+
+public:
+ explicit ExtCaptureItem(QQuickItem* parent = nullptr);
+ ~ExtCaptureItem() override;
+
+ QString windowId() const { return m_windowId; }
+ void setWindowId(const QString& id);
+ QVariantList windows() const;
+ QStringList availableAppIds() const;
+ bool active() const { return m_active; }
+ bool running() const { return m_running; }
+
+ void setAppId(const QString& id);
+ void setActive(bool active);
+
+protected:
+ // called by the Scene Graph render thread.
+ QSGNode* updatePaintNode(QSGNode* old, UpdatePaintNodeData*) override;
+ void componentComplete() override;
+
+signals:
+ void windowIdChanged();
+ void windowsChanged();
+ void activeChanged();
+ void runningChanged();
+
+private:
+ void initWayland();
+ void initGbm();
+ void tryStartCapture();
+ void stopCapture();
+ void requestNextFrame();
+ void allocateBuffers();
+ void createWaylandBuffer(GbmBuffer* buf);
+
+ // Returns the buffer that is not currently in use by Qt.
+ GbmBuffer* acquireCaptureBuffer();
+
+ QString m_windowId;
+ bool m_active = false;
+ bool m_running = false;
+ bool m_waylandReady = false;
+
+ // Capture geometry (set by session::buffer_size event)
+ QSize m_captureSize;
+
+ // Supported DMA-BUF formats reported by the session
+ struct FormatInfo { uint32_t format; uint64_t modifier; };
+ QList m_dmabufFormats;
+ bool m_formatsReceived = false;
+
+ wl_display* m_display = nullptr;
+ wl_registry* m_registry = nullptr;
+
+ ext_image_copy_capture_manager_v1* m_captureManager = nullptr;
+ ext_foreign_toplevel_image_capture_source_manager_v1* m_sourceManager = nullptr;
+ ext_foreign_toplevel_list_v1* m_toplevelList = nullptr;
+ zwp_linux_dmabuf_v1* m_dmabuf = nullptr;
+
+ ext_image_capture_source_v1* m_source = nullptr;
+ ext_image_copy_capture_session_v1* m_session = nullptr;
+ ext_image_copy_capture_frame_v1* m_frame = nullptr;
+
+ // The toplevel handle we want to capture
+ ext_foreign_toplevel_handle_v1* m_targetHandle = nullptr;
+ // Temporary per-toplevel state during discovery
+ struct ToplevelInfo {
+ ext_foreign_toplevel_handle_v1* handle = nullptr;
+ QString appId;
+ QString title;
+ QString identifier;
+ bool matched = false;
+ };
+ QList m_pendingToplevels;
+
+ int m_drmFd = -1;
+ gbm_device* m_gbmDev = nullptr;
+
+ int m_pendingIdx = -1;
+
+ // Double-buffering: one buffer captured by compositor, one displayed by Qt.
+ static constexpr int POOL = 2;
+ std::array, POOL> m_buffers;
+ int m_captureIdx = 0; // index of the buffer we last sent to the compositor
+ int m_displayIdx = -1;// index of the buffer ready for Qt to display
+
+ // Qt Scene Graph (render thread side)
+ // Protected by m_sgMutex.
+ QMutex m_sgMutex;
+ GbmBuffer* m_pendingBuffer = nullptr; // written by Wayland CB, read by SG
+ EGLDisplay m_eglDisplay = EGL_NO_DISPLAY;
+ // EGL/GL extension function pointers, resolved once (stored as void* to
+ // avoid pulling gl2ext.h into a MOC-parsed header).
+ void* m_eglCreateImage = nullptr; // PFNEGLCREATEIMAGEKHRPROC
+ void* m_eglDestroyImage = nullptr; // PFNEGLDESTROYIMAGEKHRPROC
+ void* m_glEGLImageTarget = nullptr; // PFNGLEGLIMAGETARGETTEXTURE2DOESPROC
+
+public:
+ static void onRegistryGlobal(void*, wl_registry*, uint32_t name,
+ const char* iface, uint32_t version);
+ static void onRegistryGlobalRemove(void*, wl_registry*, uint32_t name);
+
+ // ext_foreign_toplevel_list
+ static void onToplevelListToplevel(void*, ext_foreign_toplevel_list_v1*,
+ ext_foreign_toplevel_handle_v1* handle);
+ static void onToplevelListFinished(void*, ext_foreign_toplevel_list_v1*);
+
+ // ext_foreign_toplevel_handle
+ static void onToplevelAppId(void*, ext_foreign_toplevel_handle_v1*, const char* app_id);
+ static void onToplevelTitle(void*, ext_foreign_toplevel_handle_v1*, const char* title);
+ static void onToplevelIdentifier(void*, ext_foreign_toplevel_handle_v1*, const char* id);
+ static void onToplevelDone(void*, ext_foreign_toplevel_handle_v1*);
+ static void onToplevelClosed(void*, ext_foreign_toplevel_handle_v1*);
+
+ // ext_image_copy_capture_session
+ static void onSessionBufferSize(void*, ext_image_copy_capture_session_v1*,
+ uint32_t w, uint32_t h);
+ static void onSessionShmFormat(void*, ext_image_copy_capture_session_v1*, uint32_t fmt);
+ static void onSessionDmabufDevice(void*, ext_image_copy_capture_session_v1*,
+ wl_array* device);
+ static void onSessionDmabufFormat(void*, ext_image_copy_capture_session_v1*,
+ uint32_t fmt, wl_array* modifiers);
+ static void onSessionDone(void*, ext_image_copy_capture_session_v1*);
+ static void onSessionStopped(void*, ext_image_copy_capture_session_v1*);
+
+ // ext_image_copy_capture_frame
+ static void onFrameTransform(void*, ext_image_copy_capture_frame_v1*, uint32_t transform);
+ static void onFrameDamage(void*, ext_image_copy_capture_frame_v1*,
+ int32_t x, int32_t y, int32_t w, int32_t h);
+ static void onFramePresentationTime(void*, ext_image_copy_capture_frame_v1*,
+ uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec);
+ static void onFrameReady(void*, ext_image_copy_capture_frame_v1*);
+ static void onFrameFailed(void*, ext_image_copy_capture_frame_v1*, uint32_t reason);
+};
\ No newline at end of file
diff --git a/src/share/sleex/plugins/src/Sleex/widgets/GbmBuffer.cpp b/src/share/sleex/plugins/src/Sleex/widgets/GbmBuffer.cpp
new file mode 100644
index 00000000..13c8617b
--- /dev/null
+++ b/src/share/sleex/plugins/src/Sleex/widgets/GbmBuffer.cpp
@@ -0,0 +1,98 @@
+#include "GbmBuffer.hpp"
+#include
+#include
+#include
+#include
+#include
+
+bool GbmBuffer::allocate(gbm_device* dev,
+ int width,
+ int height,
+ uint32_t format,
+ uint64_t modifier)
+{
+ release();
+
+ m_width = width;
+ m_height = height;
+ m_format = format;
+ m_modifier = modifier;
+
+ if (modifier != DRM_FORMAT_MOD_INVALID && modifier != DRM_FORMAT_MOD_LINEAR) {
+ const uint64_t mods[] = { modifier };
+ m_bo = gbm_bo_create_with_modifiers2(dev, width, height, format, mods, 1,
+ GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR);
+ }
+ if (!m_bo) {
+ m_bo = gbm_bo_create(dev, width, height, format,
+ GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR);
+ }
+ if (!m_bo) return false;
+
+ m_fd = gbm_bo_get_fd(m_bo);
+ m_stride = gbm_bo_get_stride(m_bo);
+
+ return (m_fd >= 0);
+}
+
+GbmBuffer::~GbmBuffer()
+{
+ release();
+}
+
+GbmBuffer::GbmBuffer(GbmBuffer&& o) noexcept
+ : m_bo(o.m_bo), m_fd(o.m_fd), m_stride(o.m_stride),
+ m_format(o.m_format), m_modifier(o.m_modifier),
+ m_width(o.m_width), m_height(o.m_height),
+ m_wlBuffer(o.m_wlBuffer), m_qtTexture(o.m_qtTexture),
+ eglImage(o.eglImage), inUse(o.inUse), ready(o.ready)
+{
+ o.m_bo = nullptr;
+ o.m_fd = -1;
+ o.m_wlBuffer = nullptr;
+ o.m_qtTexture = nullptr;
+ o.eglImage = EGL_NO_IMAGE_KHR;
+}
+
+GbmBuffer& GbmBuffer::operator=(GbmBuffer&& o) noexcept
+{
+ if (this != &o) {
+ release();
+ m_bo = o.m_bo; o.m_bo = nullptr;
+ m_fd = o.m_fd; o.m_fd = -1;
+ m_stride = o.m_stride;
+ m_format = o.m_format;
+ m_modifier = o.m_modifier;
+ m_width = o.m_width;
+ m_height = o.m_height;
+ m_wlBuffer = o.m_wlBuffer; o.m_wlBuffer = nullptr;
+ m_qtTexture = o.m_qtTexture; o.m_qtTexture = nullptr;
+ eglImage = o.eglImage; o.eglImage = EGL_NO_IMAGE_KHR;
+ inUse = o.inUse;
+ ready = o.ready;
+ }
+ return *this;
+}
+
+void GbmBuffer::release()
+{
+ if (m_qtTexture) {
+ delete m_qtTexture;
+ m_qtTexture = nullptr;
+ }
+
+ if (m_wlBuffer) {
+ wl_buffer_destroy(m_wlBuffer);
+ m_wlBuffer = nullptr;
+ }
+ if (m_fd >= 0) {
+ close(m_fd);
+ m_fd = -1;
+ }
+ if (m_bo) {
+ gbm_bo_destroy(m_bo);
+ m_bo = nullptr;
+ }
+ inUse = false;
+ ready = false;
+}
\ No newline at end of file
diff --git a/src/share/sleex/plugins/src/Sleex/widgets/GbmBuffer.hpp b/src/share/sleex/plugins/src/Sleex/widgets/GbmBuffer.hpp
new file mode 100644
index 00000000..1cee75fd
--- /dev/null
+++ b/src/share/sleex/plugins/src/Sleex/widgets/GbmBuffer.hpp
@@ -0,0 +1,61 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+
+struct wl_buffer;
+class QSGTexture; // Forward declaration pour Ʃviter d'inclure tout Qt ici
+
+class GbmBuffer {
+public:
+ GbmBuffer() = default;
+
+ bool allocate(gbm_device* dev,
+ int width,
+ int height,
+ uint32_t format,
+ uint64_t modifier);
+
+ ~GbmBuffer();
+
+ // Non-copyable, moveable
+ GbmBuffer(const GbmBuffer&) = delete;
+ GbmBuffer& operator=(const GbmBuffer&) = delete;
+ GbmBuffer(GbmBuffer&&) noexcept;
+ GbmBuffer& operator=(GbmBuffer&&) noexcept;
+
+ bool isValid() const { return m_bo != nullptr; }
+ int fd() const { return m_fd; }
+ uint32_t stride() const { return m_stride; }
+ uint32_t offset() const { return 0; }
+ uint32_t format() const { return m_format; }
+ uint64_t modifier() const { return m_modifier; }
+ int width() const { return m_width; }
+ int height() const { return m_height; }
+
+ wl_buffer* waylandBuffer() const { return m_wlBuffer; }
+ void setWaylandBuffer(wl_buffer* b) { m_wlBuffer = b; }
+
+ QSGTexture* qtTexture() const { return m_qtTexture; }
+ void setQtTexture(QSGTexture* tex) { m_qtTexture = tex; }
+ EGLImage eglImage = EGL_NO_IMAGE_KHR;
+
+ // Ping-pong flags
+ bool isCapturing = false;
+ bool ready = false;
+ bool inUse = false;
+private:
+ void release();
+
+ gbm_bo* m_bo = nullptr;
+ int m_fd = -1;
+ uint32_t m_stride = 0;
+ uint32_t m_format = 0;
+ uint64_t m_modifier = 0;
+ int m_width = 0;
+ int m_height = 0;
+ wl_buffer* m_wlBuffer = nullptr;
+ QSGTexture* m_qtTexture = nullptr;
+};
\ No newline at end of file
diff --git a/src/share/sleex/plugins/src/Sleex/widgets/plugin.cpp b/src/share/sleex/plugins/src/Sleex/widgets/plugin.cpp
old mode 100644
new mode 100755
index 1114bd49..c67f993d
--- a/src/share/sleex/plugins/src/Sleex/widgets/plugin.cpp
+++ b/src/share/sleex/plugins/src/Sleex/widgets/plugin.cpp
@@ -1,6 +1,6 @@
#include
-class SleexUtilsPlugin : public QQmlExtensionPlugin {
+class SleexWidgetsPlugin : public QQmlExtensionPlugin {
Q_OBJECT
Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
From af77470b41cb10e3eadd8ab142995471688bc0f6 Mon Sep 17 00:00:00 2001
From: Ardox
Date: Fri, 10 Apr 2026 14:11:45 +0200
Subject: [PATCH 29/56] Animate lockscreen and greeter
---
src/share/sleex/greetd.qml | 15 +++++++--------
.../sleex/modules/lockscreen/LockSurface.qml | 5 ++---
2 files changed, 9 insertions(+), 11 deletions(-)
diff --git a/src/share/sleex/greetd.qml b/src/share/sleex/greetd.qml
index 71ebd7f5..5f09972b 100755
--- a/src/share/sleex/greetd.qml
+++ b/src/share/sleex/greetd.qml
@@ -100,18 +100,17 @@ ShellRoot {
color: "transparent"
WlrLayershell.exclusionMode: ExclusionMode.Ignore
- Image {
+ WallpaperDisplay {
id: backgroundImage
anchors.fill: parent
source: Config.options.background.wallpaperPath
- fillMode: Image.PreserveAspectCrop
- GaussianBlur {
- anchors.fill: parent
- source: backgroundImage
- radius: 10
- opacity: parent.opacity
- }
+ // GaussianBlur {
+ // anchors.fill: parent
+ // source: backgroundImage
+ // radius: 10
+ // opacity: parent.opacity
+ // }
}
RippleButton {
diff --git a/src/share/sleex/modules/lockscreen/LockSurface.qml b/src/share/sleex/modules/lockscreen/LockSurface.qml
index 4659be66..8ace81b4 100644
--- a/src/share/sleex/modules/lockscreen/LockSurface.qml
+++ b/src/share/sleex/modules/lockscreen/LockSurface.qml
@@ -44,11 +44,10 @@ FocusScope {
}
// Wallpaper & Scrim
- Image {
+ WallpaperDisplay {
id: wallpaper
anchors.fill: parent
source: Config.options.background.wallpaperPath
- fillMode: Image.PreserveAspectCrop
opacity: root.visualsReady ? 1 : 0
Behavior on opacity {
@@ -64,7 +63,7 @@ FocusScope {
radius: 15
opacity: parent.opacity
}
-
+
MouseArea {
anchors.fill: parent
onClicked: passwordInput.forceActiveFocus()
From 481e56ce3ec91a8480ef7ee91bc6ebde0e8ed656 Mon Sep 17 00:00:00 2001
From: Ardox
Date: Fri, 10 Apr 2026 14:12:35 +0200
Subject: [PATCH 30/56] Fix switchwall script
---
src/share/sleex/scripts/colors/switchwall.sh | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/share/sleex/scripts/colors/switchwall.sh b/src/share/sleex/scripts/colors/switchwall.sh
index 0b206cdd..45826ace 100755
--- a/src/share/sleex/scripts/colors/switchwall.sh
+++ b/src/share/sleex/scripts/colors/switchwall.sh
@@ -79,15 +79,15 @@ check_and_prompt_upscale() {
is_video() {
local extension="${1##*.}"
- [[ "$extension" == "mp4" || "$extension" == "mkv" || "$extension" == "webm" ]] && return 0 || return 1 [cite: 4, 5]
+ [[ "$extension" == "mp4" || "$extension" == "mkv" || "$extension" == "webm" ]] && return 0 || return 1
}
update_wallpaper_config() {
local wallpaper_path="$1"
if [[ -f "$DB_CONFIG" ]]; then
- sqlite3 "$DB_CONFIG" "UPDATE sleex_settings SET config_json = json_set(config_json, '$.wallpaperPath', '$wallpaper_path') WHERE module='background';" [cite: 11]
- qs -p /usr/share/sleex/ ipc call background forceWallpaperReload "$wallpaper_path" [cite: 21, 53]
+ sqlite3 "$DB_CONFIG" "UPDATE sleex_settings SET config_json = json_set(config_json, '$.wallpaperPath', '$wallpaper_path') WHERE module='background';"
+ qs -p /usr/share/sleex/ ipc call background forceWallpaperReload "$wallpaper_path"
qs -p /usr/share/sleex/settings.qml ipc call settings reloadWallpaper "$wallpaper_path"
fi
}
From 8dd0b2bdc6b694cdbc8b1ebf67fe9d73afc7f211 Mon Sep 17 00:00:00 2001
From: Ardox
Date: Tue, 5 May 2026 19:07:39 +0200
Subject: [PATCH 31/56] Add VSCode settings for CMake configuration
---
.vscode/settings.json | 3 +++
1 file changed, 3 insertions(+)
create mode 100644 .vscode/settings.json
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 00000000..9ddf6b28
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "cmake.ignoreCMakeListsMissing": true
+}
\ No newline at end of file
From fdf0087162e2528462f023b596daf981c56c9ca8 Mon Sep 17 00:00:00 2001
From: Ardox
Date: Tue, 5 May 2026 19:43:51 +0200
Subject: [PATCH 32/56] desktop icons: Add hot reload to
---
.vscode/c_cpp_properties.json | 19 +++++++++++++++++++
.../plugins/src/Sleex/utils/DesktopModel.cpp | 15 +++++++++++++++
.../plugins/src/Sleex/utils/DesktopModel.hpp | 4 ++++
3 files changed, 38 insertions(+)
create mode 100644 .vscode/c_cpp_properties.json
diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json
new file mode 100644
index 00000000..6debbd45
--- /dev/null
+++ b/.vscode/c_cpp_properties.json
@@ -0,0 +1,19 @@
+{
+ "configurations": [
+ {
+ "name": "Linux",
+ "includePath": [
+ "${workspaceFolder}/**",
+ "/usr/include/qt/QtCore",
+ "/usr/include/qt",
+ "/usr/include/qt/QtQml"
+ ],
+ "defines": [],
+ "compilerPath": "/usr/bin/clang",
+ "cStandard": "c17",
+ "cppStandard": "c++17",
+ "intelliSenseMode": "linux-clang-x64"
+ }
+ ],
+ "version": 4
+}
\ No newline at end of file
diff --git a/src/share/sleex/plugins/src/Sleex/utils/DesktopModel.cpp b/src/share/sleex/plugins/src/Sleex/utils/DesktopModel.cpp
index a28f5a64..e1e4ce79 100644
--- a/src/share/sleex/plugins/src/Sleex/utils/DesktopModel.cpp
+++ b/src/share/sleex/plugins/src/Sleex/utils/DesktopModel.cpp
@@ -35,6 +35,17 @@ QHash DesktopModel::roleNames() const {
}
void DesktopModel::loadDirectory(const QString &path) {
+ m_watchedPath = path;
+
+ if (!m_watcher.directories().isEmpty())
+ m_watcher.removePaths(m_watcher.directories());
+
+ m_watcher.addPath(path);
+
+ connect(&m_watcher, &QFileSystemWatcher::directoryChanged,
+ this, &DesktopModel::onDirectoryChanged,
+ Qt::UniqueConnection);
+
beginResetModel();
m_items.clear();
@@ -65,6 +76,10 @@ void DesktopModel::loadDirectory(const QString &path) {
endResetModel();
}
+void DesktopModel::onDirectoryChanged() {
+ loadDirectory(m_watchedPath);
+}
+
void DesktopModel::moveIcon(int index, int newX, int newY) {
if (index < 0 || index >= m_items.size()) return;
diff --git a/src/share/sleex/plugins/src/Sleex/utils/DesktopModel.hpp b/src/share/sleex/plugins/src/Sleex/utils/DesktopModel.hpp
index eeebb2cf..2912426d 100644
--- a/src/share/sleex/plugins/src/Sleex/utils/DesktopModel.hpp
+++ b/src/share/sleex/plugins/src/Sleex/utils/DesktopModel.hpp
@@ -4,6 +4,7 @@
#include
#include
#include
+#include
struct DesktopItem {
QString fileName;
@@ -38,5 +39,8 @@ class DesktopModel : public QAbstractListModel {
private:
QList m_items;
+ QString m_watchedPath;
+ QFileSystemWatcher m_watcher;
void saveCurrentLayout();
+ void onDirectoryChanged();
};
\ No newline at end of file
From 2e87a54128c68edddca39daad5e59c2f7f709d66 Mon Sep 17 00:00:00 2001
From: Ardox
Date: Tue, 5 May 2026 20:23:10 +0200
Subject: [PATCH 33/56] Desktop icons: proper initial position handling
Co-authored-by: Copilot
---
.vscode/c_cpp_properties.json | 3 ++-
.../sleex/modules/background/DesktopIcons.qml | 1 +
.../plugins/src/Sleex/utils/DesktopModel.cpp | 27 ++++++++++++++++---
.../plugins/src/Sleex/utils/DesktopModel.hpp | 13 +++++++++
4 files changed, 40 insertions(+), 4 deletions(-)
diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json
index 6debbd45..34a94537 100644
--- a/.vscode/c_cpp_properties.json
+++ b/.vscode/c_cpp_properties.json
@@ -6,7 +6,8 @@
"${workspaceFolder}/**",
"/usr/include/qt/QtCore",
"/usr/include/qt",
- "/usr/include/qt/QtQml"
+ "/usr/include/qt/QtQml",
+ "/usr/include/qt/QtGui"
],
"defines": [],
"compilerPath": "/usr/bin/clang",
diff --git a/src/share/sleex/modules/background/DesktopIcons.qml b/src/share/sleex/modules/background/DesktopIcons.qml
index 10cbe8c7..14ef13c6 100644
--- a/src/share/sleex/modules/background/DesktopIcons.qml
+++ b/src/share/sleex/modules/background/DesktopIcons.qml
@@ -24,6 +24,7 @@ Item {
DesktopModel {
id: desktopModel
+ rows: Math.max(1, Math.floor(gridArea.height / cellHeight))
Component.onCompleted: loadDirectory(FileUtils.trimFileProtocol(Directories.desktop))
}
diff --git a/src/share/sleex/plugins/src/Sleex/utils/DesktopModel.cpp b/src/share/sleex/plugins/src/Sleex/utils/DesktopModel.cpp
index e1e4ce79..b87dbb66 100644
--- a/src/share/sleex/plugins/src/Sleex/utils/DesktopModel.cpp
+++ b/src/share/sleex/plugins/src/Sleex/utils/DesktopModel.cpp
@@ -2,6 +2,7 @@
#include "DesktopStateManager.hpp"
#include
#include
+#include
DesktopModel::DesktopModel(QObject *parent) : QAbstractListModel(parent) {}
@@ -34,6 +35,17 @@ QHash DesktopModel::roleNames() const {
return roles;
}
+QPoint DesktopModel::getEmptySpot(const QSet &occupied) const {
+ for (int x = 0; ; ++x) {
+ for (int y = 0; y < m_rows; ++y) {
+ QString key = QString::number(x) + "," + QString::number(y);
+ if (!occupied.contains(key)) {
+ return QPoint(x, y);
+ }
+ }
+ }
+}
+
void DesktopModel::loadDirectory(const QString &path) {
m_watchedPath = path;
@@ -56,6 +68,14 @@ void DesktopModel::loadDirectory(const QString &path) {
DesktopStateManager sm;
QVariantMap savedLayout = sm.getLayout();
+ QSet occupied;
+ for (const QFileInfo &fileInfo : list) {
+ if (savedLayout.contains(fileInfo.fileName())) {
+ QVariantMap pos = savedLayout[fileInfo.fileName()].toMap();
+ occupied.insert(QString::number(pos["x"].toInt()) + "," + QString::number(pos["y"].toInt()));
+ }
+ }
+
for (const QFileInfo &fileInfo : list) {
DesktopItem item;
item.fileName = fileInfo.fileName();
@@ -67,9 +87,10 @@ void DesktopModel::loadDirectory(const QString &path) {
item.gridX = pos["x"].toInt();
item.gridY = pos["y"].toInt();
} else {
- // TODO: make getEmptySpot in C++ and call it here to get the initial position for new icons
- item.gridX = 0;
- item.gridY = 0;
+ QPoint spot = getEmptySpot(occupied);
+ item.gridX = spot.x();
+ item.gridY = spot.y();
+ occupied.insert(QString::number(item.gridX) + "," + QString::number(item.gridY));
}
m_items.append(item);
}
diff --git a/src/share/sleex/plugins/src/Sleex/utils/DesktopModel.hpp b/src/share/sleex/plugins/src/Sleex/utils/DesktopModel.hpp
index 2912426d..a4090c34 100644
--- a/src/share/sleex/plugins/src/Sleex/utils/DesktopModel.hpp
+++ b/src/share/sleex/plugins/src/Sleex/utils/DesktopModel.hpp
@@ -37,10 +37,23 @@ class DesktopModel : public QAbstractListModel {
Q_INVOKABLE void moveIcon(int index, int newX, int newY);
Q_INVOKABLE void massMove(const QVariantList &selectedPathsList, const QString &leaderPath, int targetX, int targetY, int maxCol, int maxRow);
+ Q_PROPERTY(int rows READ rows WRITE setRows NOTIFY rowsChanged)
+
+public:
+ int rows() const { return m_rows; }
+ void setRows(int r) {
+ if (m_rows != r) { m_rows = r; emit rowsChanged(); }
+ }
+
+signals:
+ void rowsChanged();
+
private:
+ int m_rows = 1;
QList m_items;
QString m_watchedPath;
QFileSystemWatcher m_watcher;
void saveCurrentLayout();
+ QPoint getEmptySpot(const QSet &occupied) const;
void onDirectoryChanged();
};
\ No newline at end of file
From 3e1cd5476061a9ba9565c9a02c2c5450558d1ce2 Mon Sep 17 00:00:00 2001
From: Ardox
Date: Tue, 5 May 2026 21:41:07 +0200
Subject: [PATCH 34/56] Background: Add postit widget
---
.../sleex/modules/background/Background.qml | 3 +
.../background/BackgroundContextMenu.qml | 26 +++++++
src/share/sleex/modules/background/PostIt.qml | 71 +++++++++++++++++
.../modules/background/PostItDelegate.qml | 78 +++++++++++++++++++
.../src/Sleex/utils/DesktopStateManager.cpp | 42 +++++++++-
.../src/Sleex/utils/DesktopStateManager.hpp | 5 ++
6 files changed, 224 insertions(+), 1 deletion(-)
create mode 100644 src/share/sleex/modules/background/PostIt.qml
create mode 100644 src/share/sleex/modules/background/PostItDelegate.qml
diff --git a/src/share/sleex/modules/background/Background.qml b/src/share/sleex/modules/background/Background.qml
index 37a2870a..c9955149 100644
--- a/src/share/sleex/modules/background/Background.qml
+++ b/src/share/sleex/modules/background/Background.qml
@@ -10,6 +10,7 @@ import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import Quickshell.Wayland
+import Sleex.Utils
Scope {
id: root
@@ -184,6 +185,8 @@ Scope {
Watermark { visibleWatermark: Config.options.background.showWatermark }
Quote { visibleQuote: Config.options.background.enableQuote }
+ PostIt { id: postItManager; z: 50 }
+
}
}
}
diff --git a/src/share/sleex/modules/background/BackgroundContextMenu.qml b/src/share/sleex/modules/background/BackgroundContextMenu.qml
index 58b1d3fb..b511c66d 100755
--- a/src/share/sleex/modules/background/BackgroundContextMenu.qml
+++ b/src/share/sleex/modules/background/BackgroundContextMenu.qml
@@ -142,6 +142,32 @@ Item {
bgMenu.close()
}
}
+
+ Rectangle {
+ Layout.fillWidth: true
+ implicitHeight: 1
+ color: Appearance.m3colors.m3outlineVariant
+ Layout.topMargin: 4
+ Layout.bottomMargin: 4
+ }
+
+ RippleButton {
+ Layout.fillWidth: true
+ buttonRadius: popupBackground.radius - popupBackground.padding
+
+ contentItem: RowLayout {
+ spacing: 8
+ anchors.fill: parent
+ anchors.margins: 12
+ MaterialSymbol { text: "note_add"; iconSize: 20 }
+ StyledText { text: "New Post-it"; Layout.fillWidth: true }
+ }
+
+ onClicked: {
+ postItManager.createNote(bgMenu.menuX, bgMenu.menuY)
+ bgMenu.close()
+ }
+ }
}
}
diff --git a/src/share/sleex/modules/background/PostIt.qml b/src/share/sleex/modules/background/PostIt.qml
new file mode 100644
index 00000000..c3ef1fe2
--- /dev/null
+++ b/src/share/sleex/modules/background/PostIt.qml
@@ -0,0 +1,71 @@
+import QtQuick
+import Sleex.Utils
+import qs.services
+
+Item {
+ id: postItManager
+ anchors.fill: parent
+ z: 50
+
+ ListModel {
+ id: postItModel
+ Component.onCompleted: {
+ let notes = DesktopStateManager.getPostIts()
+ for (let i = 0; i < notes.length; i++) {
+ postItModel.append(notes[i])
+ }
+ }
+ }
+
+ function saveNotes() {
+ let arr = []
+ for(let i = 0; i < postItModel.count; i++) {
+ let item = postItModel.get(i)
+ arr.push({
+ posX: item.posX,
+ posY: item.posY,
+ text: item.text
+ })
+ }
+ DesktopStateManager.savePostIts(arr)
+ }
+
+ function createNote(spawnX, spawnY) {
+ postItModel.append({
+ "posX": spawnX,
+ "posY": spawnY,
+ "text": ""
+ })
+ saveNotes()
+ }
+
+ Repeater {
+ model: postItModel
+ delegate: PostItDelegate {
+ required property real posX
+ required property real posY
+ required property string text
+ required property int index
+
+ x: posX
+ y: posY
+ noteText: text
+
+ onDeleteRequested: {
+ postItModel.remove(index)
+ postItManager.saveNotes()
+ }
+
+ onPositionUpdated: (newX, newY) => {
+ postItModel.setProperty(index, "posX", newX)
+ postItModel.setProperty(index, "posY", newY)
+ postItManager.saveNotes()
+ }
+
+ onTextUpdated: (newText) => {
+ postItModel.setProperty(index, "text", newText)
+ postItManager.saveNotes()
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/share/sleex/modules/background/PostItDelegate.qml b/src/share/sleex/modules/background/PostItDelegate.qml
new file mode 100644
index 00000000..6a3cfab5
--- /dev/null
+++ b/src/share/sleex/modules/background/PostItDelegate.qml
@@ -0,0 +1,78 @@
+import QtQuick
+import QtQuick.Controls
+import qs.modules.common.widgets
+import qs.modules.common
+
+Rectangle {
+ id: postitRoot
+ width: 200
+ height: 200
+ color: "#fdfd96"
+ border.color: "#d9d97c"
+ border.width: 1
+ radius: 4
+
+ property string noteText: ""
+ signal deleteRequested()
+ signal textUpdated(string newText)
+ signal positionUpdated(real newX, real newY)
+
+ DragHandler {
+ target: postitRoot
+ cursorShape: active ? Qt.ClosedHandCursor : Qt.OpenHandCursor
+
+ onActiveChanged: {
+ if (!active) {
+ postitRoot.positionUpdated(postitRoot.x, postitRoot.y)
+ }
+ }
+ }
+
+ TextEdit {
+ id: editor
+ anchors.fill: parent
+ anchors.margins: 12
+ text: postitRoot.noteText
+ wrapMode: TextEdit.Wrap
+ font.pointSize: 12
+ color: "#222"
+ readOnly: true
+
+ Keys.onPressed: function (event) {
+ if (event.key === Qt.Key_Escape) {
+ readOnly = true
+ postitRoot.textUpdated(text)
+ readOnly = true
+ event.accepted = true
+ }
+ }
+ }
+
+ RippleButton {
+ id: deleteButton
+ anchors.top: parent.top
+ anchors.right: parent.right
+ anchors.margins: 4
+ width: 24
+ height: 24
+ buttonRadius: width / 2
+ z: 100
+
+ MaterialSymbol {
+ anchors.centerIn: parent;
+ text: "close";
+ iconSize: 16
+ color: Appearance.colors.colOnError
+ }
+
+ onClicked: postitRoot.deleteRequested()
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onDoubleClicked: {
+ editor.readOnly = false
+ editor.forceActiveFocus()
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/share/sleex/plugins/src/Sleex/utils/DesktopStateManager.cpp b/src/share/sleex/plugins/src/Sleex/utils/DesktopStateManager.cpp
index be6993c4..88ef6ea2 100644
--- a/src/share/sleex/plugins/src/Sleex/utils/DesktopStateManager.cpp
+++ b/src/share/sleex/plugins/src/Sleex/utils/DesktopStateManager.cpp
@@ -5,6 +5,7 @@
#include
#include
#include
+#include
DesktopStateManager::DesktopStateManager(QObject *parent) : QObject(parent) {}
@@ -26,7 +27,7 @@ void DesktopStateManager::saveLayout(const QVariantMap& layout) {
file.write(doc.toJson(QJsonDocument::Indented));
file.close();
} else {
- qWarning() << "Sleex: Impossible de sauvegarder le layout du bureau dans" << getConfigFilePath();
+ qWarning() << "Sleex: Cannot save desktop layout to" << getConfigFilePath();
}
}
@@ -46,4 +47,43 @@ QVariantMap DesktopStateManager::getLayout() {
}
return QVariantMap();
+}
+
+QString DesktopStateManager::getPostItsFilePath() const {
+ QString configDir = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + "/sleex";
+ QDir dir(configDir);
+ if (!dir.exists()) dir.mkpath(".");
+
+ return configDir + "/postits.json";
+}
+
+void DesktopStateManager::savePostIts(const QVariantList& postits) {
+ QJsonArray jsonArr = QJsonArray::fromVariantList(postits);
+ QJsonDocument doc(jsonArr);
+ QFile file(getPostItsFilePath());
+
+ if (file.open(QIODevice::WriteOnly)) {
+ file.write(doc.toJson(QJsonDocument::Indented));
+ file.close();
+ } else {
+ qWarning() << "Sleex: Cannot save post-its to" << getPostItsFilePath();
+ }
+}
+
+QVariantList DesktopStateManager::getPostIts() {
+ QFile file(getPostItsFilePath());
+
+ if (!file.open(QIODevice::ReadOnly)) {
+ return QVariantList();
+ }
+
+ QByteArray data = file.readAll();
+ file.close();
+
+ QJsonDocument doc = QJsonDocument::fromJson(data);
+ if (doc.isArray()) {
+ return doc.array().toVariantList();
+ }
+
+ return QVariantList();
}
\ No newline at end of file
diff --git a/src/share/sleex/plugins/src/Sleex/utils/DesktopStateManager.hpp b/src/share/sleex/plugins/src/Sleex/utils/DesktopStateManager.hpp
index a83271f9..55af1740 100644
--- a/src/share/sleex/plugins/src/Sleex/utils/DesktopStateManager.hpp
+++ b/src/share/sleex/plugins/src/Sleex/utils/DesktopStateManager.hpp
@@ -2,6 +2,7 @@
#include
#include
+#include
#include
class DesktopStateManager : public QObject {
@@ -14,7 +15,11 @@ class DesktopStateManager : public QObject {
Q_INVOKABLE void saveLayout(const QVariantMap& layout);
Q_INVOKABLE QVariantMap getLayout();
+
+ Q_INVOKABLE void savePostIts(const QVariantList& postits);
+ Q_INVOKABLE QVariantList getPostIts();
private:
QString getConfigFilePath() const;
+ QString getPostItsFilePath() const;
};
\ No newline at end of file
From 66fadce60ca941451142337846fe9c19ac514733 Mon Sep 17 00:00:00 2001
From: Limsayo
Date: Tue, 12 May 2026 11:50:22 +0200
Subject: [PATCH 35/56] fix(hyprland): replace deprecated togglesplit
dispatcher with layoutmsg
---
src/etc/sleex/hyprland/keybinds.conf | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/etc/sleex/hyprland/keybinds.conf b/src/etc/sleex/hyprland/keybinds.conf
index 5a87f549..ddee3a63 100644
--- a/src/etc/sleex/hyprland/keybinds.conf
+++ b/src/etc/sleex/hyprland/keybinds.conf
@@ -95,7 +95,7 @@ bindm = Super, mouse:273, resizewindow
# Toggle focused window split
$d=[$wm]
-bindd = Super, J, $d toggle split, togglesplit # Toggle split
+bindd = Super, J, $d toggle split,layoutmsg, togglesplit # Toggle split
#!
##! Workspace navigation
From 7ca16d33b686023e2e5aace79808a3417693bfcc Mon Sep 17 00:00:00 2001
From: Ardox
Date: Tue, 12 May 2026 19:50:56 +0200
Subject: [PATCH 36/56] Readd fhtc to cmakelists
---
src/share/sleex/plugins/src/Sleex/CMakeLists.txt | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/share/sleex/plugins/src/Sleex/CMakeLists.txt b/src/share/sleex/plugins/src/Sleex/CMakeLists.txt
index ef3fd398..4c51fca7 100644
--- a/src/share/sleex/plugins/src/Sleex/CMakeLists.txt
+++ b/src/share/sleex/plugins/src/Sleex/CMakeLists.txt
@@ -36,4 +36,5 @@ endfunction()
add_subdirectory(services)
add_subdirectory(core)
add_subdirectory(utils)
-add_subdirectory(widgets)
\ No newline at end of file
+add_subdirectory(widgets)
+add_subdirectory(fhtc)
From ed7cddafa05e9634700dc1a6c2a9b719558ae311 Mon Sep 17 00:00:00 2001
From: Ardox
Date: Tue, 12 May 2026 20:11:28 +0200
Subject: [PATCH 37/56] Replace hyprland configs with fhtc
---
src/etc/sleex/compositor.toml | 7 ++
src/etc/sleex/compositor/envs.toml | 19 +++
src/etc/sleex/compositor/execs.toml | 12 ++
src/etc/sleex/compositor/general.toml | 76 ++++++++++++
src/etc/sleex/compositor/keybinds.toml | 115 ++++++++++++++++++
src/etc/sleex/compositor/rules.toml | 52 +++++++++
src/etc/sleex/hyprland.conf | 23 ----
src/etc/sleex/hyprland/colors.conf | 32 -----
src/etc/sleex/hyprland/env.conf | 28 -----
src/etc/sleex/hyprland/execs.conf | 18 ---
src/etc/sleex/hyprland/general.conf | 144 -----------------------
src/etc/sleex/hyprland/keybinds.conf | 155 -------------------------
src/etc/sleex/hyprland/rules.conf | 104 -----------------
13 files changed, 281 insertions(+), 504 deletions(-)
create mode 100644 src/etc/sleex/compositor.toml
create mode 100644 src/etc/sleex/compositor/envs.toml
create mode 100644 src/etc/sleex/compositor/execs.toml
create mode 100644 src/etc/sleex/compositor/general.toml
create mode 100644 src/etc/sleex/compositor/keybinds.toml
create mode 100644 src/etc/sleex/compositor/rules.toml
delete mode 100644 src/etc/sleex/hyprland.conf
delete mode 100644 src/etc/sleex/hyprland/colors.conf
delete mode 100644 src/etc/sleex/hyprland/env.conf
delete mode 100644 src/etc/sleex/hyprland/execs.conf
delete mode 100644 src/etc/sleex/hyprland/general.conf
delete mode 100644 src/etc/sleex/hyprland/keybinds.conf
delete mode 100644 src/etc/sleex/hyprland/rules.conf
diff --git a/src/etc/sleex/compositor.toml b/src/etc/sleex/compositor.toml
new file mode 100644
index 00000000..0c040e63
--- /dev/null
+++ b/src/etc/sleex/compositor.toml
@@ -0,0 +1,7 @@
+imports = [
+ "/etc/sleex/compositor/general.toml",
+ "/etc/sleex/compositor/keybinds.toml",
+ "/etc/sleex/compositor/rules.toml",
+ "/etc/sleex/compositor/execs.toml",
+ "/etc/sleex/compositor/envs.toml",
+]
diff --git a/src/etc/sleex/compositor/envs.toml b/src/etc/sleex/compositor/envs.toml
new file mode 100644
index 00000000..bf8bf27e
--- /dev/null
+++ b/src/etc/sleex/compositor/envs.toml
@@ -0,0 +1,19 @@
+[env]
+# Themes
+QT_QPA_PLATFORM = "wayland"
+QT_QPA_PLATFORMTHEME = "kde"
+QT_STYLE_OVERRIDE = "kvantum"
+WLR_NO_HARDWARE_CURSORS = "1"
+
+XDG_SESSION_TYPE = "wayland"
+GDK_BACKEND = "wayland"
+SDL_VIDEODRIVER = "wayland"
+CLUTTER_BACKEND = "wayland"
+MOZ_ENABLE_WAYLAND = "1"
+
+# Screen tearing
+# WLR_DRM_NO_ATOMIC, 1
+
+# Others
+
+SLEEX_VIRTUAL_ENV = "~/.local/state/sleex/.venv"
diff --git a/src/etc/sleex/compositor/execs.toml b/src/etc/sleex/compositor/execs.toml
new file mode 100644
index 00000000..9cc51af4
--- /dev/null
+++ b/src/etc/sleex/compositor/execs.toml
@@ -0,0 +1,12 @@
+# Autostart programs.
+autostart = [
+ #"swww-daemon",
+ "qs -p /usr/share/sleex/",
+ "/usr/lib/geoclue-2.0/demos/agent & gammastep",
+ "gnome-keyring-daemon --start --components=secrets",
+ "/usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1 || /usr/libexec/polkit-gnome-authentication-agent-1",
+ "dbus-update-activation-environment --all",
+ "sleep 1 && dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP",
+ "wl-paste --type text --watch cliphist store",
+ "wl-paste --type image --watch cliphist store"
+]
diff --git a/src/etc/sleex/compositor/general.toml b/src/etc/sleex/compositor/general.toml
new file mode 100644
index 00000000..2805ace2
--- /dev/null
+++ b/src/etc/sleex/compositor/general.toml
@@ -0,0 +1,76 @@
+# You can find everything you want by reading the wiki!
+# https://nferhat.github.io/fht-compositor
+
+# Input configuration.
+# SEE: man 7 xkeyboard-config
+[input.keyboard]
+layout = "us"
+rules = ""
+repeat-rate = 50
+repeat-delay = 250
+options = "numpad:mac" # This makes the numpad keys work as expected when numlock is off.
+
+[input.touchpad]
+natural-scrolling = true
+
+[cursor]
+name = "Bibata-Modern-Classic"
+size = 24
+
+
+# ---------------------------------------------------------
+
+# Output configuration (examples)
+
+# [outputs.HDMI-A-1]
+# mode = "1920x1080@144"
+
+# [outputs.DP-1]
+# mode = "1920x1080@75"
+# position.x = 1920
+
+# ---------------------------------------------------------
+
+# General compositor behaviour
+[general]
+cursor-warps = true # Move the cursor to newly opened windows
+focus-new-windows = true # Give newly inserted windows keyboard focus
+focus-follows-mouse = true # When true, hovering over a window will focus it without clicking
+
+layouts = ["tile", "floating"]
+nmaster = 1 # Only one master client
+mwfact = 0.5 # And share the screen size between the master and slave equally
+inner-gaps = 7 # gaps between tiles
+outer-gaps = 7 # gaps around the screen
+
+# ---------------------------------------------------------
+
+# Decorations: I.E the pretty
+# All the decorations can be overriden on a per-window basis using window rules.
+[decorations]
+decoration-mode = "force-server-side"
+
+# Border around windows!
+[decorations.border]
+thickness = 0
+radius = 10
+focused-color = "#ffffff"
+normal-color = "#222230"
+
+# Shadows! Very useful for floating windows.
+[decorations.shadow]
+color = "black" # by default its rgba(black, 0.5)
+floating-only = false # Up to you, I don't judge!
+
+# Blur, because yes!
+[decorations.blur]
+disable = false
+passes = 4
+radius = 5
+noise = 0.05
+
+# ---------------------------------------------------------
+
+# Animations. Makes everything smooooooooooooooth!
+[animations]
+disable = false
diff --git a/src/etc/sleex/compositor/keybinds.toml b/src/etc/sleex/compositor/keybinds.toml
new file mode 100644
index 00000000..8a613237
--- /dev/null
+++ b/src/etc/sleex/compositor/keybinds.toml
@@ -0,0 +1,115 @@
+# Key bindings.
+[keybinds]
+
+
+Super-Ctrl-q = "quit"
+Super-Ctrl-r = "reload-config"
+
+Super-Return = { action = "run-command", arg = "foot" }
+Super-Shift-s = { action = "run-command", arg = """grim -g "`slurp -o`" - | wl-copy --type image/png""" }
+Super-w = { action = "run-command", arg = "firefox" }
+Super-c = { action = "run-command", arg = "code --enable-features=UseOzonePlatform --ozone-platform=wayland" }
+# Super-a = { action = "run-command", arg = "waydroid show-full-ui" }
+Super-e = { action = "run-command", arg = "pcmanfm-qt" }
+
+Super-a = { action = "run-command", arg = "qs -p Documents/sleex/src/share/sleex/ ipc call overview toggle" }
+
+# If you need to run an action even when the compositor is locked, here's how you can achieve this.
+XF86AudioRaiseVolume.action = "run-command"
+XF86AudioRaiseVolume.arg = "wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+"
+XF86AudioRaiseVolume.allow-while-locked = true
+XF86AudioLowerVolume.action = "run-command"
+XF86AudioLowerVolume.arg = "wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%-"
+XF86AudioLowerVolume.allow-while-locked = true
+XF86AudioMute.action = "run-command"
+XF86AudioMute.arg = "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"
+
+# Focus management, defaults are similar to what DWM provides
+Super-Right = "focus-next-window"
+Super-Left = "focus-previous-window"
+
+# Window management
+Super-m = "maximize-focused-window"
+Super-f = "fullscreen-focused-window"
+Super-q = "close-focused-window"
+Super-Ctrl-Space = "float-focused-window"
+Super-Shift-Right = "swap-with-next-window"
+Super-Shift-Left = "swap-with-previous-window"
+# Super-Ctrl-p = "pin-focused-window"
+
+# Transient layout changes.
+Super-Space = "select-next-layout"
+Super-Ctrl-Left = { action = "change-mwfact", arg = -0.1 }
+Super-Ctrl-Right = { action = "change-mwfact", arg = +0.1 }
+Super-Shift-h = { action = "change-nmaster", arg = +1 }
+Super-Shift-l = { action = "change-nmaster", arg = -1 }
+Super-i = { action = "change-window-proportion", arg = +0.5 }
+Super-o = { action = "change-window-proportion", arg = -0.5 }
+
+# Workspaces
+Super-ampersand = { action = "focus-workspace", arg = 0 }
+Super-eacute = { action = "focus-workspace", arg = 1 }
+Super-quotedbl = { action = "focus-workspace", arg = 2 }
+Super-apostrophe = { action = "focus-workspace", arg = 3 }
+Super-parenleft = { action = "focus-workspace", arg = 4 }
+Super-minus = { action = "focus-workspace", arg = 5 }
+Super-egrave = { action = "focus-workspace", arg = 6 }
+Super-underscore = { action = "focus-workspace", arg = 7 }
+Super-agrave = { action = "focus-workspace", arg = 8 }
+# For QWERTY users
+Super-1 = { action = "focus-workspace", arg = 0 }
+Super-2 = { action = "focus-workspace", arg = 1 }
+Super-3 = { action = "focus-workspace", arg = 2 }
+Super-4 = { action = "focus-workspace", arg = 3 }
+Super-5 = { action = "focus-workspace", arg = 4 }
+Super-6 = { action = "focus-workspace", arg = 5 }
+Super-7 = { action = "focus-workspace", arg = 6 }
+Super-8 = { action = "focus-workspace", arg = 7 }
+Super-9 = { action = "focus-workspace", arg = 8 }
+
+# Sending windows to workspaces
+Super-Ctrl-ampersand = { action = "send-to-workspace", arg = 0 }
+Super-Ctrl-eacute = { action = "send-to-workspace", arg = 1 }
+Super-Ctrl-quotedbl = { action = "send-to-workspace", arg = 2 }
+Super-Ctrl-apostrophe = { action = "send-to-workspace", arg = 3 }
+Super-Ctrl-parenleft = { action = "send-to-workspace", arg = 4 }
+Super-Ctrl-minus = { action = "send-to-workspace", arg = 5 }
+Super-Ctrl-egrave = { action = "send-to-workspace", arg = 6 }
+Super-Ctrl-underscore = { action = "send-to-workspace", arg = 7 }
+Super-Ctrl-agrave = { action = "send-to-workspace", arg = 8 }
+# For QWERTY users
+Super-Ctrl-1 = { action = "send-to-workspace", arg = 0 }
+Super-Ctrl-2 = { action = "send-to-workspace", arg = 1 }
+Super-Ctrl-3 = { action = "send-to-workspace", arg = 2 }
+Super-Ctrl-4 = { action = "send-to-workspace", arg = 3 }
+Super-Ctrl-5 = { action = "send-to-workspace", arg = 4 }
+Super-Ctrl-6 = { action = "send-to-workspace", arg = 5 }
+Super-Ctrl-7 = { action = "send-to-workspace", arg = 6 }
+Super-Ctrl-8 = { action = "send-to-workspace", arg = 7 }
+Super-Ctrl-9 = { action = "send-to-workspace", arg = 8 }
+
+# Sleex shell
+Super-d = { action = "global-shortcut", arg = "quickshell:dashboardToggle" }
+Super-Super_L = { action = "global-shortcut", arg = "quickshell:overviewToggleReleaseInterrupt" }
+Super-v = { action = "global-shortcut", arg = "quickshell:overviewClipboardToggle" }
+Super-semicolon = { action = "global-shortcut", arg = "quickshell:overviewEmojiToggle" }
+Super-f1 = { action = "global-shortcut", arg = "quickshell:cheatsheetToggle" }
+Ctrl-Alt-Delete = { action = "global-shortcut", arg = "quickshell:sessionToggle" }
+Super-t = { action = "global-shortcut", arg = "quickshell:wppselectorToggle" }
+Super-l = { action = "global-shortcut", arg = "quickshell:lockScreen" }
+
+# ---------------------------------------------------------
+
+# Mouse bindings
+# Refer to section 2.4: mousebinds
+[mousebinds]
+Super-Left = "swap-tile"
+Super-Right = "resize-tile"
+Super-scrollup = "focus-next-workspace"
+Super-scrolldown = "focus-previous-workspace"
+
+[gesturebinds]
+focus-next-workspace = { fingers = 4, direction = "left" }
+focus-previous-workspace = { fingers = 4, direction = "right" }
+fullscreen-focused-window = { fingers = 4, direction = "up" }
+float-focused-window = { fingers = 4, direction = "down" }
diff --git a/src/etc/sleex/compositor/rules.toml b/src/etc/sleex/compositor/rules.toml
new file mode 100644
index 00000000..38feb13e
--- /dev/null
+++ b/src/etc/sleex/compositor/rules.toml
@@ -0,0 +1,52 @@
+# Window rules
+[[rules]]
+match-app-id = [
+ "blueberry.py",
+ "com.axos-project.axinstall",
+ "steam",
+ "org.pulseaudio.pavucontrol",
+ "cheese",
+ "timeshift-gtk",
+ "org.gnome.PowerStats",
+ "lstopo",
+ "org.gnome.ColorProfileViewer",
+ "qt5ct",
+ "org.kde.plasmawindowed",
+ "kcm.networkmanagement",
+ "gcr-prompter"
+ ]
+match-title = [
+ "Open File",
+ "Select a File",
+ "Choose wallpaper",
+ "Open Folder",
+ "Save File",
+ "Save As",
+ "Library",
+ "File Upload",
+ "Sleex Settings",
+ "Calculator",
+ "BoxBuddy",
+ "Popsicle",
+ "Trayscale",
+ "Choose Files",
+ "Save Image",
+ "Save File"
+]
+floating = true
+centered = true
+
+[[rules]]
+is-floating = true
+ontop = true
+
+[[rules]]
+match-app-id = ["org.gnome.Calculator"]
+# pinned = true
+floating = true
+centered = true
+
+# Layer-shell rules
+#[[layer-rules]]
+#match-namespace = ["quickshell:*"]
+#blur = { disable = false, noise = 0, passes = 4, radius = 1 }
diff --git a/src/etc/sleex/hyprland.conf b/src/etc/sleex/hyprland.conf
deleted file mode 100644
index ef63b2f2..00000000
--- a/src/etc/sleex/hyprland.conf
+++ /dev/null
@@ -1,23 +0,0 @@
-# This file sources other files in `hyprland` and `custom` folders
-# You wanna add your stuff in file in `custom`
-exec = hyprctl dispatch submap global
-submap = global
-# Defaults
-source=/etc/sleex/hyprland/env.conf
-source=/etc/sleex/hyprland/execs.conf
-source=/etc/sleex/hyprland/general.conf
-source=/etc/sleex/hyprland/rules.conf
-source=/etc/sleex/hyprland/colors.conf
-source=/etc/sleex/hyprland/keybinds.conf
-
-# Custom
-source=~/.config/hypr/custom/env.conf
-source=~/.config/hypr/custom/execs.conf
-source=~/.config/hypr/custom/general.conf
-source=~/.config/hypr/custom/rules.conf
-source=~/.config/hypr/custom/keybinds.conf
-
-source=~/.config/hypr/monitors.conf
-
-# Applications bindings
-source=~/.config/hypr/apps.conf
\ No newline at end of file
diff --git a/src/etc/sleex/hyprland/colors.conf b/src/etc/sleex/hyprland/colors.conf
deleted file mode 100644
index eb97b9e5..00000000
--- a/src/etc/sleex/hyprland/colors.conf
+++ /dev/null
@@ -1,32 +0,0 @@
-general {
- col.active_border = rgba(dfe3e739)
- col.inactive_border = rgba(8b919830)
-}
-
-misc {
- background_color = rgba(101417FF)
-}
-
-plugin {
- hyprbars {
- # Honestly idk if it works like css, but well, why not
- bar_text_font = Rubik, Geist, AR One Sans, Reddit Sans, Inter, Roboto, Ubuntu, Noto Sans, sans-serif
- bar_height = 30
- bar_padding = 10
- bar_button_padding = 5
- bar_precedence_over_border = true
- bar_part_of_window = true
-
- bar_color = rgba(101417FF)
- col.text = rgba(dfe3e7FF)
-
-
- # example buttons (R -> L)
- # hyprbars-button = color, size, on-click
- hyprbars-button = rgb(dfe3e7), 13, ó°, hyprctl dispatch killactive
- hyprbars-button = rgb(dfe3e7), 13, ó°Æ, hyprctl dispatch fullscreen 1
- hyprbars-button = rgb(dfe3e7), 13, ó°°, hyprctl dispatch movetoworkspacesilent special
- }
-}
-
-windowrule = border_color rgba(93cdf6AA) rgba(93cdf677), match:pin true
diff --git a/src/etc/sleex/hyprland/env.conf b/src/etc/sleex/hyprland/env.conf
deleted file mode 100644
index 938e0605..00000000
--- a/src/etc/sleex/hyprland/env.conf
+++ /dev/null
@@ -1,28 +0,0 @@
-# ######### Input method ##########
-# See https://fcitx-im.org/wiki/Using_Fcitx_5_on_Wayland
-env = QT_IM_MODULE, fcitx
-env = XMODIFIERS, @im=fcitx
-# env = GTK_IM_MODULE, wayland # Crashes electron apps in xwayland
-env = SDL_IM_MODULE, fcitx
-env = GLFW_IM_MODULE, ibus
-env = INPUT_METHOD, fcitx
-
-# ############ Themes #############
-env = QT_QPA_PLATFORM, wayland
-env = QT_QPA_PLATFORMTHEME, qt6ct
-env = QT_STYLE_OVERRIDE, kvantum
-# env = WLR_NO_HARDWARE_CURSORS, 1
-
-env = XDG_SESSION_TYPE, wayland
-env = GDK_BACKEND, wayland
-env = QT_QPA_PLATFORM, wayland
-env = SDL_VIDEODRIVER, wayland
-env = CLUTTER_BACKEND, wayland
-env = MOZ_ENABLE_WAYLAND, 1
-
-# ######## Screen tearing #########
-# env = WLR_DRM_NO_ATOMIC, 1
-
-# ############ Others #############
-
-env = SLEEX_VIRTUAL_ENV, ~/.local/state/sleex/.venv
diff --git a/src/etc/sleex/hyprland/execs.conf b/src/etc/sleex/hyprland/execs.conf
deleted file mode 100644
index b8119262..00000000
--- a/src/etc/sleex/hyprland/execs.conf
+++ /dev/null
@@ -1,18 +0,0 @@
-# Bar, wallpaper
-# exec-once = swww-daemon
-exec-once = qs -p /usr/share/sleex &
-
-# Core components (authentication, lock screen, notification daemon)
-exec-once = dbus-update-activation-environment --all
-exec-once = sleep 1 && dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP # Some fix idk
-
-# Audio
-exec-once = easyeffects --gapplication-service
-
-# Clipboard: history
-# exec-once = wl-paste --watch cliphist store &
-exec-once = wl-paste --type text --watch cliphist store
-exec-once = wl-paste --type image --watch cliphist store
-
-# Cursor
-exec-once = hyprctl setcursor Bibata-Modern-Classic 24
diff --git a/src/etc/sleex/hyprland/general.conf b/src/etc/sleex/hyprland/general.conf
deleted file mode 100644
index e1cbeb17..00000000
--- a/src/etc/sleex/hyprland/general.conf
+++ /dev/null
@@ -1,144 +0,0 @@
-input {
- # Keyboard: Add a layout and uncomment kb_options for Win+Space switching shortcut
- kb_layout = us
- # kb_options = grp:win_space_toggle
- numlock_by_default = true
- repeat_delay = 250
- repeat_rate = 35
-
- touchpad {
- natural_scroll = yes
- disable_while_typing = true
- clickfinger_behavior = true
- scroll_factor = 0.5
- }
- special_fallthrough = true
- follow_mouse = 1
-}
-
-binds {
- # focus_window_on_workspace_c# For Auto-run stuff see execs.confhange = true
- scroll_event_delay = 0
-}
-
-gestures {
- workspace_swipe_distance = 700
- workspace_swipe_cancel_ratio = 0.2
- workspace_swipe_min_speed_to_force = 5
- workspace_swipe_direction_lock = true
- workspace_swipe_direction_lock_threshold = 10
- workspace_swipe_create_new = true
-}
-
-gesture = 4, horizontal, workspace
-gesture = 4, vertical, fullscreen
-
-general {
- # Gaps and border
- gaps_in = 2
- gaps_out = 4
- gaps_workspaces = 50
- border_size = 1
-
- # Fallback colors
- col.active_border = rgba(0DB7D4FF)
- col.inactive_border = rgba(31313600)
-
- resize_on_border = true
- no_focus_fallback = true
- layout = dwindle
-
- #focus_to_other_workspaces = true # ahhhh i still haven't properly implemented this
- allow_tearing = true # This just allows the `immediate` window rule to work
-}
-
-dwindle {
- preserve_split = true
- # no_gaps_when_only = 1
- smart_split = false
- smart_resizing = false
-}
-
-decoration {
- rounding = 7
-
- blur {
- enabled = true
- xray = true
- special = false
- new_optimizations = true
- size = 5
- passes = 4
- brightness = 1
- noise = 0.01
- contrast = 1
- popups = true
- popups_ignorealpha = 0.6
- }
-
- # Dim
- dim_inactive = false
- dim_strength = 0.1
- dim_special = 0
-}
-
-animations {
- enabled = true
- # Animation curves
-
- bezier = linear, 0, 0, 1, 1
- bezier = md3_standard, 0.2, 0, 0, 1
- bezier = md3_decel, 0.05, 0.7, 0.1, 1
- bezier = md3_accel, 0.3, 0, 0.8, 0.15
- bezier = overshot, 0.05, 0.9, 0.1, 1.1
- bezier = crazyshot, 0.1, 1.5, 0.76, 0.92
- bezier = hyprnostretch, 0.05, 0.9, 0.1, 1.0
- bezier = menu_decel, 0.1, 1, 0, 1
- bezier = menu_accel, 0.38, 0.04, 1, 0.07
- bezier = easeInOutCirc, 0.85, 0, 0.15, 1
- bezier = easeOutCirc, 0, 0.55, 0.45, 1
- bezier = easeOutExpo, 0.16, 1, 0.3, 1
- bezier = softAcDecel, 0.26, 0.26, 0.15, 1
- bezier = md2, 0.4, 0, 0.2, 1 # use with .2s duration
- # Animation configs
- animation = windows, 1, 3, md3_decel, popin 60%
- animation = windowsIn, 1, 3, md3_decel, popin 60%
- animation = windowsOut, 1, 3, md3_accel, popin 60%
- animation = border, 1, 10, default
- animation = fade, 1, 3, md3_decel
- # animation = layers, 1, 2, md3_decel, slide
- animation = layersIn, 1, 3, menu_decel, slide
- animation = layersOut, 1, 1.6, menu_accel
- animation = fadeLayersIn, 1, 2, menu_decel
- animation = fadeLayersOut, 1, 4.5, menu_accel
- animation = workspaces, 1, 7, menu_decel, slide
- # animation = workspaces, 1, 2.5, softAcDecel, slide
- # animation = workspaces, 1, 7, menu_decel, slidefade 15%
- # animation = specialWorkspace, 1, 3, md3_decel, slidefadevert 15%
- animation = specialWorkspace, 1, 3, md3_decel, slidevert
-}
-
-misc {
- vfr = 1
- vrr = 1
- animate_manual_resizes = false
- animate_mouse_windowdragging = false
- enable_swallow = false
- swallow_regex = (foot|kitty|allacritty|Alacritty)
-
- disable_hyprland_logo = true
- force_default_wallpaper = 0
- on_focus_under_fullscreen = 2
- allow_session_lock_restore = true
-
- initial_workspace_tracking = false
-
- disable_xdg_env_checks = true # Because Hyprland doesn't seems to like Sleex
-
- session_lock_xray = true
-}
-
-ecosystem {
- no_update_news = true
- no_donation_nag = true
-}
diff --git a/src/etc/sleex/hyprland/keybinds.conf b/src/etc/sleex/hyprland/keybinds.conf
deleted file mode 100644
index ddee3a63..00000000
--- a/src/etc/sleex/hyprland/keybinds.conf
+++ /dev/null
@@ -1,155 +0,0 @@
-# Lines ending with `# [hidden]` won't be shown on cheatsheet
-# Lines starting with #! are section headings
-
-
-#!
-##! Shell
-# These absolutely need to be on top, or they won't work consistently
-bindid = Super, Super_L, Toggle overview, global, quickshell:overviewToggleRelease # Toggle overview/launcher
-binditn = Super, catchall, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
-bind = Ctrl, Super_L, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
-bind = Super, mouse:272, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
-bind = Super, mouse:273, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
-bind = Super, mouse:274, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
-bind = Super, mouse:275, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
-bind = Super, mouse:276, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
-bind = Super, mouse:277, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
-bind = Super, mouse_up, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
-bind = Super, mouse_down,global, quickshell:overviewToggleReleaseInterrupt # [hidden]
-
-bindit = ,Super_L, global, quickshell:workspaceNumber # [hidden],
-bindd = Super, V, Clipboard history >> clipboard, global, quickshell:overviewClipboardToggle # Clipboard history >> clipboard
-bindd = Super, semicolon, Emoji >> clipboard, global, quickshell:overviewEmojiToggle # Emoji >> clipboard
-bindd = Super, D, Toggle dashboard, global, quickshell:dashboardToggle # Toggle dashboard
-bindd = Super, F1, Toggle cheatsheet, global, quickshell:cheatsheetToggle # Toggle cheatsheet
-bindd = Ctrl+Alt, Delete, Toggle session menu, global, quickshell:sessionToggle # Toggle session menu
-bindd = Super, T, Toggle wallpaper selector, global, quickshell:wppselectorToggle # Toggle wallpaper selector
-
-binde = Ctrl+Super, Minus, exec, qs -p /usr/share/sleex/ ipc call zoom zoomOut # Zoom out
-binde = Ctrl+Super, Equal, exec, qs -p /usr/share/sleex/ ipc call zoom zoomIn # Zoom in
-
-# Debug
-bind = Super+Alt, F12, exec, notify-send 'Test notification' "Here's a really long message to test truncation and wrapping\nYou can middle click or flick this notification to dismiss it!" -a 'Shell' -A "Test1=I got it!" -A "Test2=Another action" -t 5000 # [hidden]
-bind = Super+Alt, Equal, exec, notify-send "Urgent notification" "Ah hell no" -u critical -a 'Hyprland keybind' # [hidden]
-
-bindl = Super ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_SOURCE@ toggle # Mute the mic
-bindl = ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle # Mute the sink
-bindle=, XF86AudioRaiseVolume, exec, wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+ # Increase the volume
-bindle=, XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%- # Decrease the volume
-bindle=, XF86MonBrightnessUp, exec, qs -p /usr/share/sleex/ ipc call brightness increment # [hidden]
-bindle=, XF86MonBrightnessDown, exec, qs -p /usr/share/sleex/ ipc call brightness decrement # [hidden]
-
-# bindle = , XF86KbdBrightnessUp, exec, brightnessctl -d 'white:kbd_backlight' set '1+'
-# bindle = , XF86KbdBrightnessDown, exec, brightnessctl -d 'white:kbd_backlight' set '1-'
-# bindl = , XF86KbLightOnOff, exec, brightnessctl -d 'white:kbd_backlight' set 0
-
-#!
-##! Essentials for beginners
-
-bind = Super, Return, exec, foot # Launch foot (terminal)
-bind = , Super, exec, true # Open app launcher
-bind = Ctrl+Super, T, exec, /usr/share/sleex/scripts/colors/switchwall.sh # Change wallpaper
-
-##! Actions
-
-# OCR
-bind = Super+Shift,T,exec,grim -g "$(slurp $SLURP_ARGS)" "tmp.png" && tesseract "tmp.png" - | wl-copy && rm "tmp.png" # Screen snip to text >> clipboard
-
-# Color picker
-bind = Super+Shift, X, exec, hyprpicker -a # Pick color (Hex) >> clipboard
-
-# Screenshot
-bindl=,Print,exec,grim - | wl-copy # Screenshot >> clipboard
-bindl= Ctrl,Print, exec, mkdir -p ~/Pictures/Screenshots && /usr/share/sleex/scripts/grimblast.sh copysave screen ~/Pictures/Screenshots/Screenshot_"$(date '+%Y-%m-%d_%H.%M.%S')".png # Screenshot >> clipboard & file
-bind = Super+Shift+Alt, S, exec, grim -g "$(slurp)" - | swappy -f - # Screen snip >> edit
-bind = Super+Shift, S, exec, /usr/share/sleex/scripts/grimblast.sh --freeze copy area # Screen snip
-
-# Recording stuff
-bind = Super+Alt, R, exec, /usr/share/sleex/scripts/record-script.sh # Record region (no sound)
-bind = Ctrl+Alt, R, exec, /usr/share/sleex/scripts/record-script.sh --fullscreen # Record screen (no sound)
-bind = Super+Shift+Alt, R, exec, /usr/share/sleex/scripts/record-script.sh --fullscreen-sound # Record screen (with sound)
-
-##! Session
-# bind = Super, L, exec, hyprlock # Lock session
-bind = Super, L, global, quickshell:lockScreen # Lock session
-bind = Super, End, exec, pkill qs && qs -p /usr/share/sleex/shell.qml # Restart Shell
-bind = Super+Ctrl, End, exec, qs -p /usr/share/sleex/shell.qml # Start Shell
-
-#!
-##! Window management
-# Focusing
-#/# bind = Super, ā/ā/ā/ā,, # Move focus in direction
-bind = Super, Left, movefocus, l # [hidden]
-bind = Super, Right, movefocus, r # [hidden]
-bind = Super, Up, movefocus, u # [hidden]
-bind = Super, Down, movefocus, d # [hidden]
-bind = Super, Q, killactive, # Close an app
-bind = Super+Shift+Alt, Q, exec, hyprctl kill # Pick and kill a window
-
-##! Window arrangement
-# Positioning mode
-bind = Super+Alt, Space, togglefloating, # Toggle floating
-bind = Super, F, fullscreen,
-bindm = Super, mouse:272, movewindow
-bindm = Super, mouse:273, resizewindow
-
-# Toggle focused window split
-$d=[$wm]
-bindd = Super, J, $d toggle split,layoutmsg, togglesplit # Toggle split
-
-#!
-##! Workspace navigation
-# Switching
-#/# bind = Super, Hash,, # Focus workspace # (1, 2, 3, 4, ...)
-bind = Super, code:10, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh workspace 1 # [hidden]
-bind = Super, code:11, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh workspace 2 # [hidden]
-bind = Super, code:12, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh workspace 3 # [hidden]
-bind = Super, code:13, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh workspace 4 # [hidden]
-bind = Super, code:14, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh workspace 5 # [hidden]
-bind = Super, code:15, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh workspace 6 # [hidden]
-bind = Super, code:16, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh workspace 7 # [hidden]
-bind = Super, code:17, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh workspace 8 # [hidden]
-bind = Super, code:18, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh workspace 9 # [hidden]
-bind = Super, code:19, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh workspace 10 # [hidden]
-
-
-#/# bind = Ctrl+Super, ā/ā,, # Workspace: focus left/right
-bind = Ctrl+Super, Right, workspace, +1 # [hidden]
-bind = Ctrl+Super, Left, workspace, -1 # [hidden]
-
-#/# bind = Super, Scroll ā/ā,, # Workspace: focus left/right
-bind = Super, mouse_up, workspace, +1 # [hidden]
-bind = Super, mouse_down, workspace, -1 # [hidden]
-
-#/# bind = Super, Page_ā/ā,, # Workspace: focus left/right
-bind = Super, Page_Down, workspace, +1 # [hidden]
-bind = Super, Page_Up, workspace, -1 # [hidden]
-
-##! Workspace management
-# Move window to workspace Super + Ctrl + [0-9]
-#/# bind = Super+Ctrl, Hash,, # Window: move to workspace # (1, 2, 3, 4, ...)
-bind = Super+Ctrl, code:10, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh movetoworkspacesilent 1 # [hidden]
-bind = Super+Ctrl, code:11, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh movetoworkspacesilent 2 # [hidden]
-bind = Super+Ctrl, code:12, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh movetoworkspacesilent 3 # [hidden]
-bind = Super+Ctrl, code:13, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh movetoworkspacesilent 4 # [hidden]
-bind = Super+Ctrl, code:14, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh movetoworkspacesilent 5 # [hidden]
-bind = Super+Ctrl, code:15, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh movetoworkspacesilent 6 # [hidden]
-bind = Super+Ctrl, code:16, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh movetoworkspacesilent 7 # [hidden]
-bind = Super+Ctrl, code:17, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh movetoworkspacesilent 8 # [hidden]
-bind = Super+Ctrl, code:18, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh movetoworkspacesilent 9 # [hidden]
-bind = Super+Ctrl, code:19, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh movetoworkspacesilent 10 # [hidden]
-
-
-bind = Ctrl+Super+Shift, Right, movetoworkspace, +1 # [hidden]
-bind = Ctrl+Super+Shift, Left, movetoworkspace, -1 # [hidden]
-
-#/# bind = Super+Shift, Scroll ā/ā,, # Window: move to workspace left/right
-bind = Super+Shift, mouse_down, movetoworkspace, -1 # [hidden]
-bind = Super+Shift, mouse_up, movetoworkspace, +1 # [hidden]
-
-
-##! Media
-bindl= ,XF86AudioNext, exec, playerctl next || playerctl position `bc <<< "100 * $(playerctl metadata mpris:length) / 1000000 / 100"` # Next track
-bindl= ,XF86AudioPrev, exec, playerctl previous # Previous track
-bindl= ,XF86AudioPlay, exec, playerctl play-pause # Media play/pause
-bindl= ,XF86AudioPause, exec, playerctl play-pause # [hidden]
diff --git a/src/etc/sleex/hyprland/rules.conf b/src/etc/sleex/hyprland/rules.conf
deleted file mode 100644
index 73214511..00000000
--- a/src/etc/sleex/hyprland/rules.conf
+++ /dev/null
@@ -1,104 +0,0 @@
-# ######## Window rules ########
-# windowrule = noblur,.*
-windowrule = match:class ^(blueberry.py)$, float on
-windowrule = match:title ^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$, float on
-windowrule = match:class ^(com.axos-project.axinstall)$, float on
-windowrule = match:title ^(Open File)(.*)$, center on
-windowrule = match:title ^(Select a File)(.*)$, center on
-windowrule = match:title ^(Choose wallpaper)(.*)$, center on
-windowrule = match:title ^(Open Folder)(.*)$, center on
-windowrule = match:title ^(Save As)(.*)$, center on
-windowrule = match:title ^(Library)(.*)$, center on
-windowrule = match:title ^(File Upload)(.*)$, center on
-
-# Dialogs
-windowrule=match:title ^(Open File)(.*)$, float on
-windowrule=match:title ^(Select a File)(.*)$, float on
-windowrule=match:title ^(Choose wallpaper)(.*)$, float on
-windowrule=match:title ^(Open Folder)(.*)$, float on
-windowrule=match:title ^(Save As)(.*)$, float on
-windowrule=match:title ^(Library)(.*)$, float on
-windowrule=match:title ^(File Upload)(.*)$, float on
-#windowrule=match:title ^(Sleex Settings)$, float on
-windowrule=match:title ^(Calculator)$, float on
-windowrule=match:title ^(BoxBuddy)$, float on
-windowrule=match:title ^(Popsicle)$, float on
-windowrule=match:title ^(Trayscale)$, float on
-windowrule=match:class ^(org.pulseaudio.pavucontrol)$, float on
-windowrule=match:class ^(org.pulseaudio.pavucontrol)$, size (monitor_w*.45) (monitor_h*.45)
-windowrule=match:class ^(org.pulseaudio.pavucontrol)$, center on
-windowrule=match:class ^(cheese)$, float on
-windowrule=match:class ^(timeshift-gtk)$, float on
-windowrule=match:class ^(lstopo)$, float on
-windowrule=match:class ^(org.gnome.ColorProfileViewer)$, float on
-windowrule=match:class ^(qt5ct)$, float on
-windowrule=match:class ^(rustdesk)$, float on
-windowrule=match:title ^(Choose Files)$, float on
-windowrule=match:title ^(Save Image)$, float on
-windowrule=match:title ^(Save File)$, float on
-windowrule=match:class ^(org.kde.plasmawindowed)$, float on
-windowrule=match:class ^(kcm.networkmanagement)$, float on
-
-# Tearing
-windowrule = match:title .*\.exe, immediate on
-windowrule = match:title .*minecraft.*, immediate on
-windowrule = match:class ^(steam_app).*, immediate on
-
-# No shadow for tiled windows
-windowrule = match:float 0, no_shadow on
-
-# ######## Layer rules ########
-layerrule = match:namespace .*, xray on
-# layerrule = match:namespace .*, no_anim on
-layerrule = match:namespace walker, no_anim on
-layerrule = match:namespace selection, no_anim on
-layerrule = match:namespace overview, no_anim on
-layerrule = match:namespace anyrun, no_anim on
-layerrule = match:namespace indicator.*, no_anim on
-layerrule = match:namespace osk, no_anim on
-layerrule = match:namespace hyprpicker, no_anim on
-
-layerrule = match:namespace noanim, no_anim on
-layerrule = match:namespace gtk-layer-shell, blur on
-layerrule = match:namespace gtk-layer-shell, ignore_alpha 0
-layerrule = match:namespace launcher, blur on
-layerrule = match:namespace launcher, ignore_alpha 0.5
-layerrule = match:namespace notifications, blur on
-layerrule = match:namespace notifications, ignore_alpha 0.69
-layerrule = match:namespace logout_dialog # wlogout, blur on
-
-# Picture-in-Picture
-windowrule = match:title ^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$, float on
-windowrule = match:title ^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$, keep_aspect_ratio on
-windowrule = match:title ^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$, move (monitor_w*.73) (monitor_h*.72)
-windowrule = match:title ^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$, size (monitor_w*.25) (monitor_h*.25)
-windowrule = match:title ^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$, float on
-windowrule = match:title ^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$, pin on
-
-
-# Shell
-layerrule = match:namespace quickshell:.*, blur_popups on
-layerrule = match:namespace quickshell:.*, blur on
-layerrule = match:namespace quickshell:.*, ignore_alpha 0.2
-layerrule = match:namespace quickshell:bar, animation slide
-layerrule = match:namespace quickshell:cheatsheet, animation slide bottom
-layerrule = match:namespace quickshell:dock, animation slide bottom
-layerrule = match:namespace quickshell:lockWindowPusher, no_anim on
-layerrule = match:namespace quickshell:notificationPopup, animation fade
-layerrule = match:namespace quickshell:overlay, no_anim on
-layerrule = match:namespace quickshell:overlay, ignore_alpha 1
-layerrule = match:namespace quickshell:overview, animation slide top
-layerrule = match:namespace quickshell:osk, animation slide bottom
-layerrule = match:namespace quickshell:polkit, no_anim on
-layerrule = match:namespace quickshell:popup, xray off # No weird color for bar tooltips (this in theory should suffice)
-layerrule = match:namespace quickshell:popup, ignore_alpha 1 # No weird color for bar tooltips (but somehow this is necessary)
-layerrule = match:namespace quickshell:reloadPopup, animation slide
-layerrule = match:namespace quickshell:regionSelector, no_anim on
-layerrule = match:namespace quickshell:screenshot, no_anim on
-layerrule = match:namespace quickshell:session, blur on
-layerrule = match:namespace quickshell:session, ignore_alpha 0
-layerrule = match:namespace quickshell:dashboard, animation slide bottom
-layerrule = match:namespace quickshell:wallpaperSelector, animation slide top
-layerrule = match:namespace quickshell:batterywarning, blur off
-layerrule = match:namespace quickshell:batterywarning, ignore_alpha 1
-layerrule = match:namespace quickshell:batterywarning, no_anim on
From d0b99ea773baa40d58ca47c7c08c512a48685459 Mon Sep 17 00:00:00 2001
From: Ardox
Date: Tue, 12 May 2026 20:16:24 +0200
Subject: [PATCH 38/56] Update launch script with fhtc
Co-authored-by: Copilot
---
src/bin/sleex | 25 +++++++++++++------------
1 file changed, 13 insertions(+), 12 deletions(-)
diff --git a/src/bin/sleex b/src/bin/sleex
index ee2218d0..3640c400 100755
--- a/src/bin/sleex
+++ b/src/bin/sleex
@@ -1,10 +1,9 @@
#!/bin/sh
export XDG_CURRENT_DESKTOP="Sleex"
-export HYPRLAND_INSTANCE_SIGNATURE=""
systemctl --user import-environment XDG_CURRENT_DESKTOP
# Check if config dirs/files exist, if not copy them from /etc/skel
-for dir in fish hypr anyrun fuzzel Kvantum matugen vdirsyncer kde-material-you-colors mpv wlogout qt5ct xdg-desktop-portal fontconfig khal qt6ct zshrc.d foot; do
+for dir in fish fht anyrun fuzzel Kvantum matugen vdirsyncer kde-material-you-colors mpv wlogout qt5ct xdg-desktop-portal fontconfig khal qt6ct zshrc.d foot; do
if [ ! -d ~/.config/$dir ]; then
cp -r /etc/skel/.config/$dir ~/.config/$dir
chown -R $USER:$USER ~/.config/$dir
@@ -12,9 +11,9 @@ for dir in fish hypr anyrun fuzzel Kvantum matugen vdirsyncer kde-material-you-c
done
# Create Sleex custom files
-if [ ! -d ~/.config/hypr/custom ]; then
- mkdir -p ~/.config/hypr/custom
- cp -r /etc/skel/.config/hypr/custom/* ~/.config/hypr/custom
+if [ ! -d ~/.config/fht/custom ]; then
+ mkdir -p ~/.config/fht/custom
+ cp -r /etc/skel/.config/fht/custom/* ~/.config/fht/custom
fi
if [ ! -d ~/.local/share/khal/calendars/default ]; then
@@ -32,17 +31,19 @@ done
# First run setup
if [ ! -f ~/.local/state/sleex/user/first_run.txt ]; then
# Sets up the user's environment to use Sleex.
- CONFIG_FILE="$HOME/.config/hypr/custom/general.conf"
+ CONFIG_FILE="$HOME/.config/fht/custom/general.conf"
VC_KEYMAP=$(grep -i KEYMAP /etc/vconsole.conf | cut -d= -f2 | tr -d '"')
LAYOUT=$(echo "$VC_KEYMAP" | awk '{print tolower($0)}')
echo "Setting keyboard layout to '$LAYOUT' in $CONFIG_FILE."
- if grep -q "^input:kb_layout" "$CONFIG_FILE"; then
- sed -i "s/^input:kb_layout.*/input:kb_layout = $LAYOUT/" "$CONFIG_FILE"
+ if grep -q "^\[input\.keyboard\]" "$CONFIG_FILE"; then
+ sed -i "/^\[input\.keyboard\]/,/^\[/ s/^layout = .*/layout = \"$LAYOUT\"/" "$CONFIG_FILE"
else
- echo "input:kb_layout = $LAYOUT" >> "$CONFIG_FILE"
+ echo "" >> "$CONFIG_FILE"
+ echo "[input.keyboard]" >> "$CONFIG_FILE"
+ echo "layout = \"$LAYOUT\"" >> "$CONFIG_FILE"
fi
if lsmod | grep -q '^nvidia'; then
@@ -50,7 +51,7 @@ if [ ! -f ~/.local/state/sleex/user/first_run.txt ]; then
{
echo "env = LIBVA_DRIVER_NAME,nvidia"
echo "env = __GLX_VENDOR_LIBRARY_NAME,nvidia"
- } >> ~/.config/hypr/custom/env.conf
+ } >> ~/.config/fht/custom/env.conf
fi
fi
@@ -60,5 +61,5 @@ if [ ! -f ~/.config/user-dirs.dirs ]; then
fi
-# Start Hyprland with the Sleex config
-start-hyprland -- --config /etc/sleex/hyprland.conf > /dev/null 2>&1
+# Start with the Sleex config
+fht-compositor -c /etc/sleex/compositor.toml > /dev/null 2>&1
\ No newline at end of file
From b4a671b22327c37ca358966a20c925f3f1933391 Mon Sep 17 00:00:00 2001
From: Ardox
Date: Tue, 12 May 2026 20:17:18 +0200
Subject: [PATCH 39/56] Remove hyprland matugen templates
---
.../src/.config/matugen/config.toml | 8 --
.../matugen/templates/hyprland/colors.conf | 32 -------
.../matugen/templates/hyprland/hyprlock.conf | 93 -------------------
3 files changed, 133 deletions(-)
delete mode 100644 sleex-user-config/src/.config/matugen/templates/hyprland/colors.conf
delete mode 100644 sleex-user-config/src/.config/matugen/templates/hyprland/hyprlock.conf
diff --git a/sleex-user-config/src/.config/matugen/config.toml b/sleex-user-config/src/.config/matugen/config.toml
index 0f081295..eec40b84 100644
--- a/sleex-user-config/src/.config/matugen/config.toml
+++ b/sleex-user-config/src/.config/matugen/config.toml
@@ -5,14 +5,6 @@ version_check = false
input_path = '~/.config/matugen/templates/colors.json'
output_path = '~/.local/state/sleex/user/generated/colors.json'
-[templates.hyprland]
-input_path = '~/.config/matugen/templates/hyprland/colors.conf'
-output_path = '~/.config/hypr/hyprland/colors.conf'
-
-[templates.hyprlock]
-input_path = '~/.config/matugen/templates/hyprland/hyprlock.conf'
-output_path = '~/.config/hypr/hyprlock.conf'
-
[templates.fuzzel]
input_path = '~/.config/matugen/templates/fuzzel/fuzzel_theme.ini'
output_path = '~/.config/fuzzel/fuzzel_theme.ini'
diff --git a/sleex-user-config/src/.config/matugen/templates/hyprland/colors.conf b/sleex-user-config/src/.config/matugen/templates/hyprland/colors.conf
deleted file mode 100644
index 67fdaea7..00000000
--- a/sleex-user-config/src/.config/matugen/templates/hyprland/colors.conf
+++ /dev/null
@@ -1,32 +0,0 @@
-general {
- col.active_border = rgba({{colors.on_surface.default.hex_stripped}}39)
- col.inactive_border = rgba({{colors.outline.default.hex_stripped}}30)
-}
-
-misc {
- background_color = rgba({{colors.surface.dark.hex_stripped}}FF)
-}
-
-plugin {
- hyprbars {
- # Honestly idk if it works like css, but well, why not
- bar_text_font = Rubik, Geist, AR One Sans, Reddit Sans, Inter, Roboto, Ubuntu, Noto Sans, sans-serif
- bar_height = 30
- bar_padding = 10
- bar_button_padding = 5
- bar_precedence_over_border = true
- bar_part_of_window = true
-
- bar_color = rgba({{colors.background.default.hex_stripped}}FF)
- col.text = rgba({{colors.on_background.default.hex_stripped}}FF)
-
-
- # example buttons (R -> L)
- # hyprbars-button = color, size, on-click
- hyprbars-button = rgb({{colors.on_background.default.hex_stripped}}), 13, ó°, hyprctl dispatch killactive
- hyprbars-button = rgb({{colors.on_background.default.hex_stripped}}), 13, ó°Æ, hyprctl dispatch fullscreen 1
- hyprbars-button = rgb({{colors.on_background.default.hex_stripped}}), 13, ó°°, hyprctl dispatch movetoworkspacesilent special
- }
-}
-
-windowrulev2 = bordercolor rgba({{colors.primary.default.hex_stripped}}AA) rgba({{colors.primary.default.hex_stripped}}77),pinned:1
diff --git a/sleex-user-config/src/.config/matugen/templates/hyprland/hyprlock.conf b/sleex-user-config/src/.config/matugen/templates/hyprland/hyprlock.conf
deleted file mode 100644
index eab16719..00000000
--- a/sleex-user-config/src/.config/matugen/templates/hyprland/hyprlock.conf
+++ /dev/null
@@ -1,93 +0,0 @@
-$text_color = rgba({{colors.primary_fixed.default.hex_stripped}}FF)
-$entry_background_color = rgba({{colors.on_primary_fixed.default.hex_stripped}}11)
-$entry_border_color = rgba({{colors.outline.default.hex_stripped}}55)
-$entry_color = rgba({{colors.primary_fixed.default.hex_stripped}}FF)
-$font_family = Rubik Light
-$font_family_clock = Rubik Light
-$font_material_symbols = Material Symbols Rounded
-
-background {
- color = rgba(181818FF)
-
- path = {{image}}
- blur_size = 15
- blur_passes = 4
- brightness = 0.33
-}
-input-field {
- monitor =
- size = 250, 50
- outline_thickness = 2
- dots_size = 0.1
- dots_spacing = 0.3
- outer_color = $entry_border_color
- inner_color = $entry_background_color
- font_color = $entry_color
- fade_on_empty = true
-
- position = 0, 20
- halign = center
- valign = center
-}
-
-label { # Caps Lock Warning
- monitor =
- text = cmd[update:250] ${XDG_CONFIG_HOME:-$HOME/.config}/hypr/hyprlock/check-capslock.sh
- color = $text_color
- font_size = 13
- font_family = $font_family
- position = 0, -25
- halign = center
- valign = center
-}
-
-
-label { # Clock
- monitor =
- text = $TIME
- color = $text_color
- font_size = 65
- font_family = $font_family_clock
-
- position = 0, 300
- halign = center
- valign = center
-}
-label { # Date
- monitor =
- text = cmd[update:5000] date +"%A, %B %d"
- color = $text_color
- font_size = 17
- font_family = $font_family
-
- position = 0, 240
- halign = center
- valign = center
-}
-
-label { # User
- monitor =
- text = ļ¾ $USER
- color = $text_color
- outline_thickness = 2
- dots_size = 0.2 # Scale of input-field height, 0.2 - 0.8
- dots_spacing = 0.2 # Scale of dots' absolute size, 0.0 - 1.0
- dots_center = true
- font_size = 20
- font_family = $font_family
- position = 0, 50
- halign = center
- valign = bottom
-}
-
-label { # Status
- monitor =
- text = cmd[update:5000] ${XDG_CONFIG_HOME:-$HOME/.config}/hypr/hyprlock/status.sh
- color = $text_color
- font_size = 14
- font_family = $font_family
-
- position = 30, -30
- halign = left
- valign = top
-}
\ No newline at end of file
From 03bfdb8748e8650906a98b59aaf36b11931d6a32 Mon Sep 17 00:00:00 2001
From: Ardox
Date: Tue, 12 May 2026 20:53:12 +0200
Subject: [PATCH 40/56] Replace hypr custom config with fhtc
---
sleex-user-config/src/.config/fht/apps.toml | 16 +++++++++
.../custom/env.conf => fht/custom/env.toml} | 2 +-
.../src/.config/fht/custom/execs.toml | 3 ++
.../general.conf => fht/custom/general.toml} | 0
.../custom/keybinds.toml} | 2 +-
.../src/.config/fht/custom/rules.toml | 4 +++
.../src/.config/fht/monitors.toml | 9 +++++
sleex-user-config/src/.config/hypr/apps.conf | 15 --------
.../src/.config/hypr/custom/execs.conf | 3 --
.../src/.config/hypr/custom/rules.conf | 5 ---
.../scripts/__restore_video_wallpaper.sh | 2 --
.../src/.config/hypr/hypridle.conf | 36 -------------------
.../src/.config/hypr/monitors.conf | 3 --
.../src/.config/hypr/workspaces.conf | 0
src/etc/sleex/compositor.toml | 3 ++
src/etc/sleex/compositor/general.toml | 12 -------
src/etc/sleex/compositor/keybinds.toml | 5 ---
17 files changed, 37 insertions(+), 83 deletions(-)
create mode 100644 sleex-user-config/src/.config/fht/apps.toml
rename sleex-user-config/src/.config/{hypr/custom/env.conf => fht/custom/env.toml} (58%)
create mode 100644 sleex-user-config/src/.config/fht/custom/execs.toml
rename sleex-user-config/src/.config/{hypr/custom/general.conf => fht/custom/general.toml} (100%)
rename sleex-user-config/src/.config/{hypr/custom/keybinds.conf => fht/custom/keybinds.toml} (58%)
create mode 100644 sleex-user-config/src/.config/fht/custom/rules.toml
create mode 100644 sleex-user-config/src/.config/fht/monitors.toml
delete mode 100644 sleex-user-config/src/.config/hypr/apps.conf
delete mode 100644 sleex-user-config/src/.config/hypr/custom/execs.conf
delete mode 100644 sleex-user-config/src/.config/hypr/custom/rules.conf
delete mode 100644 sleex-user-config/src/.config/hypr/custom/scripts/__restore_video_wallpaper.sh
delete mode 100755 sleex-user-config/src/.config/hypr/hypridle.conf
delete mode 100644 sleex-user-config/src/.config/hypr/monitors.conf
delete mode 100644 sleex-user-config/src/.config/hypr/workspaces.conf
diff --git a/sleex-user-config/src/.config/fht/apps.toml b/sleex-user-config/src/.config/fht/apps.toml
new file mode 100644
index 00000000..bc76b458
--- /dev/null
+++ b/sleex-user-config/src/.config/fht/apps.toml
@@ -0,0 +1,16 @@
+# Terminal
+Super-Return = { action = "run-command", arg = "foot" }
+# Code
+Super-c = { action = "run-command", arg = "code --enable-features=UseOzonePlatform --ozone-platform=wayland" }
+# Browser
+Super-w = { action = "run-command", arg = "firefox" }
+# File manager
+Super-e = { action = "run-command", arg = "pcmanfm-qt" }
+# Text editor
+Super-x = { action = "run-command", arg = "kwrite" }
+# Sleex settings
+Super-i = { action = "run-command", arg = "qs -p /usr/share/sleex/settings.qml" }
+# Volume mixer
+Ctrl-Super-v = { action = "run-command", arg = "pavucontrol" }
+# System monitor
+Ctrl-Shift-Esc = { action = "run-command", arg = "missioncenter" }
\ No newline at end of file
diff --git a/sleex-user-config/src/.config/hypr/custom/env.conf b/sleex-user-config/src/.config/fht/custom/env.toml
similarity index 58%
rename from sleex-user-config/src/.config/hypr/custom/env.conf
rename to sleex-user-config/src/.config/fht/custom/env.toml
index bc21f238..b6a2a503 100644
--- a/sleex-user-config/src/.config/hypr/custom/env.conf
+++ b/sleex-user-config/src/.config/fht/custom/env.toml
@@ -1,3 +1,3 @@
# This is a part of the Sleex's compositor user configuration
# You can put extra environment variables here
-# https://wiki.hyprland.org/Configuring/Environment-variables/
+# https://nferhat.github.io/fht-compositor/configuration/introduction.html#env
\ No newline at end of file
diff --git a/sleex-user-config/src/.config/fht/custom/execs.toml b/sleex-user-config/src/.config/fht/custom/execs.toml
new file mode 100644
index 00000000..b452a4c0
--- /dev/null
+++ b/sleex-user-config/src/.config/fht/custom/execs.toml
@@ -0,0 +1,3 @@
+# This is a part of the Sleex's compositor user configuration
+# You can put extra executables here
+# https://nferhat.github.io/fht-compositor/configuration/introduction.html#autostart
\ No newline at end of file
diff --git a/sleex-user-config/src/.config/hypr/custom/general.conf b/sleex-user-config/src/.config/fht/custom/general.toml
similarity index 100%
rename from sleex-user-config/src/.config/hypr/custom/general.conf
rename to sleex-user-config/src/.config/fht/custom/general.toml
diff --git a/sleex-user-config/src/.config/hypr/custom/keybinds.conf b/sleex-user-config/src/.config/fht/custom/keybinds.toml
similarity index 58%
rename from sleex-user-config/src/.config/hypr/custom/keybinds.conf
rename to sleex-user-config/src/.config/fht/custom/keybinds.toml
index 107ff446..affa2121 100644
--- a/sleex-user-config/src/.config/hypr/custom/keybinds.conf
+++ b/sleex-user-config/src/.config/fht/custom/keybinds.toml
@@ -1,4 +1,4 @@
# This is a part of the Sleex's compositor user configuration
# You can put your preferred keybinds here
-# https://wiki.hyprland.org/Configuring/Binds/
+# https://nferhat.github.io/fht-compositor/configuration/keybindings.html
diff --git a/sleex-user-config/src/.config/fht/custom/rules.toml b/sleex-user-config/src/.config/fht/custom/rules.toml
new file mode 100644
index 00000000..b65778c7
--- /dev/null
+++ b/sleex-user-config/src/.config/fht/custom/rules.toml
@@ -0,0 +1,4 @@
+# This is a part of the Sleex's compositor user configuration
+# You can put custom rules here
+# https://nferhat.github.io/fht-compositor/configuration/window-rules.html
+# https://nferhat.github.io/fht-compositor/configuration/layer-rules.html
\ No newline at end of file
diff --git a/sleex-user-config/src/.config/fht/monitors.toml b/sleex-user-config/src/.config/fht/monitors.toml
new file mode 100644
index 00000000..78917d63
--- /dev/null
+++ b/sleex-user-config/src/.config/fht/monitors.toml
@@ -0,0 +1,9 @@
+# Output configuration (examples)
+# https://nferhat.github.io/fht-compositor/configuration/outputs.html
+
+# [outputs.HDMI-A-1]
+# mode = "1920x1080@144"
+
+# [outputs.DP-1]
+# mode = "1920x1080@75"
+# position.x = 1920
\ No newline at end of file
diff --git a/sleex-user-config/src/.config/hypr/apps.conf b/sleex-user-config/src/.config/hypr/apps.conf
deleted file mode 100644
index 716a58d1..00000000
--- a/sleex-user-config/src/.config/hypr/apps.conf
+++ /dev/null
@@ -1,15 +0,0 @@
-# This is a part of the Sleex's compositor user configuration
-# This file is permanent, you can edit it safely to add your preferred app keybinds
-# Be just careful to not duplicate existing keybinds, including those in other config files
-
-#!
-##! Apps
-bind = Super, Return, exec, # Launch foot (terminal)
-bind = Super, C, exec, code --password-store=gnome --enable-features=UseOzonePlatform --ozone-platform=wayland # Launch VSCode (editor)
-bind = Super, E, exec, pcmanfm-qt # Launch file manager
-bind = Super, W, exec, firefox # launch Firefox
-bind = Super, X, exec, kwrite # Launch Text Editor
-bind = Super, I, exec, qs -p /usr/share/sleex/settings.qml # Launch Sleex Settings
-bind = Ctrl+Super, V, exec, pavucontrol # Launch pavucontrol (volume mixer)
-bind = Ctrl+Super+Shift, V, exec, easyeffects # Launch EasyEffects (equalizer & other audio effects)
-bind = Ctrl+Shift, Escape, exec, missioncenter # Launch System monitor
diff --git a/sleex-user-config/src/.config/hypr/custom/execs.conf b/sleex-user-config/src/.config/hypr/custom/execs.conf
deleted file mode 100644
index 5969a35c..00000000
--- a/sleex-user-config/src/.config/hypr/custom/execs.conf
+++ /dev/null
@@ -1,3 +0,0 @@
-# This is a part of the Sleex's compositor user configuration
-# You can make apps auto-start here
-# Relevant Hyprland wiki section: https://wiki.hyprland.org/Configuring/Keywords/#executing
diff --git a/sleex-user-config/src/.config/hypr/custom/rules.conf b/sleex-user-config/src/.config/hypr/custom/rules.conf
deleted file mode 100644
index c4bda6b4..00000000
--- a/sleex-user-config/src/.config/hypr/custom/rules.conf
+++ /dev/null
@@ -1,5 +0,0 @@
-# This is a part of the Sleex's compositor user configuration
-# You can put custom rules here
-# Window/layer rules: https://wiki.hyprland.org/Configuring/Window-Rules/
-# Workspace rules: https://wiki.hyprland.org/Configuring/Workspace-Rules/
-
diff --git a/sleex-user-config/src/.config/hypr/custom/scripts/__restore_video_wallpaper.sh b/sleex-user-config/src/.config/hypr/custom/scripts/__restore_video_wallpaper.sh
deleted file mode 100644
index ea4b4fbd..00000000
--- a/sleex-user-config/src/.config/hypr/custom/scripts/__restore_video_wallpaper.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/bash
-# The content of this script will be generated by switchwall.sh - Don't modify it by yourself.
diff --git a/sleex-user-config/src/.config/hypr/hypridle.conf b/sleex-user-config/src/.config/hypr/hypridle.conf
deleted file mode 100755
index 98f11f27..00000000
--- a/sleex-user-config/src/.config/hypr/hypridle.conf
+++ /dev/null
@@ -1,36 +0,0 @@
-general {
- lock_cmd = qs -p /usr/share/sleex ipc call lock lock
- before_sleep_cmd = qs -p /usr/share/sleex ipc call lock lock # lock before suspend.
- after_sleep_cmd = hyprctl dispatch dpms on # to avoid having to press a key twice to turn on the display.
-}
-
-listener {
- timeout = 120 # 2min.
- on-timeout = brightnessctl -s set 10 # set monitor backlight to minimum, avoid 0 on OLED monitor.
- on-resume = brightnessctl -r # monitor backlight restore.
-}
-
-# turn off keyboard backlight if present (auto-detect device)
-listener {
- timeout = 300 # 5min.
- on-timeout = KBD=$(ls /sys/class/leds | grep -i kbd_backlight | head -n1) && \
- [ -n "$KBD" ] && brightnessctl -sd "$KBD" set 0
- on-resume = KBD=$(ls /sys/class/leds | grep -i kbd_backlight | head -n1) && \
- [ -n "$KBD" ] && brightnessctl -rd "$KBD"
-}
-
-listener {
- timeout = 120 # 2min.
- on-timeout = qs -p /usr/share/sleex ipc call lock lock # lock screen when timeout has passed
-}
-
-# listener {
-# timeout = 220 # 3min, 40sec
-# on-timeout = hyprctl dispatch dpms off # screen off when timeout has passed
-# on-resume = hyprctl dispatch dpms on && brightnessctl -r # screen on when activity is detected after timeout has fired.
-# }
-
-listener {
- timeout = 600 # 10min
- on-timeout = systemctl suspend # suspend pc
-}
diff --git a/sleex-user-config/src/.config/hypr/monitors.conf b/sleex-user-config/src/.config/hypr/monitors.conf
deleted file mode 100644
index a9851bb1..00000000
--- a/sleex-user-config/src/.config/hypr/monitors.conf
+++ /dev/null
@@ -1,3 +0,0 @@
-# Generated by nwg-displays on 2025-06-09 at 12:05:41. Do not edit manually, use `nwg-displays` instead.
-
-monitor=,preferred,auto,1
diff --git a/sleex-user-config/src/.config/hypr/workspaces.conf b/sleex-user-config/src/.config/hypr/workspaces.conf
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/etc/sleex/compositor.toml b/src/etc/sleex/compositor.toml
index 0c040e63..457348a5 100644
--- a/src/etc/sleex/compositor.toml
+++ b/src/etc/sleex/compositor.toml
@@ -4,4 +4,7 @@ imports = [
"/etc/sleex/compositor/rules.toml",
"/etc/sleex/compositor/execs.toml",
"/etc/sleex/compositor/envs.toml",
+
+ "~/.config/fht/monitors.toml",
+ "~/.config/fht/apps.toml",
]
diff --git a/src/etc/sleex/compositor/general.toml b/src/etc/sleex/compositor/general.toml
index 2805ace2..d7a189ed 100644
--- a/src/etc/sleex/compositor/general.toml
+++ b/src/etc/sleex/compositor/general.toml
@@ -17,18 +17,6 @@ natural-scrolling = true
name = "Bibata-Modern-Classic"
size = 24
-
-# ---------------------------------------------------------
-
-# Output configuration (examples)
-
-# [outputs.HDMI-A-1]
-# mode = "1920x1080@144"
-
-# [outputs.DP-1]
-# mode = "1920x1080@75"
-# position.x = 1920
-
# ---------------------------------------------------------
# General compositor behaviour
diff --git a/src/etc/sleex/compositor/keybinds.toml b/src/etc/sleex/compositor/keybinds.toml
index 8a613237..bec4cd15 100644
--- a/src/etc/sleex/compositor/keybinds.toml
+++ b/src/etc/sleex/compositor/keybinds.toml
@@ -5,12 +5,7 @@
Super-Ctrl-q = "quit"
Super-Ctrl-r = "reload-config"
-Super-Return = { action = "run-command", arg = "foot" }
Super-Shift-s = { action = "run-command", arg = """grim -g "`slurp -o`" - | wl-copy --type image/png""" }
-Super-w = { action = "run-command", arg = "firefox" }
-Super-c = { action = "run-command", arg = "code --enable-features=UseOzonePlatform --ozone-platform=wayland" }
-# Super-a = { action = "run-command", arg = "waydroid show-full-ui" }
-Super-e = { action = "run-command", arg = "pcmanfm-qt" }
Super-a = { action = "run-command", arg = "qs -p Documents/sleex/src/share/sleex/ ipc call overview toggle" }
From 31045468afd52d2401cb060d420bf3ae63039939 Mon Sep 17 00:00:00 2001
From: Ardox
Date: Tue, 12 May 2026 20:57:09 +0200
Subject: [PATCH 41/56] Update dependencies for FHTC
---
PKGBUILD | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/PKGBUILD b/PKGBUILD
index a41a63ca..4c87324c 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -7,15 +7,15 @@ depends=(
# Audio
'cava' 'pavucontrol-qt' 'wireplumber' 'libdbusmenu-gtk3' 'playerctl'
# Backlight
- 'hyprsunset' 'geoclue' 'brightnessctl' 'ddcutil'
+ 'gammastep' 'geoclue' 'brightnessctl' 'ddcutil'
# Basic
"axel" "bc" "coreutils" "cliphist" "cmake" "curl" "rsync" "wget" "ripgrep" "jq" "meson" "xdg-user-dirs" "foot" "power-profiles-daemon" "mission-center" "kvantum" "inotify-tools" "lm_sensors" "qt5ct" "qt6ct" "sqlite"
# Cursor
"bibata-cursor-theme-bin"
# Fonts & Themes
'adw-gtk-theme' 'breeze-plus' 'eza' 'fish' 'fontconfig' 'kde-material-you-colors' 'matugen-bin' 'starship' 'ttf-gabarito-git' 'ttf-jetbrains-mono-nerd' 'ttf-material-design-icons-extended' 'ttf-material-symbols-variable' 'ttf-readex-pro' 'ttf-rubik-vf' 'ttf-twemoji'
- # Hyprland dependencies
- 'hyprutils' 'hyprpicker' 'hyprlang' 'hyprland-qt-support' 'hyprland-guiutils' 'hyprcursor' 'hyprwayland-scanner' 'hyprland' 'xdg-desktop-portal-hyprland' 'wl-clipboard' 'hyprlock'
+ # Fhtc dependencies
+ 'fht-compositor-git' 'fht-share-picker-git' 'wl-clipboard'
# QT/KDE dependencies
'bluedevil' 'gnome-keyring' 'networkmanager' 'polkit-kde-agent' 'pcmanfm-qt' 'kwrite' "libnm" "gio-qt" "qt6-connectivity"
# Microtex
@@ -25,7 +25,7 @@ depends=(
# Python deps
'clang' 'uv' 'gtk4' 'libadwaita' 'libsoup3' 'libportal-gtk4' 'gobject-introspection' 'sassc' 'python-setproctitle' 'python-pywayland'
# Screencast/Screenrecord
- 'hyprshot' 'ksnip' 'wf-recorder' 'slurp' 'grim' 'tesseract' 'tesseract-data-eng'
+ 'ksnip' 'wf-recorder' 'slurp' 'grim' 'tesseract' 'tesseract-data-eng'
# Tools
'kdialog' 'qt6-5compat' 'qt6-avif-image-plugin' 'qt6-base' 'qt6-declarative' 'qt6-imageformats' 'qt6-multimedia' 'qt6-positioning' 'qt6-quicktimeline' 'qt6-sensors' 'qt6-svg' 'qt6-tools' 'qt6-translations' 'qt6-virtualkeyboard' 'qt6-wayland' 'syntax-highlighting' 'upower' 'wtype' 'ydotool' 'fprintd' 'khal' 'vdirsyncer' 'python-aiohttp-oauthlib' 'swappy' 'hypnos' 'bluez-utils'
# Widgets
@@ -36,7 +36,6 @@ depends=(
"sleex-artworks"
)
optdepends=(
- "hyprwayland-scanner: Wayland protocol scanner for Hyprland"
"neofetch: Fancy system info in your terminal"
"firefox: Web browser"
"pipewire-pulse: PulseAudio replacement via PipeWire"
From 879151c9243298b7a4f96a6e5f9ad46cb3edaab3 Mon Sep 17 00:00:00 2001
From: Ardox
Date: Tue, 12 May 2026 21:02:01 +0200
Subject: [PATCH 42/56] Update sleex user config hyprland bindings with fhtc
---
README.md | 39 ++-----------------
sleex-user-config/PKGBUILD | 4 --
sleex-user-config/README.md | 8 ----
.../src/.config/fish/auto-Hypr.fish | 5 ---
.../xdg-desktop-portal/hyprland-portals.conf | 3 --
.../src/.config/zshrc.d/auto-Hypr.sh | 5 ---
src/bin/rubyshot | 6 ---
.../libalpm/hooks/sleex-hyprctl-reload.hook | 12 ------
8 files changed, 3 insertions(+), 79 deletions(-)
delete mode 100644 sleex-user-config/README.md
delete mode 100644 sleex-user-config/src/.config/fish/auto-Hypr.fish
delete mode 100644 sleex-user-config/src/.config/xdg-desktop-portal/hyprland-portals.conf
delete mode 100644 sleex-user-config/src/.config/zshrc.d/auto-Hypr.sh
delete mode 100755 src/bin/rubyshot
delete mode 100644 src/share/libalpm/hooks/sleex-hyprctl-reload.hook
diff --git a/README.md b/README.md
index 0bafbfa7..15851dad 100644
--- a/README.md
+++ b/README.md
@@ -2,41 +2,8 @@
-Sleex is the third desktop environement of AxOS. It is based on Hyprland with Quickshell.
+# [WIP] Sleex with the Fht compositor
-## Features
-- AI chat integration with external providers (Gemini, OpenAI...)
-- Smooth animations
-- Tiling window management for seamless multitasking
-- Adaptative color scheme based on the wallpaper
-- Ready to use
-- Multiple available built in tools
-- Looks good
+Not yet ready for public use. This branch is for testing the Fht compositor with Sleex (and get rid of Hyprland).
-| |
-|------|
-|  |
-
-## Installation
-
-> [!IMPORTANT]
-> If you already have an hyprland config, this will erase it.
-
-If you are using AxOS, you can simply use epsilon:
-```
-epsi i sleex
-```
-
-### Other arch-based distributions
-
-Sleex is available on the AUR as `sleex-git`. [See the page](https://aur.archlinux.org/packages/sleex-git).
-You can install it using an aur helper such as `yay` or `paru`.
-
-Then, you can start sleex by selecting the sleex session on your greeter
-
-## License
-Sleex is licensed under the GNU General Public License v3.0
-
-## Special thanks
-- [@end-4](https://github.com/end-4/): Great inspiration | Sleex is based on his work
-- [@xHyperVoid](https://github.com/xHyperVoid): Designer | Made the logo and the wallpapers
+[Fht compositor](https://nferhat.github.io/fht-compositor/) is a new Wayland compositor written in Rust. It is designed to be fast, lightweight, and easy to use. It is also highly customizable, allowing users to create their own unique desktop experience.
\ No newline at end of file
diff --git a/sleex-user-config/PKGBUILD b/sleex-user-config/PKGBUILD
index feabad3b..b23824d4 100644
--- a/sleex-user-config/PKGBUILD
+++ b/sleex-user-config/PKGBUILD
@@ -5,10 +5,6 @@ pkgdesc="User configuration for Sleex desktop environment"
arch=("x86_64")
depend=("sleex")
-provides=("axskel-hypr")
-replaces=("axskel-hypr")
-conflicts=("axskel-hypr")
-
package(){
mkdir -p ${pkgdir}/etc/skel/
cp -r ${srcdir}/.config/ ${pkgdir}/etc/skel/
diff --git a/sleex-user-config/README.md b/sleex-user-config/README.md
deleted file mode 100644
index 93a38383..00000000
--- a/sleex-user-config/README.md
+++ /dev/null
@@ -1,8 +0,0 @@
-# axskel-hypr
-
-`axskel-hypr` is a custom desktop environment setup, featuring the **Hyprland** window manager and pre-configured settings. This package includes configurations for **Hyprland**, alongside several enhancements for usability, aesthetics, and functionality.
-
-
-
-After installation, the package deploys configuration files to `/etc/skel`. Customize these files as needed to tailor your desktop environment.
-
diff --git a/sleex-user-config/src/.config/fish/auto-Hypr.fish b/sleex-user-config/src/.config/fish/auto-Hypr.fish
deleted file mode 100644
index c3c1890f..00000000
--- a/sleex-user-config/src/.config/fish/auto-Hypr.fish
+++ /dev/null
@@ -1,5 +0,0 @@
-# Auto start Hyprland on tty1
-if test -z "$DISPLAY" ;and test "$XDG_VTNR" -eq 1
- mkdir -p ~/.cache
- exec Hyprland > ~/.cache/hyprland.log ^&1
-end
diff --git a/sleex-user-config/src/.config/xdg-desktop-portal/hyprland-portals.conf b/sleex-user-config/src/.config/xdg-desktop-portal/hyprland-portals.conf
deleted file mode 100644
index 62f3d568..00000000
--- a/sleex-user-config/src/.config/xdg-desktop-portal/hyprland-portals.conf
+++ /dev/null
@@ -1,3 +0,0 @@
-[preferred]
-default = hyprland;gtk
-org.freedesktop.impl.portal.FileChooser = kde
diff --git a/sleex-user-config/src/.config/zshrc.d/auto-Hypr.sh b/sleex-user-config/src/.config/zshrc.d/auto-Hypr.sh
deleted file mode 100644
index 55eca5e0..00000000
--- a/sleex-user-config/src/.config/zshrc.d/auto-Hypr.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-# Auto start Hyprland on tty1
-if [ -z "$DISPLAY" ] && [ "$XDG_VTNR" -eq 1 ]; then
- mkdir -p ~/.cache
- exec Hyprland > ~/.cache/hyprland.log 2>&1
-fi
diff --git a/src/bin/rubyshot b/src/bin/rubyshot
deleted file mode 100755
index 8431bd69..00000000
--- a/src/bin/rubyshot
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/usr/bin/bash
-
-WORKSPACES="$(hyprctl monitors -j | jq -r 'map(.activeWorkspace.id)')"
-WINDOWS="$(hyprctl clients -j | jq -r --argjson workspaces "$WORKSPACES" 'map(select([.workspace.id] | inside($workspaces)))' )"
-GEOM=$(echo "$WINDOWS" | jq -r '.[] | "\(.at[0]),\(.at[1]) \(.size[0])x\(.size[1])"' | slurp -f '%x %y %w %h')
-wayshot -s "$GEOM" --stdout ${#:+"$@"}
\ No newline at end of file
diff --git a/src/share/libalpm/hooks/sleex-hyprctl-reload.hook b/src/share/libalpm/hooks/sleex-hyprctl-reload.hook
deleted file mode 100644
index a1351b4d..00000000
--- a/src/share/libalpm/hooks/sleex-hyprctl-reload.hook
+++ /dev/null
@@ -1,12 +0,0 @@
-## Copyright (C) 2025 Ardox
-
-[Trigger]
-Operation = Install
-Operation = Upgrade
-Type = Package
-Target = sleex
-
-[Action]
-Description = reloading hyprctl configuration
-When = PostTransaction
-Exec = /usr/bin/sleep 5 && /usr/bin/hyprctl reload &
From ca05ee9e4416df0aa0c1841538966911ac1872b5 Mon Sep 17 00:00:00 2001
From: Ardox
Date: Tue, 12 May 2026 21:15:43 +0200
Subject: [PATCH 43/56] Fix dashboard opening on fhtc
---
.../sleex/modules/dashboard/Dashboard.qml | 38 +++++++++----------
1 file changed, 19 insertions(+), 19 deletions(-)
diff --git a/src/share/sleex/modules/dashboard/Dashboard.qml b/src/share/sleex/modules/dashboard/Dashboard.qml
index a0fd60a7..1b6ebb23 100644
--- a/src/share/sleex/modules/dashboard/Dashboard.qml
+++ b/src/share/sleex/modules/dashboard/Dashboard.qml
@@ -14,6 +14,7 @@ import Quickshell.Widgets
import Quickshell.Wayland
import Quickshell.Hyprland
import Quickshell.Bluetooth
+import Sleex.Fhtc
Scope {
id: dashboardScope
@@ -28,8 +29,8 @@ Scope {
PanelWindow {
id: dashboardRoot
required property var modelData
- readonly property HyprlandMonitor monitor: Hyprland.monitorFor(screen)
- property bool monitorIsFocused: (Hyprland.focusedMonitor?.id == monitor.id)
+ required property ShellScreen screen
+ property bool monitorIsFocused: (FhtcMonitors.activeMonitorName === screen.name)
screen: modelData
visible: GlobalStates.dashboardOpen && monitorIsFocused
@@ -44,13 +45,13 @@ Scope {
Region { id: emptyRegion }
- HyprlandFocusGrab {
- id: grab
- windows: [ dashboardRoot ]
- property bool canBeActive: dashboardRoot.monitorIsFocused
- active: false
- onCleared: () => { if (!active) ipc.close() }
- }
+ // HyprlandFocusGrab {
+ // id: grab
+ // windows: [ dashboardRoot ]
+ // property bool canBeActive: dashboardRoot.monitorIsFocused
+ // active: false
+ // onCleared: () => { if (!active) ipc.close() }
+ // }
Connections {
target: GlobalStates
@@ -158,10 +159,6 @@ Scope {
bottom: parent.bottom
right: parent.right
left: parent.left
- topMargin: Appearance.sizes.hyprlandGapsOut
- rightMargin: Appearance.sizes.hyprlandGapsOut
- bottomMargin: Appearance.sizes.hyprlandGapsOut
- leftMargin: Appearance.sizes.elevationMargin
}
focus: GlobalStates.dashboardOpen && dashboardRoot.monitorIsFocused
Keys.onPressed: (event) => {
@@ -180,10 +177,10 @@ Scope {
Rectangle {
id: dashboardBackground
anchors.fill: parent
- implicitHeight: parent.height - Appearance.sizes.hyprlandGapsOut * 2
- implicitWidth: dashboardWidth - Appearance.sizes.hyprlandGapsOut * 2
+ implicitHeight: parent.height
+ implicitWidth: dashboardWidth
color: Appearance.colors.colLayer0
- radius: Appearance.rounding.screenRounding - Appearance.sizes.hyprlandGapsOut + 1
+ radius: Appearance.rounding.screenRounding
ColumnLayout {
spacing: dashboardPadding
@@ -229,10 +226,10 @@ Scope {
toggled: false
buttonIcon: "restart_alt"
onClicked: {
- Hyprland.dispatch("reload")
+ FhtcIpc.dispatch("reload-config")
Quickshell.reload(true)
}
- StyledToolTip { text: qsTr("Reload Hyprland & Quickshell") }
+ StyledToolTip { text: qsTr("Reload Fhtc & Quickshell") }
}
QuickToggleButton {
toggled: false
@@ -246,7 +243,10 @@ Scope {
QuickToggleButton {
toggled: false
buttonIcon: "power_settings_new"
- onClicked: Hyprland.dispatch("global quickshell:sessionOpen")
+ onClicked: () => {
+ Quickshell.execDetached(["qs", "-p", "/usr/share/sleex/", "ipc", "call", "session", "open"])
+ ipc.close()
+ }
StyledToolTip { text: qsTr("Session") }
}
}
From ecd2c8bd0149209ebd6cbc93a8c235134815e467 Mon Sep 17 00:00:00 2001
From: Ardox
Date: Tue, 12 May 2026 21:21:59 +0200
Subject: [PATCH 44/56] Improve overview fhtc integration
---
src/share/sleex/modules/overview/Overview.qml | 18 ++----------------
1 file changed, 2 insertions(+), 16 deletions(-)
diff --git a/src/share/sleex/modules/overview/Overview.qml b/src/share/sleex/modules/overview/Overview.qml
index ca55ecc1..e9ee7144 100644
--- a/src/share/sleex/modules/overview/Overview.qml
+++ b/src/share/sleex/modules/overview/Overview.qml
@@ -34,10 +34,6 @@ Scope {
mask: Region {
item: GlobalStates.overviewOpen ? columnLayout : null
}
- HyprlandWindow.visibleMask: Region {
- item: GlobalStates.overviewOpen ? columnLayout : null
- }
-
anchors {
top: true
@@ -46,16 +42,6 @@ Scope {
bottom: true
}
- HyprlandFocusGrab {
- id: grab
- windows: [ root ]
- property bool canBeActive: root.monitorIsFocused
- active: false
- onCleared: () => {
- if (!active) GlobalStates.overviewOpen = false
- }
- }
-
Connections {
target: GlobalStates
function onOverviewOpenChanged() {
@@ -102,9 +88,9 @@ Scope {
if (event.key === Qt.Key_Escape) {
GlobalStates.overviewOpen = false;
} else if (event.key === Qt.Key_Left) {
- if (!root.searchingText) Hyprland.dispatch("workspace -1");
+ if (!root.searchingText) FhtcIpc.dispatch("focus-previous-workspace");
} else if (event.key === Qt.Key_Right) {
- if (!root.searchingText) Hyprland.dispatch("workspace +1");
+ if (!root.searchingText) FhtcIpc.dispatch("focus-next-workspace");
}
}
From 00743313ef4e34b92e802549020c4cc2efafa962 Mon Sep 17 00:00:00 2001
From: Ardox
Date: Tue, 12 May 2026 21:36:23 +0200
Subject: [PATCH 45/56] Remove old grab bindings
---
src/share/sleex/modules/overview/Overview.qml | 11 -----------
1 file changed, 11 deletions(-)
diff --git a/src/share/sleex/modules/overview/Overview.qml b/src/share/sleex/modules/overview/Overview.qml
index e9ee7144..20a72edb 100644
--- a/src/share/sleex/modules/overview/Overview.qml
+++ b/src/share/sleex/modules/overview/Overview.qml
@@ -52,21 +52,10 @@ Scope {
if (!overviewScope.dontAutoCancelSearch) {
searchWidget.cancelSearch()
}
- delayedGrabTimer.start()
}
}
}
- Timer {
- id: delayedGrabTimer
- interval: Config.options.hacks.arbitraryRaceConditionDelay
- repeat: false
- onTriggered: {
- if (!grab.canBeActive) return
- grab.active = GlobalStates.overviewOpen
- }
- }
-
implicitWidth: columnLayout.implicitWidth
implicitHeight: columnLayout.implicitHeight
From 16ec55f5adc93015f2bfd6fa053995b031b3bfb5 Mon Sep 17 00:00:00 2001
From: Ardox
Date: Tue, 12 May 2026 21:42:50 +0200
Subject: [PATCH 46/56] Fix overview map offset
---
src/share/sleex/modules/overview/OverviewWidget.qml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/share/sleex/modules/overview/OverviewWidget.qml b/src/share/sleex/modules/overview/OverviewWidget.qml
index 6894298a..25d234b0 100644
--- a/src/share/sleex/modules/overview/OverviewWidget.qml
+++ b/src/share/sleex/modules/overview/OverviewWidget.qml
@@ -161,8 +161,8 @@ Item {
property int workspaceColIndex: (modelData?.["workspace-id"] ?? 0) % Config.options.overview.numOfCols
property int workspaceRowIndex: Math.floor((modelData?.["workspace-id"] ?? 0) % root.workspacesShown / Config.options.overview.numOfCols)
- xOffset: (root.workspaceImplicitWidth + workspaceSpacing) * workspaceColIndex - ((root.focusedScreen?.x ?? 0) * root.scale)
- yOffset: (root.workspaceImplicitHeight + workspaceSpacing) * workspaceRowIndex - ((root.focusedScreen?.y ?? 0) * root.scale)
+ xOffset: (root.workspaceImplicitWidth + workspaceSpacing) * workspaceColIndex
+ yOffset: (root.workspaceImplicitHeight + workspaceSpacing) * workspaceRowIndex
Timer {
id: updateWindowPosition
From b1c8d6bec11b48988ef973296683b47d71bc00d9 Mon Sep 17 00:00:00 2001
From: Ardox
Date: Wed, 13 May 2026 10:26:53 +0200
Subject: [PATCH 47/56] Update WlrLayershell keyboardFocus to exclusive instead
of onDemand
---
src/share/sleex/modules/dashboard/Dashboard.qml | 9 +--------
src/share/sleex/modules/overview/Overview.qml | 2 +-
src/share/sleex/modules/polkit/Polkit.qml | 2 +-
3 files changed, 3 insertions(+), 10 deletions(-)
diff --git a/src/share/sleex/modules/dashboard/Dashboard.qml b/src/share/sleex/modules/dashboard/Dashboard.qml
index 1b6ebb23..8c4b32b1 100644
--- a/src/share/sleex/modules/dashboard/Dashboard.qml
+++ b/src/share/sleex/modules/dashboard/Dashboard.qml
@@ -39,20 +39,13 @@ Scope {
implicitHeight: Screen.height
WlrLayershell.namespace: "quickshell:dashboard"
WlrLayershell.layer: WlrLayer.Overlay
+ WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
color: "transparent"
mask: GlobalStates.dashboardOpen ? null : emptyRegion
Region { id: emptyRegion }
- // HyprlandFocusGrab {
- // id: grab
- // windows: [ dashboardRoot ]
- // property bool canBeActive: dashboardRoot.monitorIsFocused
- // active: false
- // onCleared: () => { if (!active) ipc.close() }
- // }
-
Connections {
target: GlobalStates
function onDashboardOpenChanged() {
diff --git a/src/share/sleex/modules/overview/Overview.qml b/src/share/sleex/modules/overview/Overview.qml
index 20a72edb..8a645eda 100644
--- a/src/share/sleex/modules/overview/Overview.qml
+++ b/src/share/sleex/modules/overview/Overview.qml
@@ -28,7 +28,7 @@ Scope {
WlrLayershell.namespace: "quickshell:overview"
WlrLayershell.layer: WlrLayer.Overlay
- WlrLayershell.keyboardFocus: GlobalStates.overviewOpen ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
+ WlrLayershell.keyboardFocus: GlobalStates.overviewOpen ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
color: "transparent"
mask: Region {
diff --git a/src/share/sleex/modules/polkit/Polkit.qml b/src/share/sleex/modules/polkit/Polkit.qml
index 6a8ac3bf..aacac6b6 100644
--- a/src/share/sleex/modules/polkit/Polkit.qml
+++ b/src/share/sleex/modules/polkit/Polkit.qml
@@ -29,7 +29,7 @@ Scope {
color: "transparent"
WlrLayershell.namespace: "quickshell:polkit"
- WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
+ WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
WlrLayershell.layer: WlrLayer.Overlay
exclusionMode: ExclusionMode.Ignore
From 014b8b60fdf9a230d418a57cb8fcba2efdad0af3 Mon Sep 17 00:00:00 2001
From: Ardox
Date: Wed, 13 May 2026 11:31:18 +0200
Subject: [PATCH 48/56] Reduce hyprland dependency
---
src/share/sleex/GlobalStates.qml | 32 +-
.../background/BackgroundContextMenu.qml | 3 +-
src/share/sleex/modules/bar/ActiveWindow.qml | 2 -
src/share/sleex/modules/bar/AudioOsd.qml | 1 -
src/share/sleex/modules/bar/Bar.qml | 8 +-
src/share/sleex/modules/bar/BrightnessOsd.qml | 4 +-
src/share/sleex/modules/bar/SysTray.qml | 22 +-
.../sleex/modules/cheatsheet/Cheatsheet.qml | 12 +-
src/share/sleex/modules/common/Appearance.qml | 6 +-
src/share/sleex/modules/common/Config.qml | 2 +-
.../sleex/modules/common/Directories.qml | 1 -
.../common/widgets/ConfigSelectionArray.qml | 1 -
.../sleex/modules/common/widgets/Favicon.qml | 1 -
.../widgets/LightDarkPreferenceButton.qml | 1 -
.../common/widgets/NotificationGroup.qml | 1 -
.../common/widgets/NotificationItem.qml | 1 -
.../common/widgets/NotificationListView.qml | 1 -
.../common/widgets/SelectionGroupButton.qml | 1 -
.../modules/common/widgets/WindowDialog.qml | 2 +-
.../ai/aiChat/AnnotationSourceButton.qml | 1 -
.../dashboard/ai/aiChat/MessageTextBlock.qml | 1 -
.../dashboard/ai/aiChat/SearchQueryButton.qml | 1 -
.../quickToggles/BluetoothToggle.qml | 2 -
.../dashboard/quickToggles/GameMode.qml | 26 --
.../dashboard/quickToggles/NetworkToggle.qml | 1 -
src/share/sleex/modules/dock/Dock.qml | 12 +-
src/share/sleex/modules/dock/DockApps.qml | 2 +-
src/share/sleex/modules/dock/DockButton.qml | 6 +-
.../sleex/modules/dock/DockSeparator.qml | 2 +-
.../modules/mediaControls/MediaControls.qml | 1 -
.../modules/mediaControls/PlayerControl.qml | 1 -
.../mediaControls/PlayerControlBlank.qml | 1 -
.../notificationPopup/NotificationPopup.qml | 4 +-
.../OnScreenDisplayBrightness.qml | 3 +-
.../onScreenDisplay/OnScreenDisplayVolume.qml | 3 +-
.../sleex/modules/overview/OverviewWidget.qml | 1 -
.../sleex/modules/overview/OverviewWindow.qml | 1 -
.../sleex/modules/overview/SearchItem.qml | 1 -
src/share/sleex/modules/polkit/Polkit.qml | 1 -
.../modules/screenCorners/ScreenCorners.qml | 15 -
src/share/sleex/modules/session/Session.qml | 2 +-
src/share/sleex/modules/settings/Display.qml | 4 +-
src/share/sleex/modules/settings/Style.qml | 1 -
.../displaySettings/DisplaySettings.qml | 2 +-
.../sleex/modules/sidebarLeft/SidebarLeft.qml | 202 -------------
.../sidebarLeft/SidebarLeftContent.qml | 44 ---
.../aiChat/AnnotationSourceButton.qml | 1 -
.../sidebarLeft/aiChat/MessageTextBlock.qml | 1 -
.../wallpaperSelector/WallpaperSelector.qml | 28 +-
src/share/sleex/scripts/colors/switchwall.sh | 15 +-
src/share/sleex/scripts/grimblast.sh | 277 ------------------
src/share/sleex/scripts/grimshot.sh | 41 +++
.../scripts/hyprland/workspace_action.sh | 2 -
src/share/sleex/scripts/record-script.sh | 2 +-
src/share/sleex/services/Audio.qml | 3 -
src/share/sleex/services/Brightness.qml | 7 +-
src/share/sleex/services/ConfigLoader.qml | 2 -
.../sleex/services/FirstRunExperience.qml | 1 -
src/share/sleex/services/HyprlandData.qml | 85 ------
src/share/sleex/services/LatexRenderer.qml | 1 -
src/share/sleex/services/Monitors.qml | 170 -----------
src/share/sleex/services/NightLight.qml | 13 +-
.../sleex/services/PersistentStateManager.qml | 1 -
src/share/sleex/services/Ydotool.qml | 1 -
src/share/sleex/settings.qml | 1 -
src/share/sleex/shell.qml | 3 -
66 files changed, 118 insertions(+), 980 deletions(-)
delete mode 100644 src/share/sleex/modules/dashboard/quickToggles/GameMode.qml
delete mode 100644 src/share/sleex/modules/sidebarLeft/SidebarLeft.qml
delete mode 100644 src/share/sleex/modules/sidebarLeft/SidebarLeftContent.qml
delete mode 100755 src/share/sleex/scripts/grimblast.sh
create mode 100755 src/share/sleex/scripts/grimshot.sh
delete mode 100755 src/share/sleex/scripts/hyprland/workspace_action.sh
delete mode 100644 src/share/sleex/services/HyprlandData.qml
delete mode 100644 src/share/sleex/services/Monitors.qml
diff --git a/src/share/sleex/GlobalStates.qml b/src/share/sleex/GlobalStates.qml
index 68c1a47a..779cf39b 100644
--- a/src/share/sleex/GlobalStates.qml
+++ b/src/share/sleex/GlobalStates.qml
@@ -18,13 +18,13 @@ Singleton {
property bool wppselectorOpen: false
property bool screenLocked: false
- property real screenZoom: 1
- onScreenZoomChanged: {
- Quickshell.execDetached(["hyprctl", "keyword", "cursor:zoom_factor", `${root.screenZoom.toString()}`]);
- }
- Behavior on screenZoom {
- animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
- }
+ // property real screenZoom: 1
+ // onScreenZoomChanged: {
+ // Quickshell.execDetached(["hyprctl", "keyword", "cursor:zoom_factor", `${root.screenZoom.toString()}`]);
+ // }
+ // Behavior on screenZoom {
+ // animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
+ // }
// When user is not reluctant while pressing super, they probably don't need to see workspace numbers
onSuperReleaseMightTriggerChanged: {
@@ -54,17 +54,17 @@ Singleton {
}
}
- IpcHandler {
- target: "zoom"
+ // IpcHandler {
+ // target: "zoom"
- function zoomIn() {
- screenZoom = Math.min(screenZoom + 0.4, 3.0)
- }
+ // function zoomIn() {
+ // screenZoom = Math.min(screenZoom + 0.4, 3.0)
+ // }
- function zoomOut() {
- screenZoom = Math.max(screenZoom - 0.4, 1)
- }
- }
+ // function zoomOut() {
+ // screenZoom = Math.max(screenZoom - 0.4, 1)
+ // }
+ // }
IpcHandler {
diff --git a/src/share/sleex/modules/background/BackgroundContextMenu.qml b/src/share/sleex/modules/background/BackgroundContextMenu.qml
index b511c66d..7efb9713 100755
--- a/src/share/sleex/modules/background/BackgroundContextMenu.qml
+++ b/src/share/sleex/modules/background/BackgroundContextMenu.qml
@@ -2,7 +2,6 @@ import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Widgets
-import Quickshell.Hyprland
import qs.modules.common
import qs.modules.common.widgets
import qs.modules.common.functions
@@ -112,7 +111,7 @@ Item {
}
onClicked: {
- Hyprland.dispatch("global quickshell:sessionOpen")
+ Quickshell.execDetached(["qs", "-p", "/usr/share/sleex", "ipc", "call", "session", "toggle"])
bgMenu.close()
}
}
diff --git a/src/share/sleex/modules/bar/ActiveWindow.qml b/src/share/sleex/modules/bar/ActiveWindow.qml
index 036123e1..dd93452c 100644
--- a/src/share/sleex/modules/bar/ActiveWindow.qml
+++ b/src/share/sleex/modules/bar/ActiveWindow.qml
@@ -4,13 +4,11 @@ import qs.services
import QtQuick
import QtQuick.Layouts
import Quickshell.Wayland
-import Quickshell.Hyprland
import Sleex.Fhtc
Item {
id: root
required property var bar
- readonly property HyprlandMonitor monitor: Hyprland.monitorFor(bar.screen)
implicitWidth: colLayout.implicitWidth
diff --git a/src/share/sleex/modules/bar/AudioOsd.qml b/src/share/sleex/modules/bar/AudioOsd.qml
index d1a909de..b310f3e7 100644
--- a/src/share/sleex/modules/bar/AudioOsd.qml
+++ b/src/share/sleex/modules/bar/AudioOsd.qml
@@ -1,5 +1,4 @@
import Quickshell
-import Quickshell.Hyprland
import QtQuick.Layouts
import QtQuick
import qs.modules.common
diff --git a/src/share/sleex/modules/bar/Bar.qml b/src/share/sleex/modules/bar/Bar.qml
index 60f1e109..ddc0aaa5 100644
--- a/src/share/sleex/modules/bar/Bar.qml
+++ b/src/share/sleex/modules/bar/Bar.qml
@@ -507,10 +507,10 @@ Scope {
NotifIndicator {}
}
- HyprlandXkbIndicator {
- Layout.alignment: Qt.AlignVCenter
- Layout.rightMargin: indicatorsRowLayout.realSpacing
- }
+ // HyprlandXkbIndicator {
+ // Layout.alignment: Qt.AlignVCenter
+ // Layout.rightMargin: indicatorsRowLayout.realSpacing
+ // }
Revealer {
diff --git a/src/share/sleex/modules/bar/BrightnessOsd.qml b/src/share/sleex/modules/bar/BrightnessOsd.qml
index 952cd601..4ffccde2 100644
--- a/src/share/sleex/modules/bar/BrightnessOsd.qml
+++ b/src/share/sleex/modules/bar/BrightnessOsd.qml
@@ -1,16 +1,16 @@
import Quickshell
-import Quickshell.Hyprland
import QtQuick
import qs.modules.common
import qs.modules.common.widgets
import qs.services
+import Sleex.Fhtc
Revealer {
id: root
reveal: showOsdValues
property bool showOsdValues: false
- property var focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name)
+ property var focusedScreen: Quickshell.screens.find(s => s.name === FhtcMonitors.activeMonitorName)
property var brightnessMonitor: Brightness.getMonitorForScreen(focusedScreen)
Connections {
diff --git a/src/share/sleex/modules/bar/SysTray.qml b/src/share/sleex/modules/bar/SysTray.qml
index 4b911182..58f63943 100644
--- a/src/share/sleex/modules/bar/SysTray.qml
+++ b/src/share/sleex/modules/bar/SysTray.qml
@@ -3,7 +3,6 @@ import qs.modules.common.widgets
import QtQuick
import QtQuick.Layouts
import Quickshell
-import Quickshell.Hyprland
import Quickshell.Services.SystemTray
Item {
@@ -26,20 +25,20 @@ Item {
}
function grabFocus() {
- focusGrab.active = true;
+ // focusGrab.active = true;
}
function setExtraWindowAndGrabFocus(window) {
root.activeMenu = window;
- root.grabFocus();
+ // root.grabFocus();
}
function releaseFocus() {
- focusGrab.active = false;
+ // focusGrab.active = false;
}
function closeOverflowMenu() {
- focusGrab.active = false;
+ // focusGrab.active = false;
}
onTrayOverflowOpenChanged: {
@@ -48,19 +47,6 @@ Item {
}
}
- HyprlandFocusGrab {
- id: focusGrab
- active: false
- windows: [trayOverflowLayout.QsWindow?.window, root.activeMenu]
- onCleared: {
- root.trayOverflowOpen = false;
- if (root.activeMenu) {
- root.activeMenu.close();
- root.activeMenu = null;
- }
- }
- }
-
GridLayout {
id: gridLayout
columns: root.vertical ? 1 : -1
diff --git a/src/share/sleex/modules/cheatsheet/Cheatsheet.qml b/src/share/sleex/modules/cheatsheet/Cheatsheet.qml
index 4ae33395..e07a06b1 100644
--- a/src/share/sleex/modules/cheatsheet/Cheatsheet.qml
+++ b/src/share/sleex/modules/cheatsheet/Cheatsheet.qml
@@ -38,23 +38,13 @@ Scope { // Scope
implicitWidth: cheatsheetBackground.width + Appearance.sizes.elevationMargin * 2
implicitHeight: cheatsheetBackground.height + Appearance.sizes.elevationMargin * 2
WlrLayershell.namespace: "quickshell:cheatsheet"
- // Hyprland 0.49: Focus is always exclusive and setting this breaks mouse focus grab
- // WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
+ WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
color: "transparent"
mask: Region {
item: cheatsheetBackground
}
- HyprlandFocusGrab { // Click outside to close
- id: grab
- windows: [ cheatsheetRoot ]
- active: cheatsheetRoot.visible
- onCleared: () => {
- if (!active) cheatsheetRoot.hide()
- }
- }
-
// Background
StyledRectangularShadow {
diff --git a/src/share/sleex/modules/common/Appearance.qml b/src/share/sleex/modules/common/Appearance.qml
index c65992bc..ee38636a 100644
--- a/src/share/sleex/modules/common/Appearance.qml
+++ b/src/share/sleex/modules/common/Appearance.qml
@@ -346,7 +346,7 @@ Singleton {
sizes: QtObject {
property real baseBarHeight: 40
property real barHeight: Config.options.bar.cornerStyle === 1 ?
- (baseBarHeight + root.sizes.hyprlandGapsOut * 2) : baseBarHeight
+ (baseBarHeight + root.sizes.gapsOut * 2) : baseBarHeight
property real barCenterSideModuleWidth: Config.options?.bar.verbose ? 360 : 140
property real barCenterSideModuleWidthShortened: 280
property real barCenterSideModuleWidthHellaShortened: 190
@@ -355,7 +355,7 @@ Singleton {
property real elevationMargin: 10
property real fabShadowRadius: 5
property real fabHoveredShadowRadius: 7
- property real hyprlandGapsOut: 5
+ property real gapsOut: 5
property real mediaControlsWidth: 440
property real mediaControlsHeight: 160
property real notificationPopupWidth: 410
@@ -366,7 +366,7 @@ Singleton {
property real sidebarWidthExtended: 750
property real baseVerticalBarWidth: 46
property real verticalBarWidth: Config.options.bar.cornerStyle === 1 ?
- (baseVerticalBarWidth + root.sizes.hyprlandGapsOut * 2) : baseVerticalBarWidth
+ (baseVerticalBarWidth + root.sizes.gapsOut * 2) : baseVerticalBarWidth
property real wallpaperSelectorWidth: 1200
property real wallpaperSelectorHeight: 690
property real wallpaperSelectorItemMargins: 8
diff --git a/src/share/sleex/modules/common/Config.qml b/src/share/sleex/modules/common/Config.qml
index e385a7a3..5518d91b 100644
--- a/src/share/sleex/modules/common/Config.qml
+++ b/src/share/sleex/modules/common/Config.qml
@@ -156,7 +156,7 @@ Singleton {
property bool background: false
property bool borderless: false // true for no grouping of items
property bool verbose: true
- property list screenList: [] // List of names, like "eDP-1", find out with 'hyprctl monitors' command
+ property list screenList: [] // List of names, like "eDP-1"
property JsonObject workspaces: JsonObject {
property int shown: 10
property bool showAppIcons: false
diff --git a/src/share/sleex/modules/common/Directories.qml b/src/share/sleex/modules/common/Directories.qml
index 5f68e9cc..8651bbdd 100644
--- a/src/share/sleex/modules/common/Directories.qml
+++ b/src/share/sleex/modules/common/Directories.qml
@@ -5,7 +5,6 @@ import qs.modules.common.functions
import Qt.labs.platform
import QtQuick
import Quickshell
-import Quickshell.Hyprland
Singleton {
// XDG Dirs, with "file://"
diff --git a/src/share/sleex/modules/common/widgets/ConfigSelectionArray.qml b/src/share/sleex/modules/common/widgets/ConfigSelectionArray.qml
index b1c279f7..04f6480b 100644
--- a/src/share/sleex/modules/common/widgets/ConfigSelectionArray.qml
+++ b/src/share/sleex/modules/common/widgets/ConfigSelectionArray.qml
@@ -3,7 +3,6 @@ import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
-import Quickshell.Hyprland
import qs.services
import qs.modules.common
import qs.modules.common.widgets
diff --git a/src/share/sleex/modules/common/widgets/Favicon.qml b/src/share/sleex/modules/common/widgets/Favicon.qml
index 7eae7fb6..29cb7689 100644
--- a/src/share/sleex/modules/common/widgets/Favicon.qml
+++ b/src/share/sleex/modules/common/widgets/Favicon.qml
@@ -10,7 +10,6 @@ import QtQuick.Controls
import QtQuick.Layouts
import Quickshell.Io
import Quickshell.Widgets
-import Quickshell.Hyprland
IconImage {
id: root
diff --git a/src/share/sleex/modules/common/widgets/LightDarkPreferenceButton.qml b/src/share/sleex/modules/common/widgets/LightDarkPreferenceButton.qml
index d380aaf3..dad9d367 100644
--- a/src/share/sleex/modules/common/widgets/LightDarkPreferenceButton.qml
+++ b/src/share/sleex/modules/common/widgets/LightDarkPreferenceButton.qml
@@ -7,7 +7,6 @@ import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import Quickshell.Io
-import Quickshell.Hyprland
GroupButton {
id: lightDarkButtonRoot
diff --git a/src/share/sleex/modules/common/widgets/NotificationGroup.qml b/src/share/sleex/modules/common/widgets/NotificationGroup.qml
index 826ba294..310c49f4 100644
--- a/src/share/sleex/modules/common/widgets/NotificationGroup.qml
+++ b/src/share/sleex/modules/common/widgets/NotificationGroup.qml
@@ -11,7 +11,6 @@ import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import Quickshell.Widgets
-import Quickshell.Hyprland
import Quickshell.Services.Notifications
/**
diff --git a/src/share/sleex/modules/common/widgets/NotificationItem.qml b/src/share/sleex/modules/common/widgets/NotificationItem.qml
index 4e526ae5..007e855f 100644
--- a/src/share/sleex/modules/common/widgets/NotificationItem.qml
+++ b/src/share/sleex/modules/common/widgets/NotificationItem.qml
@@ -11,7 +11,6 @@ import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import Quickshell.Widgets
-import Quickshell.Hyprland
import Quickshell.Services.Notifications
Item { // Notification item area
diff --git a/src/share/sleex/modules/common/widgets/NotificationListView.qml b/src/share/sleex/modules/common/widgets/NotificationListView.qml
index 6727b33c..4a3d18c8 100644
--- a/src/share/sleex/modules/common/widgets/NotificationListView.qml
+++ b/src/share/sleex/modules/common/widgets/NotificationListView.qml
@@ -7,7 +7,6 @@ import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Wayland
-import Quickshell.Hyprland
StyledListView { // Scrollable window
id: root
diff --git a/src/share/sleex/modules/common/widgets/SelectionGroupButton.qml b/src/share/sleex/modules/common/widgets/SelectionGroupButton.qml
index d6ba9091..d7a1196c 100644
--- a/src/share/sleex/modules/common/widgets/SelectionGroupButton.qml
+++ b/src/share/sleex/modules/common/widgets/SelectionGroupButton.qml
@@ -2,7 +2,6 @@ import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
-import Quickshell.Hyprland
import qs.services
import qs.modules.common
import qs.modules.common.widgets
diff --git a/src/share/sleex/modules/common/widgets/WindowDialog.qml b/src/share/sleex/modules/common/widgets/WindowDialog.qml
index 58bb577c..54529331 100644
--- a/src/share/sleex/modules/common/widgets/WindowDialog.qml
+++ b/src/share/sleex/modules/common/widgets/WindowDialog.qml
@@ -33,7 +33,7 @@ Rectangle {
dialogBackground.implicitHeight = show ? backgroundHeight : 0
}
- radius: Appearance.rounding.screenRounding - Appearance.sizes.hyprlandGapsOut + 1
+ radius: Appearance.rounding.screenRounding - Appearance.sizes.gapsOut + 1
MouseArea { // Clicking outside the dialog should dismiss
anchors.fill: parent
diff --git a/src/share/sleex/modules/dashboard/ai/aiChat/AnnotationSourceButton.qml b/src/share/sleex/modules/dashboard/ai/aiChat/AnnotationSourceButton.qml
index bd75a5d4..8746a59c 100644
--- a/src/share/sleex/modules/dashboard/ai/aiChat/AnnotationSourceButton.qml
+++ b/src/share/sleex/modules/dashboard/ai/aiChat/AnnotationSourceButton.qml
@@ -4,7 +4,6 @@ import qs.modules.common.functions
import qs.services
import QtQuick
import QtQuick.Layouts
-import Quickshell.Hyprland
RippleButton {
id: root
diff --git a/src/share/sleex/modules/dashboard/ai/aiChat/MessageTextBlock.qml b/src/share/sleex/modules/dashboard/ai/aiChat/MessageTextBlock.qml
index d68a260c..e74b45d7 100644
--- a/src/share/sleex/modules/dashboard/ai/aiChat/MessageTextBlock.qml
+++ b/src/share/sleex/modules/dashboard/ai/aiChat/MessageTextBlock.qml
@@ -8,7 +8,6 @@ import qs.modules.common.functions
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
-import Quickshell.Hyprland
ColumnLayout {
id: root
diff --git a/src/share/sleex/modules/dashboard/ai/aiChat/SearchQueryButton.qml b/src/share/sleex/modules/dashboard/ai/aiChat/SearchQueryButton.qml
index 4ad60ac9..699f8a03 100644
--- a/src/share/sleex/modules/dashboard/ai/aiChat/SearchQueryButton.qml
+++ b/src/share/sleex/modules/dashboard/ai/aiChat/SearchQueryButton.qml
@@ -5,7 +5,6 @@ import qs.services
import qs.modules.common.functions
import QtQuick
import QtQuick.Layouts
-import Quickshell.Hyprland
RippleButton {
id: root
diff --git a/src/share/sleex/modules/dashboard/quickToggles/BluetoothToggle.qml b/src/share/sleex/modules/dashboard/quickToggles/BluetoothToggle.qml
index 17591002..5c6c4fbf 100644
--- a/src/share/sleex/modules/dashboard/quickToggles/BluetoothToggle.qml
+++ b/src/share/sleex/modules/dashboard/quickToggles/BluetoothToggle.qml
@@ -8,8 +8,6 @@ import QtQuick
import Quickshell
import Quickshell.Io
import Quickshell.Bluetooth
-import Quickshell.Hyprland
-
import Sleex.Services
QuickToggleButton {
diff --git a/src/share/sleex/modules/dashboard/quickToggles/GameMode.qml b/src/share/sleex/modules/dashboard/quickToggles/GameMode.qml
deleted file mode 100644
index bc0f5497..00000000
--- a/src/share/sleex/modules/dashboard/quickToggles/GameMode.qml
+++ /dev/null
@@ -1,26 +0,0 @@
-import qs.modules.common
-import qs.modules.common.widgets
-import "../"
-import Quickshell
-import Quickshell.Io
-import Quickshell.Hyprland
-
-QuickToggleButton {
- property bool enabled: false
- buttonIcon: "gamepad"
- toggled: enabled
-
- onClicked: {
- enabled = !enabled
- if (enabled) {
- // gameModeOn.running = true
- Quickshell.execDetached(["hyprctl", "--batch", "keyword animations:enabled 0; keyword decoration:shadow:enabled 0; keyword decoration:blur:enabled 0; keyword general:gaps_in 0; keyword general:gaps_out 0; keyword general:border_size 1; keyword decoration:rounding 0; keyword general:allow_tearing 1"])
- } else {
- Quickshell.execDetached(["hyprctl reload"])
- }
- }
-
- StyledToolTip {
- text: qsTr("Game mode")
- }
-}
diff --git a/src/share/sleex/modules/dashboard/quickToggles/NetworkToggle.qml b/src/share/sleex/modules/dashboard/quickToggles/NetworkToggle.qml
index b99ec9be..4ef764ca 100644
--- a/src/share/sleex/modules/dashboard/quickToggles/NetworkToggle.qml
+++ b/src/share/sleex/modules/dashboard/quickToggles/NetworkToggle.qml
@@ -7,7 +7,6 @@ import "../"
import QtQuick
import Quickshell
import Quickshell.Io
-import Quickshell.Hyprland
import Sleex.Services
diff --git a/src/share/sleex/modules/dock/Dock.qml b/src/share/sleex/modules/dock/Dock.qml
index 78a4f234..f0aaa850 100644
--- a/src/share/sleex/modules/dock/Dock.qml
+++ b/src/share/sleex/modules/dock/Dock.qml
@@ -35,14 +35,14 @@ Scope { // Scope
}
exclusiveZone: root.pinned ? implicitHeight
- - (Appearance.sizes.hyprlandGapsOut)
- - (Appearance.sizes.elevationMargin - Appearance.sizes.hyprlandGapsOut) : 0
+ - (Appearance.sizes.gapsOut)
+ - (Appearance.sizes.elevationMargin - Appearance.sizes.gapsOut) : 0
implicitWidth: dockBackground.implicitWidth
WlrLayershell.namespace: "quickshell:dock"
color: "transparent"
- implicitHeight: (Config.options?.dock.height ?? 70) + Appearance.sizes.elevationMargin + Appearance.sizes.hyprlandGapsOut
+ implicitHeight: (Config.options?.dock.height ?? 70) + Appearance.sizes.elevationMargin + Appearance.sizes.gapsOut
mask: Region {
item: dockMouseArea
@@ -79,7 +79,7 @@ Scope { // Scope
}
implicitWidth: dockRow.implicitWidth + 5 * 2
- height: parent.height - Appearance.sizes.elevationMargin - Appearance.sizes.hyprlandGapsOut
+ height: parent.height - Appearance.sizes.elevationMargin - Appearance.sizes.gapsOut
StyledRectangularShadow {
target: dockVisualBackground
@@ -89,7 +89,7 @@ Scope { // Scope
property real margin: Appearance.sizes.elevationMargin
anchors.fill: parent
anchors.topMargin: Appearance.sizes.elevationMargin
- anchors.bottomMargin: Appearance.sizes.hyprlandGapsOut
+ anchors.bottomMargin: Appearance.sizes.gapsOut
color: Appearance.colors.colLayer0
border.width: 1
border.color: Appearance.m3colors.m3outlineVariant
@@ -105,7 +105,7 @@ Scope { // Scope
property real padding: 5
VerticalButtonGroup {
- Layout.topMargin: Appearance.sizes.hyprlandGapsOut // why does this work
+ Layout.topMargin: Appearance.sizes.gapsOut // why does this work
GroupButton { // Pin button
baseWidth: 35
baseHeight: 35
diff --git a/src/share/sleex/modules/dock/DockApps.qml b/src/share/sleex/modules/dock/DockApps.qml
index d8bb3351..dbe5244d 100644
--- a/src/share/sleex/modules/dock/DockApps.qml
+++ b/src/share/sleex/modules/dock/DockApps.qml
@@ -24,7 +24,7 @@ Item {
property bool requestDockShow: previewPopup.show
Layout.fillHeight: true
- Layout.topMargin: Appearance.sizes.hyprlandGapsOut // why does this work
+ Layout.topMargin: Appearance.sizes.gapsOut // why does this work
implicitWidth: listView.implicitWidth
StyledListView {
diff --git a/src/share/sleex/modules/dock/DockButton.qml b/src/share/sleex/modules/dock/DockButton.qml
index 61655782..e7ec6a8f 100644
--- a/src/share/sleex/modules/dock/DockButton.qml
+++ b/src/share/sleex/modules/dock/DockButton.qml
@@ -7,10 +7,10 @@ import QtQuick.Layouts
RippleButton {
Layout.fillHeight: true
- Layout.topMargin: Appearance.sizes.elevationMargin - Appearance.sizes.hyprlandGapsOut
+ Layout.topMargin: Appearance.sizes.elevationMargin - Appearance.sizes.gapsOut
implicitWidth: implicitHeight - topInset - bottomInset
buttonRadius: Appearance.rounding.normal
- topInset: Appearance.sizes.hyprlandGapsOut + dockRow.padding
- bottomInset: Appearance.sizes.hyprlandGapsOut + dockRow.padding
+ topInset: Appearance.sizes.gapsOut + dockRow.padding
+ bottomInset: Appearance.sizes.gapsOut + dockRow.padding
}
diff --git a/src/share/sleex/modules/dock/DockSeparator.qml b/src/share/sleex/modules/dock/DockSeparator.qml
index 419b0fed..48460e9f 100644
--- a/src/share/sleex/modules/dock/DockSeparator.qml
+++ b/src/share/sleex/modules/dock/DockSeparator.qml
@@ -6,7 +6,7 @@ import QtQuick.Layouts
Rectangle {
Layout.topMargin: Appearance.sizes.elevationMargin + dockRow.padding + Appearance.rounding.normal
- Layout.bottomMargin: Appearance.sizes.hyprlandGapsOut + dockRow.padding + Appearance.rounding.normal
+ Layout.bottomMargin: Appearance.sizes.gapsOut + dockRow.padding + Appearance.rounding.normal
Layout.fillHeight: true
implicitWidth: 1
color: Appearance.colors.colOutlineVariant
diff --git a/src/share/sleex/modules/mediaControls/MediaControls.qml b/src/share/sleex/modules/mediaControls/MediaControls.qml
index 0f31b25e..0417b3e8 100644
--- a/src/share/sleex/modules/mediaControls/MediaControls.qml
+++ b/src/share/sleex/modules/mediaControls/MediaControls.qml
@@ -12,7 +12,6 @@ import Quickshell.Io
import Quickshell.Services.Mpris
import Quickshell.Widgets
import Quickshell.Wayland
-import Quickshell.Hyprland
Item {
id: root
diff --git a/src/share/sleex/modules/mediaControls/PlayerControl.qml b/src/share/sleex/modules/mediaControls/PlayerControl.qml
index 94ad7577..d0aa6c10 100644
--- a/src/share/sleex/modules/mediaControls/PlayerControl.qml
+++ b/src/share/sleex/modules/mediaControls/PlayerControl.qml
@@ -12,7 +12,6 @@ import Quickshell.Io
import Quickshell.Services.Mpris
import Quickshell.Widgets
import Quickshell.Wayland
-import Quickshell.Hyprland
Item { // Player instance
id: playerController
diff --git a/src/share/sleex/modules/mediaControls/PlayerControlBlank.qml b/src/share/sleex/modules/mediaControls/PlayerControlBlank.qml
index 493b8623..7d0f92e6 100644
--- a/src/share/sleex/modules/mediaControls/PlayerControlBlank.qml
+++ b/src/share/sleex/modules/mediaControls/PlayerControlBlank.qml
@@ -14,7 +14,6 @@ import Quickshell.Io
import Quickshell.Services.Mpris
import Quickshell.Widgets
import Quickshell.Wayland
-import Quickshell.Hyprland
Item {
id: playerControllerBlank
diff --git a/src/share/sleex/modules/notificationPopup/NotificationPopup.qml b/src/share/sleex/modules/notificationPopup/NotificationPopup.qml
index a302a45c..c5730060 100644
--- a/src/share/sleex/modules/notificationPopup/NotificationPopup.qml
+++ b/src/share/sleex/modules/notificationPopup/NotificationPopup.qml
@@ -7,7 +7,7 @@ import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Wayland
-import Quickshell.Hyprland
+import Sleex.Fhtc
Scope {
id: notificationPopup
@@ -17,7 +17,7 @@ Scope {
PanelWindow {
id: root
visible: (Notifications.popupList.length > 0)
- screen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name) ?? null
+ screen: Quickshell.screens.find(s => s.name === FhtcMonitors.activeMonitorName === screen.name) ?? null
WlrLayershell.namespace: "quickshell:notificationPopup"
WlrLayershell.layer: WlrLayer.Overlay
diff --git a/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayBrightness.qml b/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayBrightness.qml
index 94d7018b..f396b3c7 100644
--- a/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayBrightness.qml
+++ b/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayBrightness.qml
@@ -8,11 +8,12 @@ import Quickshell
import Quickshell.Io
import Quickshell.Hyprland
import Quickshell.Wayland
+import Sleex.Fhtc
Scope {
id: root
property bool showOsdValues: false
- property var focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name)
+ property var focusedScreen: Quickshell.screens.find(s => s.name === FhtcMonitors.activeMonitorName === screen.name)
property var brightnessMonitor: Brightness.getMonitorForScreen(focusedScreen)
function triggerOsd() {
diff --git a/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayVolume.qml b/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayVolume.qml
index 8c47e0c5..75ac9209 100644
--- a/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayVolume.qml
+++ b/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayVolume.qml
@@ -8,12 +8,13 @@ import Quickshell
import Quickshell.Io
import Quickshell.Wayland
import Quickshell.Hyprland
+import Sleex.Fhtc
Scope {
id: root
property bool showOsdValues: false
property string protectionMessage: ""
- property var focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name)
+ property var focusedScreen: Quickshell.screens.find(s => s.name === FhtcMonitors.activeMonitorName === screen.name)
function triggerOsd() {
showOsdValues = true
diff --git a/src/share/sleex/modules/overview/OverviewWidget.qml b/src/share/sleex/modules/overview/OverviewWidget.qml
index 25d234b0..a79e188b 100644
--- a/src/share/sleex/modules/overview/OverviewWidget.qml
+++ b/src/share/sleex/modules/overview/OverviewWidget.qml
@@ -10,7 +10,6 @@ import Quickshell
import Quickshell.Io
import Quickshell.Widgets
import Quickshell.Wayland
-import Quickshell.Hyprland
import Sleex.Fhtc
Item {
diff --git a/src/share/sleex/modules/overview/OverviewWindow.qml b/src/share/sleex/modules/overview/OverviewWindow.qml
index c6410a68..c8c6caf9 100644
--- a/src/share/sleex/modules/overview/OverviewWindow.qml
+++ b/src/share/sleex/modules/overview/OverviewWindow.qml
@@ -11,7 +11,6 @@ import Quickshell
import Quickshell.Widgets
import Quickshell.Io
import Quickshell.Wayland
-import Quickshell.Hyprland
Item { // Window
id: root
diff --git a/src/share/sleex/modules/overview/SearchItem.qml b/src/share/sleex/modules/overview/SearchItem.qml
index 272907fd..f14f5430 100644
--- a/src/share/sleex/modules/overview/SearchItem.qml
+++ b/src/share/sleex/modules/overview/SearchItem.qml
@@ -8,7 +8,6 @@ import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Widgets
-import Quickshell.Hyprland
RippleButton {
id: root
diff --git a/src/share/sleex/modules/polkit/Polkit.qml b/src/share/sleex/modules/polkit/Polkit.qml
index aacac6b6..85cbfac0 100644
--- a/src/share/sleex/modules/polkit/Polkit.qml
+++ b/src/share/sleex/modules/polkit/Polkit.qml
@@ -6,7 +6,6 @@ import qs.modules.common.functions
import QtQuick
import Quickshell
import Quickshell.Wayland
-import Quickshell.Hyprland
Scope {
id: root
diff --git a/src/share/sleex/modules/screenCorners/ScreenCorners.qml b/src/share/sleex/modules/screenCorners/ScreenCorners.qml
index 91aeb21e..f487b412 100644
--- a/src/share/sleex/modules/screenCorners/ScreenCorners.qml
+++ b/src/share/sleex/modules/screenCorners/ScreenCorners.qml
@@ -5,7 +5,6 @@ import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Wayland
-import Quickshell.Hyprland
Scope {
id: screenCorners
@@ -26,20 +25,6 @@ Scope {
mask: Region {
item: null
}
- HyprlandWindow.visibleMask: Region {
- Region {
- item: topLeftCorner
- }
- Region {
- item: topRightCorner
- }
- Region {
- item: bottomLeftCorner
- }
- Region {
- item: bottomRightCorner
- }
- }
WlrLayershell.namespace: "quickshell:screenCorners"
WlrLayershell.layer: WlrLayer.Overlay
color: "transparent"
diff --git a/src/share/sleex/modules/session/Session.qml b/src/share/sleex/modules/session/Session.qml
index 9d7a50bf..0c937600 100644
--- a/src/share/sleex/modules/session/Session.qml
+++ b/src/share/sleex/modules/session/Session.qml
@@ -112,7 +112,7 @@ Scope {
id: sessionLogout
buttonIcon: "logout"
buttonText: qsTr("Logout")
- onClicked: { Quickshell.execDetached(["pkill", "Hyprland"]); sessionRoot.hide() }
+ onClicked: { Quickshell.execDetached(["pkill", "sleex"]); sessionRoot.hide() }
onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText }
KeyNavigation.left: sessionSleep
KeyNavigation.right: sessionTaskManager
diff --git a/src/share/sleex/modules/settings/Display.qml b/src/share/sleex/modules/settings/Display.qml
index c06df541..a4dc4c63 100644
--- a/src/share/sleex/modules/settings/Display.qml
+++ b/src/share/sleex/modules/settings/Display.qml
@@ -5,14 +5,14 @@ import Quickshell
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
-import Quickshell.Hyprland
+import Sleex.Fhtc
import Sleex.Services
import "displaySettings" as DS
ContentPage {
id: root
- property var focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name)
+ property var focusedScreen: Quickshell.screens.find(s => s.name === FhtcMonitors.activeMonitorName === screen.name)
property var brightnessMonitor: Brightness.getMonitorForScreen(focusedScreen)
property int nlStartHour: parseInt(Config.options.display.nightLightFrom?.split(":")[0] ?? "20")
diff --git a/src/share/sleex/modules/settings/Style.qml b/src/share/sleex/modules/settings/Style.qml
index f7815cce..69990498 100644
--- a/src/share/sleex/modules/settings/Style.qml
+++ b/src/share/sleex/modules/settings/Style.qml
@@ -3,7 +3,6 @@ import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
-import Quickshell.Hyprland
import Quickshell.Widgets
import Qt5Compat.GraphicalEffects
import QtMultimedia
diff --git a/src/share/sleex/modules/settings/displaySettings/DisplaySettings.qml b/src/share/sleex/modules/settings/displaySettings/DisplaySettings.qml
index e154e08e..13164aa8 100644
--- a/src/share/sleex/modules/settings/displaySettings/DisplaySettings.qml
+++ b/src/share/sleex/modules/settings/displaySettings/DisplaySettings.qml
@@ -267,7 +267,7 @@ Item {
target === "None" ? "" : target
)
- // If un-mirroring, give Hyprland more time to re-expose the monitor
+ // If un-mirroring, give the compositor more time to re-expose the monitor
if (target === "None") {
Qt.callLater(() => {
Qt.createQmlObject('import QtQuick 2.0; Timer { interval: 1000; running: true; onTriggered: Monitors.refresh() }', root)
diff --git a/src/share/sleex/modules/sidebarLeft/SidebarLeft.qml b/src/share/sleex/modules/sidebarLeft/SidebarLeft.qml
deleted file mode 100644
index 268851bb..00000000
--- a/src/share/sleex/modules/sidebarLeft/SidebarLeft.qml
+++ /dev/null
@@ -1,202 +0,0 @@
-import qs
-import qs.services
-import qs.modules.common
-import qs.modules.common.widgets
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtQuick.Effects
-import Qt5Compat.GraphicalEffects
-import Quickshell.Io
-import Quickshell
-import Quickshell.Widgets
-import Quickshell.Wayland
-import Quickshell.Hyprland
-
-Scope { // Scope
- id: root
- property int sidebarPadding: 15
- property bool detach: false
- property Component contentComponent: SidebarLeftContent {}
- property Item sidebarContent
-
- Component.onCompleted: {
- root.sidebarContent = contentComponent.createObject(null, {
- "scopeRoot": root,
- });
- sidebarLoader.item.contentParent.children = [root.sidebarContent];
- }
-
- onDetachChanged: {
- if (root.detach) {
- sidebarContent.parent = null; // Detach content from sidebar
- sidebarLoader.active = false; // Unload sidebar
- detachedSidebarLoader.active = true; // Load detached window
- detachedSidebarLoader.item.contentParent.children = [sidebarContent];
- } else {
- sidebarContent.parent = null; // Detach content from window
- detachedSidebarLoader.active = false; // Unload detached window
- sidebarLoader.active = true; // Load sidebar
- sidebarLoader.item.contentParent.children = [sidebarContent];
- }
- }
-
- Loader {
- id: sidebarLoader
- active: true
-
- sourceComponent: PanelWindow { // Window
- id: sidebarRoot
- visible: GlobalStates.sidebarLeftOpen
-
- property bool extend: false
- property real sidebarWidth: sidebarRoot.extend ? Appearance.sizes.sidebarWidthExtended : Appearance.sizes.sidebarWidth
- property var contentParent: sidebarLeftBackground
-
- function hide() {
- GlobalStates.sidebarLeftOpen = false
- }
-
- exclusiveZone: 0
- implicitWidth: Appearance.sizes.sidebarWidthExtended + Appearance.sizes.elevationMargin
- WlrLayershell.namespace: "quickshell:sidebarLeft"
- // Hyprland 0.49: OnDemand is Exclusive, Exclusive just breaks click-outside-to-close
- // WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
- color: "transparent"
-
- anchors {
- top: true
- left: true
- bottom: true
- }
-
- mask: Region {
- item: sidebarLeftBackground
- }
-
- HyprlandFocusGrab { // Click outside to close
- id: grab
- windows: [ sidebarRoot ]
- active: sidebarRoot.visible
- onCleared: () => {
- if (!active) sidebarRoot.hide()
- }
- }
-
- // Content
- StyledRectangularShadow {
- target: sidebarLeftBackground
- radius: sidebarLeftBackground.radius
- visible: Config.options.appearance.transparency
- }
- Rectangle {
- id: sidebarLeftBackground
- anchors.top: parent.top
- anchors.left: parent.left
- anchors.topMargin: Appearance.sizes.hyprlandGapsOut
- anchors.leftMargin: Appearance.sizes.hyprlandGapsOut
- width: sidebarRoot.sidebarWidth - Appearance.sizes.hyprlandGapsOut - Appearance.sizes.elevationMargin
- height: parent.height - Appearance.sizes.hyprlandGapsOut * 2
- color: Appearance.colors.colLayer0
- radius: Appearance.rounding.screenRounding - Appearance.sizes.hyprlandGapsOut + 1
-
- Behavior on width {
- animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
- }
-
- Keys.onPressed: (event) => {
- if (event.key === Qt.Key_Escape) {
- sidebarRoot.hide();
- }
- if (event.modifiers === Qt.ControlModifier) {
- if (event.key === Qt.Key_O) {
- sidebarRoot.extend = !sidebarRoot.extend;
- }
- else if (event.key === Qt.Key_P) {
- root.detach = !root.detach;
- }
- event.accepted = true;
- }
- }
- }
- }
- }
-
- Loader {
- id: detachedSidebarLoader
- active: false
-
- sourceComponent: FloatingWindow {
- id: detachedSidebarRoot
- visible: GlobalStates.sidebarLeftOpen
- property var contentParent: detachedSidebarBackground
-
- Rectangle {
- id: detachedSidebarBackground
- anchors.fill: parent
- color: Appearance.colors.colLayer0
-
- Keys.onPressed: (event) => {
- if (event.modifiers === Qt.ControlModifier) {
- if (event.key === Qt.Key_P) {
- root.detach = !root.detach;
- }
- event.accepted = true;
- }
- }
- }
- }
- }
-
- IpcHandler {
- target: "sidebarLeft"
-
- function toggle(): void {
- GlobalStates.sidebarLeftOpen = !GlobalStates.sidebarLeftOpen
- }
-
- function close(): void {
- GlobalStates.sidebarLeftOpen = false
- }
-
- function open(): void {
- GlobalStates.sidebarLeftOpen = true
- }
- }
-
- GlobalShortcut {
- name: "sidebarLeftToggle"
- description: qsTr("Toggles left sidebar on press")
-
- onPressed: {
- GlobalStates.sidebarLeftOpen = !GlobalStates.sidebarLeftOpen;
- }
- }
-
- GlobalShortcut {
- name: "sidebarLeftOpen"
- description: qsTr("Opens left sidebar on press")
-
- onPressed: {
- GlobalStates.sidebarLeftOpen = true;
- }
- }
-
- GlobalShortcut {
- name: "sidebarLeftClose"
- description: qsTr("Closes left sidebar on press")
-
- onPressed: {
- GlobalStates.sidebarLeftOpen = false;
- }
- }
-
- GlobalShortcut {
- name: "sidebarLeftToggleDetach"
- description: qsTr("Detach left sidebar into a window/Attach it back")
-
- onPressed: {
- root.detach = !root.detach;
- }
- }
-}
diff --git a/src/share/sleex/modules/sidebarLeft/SidebarLeftContent.qml b/src/share/sleex/modules/sidebarLeft/SidebarLeftContent.qml
deleted file mode 100644
index a2559978..00000000
--- a/src/share/sleex/modules/sidebarLeft/SidebarLeftContent.qml
+++ /dev/null
@@ -1,44 +0,0 @@
-import qs
-import qs.services
-import qs.modules.common
-import qs.modules.common.widgets
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtQuick.Effects
-import Qt5Compat.GraphicalEffects
-import Quickshell.Io
-import Quickshell
-import Quickshell.Widgets
-import Quickshell.Wayland
-import Quickshell.Hyprland
-
-Item {
- id: root
- required property var scopeRoot
- anchors.fill: parent
-
- ColumnLayout {
- anchors.fill: parent
- anchors.margins: sidebarPadding
-
- spacing: sidebarPadding
-
- SwipeView { // Content pages
- id: swipeView
- Layout.topMargin: 5
- Layout.fillWidth: true
- Layout.fillHeight: true
- spacing: 10
-
- contentChildren: [
- aiChat.createObject()
- ]
- }
-
- Component {
- id: aiChat
- AiChat {}
- }
- }
-}
\ No newline at end of file
diff --git a/src/share/sleex/modules/sidebarLeft/aiChat/AnnotationSourceButton.qml b/src/share/sleex/modules/sidebarLeft/aiChat/AnnotationSourceButton.qml
index bd75a5d4..8746a59c 100644
--- a/src/share/sleex/modules/sidebarLeft/aiChat/AnnotationSourceButton.qml
+++ b/src/share/sleex/modules/sidebarLeft/aiChat/AnnotationSourceButton.qml
@@ -4,7 +4,6 @@ import qs.modules.common.functions
import qs.services
import QtQuick
import QtQuick.Layouts
-import Quickshell.Hyprland
RippleButton {
id: root
diff --git a/src/share/sleex/modules/sidebarLeft/aiChat/MessageTextBlock.qml b/src/share/sleex/modules/sidebarLeft/aiChat/MessageTextBlock.qml
index d68a260c..e74b45d7 100644
--- a/src/share/sleex/modules/sidebarLeft/aiChat/MessageTextBlock.qml
+++ b/src/share/sleex/modules/sidebarLeft/aiChat/MessageTextBlock.qml
@@ -8,7 +8,6 @@ import qs.modules.common.functions
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
-import Quickshell.Hyprland
ColumnLayout {
id: root
diff --git a/src/share/sleex/modules/wallpaperSelector/WallpaperSelector.qml b/src/share/sleex/modules/wallpaperSelector/WallpaperSelector.qml
index 22f501e3..b5c1122c 100644
--- a/src/share/sleex/modules/wallpaperSelector/WallpaperSelector.qml
+++ b/src/share/sleex/modules/wallpaperSelector/WallpaperSelector.qml
@@ -30,19 +30,9 @@ Scope {
implicitWidth: 1920
implicitHeight: 200
WlrLayershell.namespace: "quickshell:wppselector"
- // Hyprland 0.49: Focus is always exclusive and setting this breaks mouse focus grab
- // WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
+ WlrLayershell.keyboardFocus: WlrKeyboardFocus.onDemand
color: "transparent"
- HyprlandFocusGrab {
- id: grab
- windows: [ wppselectorRoot ]
- active: GlobalStates.wppselectorOpen
- onCleared: () => {
- if (!active) wppselectorRoot.hide()
- }
- }
-
anchors {
top: true
}
@@ -54,13 +44,13 @@ Scope {
bottom: parent.bottom
right: parent.right
left: parent.left
- topMargin: Appearance.sizes.hyprlandGapsOut
- rightMargin: Appearance.sizes.hyprlandGapsOut
- bottomMargin: Appearance.sizes.hyprlandGapsOut
+ topMargin: Appearance.sizes.gapsOut
+ rightMargin: Appearance.sizes.gapsOut
+ bottomMargin: Appearance.sizes.gapsOut
leftMargin: Appearance.sizes.elevationMargin
}
- width: wppselectorWidth - Appearance.sizes.hyprlandGapsOut - Appearance.sizes.elevationMargin
- height: parent.height - Appearance.sizes.hyprlandGapsOut * 2
+ width: wppselectorWidth - Appearance.sizes.gapsOut - Appearance.sizes.elevationMargin
+ height: parent.height - Appearance.sizes.gapsOut * 2
focus: GlobalStates.wppselectorOpen
Keys.onPressed: (event) => {
@@ -77,10 +67,10 @@ Scope {
id: wppselectorBackground
anchors.fill: parent
- implicitHeight: parent.height - Appearance.sizes.hyprlandGapsOut * 2
- implicitWidth: wppselectorWidth - Appearance.sizes.hyprlandGapsOut * 2
+ implicitHeight: parent.height - Appearance.sizes.gapsOut * 2
+ implicitWidth: wppselectorWidth - Appearance.sizes.gapsOut * 2
color: Appearance.colors.colLayer0
- radius: Appearance.rounding.screenRounding - Appearance.sizes.hyprlandGapsOut + 1
+ radius: Appearance.rounding.screenRounding - Appearance.sizes.gapsOut + 1
Rectangle {
id: flickableBg
diff --git a/src/share/sleex/scripts/colors/switchwall.sh b/src/share/sleex/scripts/colors/switchwall.sh
index 45826ace..0fa5f7b1 100755
--- a/src/share/sleex/scripts/colors/switchwall.sh
+++ b/src/share/sleex/scripts/colors/switchwall.sh
@@ -51,8 +51,8 @@ post_process() {
check_and_prompt_upscale() {
local img="$1"
- min_width_desired="$(hyprctl monitors -j | jq '([.[].width] | max)' | xargs)"
- min_height_desired="$(hyprctl monitors -j | jq '([.[].height] | max)' | xargs)"
+ min_width_desired="$(fht-compositor ipc -j outputs | jq '[.[] | .size[0]] | max' | xargs)"
+ min_height_desired="$(fht-compositor ipc -j outputs | jq '[.[] | .size[1]] | max' | xargs)"
if command -v identify &>/dev/null && [ -f "$img" ]; then
local img_width img_height
@@ -100,12 +100,7 @@ switch() {
local color="$5"
local actual_wallpaper_path="$imgpath"
- read scale screenx screeny screensizey < <(hyprctl monitors -j | jq '.[] | select(.focused) | .scale, .x, .y, .height' | xargs)
- cursorposx=$(hyprctl cursorpos -j | jq '.x' 2>/dev/null) || cursorposx=960
- cursorposx=$(bc <<< "scale=0; ($cursorposx - $screenx) * $scale / 1")
- cursorposy=$(hyprctl cursorpos -j | jq '.y' 2>/dev/null) || cursorposy=540
- cursorposy=$(bc <<< "scale=0; ($cursorposy - $screeny) * $scale / 1")
- cursorposy_inverted=$((screensizey - cursorposy))
+ read scale screenx screeny screensizey < <(fht-compositor ipc -j outputs | jq --arg act "$(fht-compositor ipc -j space | jq -r '.monitors[] | select(.active).output')" '.[$act] | .scale, .position[0], .position[1], .size[1]' | xargs)
if [[ "$color_flag" == "1" ]]; then
matugen_args=(color hex "$color")
@@ -163,8 +158,8 @@ switch() {
> "$STATE_DIR"/user/generated/material_colors.scss
"$SCRIPT_DIR"/applycolor.sh
- max_width_desired="$(hyprctl monitors -j | jq '([.[].width] | min)' | xargs)"
- max_height_desired="$(hyprctl monitors -j | jq '([.[].height] | min)' | xargs)"
+ max_width_desired="$(fht-compositor ipc -j outputs | jq '[.[] | .size[0]] | min' | xargs)"
+ max_height_desired="$(fht-compositor ipc -j outputs | jq '[.[] | .size[1]] | min' | xargs)"
post_process "$max_width_desired" "$max_height_desired" "$actual_wallpaper_path"
}
diff --git a/src/share/sleex/scripts/grimblast.sh b/src/share/sleex/scripts/grimblast.sh
deleted file mode 100755
index 8c9c54f9..00000000
--- a/src/share/sleex/scripts/grimblast.sh
+++ /dev/null
@@ -1,277 +0,0 @@
-#!/usr/bin/env bash
-## Grimblast: a helper for screenshots within hyprland
-## Requirements:
-## - `grim`: screenshot utility for wayland
-## - `slurp`: to select an area
-## - `hyprctl`: to read properties of current window (provided by Hyprland)
-## - `hyprpicker`: to freeze the screen when selecting area
-## - `wl-copy`: clipboard utility (provided by wl-clipboard)
-## - `jq`: json utility to parse hyprctl output
-## - `notify-send`: to show notifications (provided by libnotify)
-## Those are needed to be installed, if unsure, run `grimblast check`
-##
-## See `man 1 grimblast` or `grimblast usage` for further details.
-
-## Author: Misterio (https://github.com/misterio77)
-
-## This tool is based on grimshot, with swaymsg commands replaced by their
-## hyprctl equivalents.
-## https://github.com/swaywm/sway/blob/master/contrib/grimshot
-getTargetDirectory() {
- test -f "${XDG_CONFIG_HOME:-$HOME/.config}/user-dirs.dirs" &&
- . "${XDG_CONFIG_HOME:-$HOME/.config}/user-dirs.dirs"
-
- echo "${XDG_SCREENSHOTS_DIR:-${XDG_PICTURES_DIR:-$HOME}}"
-}
-
-tmp_editor_directory() {
- echo "/tmp"
-}
-
-#Detect if $GRIMBLAST_EDITOR env exist
-env_editor_confirm() {
- if [ -n "$GRIMBLAST_EDITOR" ]; then
- echo "GRIMBLAST_EDITOR is set. Continuing..."
- else
- echo "GRIMBLAST_EDITOR is not set. Defaulting to gimp"
- GRIMBLAST_EDITOR=gimp
- fi
-}
-
-NOTIFY=no
-CURSOR=
-FREEZE=
-WAIT=no
-SCALE=
-HYPRPICKER_PID=-1
-
-while [ $# -gt 0 ]; do
- key="$1"
-
- case $key in
- -n | --notify)
- NOTIFY=yes
- shift # past argument
- ;;
- -c | --cursor)
- CURSOR=yes
- shift # past argument
- ;;
- -f | --freeze)
- FREEZE=yes
- shift # past argument
- ;;
- -w | --wait)
- shift
- WAIT=$1
- if echo "$WAIT" | grep "[^0-9]" -q; then
- echo "Invalid value for wait '$WAIT'" >&2
- exit 3
- fi
- shift
- ;;
- -s | --scale)
- shift # past argument
- if [ $# -gt 0 ]; then
- SCALE="$1" # assign the next argument to SCALE
- shift # past argument
- else
- echo "Error: Missing argument for --scale option."
- exit 1
- fi
- ;;
- *) # unknown option
- break # done with parsing --flags
- ;;
- esac
-done
-
-ACTION=${1:-usage}
-SUBJECT=${2:-screen}
-FILE=${3:-$(getTargetDirectory)/$(date -Ins).png}
-FILE_EDITOR=${3:-$(tmp_editor_directory)/$(date -Ins).png}
-
-if [ "$ACTION" != "save" ] && [ "$ACTION" != "copy" ] && [ "$ACTION" != "edit" ] && [ "$ACTION" != "copysave" ] && [ "$ACTION" != "check" ]; then
- echo "Usage:"
- echo " grimblast [--notify] [--cursor] [--freeze] [--wait N] [--scale ] (copy|save|copysave|edit) [active|screen|output|area] [FILE|-]"
- echo " grimblast check"
- echo " grimblast usage"
- echo ""
- echo "Commands:"
- echo " copy: Copy the screenshot data into the clipboard."
- echo " save: Save the screenshot to a regular file or '-' to pipe to STDOUT."
- echo " copysave: Combine the previous 2 options."
- echo " edit: Open screenshot in the image editor of your choice (default is gimp). See man page for info."
- echo " check: Verify if required tools are installed and exit."
- echo " usage: Show this message and exit."
- echo ""
- echo "Targets:"
- echo " active: Currently active window."
- echo " screen: All visible outputs."
- echo " output: Currently active output."
- echo " area: Manually select a region or window."
- exit
-fi
-
-notify() {
- notify-send -t 3000 -a grimblast "$@"
-}
-
-notifyOk() {
- [ "$NOTIFY" = "no" ] && return
-
- notify "$@"
-}
-
-notifyError() {
- if [ $NOTIFY = "yes" ]; then
- TITLE=${2:-"Screenshot"}
- MESSAGE=${1:-"Error taking screenshot with grim"}
- notify -u critical "$TITLE" "$MESSAGE"
- else
- echo "$1"
- fi
-}
-
-resetFade() {
- if [[ -n $FADE && -n $FADEOUT ]]; then
- hyprctl keyword animation "$FADE" >/dev/null
- hyprctl keyword animation "$FADEOUT" >/dev/null
- fi
-}
-
-killHyprpicker() {
- if [ ! $HYPRPICKER_PID -eq -1 ]; then
- kill $HYPRPICKER_PID
- fi
-}
-
-die() {
- killHyprpicker
- MSG=${1:-Bye}
- notifyError "Error: $MSG"
- exit 2
-}
-
-check() {
- COMMAND=$1
- if command -v "$COMMAND" >/dev/null 2>&1; then
- RESULT="OK"
- else
- RESULT="NOT FOUND"
- fi
- echo " $COMMAND: $RESULT"
-}
-
-takeScreenshot() {
- FILE=$1
- GEOM=$2
- OUTPUT=$3
- if [ -n "$OUTPUT" ]; then
- grim ${CURSOR:+-c} ${SCALE:+-s "$SCALE"} -o "$OUTPUT" "$FILE" || die "Unable to invoke grim"
- elif [ -z "$GEOM" ]; then
- grim ${CURSOR:+-c} ${SCALE:+-s "$SCALE"} "$FILE" || die "Unable to invoke grim"
- else
- grim ${CURSOR:+-c} ${SCALE:+-s "$SCALE"} -g "$GEOM" "$FILE" || die "Unable to invoke grim"
- resetFade
- fi
-}
-
-wait() {
- if [ "$WAIT" != "no" ]; then
- sleep "$WAIT"
- fi
-}
-
-if [ "$ACTION" = "check" ]; then
- echo "Checking if required tools are installed. If something is missing, install it to your system and make it available in PATH..."
- check grim
- check slurp
- check hyprctl
- check hyprpicker
- check wl-copy
- check jq
- check notify-send
- exit
-elif [ "$SUBJECT" = "active" ]; then
- wait
- FOCUSED=$(hyprctl activewindow -j)
- GEOM=$(echo "$FOCUSED" | jq -r '"\(.at[0]),\(.at[1]) \(.size[0])x\(.size[1])"')
- APP_ID=$(echo "$FOCUSED" | jq -r '.class')
- WHAT="$APP_ID window"
-elif [ "$SUBJECT" = "screen" ]; then
- wait
- GEOM=""
- WHAT="Screen"
-elif [ "$SUBJECT" = "output" ]; then
- wait
- GEOM=""
- OUTPUT=$(hyprctl monitors -j | jq -r '.[] | select(.focused == true)' | jq -r '.name')
- WHAT="$OUTPUT"
-elif [ "$SUBJECT" = "area" ]; then
- if [ "$FREEZE" = "yes" ] && [ "$(command -v "hyprpicker")" ] >/dev/null 2>&1; then
- hyprpicker -r -z &
- sleep 0.2
- HYPRPICKER_PID=$!
- fi
-
- # get fade & fadeOut animation and unset it
- # this removes the black border seen around screenshots
- FADE="$(hyprctl -j animations | jq -jr '.[0][] | select(.name == "fade") | .name, ",", (if .enabled == true then "1" else "0" end), ",", (.speed|floor), ",", .bezier')"
- FADEOUT="$(hyprctl -j animations | jq -jr '.[0][] | select(.name == "fadeOut") | .name, ",", (if .enabled == true then "1" else "0" end), ",", (.speed|floor), ",", .bezier')"
- hyprctl keyword animation 'fade,0,1,default' >/dev/null
- hyprctl keyword animation 'fadeOut,0,1,default' >/dev/null
-
- WORKSPACES="$(hyprctl monitors -j | jq -r 'map(.activeWorkspace.id)')"
- WINDOWS="$(hyprctl clients -j | jq -r --argjson workspaces "$WORKSPACES" 'map(select([.workspace.id] | inside($workspaces)))')"
- # shellcheck disable=2086 # if we don't split, spaces mess up slurp
- GEOM=$(echo "$WINDOWS" | jq -r '.[] | "\(.at[0]),\(.at[1]) \(.size[0])x\(.size[1])"' | slurp $SLURP_ARGS)
-
- # Check if user exited slurp without selecting the area
- if [ -z "$GEOM" ]; then
- killHyprpicker
- resetFade
- exit 1
- fi
- WHAT="Area"
- wait
-elif [ "$SUBJECT" = "window" ]; then
- die "Subject 'window' is now included in 'area'"
-else
- die "Unknown subject to take a screen shot from" "$SUBJECT"
-fi
-
-if [ "$ACTION" = "copy" ]; then
- takeScreenshot - "$GEOM" "$OUTPUT" | wl-copy --type image/png || die "Clipboard error"
- notifyOk "$WHAT copied to buffer"
-elif [ "$ACTION" = "save" ]; then
- if takeScreenshot "$FILE" "$GEOM" "$OUTPUT"; then
- TITLE="Screenshot of $SUBJECT"
- MESSAGE=$(basename "$FILE")
- notifyOk "$TITLE" "$MESSAGE" -i "$FILE"
- echo "$FILE"
- else
- notifyError "Error taking screenshot with grim"
- fi
-elif [ "$ACTION" = "edit" ]; then
- env_editor_confirm
- if takeScreenshot "$FILE_EDITOR" "$GEOM" "$OUTPUT"; then
- TITLE="Screenshot of $SUBJECT"
- MESSAGE="Open screenshot in image editor"
- notifyOk "$TITLE" "$MESSAGE" -i "$FILE_EDITOR"
- $GRIMBLAST_EDITOR "$FILE_EDITOR"
- echo "$FILE_EDITOR"
- else
- notifyError "Error taking screenshot"
- fi
-else
- if [ "$ACTION" = "copysave" ]; then
- takeScreenshot - "$GEOM" "$OUTPUT" | tee "$FILE" | wl-copy --type image/png || die "Clipboard error"
- notifyOk "$WHAT copied to buffer and saved to $FILE" -i "$FILE"
- echo "$FILE"
- else
- notifyError "Error taking screenshot with grim"
- fi
-fi
-
-killHyprpicker
\ No newline at end of file
diff --git a/src/share/sleex/scripts/grimshot.sh b/src/share/sleex/scripts/grimshot.sh
new file mode 100755
index 00000000..cffdb3c4
--- /dev/null
+++ b/src/share/sleex/scripts/grimshot.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+default_extention="png"
+directory="$HOME/Images/Screenshots/Grimshot"
+# TODO:Make automatical check for Images/... or Pictures/... and automatical mkdir
+function copy_only(){
+
+ name="$(date +"%d-%m-%Y_%H:%M").$default_extention"
+ output="/tmp/$name"
+
+ grim $output
+ wl-copy < $output
+
+ notify-send "Grimshot" "Screenshot has been made using:\nCopy only mode"
+ exit
+}
+
+function normalScreenshot() {
+ output="$directory/$name"
+
+ sleep 0.7
+ grim $output
+ wl-copy < $output
+
+ notify-send "Grimshot" "Screenshot $name has been made and copied to clipboard"
+}
+
+if [ "$1" == "--copy_only" ]; then
+ copy_only
+fi
+
+choice=$(printf "$(date +"%d-%m-%Y_%H:%M").$default_extention\nCustom name\nCopy only" | rofi -dmenu -p "Grimshot")
+
+case "$choice" in
+ "$(date +"%d-%m-%Y_%H:%M").$default_extention") name="$(date +"%d-%m-%Y_%H:%M").png";;
+ "Custom name") name=$(rofi -dmenu -p "Screenshot name").$default_extention;;
+ "Copy only") sleep 0.7 && copy_only ;;
+ *) exit
+esac
+
+normalScreenshot
\ No newline at end of file
diff --git a/src/share/sleex/scripts/hyprland/workspace_action.sh b/src/share/sleex/scripts/hyprland/workspace_action.sh
deleted file mode 100755
index 2552764b..00000000
--- a/src/share/sleex/scripts/hyprland/workspace_action.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/usr/bin/env bash
-hyprctl dispatch "$1" $(((($(hyprctl activeworkspace -j | jq -r .id) - 1) / 10) * 10 + $2))
\ No newline at end of file
diff --git a/src/share/sleex/scripts/record-script.sh b/src/share/sleex/scripts/record-script.sh
index 478c56f7..bb49aeff 100755
--- a/src/share/sleex/scripts/record-script.sh
+++ b/src/share/sleex/scripts/record-script.sh
@@ -7,7 +7,7 @@ getaudiooutput() {
pactl list sources | grep 'Name' | grep 'monitor' | cut -d ' ' -f2
}
getactivemonitor() {
- hyprctl monitors -j | jq -r '.[] | select(.focused == true) | .name'
+ fht-compositor ipc -j space | jq -r '.monitors[] | select(.active).output'
}
xdgvideo="$(xdg-user-dir VIDEOS)"
diff --git a/src/share/sleex/services/Audio.qml b/src/share/sleex/services/Audio.qml
index eb69dfbc..de1dd0ec 100644
--- a/src/share/sleex/services/Audio.qml
+++ b/src/share/sleex/services/Audio.qml
@@ -2,7 +2,6 @@ import qs.modules.common
import QtQuick
import Quickshell
import Quickshell.Services.Pipewire
-import Quickshell.Hyprland
pragma Singleton
pragma ComponentBehavior: Bound
@@ -49,10 +48,8 @@ Singleton {
}
}
- // Reverted to Hyprland.dispatch, which is the idiomatic method for this shell.
function playSound(relativeSoundPath) {
const fullPath = "/usr/share/sleex/" + relativeSoundPath;
- // Use the absolute path to paplay to be safe.
const command = ["/usr/bin/paplay", `${fullPath}`];
Quickshell.execDetached(command);
}
diff --git a/src/share/sleex/services/Brightness.qml b/src/share/sleex/services/Brightness.qml
index 552b2f88..8a61a125 100644
--- a/src/share/sleex/services/Brightness.qml
+++ b/src/share/sleex/services/Brightness.qml
@@ -8,6 +8,7 @@ import Quickshell
import Quickshell.Io
import Quickshell.Hyprland
import QtQuick
+import Sleex.Fhtc
/**
* For managing brightness of monitors. Supports both brightnessctl and ddcutil.
@@ -27,21 +28,21 @@ Singleton {
}
function increaseBrightness(): void {
- const focusedName = Hyprland.focusedMonitor.name;
+ const focusedName = FhtcMonitors.activeMonitorName;
const monitor = monitors.find(m => focusedName === m.screen.name);
if (monitor)
monitor.setBrightness(monitor.brightness + 0.05);
}
function decreaseBrightness(): void {
- const focusedName = Hyprland.focusedMonitor.name;
+ const focusedName = FhtcMonitors.activeMonitorName;
const monitor = monitors.find(m => focusedName === m.screen.name);
if (monitor)
monitor.setBrightness(monitor.brightness - 0.05);
}
function setMonitorBrightness(b): void {
- const focusedName = Hyprland.focusedMonitor.name;
+ const focusedName = FhtcMonitors.activeMonitorName;
const monitor = monitors.find(m => focusedName === m.screen.name);
if (monitor)
monitor.setBrightness(b);
diff --git a/src/share/sleex/services/ConfigLoader.qml b/src/share/sleex/services/ConfigLoader.qml
index 498dcbf0..84df1f8e 100644
--- a/src/share/sleex/services/ConfigLoader.qml
+++ b/src/share/sleex/services/ConfigLoader.qml
@@ -7,7 +7,6 @@ import qs.modules.common.functions
import QtQuick
import Quickshell
import Quickshell.Io
-import Quickshell.Hyprland
import Qt.labs.platform
/**
@@ -104,7 +103,6 @@ Singleton {
} else {
root.applyConfig(configFileView.text())
if (!root.preventNextNotification) {
- // Hyprland.dispatch(`exec notify-send "${qsTr("Shell configuration reloaded")}" "${root.filePath}"`)
} else {
root.preventNextNotification = false;
}
diff --git a/src/share/sleex/services/FirstRunExperience.qml b/src/share/sleex/services/FirstRunExperience.qml
index dff06a0c..5b4c18ee 100644
--- a/src/share/sleex/services/FirstRunExperience.qml
+++ b/src/share/sleex/services/FirstRunExperience.qml
@@ -4,7 +4,6 @@ import qs.modules.common.functions
import qs.modules.common
import Quickshell
import Quickshell.Io
-import Quickshell.Hyprland
Singleton {
id: root
diff --git a/src/share/sleex/services/HyprlandData.qml b/src/share/sleex/services/HyprlandData.qml
deleted file mode 100644
index a9868a45..00000000
--- a/src/share/sleex/services/HyprlandData.qml
+++ /dev/null
@@ -1,85 +0,0 @@
-pragma Singleton
-pragma ComponentBehavior: Bound
-
-import QtQuick
-import Quickshell
-import Quickshell.Io
-import Quickshell.Wayland
-import Quickshell.Hyprland
-
-/**
- * Provides access to some Hyprland data not available in Quickshell.Hyprland.
- */
-Singleton {
- id: root
- property var windowList: []
- property var addresses: []
- property var windowByAddress: ({})
- property var monitors: []
- property var layers: ({})
-
- function updateWindowList() {
- getClients.running = true
- getMonitors.running = true
- }
-
- function updateLayers() {
- getLayers.running = true
- }
-
- Component.onCompleted: {
- updateWindowList()
- updateLayers()
- }
-
- Connections {
- target: Hyprland
-
- function onRawEvent(event) {
- // Filter out redundant old v1 events for the same thing
- if(event.name in [
- "activewindow", "focusedmon", "monitoradded",
- "createworkspace", "destroyworkspace", "moveworkspace",
- "activespecial", "movewindow", "windowtitle"
- ]) return ;
- updateWindowList()
- }
- }
-
- Process {
- id: getClients
- command: ["bash", "-c", "hyprctl clients -j | jq -c"]
- stdout: SplitParser {
- onRead: (data) => {
- root.windowList = JSON.parse(data)
- let tempWinByAddress = {}
- for (var i = 0; i < root.windowList.length; ++i) {
- var win = root.windowList[i]
- tempWinByAddress[win.address] = win
- }
- root.windowByAddress = tempWinByAddress
- root.addresses = root.windowList.map((win) => win.address)
- }
- }
- }
- Process {
- id: getMonitors
- command: ["bash", "-c", "hyprctl monitors -j | jq -c"]
- stdout: SplitParser {
- onRead: (data) => {
- root.monitors = JSON.parse(data)
- }
- }
- }
-
- Process {
- id: getLayers
- command: ["bash", "-c", "hyprctl layers -j | jq -c"]
- stdout: SplitParser {
- onRead: (data) => {
- root.layers = JSON.parse(data)
- }
- }
- }
-}
-
diff --git a/src/share/sleex/services/LatexRenderer.qml b/src/share/sleex/services/LatexRenderer.qml
index ecfbf3bb..12c759d6 100644
--- a/src/share/sleex/services/LatexRenderer.qml
+++ b/src/share/sleex/services/LatexRenderer.qml
@@ -7,7 +7,6 @@ import qs.modules.common
import QtQuick
import Quickshell
import Quickshell.Io
-import Quickshell.Hyprland
import Qt.labs.platform
/**
diff --git a/src/share/sleex/services/Monitors.qml b/src/share/sleex/services/Monitors.qml
deleted file mode 100644
index ae24fcd2..00000000
--- a/src/share/sleex/services/Monitors.qml
+++ /dev/null
@@ -1,170 +0,0 @@
-pragma Singleton
-
-import Quickshell
-import Quickshell.Io
-import QtQuick
-
-
-Singleton {
- id: root
-
- readonly property list monitors: []
- readonly property Monitor primary: monitors.find(m => m.primary) ?? null
- property int snapThreshold: 20
-
- reloadableId: "monitors"
-
- function setMonitorPosition(name: string, x: int, y: int): void {
- const monitor = monitors.find(m => m.name === name);
- if (!monitor) return;
-
- setPositionProc.exec([
- "hyprctl", "keyword", "monitor",
- `${name},${monitor.width}x${monitor.height},${x}x${y},${monitor.scale}`
- ]);
- }
-
- function snapMonitors(primaryName: string, secondaryName: string, position: string): void {
- const primary = monitors.find(m => m.name === primaryName);
- const secondary = monitors.find(m => m.name === secondaryName);
- if (!primary || !secondary) return;
-
- let x, y;
- switch (position) {
- case "right":
- x = primary.x + primary.width;
- y = primary.y;
- break;
- case "left":
- x = primary.x - secondary.width;
- y = primary.y;
- break;
- case "top":
- x = primary.x;
- y = primary.y - secondary.height;
- break;
- case "bottom":
- x = primary.x;
- y = primary.y + primary.height;
- break;
- default:
- return;
- }
-
- setMonitorPosition(secondaryName, x, y);
- }
-
- function toggleMonitor(name: string): void {
- const monitor = monitors.find(m => m.name === name);
- if (!monitor) return;
-
- const config = monitor.enabled ?
- `${name},disabled` :
- `${name},${monitor.width}x${monitor.height},${monitor.x}x${monitor.y},${monitor.scale}`;
-
- toggleProc.exec(["hyprctl", "keyword", "monitor", config]);
- }
-
- function refreshMonitors(): void {
- getMonitors.running = true;
- }
-
- // Get monitor information
- Process {
- id: getMonitors
-
- running: true
- command: ["hyprctl", "monitors", "-j"]
- environment: ({
- LANG: "C",
- LC_ALL: "C"
- })
-
- stdout: StdioCollector {
- onStreamFinished: {
- let monitorData;
- try {
- monitorData = JSON.parse(text);
- } catch (e) {
- console.error("Failed to parse monitor data:", e);
- return;
- }
-
- const rMonitors = root.monitors;
-
- // Remove destroyed monitors
- const destroyed = rMonitors.filter(rm =>
- !monitorData.find(m => m.name === rm.name)
- );
- for (const monitor of destroyed) {
- rMonitors.splice(rMonitors.indexOf(monitor), 1).forEach(m => m.destroy());
- }
-
- // Update or create monitors
- for (const monitorInfo of monitorData) {
- const match = rMonitors.find(m => m.name === monitorInfo.name);
- if (match) {
- match.lastIpcObject = monitorInfo;
- } else {
- rMonitors.push(monitorComp.createObject(root, {
- lastIpcObject: monitorInfo
- }));
- }
- }
- }
- }
- }
-
- // Set monitor position
- Process {
- id: setPositionProc
-
- onExited: {
- getMonitors.running = true;
- }
- }
-
- // Toggle monitor
- Process {
- id: toggleProc
-
- onExited: {
- getMonitors.running = true;
- }
- }
-
- // Listen for Hyprland events
- Process {
- running: true
- command: ["socat", "-", "UNIX-CONNECT:/tmp/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock"]
-
- stdout: SplitParser {
- onRead: {
- if (data.startsWith("monitoradded") ||
- data.startsWith("monitorremoved") ||
- data.startsWith("configreloaded")) {
- getMonitors.running = true;
- }
- }
- }
- }
-
- component Monitor: QtObject {
- required property var lastIpcObject
-
- readonly property string name: lastIpcObject.name ?? ""
- readonly property int width: lastIpcObject.width ?? 0
- readonly property int height: lastIpcObject.height ?? 0
- readonly property int x: lastIpcObject.x ?? 0
- readonly property int y: lastIpcObject.y ?? 0
- readonly property real scale: lastIpcObject.scale ?? 1.0
- readonly property bool primary: lastIpcObject.focused ?? false
- readonly property bool enabled: width > 0 && height > 0
- readonly property string resolution: `${width}x${height}`
- }
-
- Component {
- id: monitorComp
- Monitor {}
- }
-}
\ No newline at end of file
diff --git a/src/share/sleex/services/NightLight.qml b/src/share/sleex/services/NightLight.qml
index 5dfa45a2..580adfe3 100644
--- a/src/share/sleex/services/NightLight.qml
+++ b/src/share/sleex/services/NightLight.qml
@@ -4,11 +4,6 @@ import qs.modules.common
import Quickshell
import Quickshell.Io
-/**
- * Simple hyprsunset service with automatic mode.
- * In theory we don't need this because hyprsunset has a config file, but it somehow doesn't work.
- * It should also be possible to control it via hyprctl, but it doesn't work consistently either so we're just killing and launching.
- */
Singleton {
id: root
property bool ready: false
@@ -55,7 +50,7 @@ Singleton {
onColorTemperatureChanged: {
if (root.active)
- Quickshell.execDetached(["hyprctl", "hyprsunset", "temperature", String(colorTemperature)]);
+ Quickshell.execDetached(["gammastep", "-O", String(colorTemperature)]);
}
function reEvaluate() {
@@ -76,12 +71,12 @@ Singleton {
function enable() {
root.active = true;
Quickshell.execDetached(["bash", "-c",
- `pidof hyprsunset || hyprsunset --temperature ${root.colorTemperature}`]);
+ `pidof gammastep || gammastep -O ${root.colorTemperature}`]);
}
function disable() {
root.active = false;
- Quickshell.execDetached(["bash", "-c", "pkill hyprsunset"]);
+ Quickshell.execDetached(["bash", "-c", "pkill gammastep"]);
}
function fetchState() {
@@ -91,7 +86,7 @@ Singleton {
Process {
id: fetchProc
running: true
- command: ["bash", "-c", "hyprctl hyprsunset temperature"]
+ command: ["bash", "-c", "hyprctl hyprsunset temperature"] // TODO: use gammastep
stdout: StdioCollector {
id: stateCollector
onStreamFinished: {
diff --git a/src/share/sleex/services/PersistentStateManager.qml b/src/share/sleex/services/PersistentStateManager.qml
index 5a5addbe..bb126267 100644
--- a/src/share/sleex/services/PersistentStateManager.qml
+++ b/src/share/sleex/services/PersistentStateManager.qml
@@ -6,7 +6,6 @@ import qs.modules.common.functions
import QtQuick
import Quickshell
import Quickshell.Io
-import Quickshell.Hyprland
import Qt.labs.platform
/**
diff --git a/src/share/sleex/services/Ydotool.qml b/src/share/sleex/services/Ydotool.qml
index ca9248cd..b7c36453 100644
--- a/src/share/sleex/services/Ydotool.qml
+++ b/src/share/sleex/services/Ydotool.qml
@@ -3,7 +3,6 @@ pragma Singleton
import qs.modules.common
import Quickshell
import Quickshell.Io
-import Quickshell.Hyprland
Singleton {
id: root
diff --git a/src/share/sleex/settings.qml b/src/share/sleex/settings.qml
index 6098bcc0..af766326 100644
--- a/src/share/sleex/settings.qml
+++ b/src/share/sleex/settings.qml
@@ -10,7 +10,6 @@ import QtQuick.Layouts
import QtQuick.Window
import Quickshell
import Quickshell.Io
-import Quickshell.Hyprland
import Quickshell.Widgets
import qs
import qs.services
diff --git a/src/share/sleex/shell.qml b/src/share/sleex/shell.qml
index 24290ae9..04ae8454 100644
--- a/src/share/sleex/shell.qml
+++ b/src/share/sleex/shell.qml
@@ -18,7 +18,6 @@ import qs.modules.polkit
import qs.modules.screenCorners
import qs.modules.session
import qs.modules.dashboard
-import qs.modules.sidebarLeft
import qs.modules.wallpaperSelector
import qs.modules.background
import qs.modules.lockscreen
@@ -46,7 +45,6 @@ ShellRoot {
property bool enableReloadPopup: true
property bool enableScreenCorners: false
property bool enableSession: true
- property bool enableSidebarLeft: false
property bool enableDashboard: true
property bool enableWallSelector: true
property bool enableBackground: true
@@ -73,7 +71,6 @@ ShellRoot {
LazyLoader { active: true; component: BatteryPopup {} }
LazyLoader { active: enableScreenCorners; component: ScreenCorners {} }
LazyLoader { active: enableSession; component: Session {} }
- LazyLoader { active: enableSidebarLeft; component: SidebarLeft {} }
LazyLoader { active: enableDashboard; component: Dashboard {} }
LazyLoader { active: enableWallSelector; component: WallpaperSelector {} }
LazyLoader { active: enableBackground; component: Background {} }
From 08d324e22a6ed37c4d8acf1b9ab0cde86220e79a Mon Sep 17 00:00:00 2001
From: Ardox
Date: Wed, 13 May 2026 11:32:15 +0200
Subject: [PATCH 49/56] Fix screen assignment logic in NotificationPopup.qml
---
src/share/sleex/modules/notificationPopup/NotificationPopup.qml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/share/sleex/modules/notificationPopup/NotificationPopup.qml b/src/share/sleex/modules/notificationPopup/NotificationPopup.qml
index c5730060..01aeec19 100644
--- a/src/share/sleex/modules/notificationPopup/NotificationPopup.qml
+++ b/src/share/sleex/modules/notificationPopup/NotificationPopup.qml
@@ -17,7 +17,7 @@ Scope {
PanelWindow {
id: root
visible: (Notifications.popupList.length > 0)
- screen: Quickshell.screens.find(s => s.name === FhtcMonitors.activeMonitorName === screen.name) ?? null
+ screen: Quickshell.screens.find(s => s.name === FhtcMonitors.activeMonitorName) ?? null
WlrLayershell.namespace: "quickshell:notificationPopup"
WlrLayershell.layer: WlrLayer.Overlay
From c25b3a05a284ccfdc2dddc15ddb942005a311315 Mon Sep 17 00:00:00 2001
From: Ardox
Date: Wed, 13 May 2026 12:01:49 +0200
Subject: [PATCH 50/56] Update keybind parser for fhtc's toml
---
src/etc/sleex/compositor/keybinds.toml | 138 +++++------
.../modules/cheatsheet/CheatsheetKeybinds.qml | 3 +-
src/share/sleex/scripts/fht/get_keybinds.py | 91 +++++++
.../sleex/scripts/hyprland/get_keybinds.py | 222 ------------------
...{HyprlandKeybinds.qml => FhtcKeybinds.qml} | 20 +-
5 files changed, 166 insertions(+), 308 deletions(-)
create mode 100755 src/share/sleex/scripts/fht/get_keybinds.py
delete mode 100755 src/share/sleex/scripts/hyprland/get_keybinds.py
rename src/share/sleex/services/{HyprlandKeybinds.qml => FhtcKeybinds.qml} (77%)
diff --git a/src/etc/sleex/compositor/keybinds.toml b/src/etc/sleex/compositor/keybinds.toml
index bec4cd15..f2d32f62 100644
--- a/src/etc/sleex/compositor/keybinds.toml
+++ b/src/etc/sleex/compositor/keybinds.toml
@@ -1,89 +1,92 @@
-# Key bindings.
-[keybinds]
-
+# Lines ending with `# [hidden]` won't be shown on cheatsheet
+# Lines starting with #! are section headings
-Super-Ctrl-q = "quit"
-Super-Ctrl-r = "reload-config"
-
-Super-Shift-s = { action = "run-command", arg = """grim -g "`slurp -o`" - | wl-copy --type image/png""" }
+[keybinds]
-Super-a = { action = "run-command", arg = "qs -p Documents/sleex/src/share/sleex/ ipc call overview toggle" }
+#!
+##! Basic
+Super-Shift-s = { action = "run-command", arg = 'grim -g "$(slurp -o)" - | wl-copy --type image/png' } # [hidden]
+Super-a = { action = "run-command", arg = "qs -p /usr/share/sleex ipc call overview toggle" } # [hidden]
-# If you need to run an action even when the compositor is locked, here's how you can achieve this.
+#!
+##! Multimedia
XF86AudioRaiseVolume.action = "run-command"
-XF86AudioRaiseVolume.arg = "wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+"
+XF86AudioRaiseVolume.arg = "wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+" # Increase volume by 5%
XF86AudioRaiseVolume.allow-while-locked = true
XF86AudioLowerVolume.action = "run-command"
-XF86AudioLowerVolume.arg = "wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%-"
+XF86AudioLowerVolume.arg = "wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%-" # Decrease volume by 5%
XF86AudioLowerVolume.allow-while-locked = true
XF86AudioMute.action = "run-command"
-XF86AudioMute.arg = "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"
+XF86AudioMute.arg = "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle" # Toggle mute
-# Focus management, defaults are similar to what DWM provides
-Super-Right = "focus-next-window"
-Super-Left = "focus-previous-window"
+#!
+##! Focus and window management
+Super-Right = "focus-next-window" # Focus the next window in the current workspace
+Super-Left = "focus-previous-window" # Focus the previous window in the current workspace
-# Window management
-Super-m = "maximize-focused-window"
-Super-f = "fullscreen-focused-window"
-Super-q = "close-focused-window"
-Super-Ctrl-Space = "float-focused-window"
-Super-Shift-Right = "swap-with-next-window"
-Super-Shift-Left = "swap-with-previous-window"
+Super-m = "maximize-focused-window" # Toggle maximize the focused window
+Super-f = "fullscreen-focused-window" # Toggle fullscreen the focused window
+Super-q = "close-focused-window" # Close the focused window
+Super-Ctrl-Space = "float-focused-window" # Toggle floating the focused window
+Super-Shift-Right = "swap-with-next-window" # Swap the focused window with the next window in the current workspace
+Super-Shift-Left = "swap-with-previous-window" # Swap the focused window with the previous window in the current workspace
# Super-Ctrl-p = "pin-focused-window"
# Transient layout changes.
-Super-Space = "select-next-layout"
-Super-Ctrl-Left = { action = "change-mwfact", arg = -0.1 }
-Super-Ctrl-Right = { action = "change-mwfact", arg = +0.1 }
-Super-Shift-h = { action = "change-nmaster", arg = +1 }
-Super-Shift-l = { action = "change-nmaster", arg = -1 }
-Super-i = { action = "change-window-proportion", arg = +0.5 }
-Super-o = { action = "change-window-proportion", arg = -0.5 }
+Super-Space = "select-next-layout" # Cycle to the next layout in the current workspace
+Super-Ctrl-Left = { action = "change-mwfact", arg = -0.1 } # Decrease the master area size by 10%
+Super-Ctrl-Right = { action = "change-mwfact", arg = 0.1 } # Increase the master area size by 10%
+Super-Shift-h = { action = "change-nmaster", arg = 1 } # Increase the number of windows in the master area by 1
+Super-Shift-l = { action = "change-nmaster", arg = -1 } # Decrease the number of windows in the master area by 1
+Super-i = { action = "change-window-proportion", arg = 0.5 } # Increase the size of the focused window
+Super-o = { action = "change-window-proportion", arg = -0.5 } # Decrease the size of the focused window
# Workspaces
-Super-ampersand = { action = "focus-workspace", arg = 0 }
-Super-eacute = { action = "focus-workspace", arg = 1 }
-Super-quotedbl = { action = "focus-workspace", arg = 2 }
-Super-apostrophe = { action = "focus-workspace", arg = 3 }
-Super-parenleft = { action = "focus-workspace", arg = 4 }
-Super-minus = { action = "focus-workspace", arg = 5 }
-Super-egrave = { action = "focus-workspace", arg = 6 }
-Super-underscore = { action = "focus-workspace", arg = 7 }
-Super-agrave = { action = "focus-workspace", arg = 8 }
+#/# Super-[1-9] = { action = "focus-workspace", arg = N } # Focus workspace N (0-indexed)
+Super-ampersand = { action = "focus-workspace", arg = 0 } # [hidden]
+Super-eacute = { action = "focus-workspace", arg = 1 } # [hidden]
+Super-quotedbl = { action = "focus-workspace", arg = 2 } # [hidden]
+Super-apostrophe = { action = "focus-workspace", arg = 3 } # [hidden]
+Super-parenleft = { action = "focus-workspace", arg = 4 } # [hidden]
+Super-minus = { action = "focus-workspace", arg = 5 } # [hidden]
+Super-egrave = { action = "focus-workspace", arg = 6 } # [hidden]
+Super-underscore = { action = "focus-workspace", arg = 7 } # [hidden]
+Super-agrave = { action = "focus-workspace", arg = 8 } # [hidden]
# For QWERTY users
-Super-1 = { action = "focus-workspace", arg = 0 }
-Super-2 = { action = "focus-workspace", arg = 1 }
-Super-3 = { action = "focus-workspace", arg = 2 }
-Super-4 = { action = "focus-workspace", arg = 3 }
-Super-5 = { action = "focus-workspace", arg = 4 }
-Super-6 = { action = "focus-workspace", arg = 5 }
-Super-7 = { action = "focus-workspace", arg = 6 }
-Super-8 = { action = "focus-workspace", arg = 7 }
-Super-9 = { action = "focus-workspace", arg = 8 }
+Super-1 = { action = "focus-workspace", arg = 0 } # [hidden]
+Super-2 = { action = "focus-workspace", arg = 1 } # [hidden]
+Super-3 = { action = "focus-workspace", arg = 2 } # [hidden]
+Super-4 = { action = "focus-workspace", arg = 3 } # [hidden]
+Super-5 = { action = "focus-workspace", arg = 4 } # [hidden]
+Super-6 = { action = "focus-workspace", arg = 5 } # [hidden]
+Super-7 = { action = "focus-workspace", arg = 6 } # [hidden]
+Super-8 = { action = "focus-workspace", arg = 7 } # [hidden]
+Super-9 = { action = "focus-workspace", arg = 8 } # [hidden]
# Sending windows to workspaces
-Super-Ctrl-ampersand = { action = "send-to-workspace", arg = 0 }
-Super-Ctrl-eacute = { action = "send-to-workspace", arg = 1 }
-Super-Ctrl-quotedbl = { action = "send-to-workspace", arg = 2 }
-Super-Ctrl-apostrophe = { action = "send-to-workspace", arg = 3 }
-Super-Ctrl-parenleft = { action = "send-to-workspace", arg = 4 }
-Super-Ctrl-minus = { action = "send-to-workspace", arg = 5 }
-Super-Ctrl-egrave = { action = "send-to-workspace", arg = 6 }
-Super-Ctrl-underscore = { action = "send-to-workspace", arg = 7 }
-Super-Ctrl-agrave = { action = "send-to-workspace", arg = 8 }
+#/#! Super-Shift-[1-9] = { action = "send-to-workspace", arg = N } # Send the focused window to workspace N (0-indexed)
+Super-Ctrl-ampersand = { action = "send-to-workspace", arg = 0 } # [hidden]
+Super-Ctrl-eacute = { action = "send-to-workspace", arg = 1 } # [hidden]
+Super-Ctrl-quotedbl = { action = "send-to-workspace", arg = 2 } # [hidden]
+Super-Ctrl-apostrophe = { action = "send-to-workspace", arg = 3 } # [hidden]
+Super-Ctrl-parenleft = { action = "send-to-workspace", arg = 4 } # [hidden]
+Super-Ctrl-minus = { action = "send-to-workspace", arg = 5 } # [hidden]
+Super-Ctrl-egrave = { action = "send-to-workspace", arg = 6 } # [hidden]
+Super-Ctrl-underscore = { action = "send-to-workspace", arg = 7 } # [hidden]
+Super-Ctrl-agrave = { action = "send-to-workspace", arg = 8 } # [hidden]
# For QWERTY users
-Super-Ctrl-1 = { action = "send-to-workspace", arg = 0 }
-Super-Ctrl-2 = { action = "send-to-workspace", arg = 1 }
-Super-Ctrl-3 = { action = "send-to-workspace", arg = 2 }
-Super-Ctrl-4 = { action = "send-to-workspace", arg = 3 }
-Super-Ctrl-5 = { action = "send-to-workspace", arg = 4 }
-Super-Ctrl-6 = { action = "send-to-workspace", arg = 5 }
-Super-Ctrl-7 = { action = "send-to-workspace", arg = 6 }
-Super-Ctrl-8 = { action = "send-to-workspace", arg = 7 }
-Super-Ctrl-9 = { action = "send-to-workspace", arg = 8 }
+Super-Ctrl-1 = { action = "send-to-workspace", arg = 0 } # [hidden]
+Super-Ctrl-2 = { action = "send-to-workspace", arg = 1 } # [hidden]
+Super-Ctrl-3 = { action = "send-to-workspace", arg = 2 } # [hidden]
+Super-Ctrl-4 = { action = "send-to-workspace", arg = 3 } # [hidden]
+Super-Ctrl-5 = { action = "send-to-workspace", arg = 4 } # [hidden]
+Super-Ctrl-6 = { action = "send-to-workspace", arg = 5 } # [hidden]
+Super-Ctrl-7 = { action = "send-to-workspace", arg = 6 } # [hidden]
+Super-Ctrl-8 = { action = "send-to-workspace", arg = 7 } # [hidden]
+Super-Ctrl-9 = { action = "send-to-workspace", arg = 8 } # [hidden]
-# Sleex shell
+#!
+##! Shell
Super-d = { action = "global-shortcut", arg = "quickshell:dashboardToggle" }
Super-Super_L = { action = "global-shortcut", arg = "quickshell:overviewToggleReleaseInterrupt" }
Super-v = { action = "global-shortcut", arg = "quickshell:overviewClipboardToggle" }
@@ -95,8 +98,7 @@ Super-l = { action = "global-shortcut", arg = "quickshell:lockScreen" }
# ---------------------------------------------------------
-# Mouse bindings
-# Refer to section 2.4: mousebinds
+## [hidden]
[mousebinds]
Super-Left = "swap-tile"
Super-Right = "resize-tile"
diff --git a/src/share/sleex/modules/cheatsheet/CheatsheetKeybinds.qml b/src/share/sleex/modules/cheatsheet/CheatsheetKeybinds.qml
index f39e27ab..97fbf480 100644
--- a/src/share/sleex/modules/cheatsheet/CheatsheetKeybinds.qml
+++ b/src/share/sleex/modules/cheatsheet/CheatsheetKeybinds.qml
@@ -9,11 +9,10 @@ import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import Quickshell.Widgets
-import Quickshell.Hyprland
Item {
id: root
- readonly property var keybinds: HyprlandKeybinds.keybinds
+ readonly property var keybinds: FhtcKeybinds.keybinds
property real spacing: 16
property real titleSpacing: 8
width: 800
diff --git a/src/share/sleex/scripts/fht/get_keybinds.py b/src/share/sleex/scripts/fht/get_keybinds.py
new file mode 100755
index 00000000..fb9c1bb8
--- /dev/null
+++ b/src/share/sleex/scripts/fht/get_keybinds.py
@@ -0,0 +1,91 @@
+#!/usr/bin/env -S _/bin/sh _-c "source $(eval echo $SLEEX_VIRTUAL_ENV)/bin/activate && exec python -E "$0" "$@""
+import argparse
+import re
+import os
+import json
+import toml
+from typing import Dict, List, Any
+
+TITLE_REGEX = r"^#+!"
+HIDE_COMMENT = "[hidden]"
+
+parser = argparse.ArgumentParser(description='Sleex TOML keybind reader')
+parser.add_argument('--path', type=str, default="/etc/sleex/compositor/keybinds.toml", help='path to config file')
+args = parser.parse_args()
+
+class Section(dict):
+ def __init__(self, name: str):
+ self["name"] = name
+ self["children"] = []
+ self["keybinds"] = []
+
+def parse_toml_config(path: str):
+ expanded_path = os.path.expanduser(os.path.expandvars(path))
+ if not os.access(expanded_path, os.R_OK):
+ return "error"
+
+ with open(expanded_path, "r") as f:
+ lines = f.readlines()
+
+ with open(expanded_path, "r") as f:
+ full_data = toml.load(f)
+
+ keybinds_data = full_data.get("keybinds", {})
+
+ root = Section("")
+ stack = [(0, root)] # (level, section_object)
+
+ for line in lines:
+ clean_line = line.strip()
+
+ if re.match(TITLE_REGEX, clean_line):
+ level = clean_line.count('#')
+ name = clean_line.replace('!', '').replace('#', '').strip()
+ new_section = Section(name)
+
+ while stack and stack[-1][0] >= level:
+ stack.pop()
+
+ stack[-1][1]["children"].append(new_section)
+ stack.append((level, new_section))
+ continue
+
+ if HIDE_COMMENT in line or not clean_line or clean_line.startswith("["):
+ continue
+
+ if "=" in clean_line:
+ key_part = clean_line.split("=")[0].strip()
+ base_key = key_part.split(".")[0]
+
+ if base_key in keybinds_data:
+ data = keybinds_data[base_key]
+ if not any(kb["key"] == base_key for kb in stack[-1][1]["keybinds"]):
+
+ comment = ""
+ if "#" in line and HIDE_COMMENT not in line:
+ comment = line.split("#")[-1].strip()
+
+ mods = base_key.split("-")
+ actual_key = mods.pop()
+
+ if isinstance(data, dict):
+ dispatcher = data.get("action", "")
+ params = str(data.get("arg", ""))
+ else:
+ dispatcher = data
+ params = ""
+
+ stack[-1][1]["keybinds"].append({
+ "mods": mods,
+ "key": actual_key,
+ "dispatcher": dispatcher,
+ "params": params,
+ "comment": comment
+ })
+ # keybinds_data.pop(base_key)
+
+ return root
+
+if __name__ == "__main__":
+ result = parse_toml_config(args.path)
+ print(json.dumps(result, indent=2))
\ No newline at end of file
diff --git a/src/share/sleex/scripts/hyprland/get_keybinds.py b/src/share/sleex/scripts/hyprland/get_keybinds.py
deleted file mode 100755
index 054b982d..00000000
--- a/src/share/sleex/scripts/hyprland/get_keybinds.py
+++ /dev/null
@@ -1,222 +0,0 @@
-#!/usr/bin/env -S\_/bin/sh\_-c\_"source\_\$(eval\_echo\_\$SLEEX_VIRTUAL_ENV)/bin/activate&&exec\_python\_-E\_"\$0"\_"\$@""
-import argparse
-import re
-import os
-from os.path import expandvars as os_expandvars
-from typing import Dict, List
-
-TITLE_REGEX = "#+!"
-HIDE_COMMENT = "[hidden]"
-MOD_SEPARATORS = ['+', ' ']
-COMMENT_BIND_PATTERN = "#/#"
-
-parser = argparse.ArgumentParser(description='Hyprland keybind reader')
-parser.add_argument('--path', type=str, default="/etc/sleex/hyprland/keybinds.conf", help='path to keybind file (sourcing isn\'t supported)')
-args = parser.parse_args()
-content_lines = []
-reading_line = 0
-
-# Little Parser made for hyprland keybindings conf file
-Variables: Dict[str, str] = {}
-
-
-class KeyBinding(dict):
- def __init__(self, mods, key, dispatcher, params, comment) -> None:
- self["mods"] = mods
- self["key"] = key
- self["dispatcher"] = dispatcher
- self["params"] = params
- self["comment"] = comment
-
-class Section(dict):
- def __init__(self, children, keybinds, name) -> None:
- self["children"] = children
- self["keybinds"] = keybinds
- self["name"] = name
-
-
-def read_content(path: str) -> str:
- if (not os.access(os.path.expanduser(os.path.expandvars(path)), os.R_OK)):
- return ("error")
- with open(os.path.expanduser(os.path.expandvars(path)), "r") as file:
- return file.read()
-
-
-def autogenerate_comment(dispatcher: str, params: str = "") -> str:
- match dispatcher:
-
- case "resizewindow":
- return "Resize window"
-
- case "movewindow":
- if(params == ""):
- return "Move window"
- else:
- return "Window: move in {} direction".format({
- "l": "left",
- "r": "right",
- "u": "up",
- "d": "down",
- }.get(params, "null"))
-
- case "pin":
- return "Window: pin (show on all workspaces)"
-
- case "splitratio":
- return "Window split ratio {}".format(params)
-
- case "togglefloating":
- return "Float/unfloat window"
-
- case "resizeactive":
- return "Resize window by {}".format(params)
-
- case "killactive":
- return "Close window"
-
- case "fullscreen":
- return "Toggle {}".format(
- {
- "0": "fullscreen",
- "1": "maximization",
- "2": "fullscreen on Hyprland's side",
- }.get(params, "null")
- )
-
- case "fakefullscreen":
- return "Toggle fake fullscreen"
-
- case "workspace":
- if params == "+1":
- return "Workspace: focus right"
- elif params == "-1":
- return "Workspace: focus left"
- return "Focus workspace {}".format(params)
-
- case "movefocus":
- return "Window: move focus {}".format(
- {
- "l": "left",
- "r": "right",
- "u": "up",
- "d": "down",
- }.get(params, "null")
- )
-
- case "swapwindow":
- return "Window: swap in {} direction".format(
- {
- "l": "left",
- "r": "right",
- "u": "up",
- "d": "down",
- }.get(params, "null")
- )
-
- case "movetoworkspace":
- if params == "+1":
- return "Window: move to right workspace (non-silent)"
- elif params == "-1":
- return "Window: move to left workspace (non-silent)"
- return "Window: move to workspace {} (non-silent)".format(params)
-
- case "movetoworkspacesilent":
- if params == "+1":
- return "Window: move to right workspace"
- elif params == "-1":
- return "Window: move to right workspace"
- return "Window: move to workspace {}".format(params)
-
- case "togglespecialworkspace":
- return "Workspace: toggle special"
-
- case "exec":
- return "Execute: {}".format(params)
-
- case _:
- return ""
-
-def get_keybind_at_line(line_number, line_start = 0):
- global content_lines
- line = content_lines[line_number]
- _, keys = line.split("=", 1)
- keys, *comment = keys.split("#", 1)
-
- mods, key, dispatcher, *params = list(map(str.strip, keys.split(",", 4)))
- params = "".join(map(str.strip, params))
-
- # Remove empty spaces
- comment = list(map(str.strip, comment))
- # Add comment if it exists, else generate it
- if comment:
- comment = comment[0]
- if comment.startswith("[hidden]"):
- return None
- else:
- comment = autogenerate_comment(dispatcher, params)
-
- if mods:
- modstring = mods + MOD_SEPARATORS[0] # Add separator at end to ensure last mod is read
- mods = []
- p = 0
- for index, char in enumerate(modstring):
- if(char in MOD_SEPARATORS):
- if(index - p > 1):
- mods.append(modstring[p:index])
- p = index+1
- else:
- mods = []
-
- return KeyBinding(mods, key, dispatcher, params, comment)
-
-def get_binds_recursive(current_content, scope):
- global content_lines
- global reading_line
- # print("get_binds_recursive({0}, {1}) [@L{2}]".format(current_content, scope, reading_line + 1))
- while reading_line < len(content_lines): # TODO: Adjust condition
- line = content_lines[reading_line]
- heading_search_result = re.search(TITLE_REGEX, line)
- # print("Read line {0}: {1}\tisHeading: {2}".format(reading_line + 1, content_lines[reading_line], "[{0}, {1}, {2}]".format(heading_search_result.start(), heading_search_result.start() == 0, ((heading_search_result != None) and (heading_search_result.start() == 0))) if heading_search_result != None else "No"))
- if ((heading_search_result != None) and (heading_search_result.start() == 0)): # Found title
- # Determine scope
- heading_scope = line.find('!')
- # Lower? Return
- if(heading_scope <= scope):
- reading_line -= 1
- return current_content
-
- section_name = line[(heading_scope+1):].strip()
- # print("[[ Found h{0} at line {1} ]] {2}".format(heading_scope, reading_line+1, content_lines[reading_line]))
- reading_line += 1
- current_content["children"].append(get_binds_recursive(Section([], [], section_name), heading_scope))
-
- elif line.startswith(COMMENT_BIND_PATTERN):
- keybind = get_keybind_at_line(reading_line, line_start=len(COMMENT_BIND_PATTERN))
- if(keybind != None):
- current_content["keybinds"].append(keybind)
-
- elif line == "" or not line.lstrip().startswith("bind"): # Comment, ignore
- pass
-
- else: # Normal keybind
- keybind = get_keybind_at_line(reading_line)
- if(keybind != None):
- current_content["keybinds"].append(keybind)
-
- reading_line += 1
-
- return current_content;
-
-def parse_keys(path: str) -> Dict[str, List[KeyBinding]]:
- global content_lines
- content_lines = read_content(path).splitlines()
- if content_lines[0] == "error":
- return "error"
- return get_binds_recursive(Section([], [], ""), 0)
-
-
-if __name__ == "__main__":
- import json
-
- ParsedKeys = parse_keys(args.path)
- print(json.dumps(ParsedKeys))
diff --git a/src/share/sleex/services/HyprlandKeybinds.qml b/src/share/sleex/services/FhtcKeybinds.qml
similarity index 77%
rename from src/share/sleex/services/HyprlandKeybinds.qml
rename to src/share/sleex/services/FhtcKeybinds.qml
index 2dace266..392b056b 100644
--- a/src/share/sleex/services/HyprlandKeybinds.qml
+++ b/src/share/sleex/services/FhtcKeybinds.qml
@@ -7,17 +7,16 @@ import QtQuick
import Quickshell
import Quickshell.Io
import Quickshell.Wayland
-import Quickshell.Hyprland
/**
- * A service that provides access to Hyprland keybinds.
+ * A service that provides access to keybinds.
* Uses the `get_keybinds.py` script to parse comments in config files in a certain format and convert to JSON.
*/
Singleton {
id: root
- property string keybindParserPath: FileUtils.trimFileProtocol(`/usr/share/sleex/scripts/hyprland/get_keybinds.py`)
- property string defaultKeybindConfigPath: FileUtils.trimFileProtocol(`/etc/sleex/hyprland/keybinds.conf`)
- property string userKeybindConfigPath: FileUtils.trimFileProtocol(`${Directories.config}/hypr/custom/keybinds.conf`)
+ property string keybindParserPath: FileUtils.trimFileProtocol(`/usr/share/sleex/scripts/fht/get_keybinds.py`)
+ property string defaultKeybindConfigPath: FileUtils.trimFileProtocol(`/etc/sleex/compositor/keybinds.toml`)
+ property string userKeybindConfigPath: FileUtils.trimFileProtocol(`${Directories.config}/fht/custom/keybinds.toml`)
property var defaultKeybinds: {"children": []}
property var userKeybinds: {"children": []}
property var keybinds: ({
@@ -27,17 +26,6 @@ Singleton {
]
})
- Connections {
- target: Hyprland
-
- function onRawEvent(event) {
- if (event.name == "configreloaded") {
- getDefaultKeybinds.running = true
- getUserKeybinds.running = true
- }
- }
- }
-
Process {
id: getDefaultKeybinds
running: true
From 4ad23533ba9fb2186ddd2c73ec13ed1b84671ebf Mon Sep 17 00:00:00 2001
From: Ardox
Date: Wed, 13 May 2026 12:11:08 +0200
Subject: [PATCH 51/56] Made cheatsheet working
---
src/etc/sleex/compositor/keybinds.toml | 30 +++++++++------------
src/share/sleex/scripts/fht/get_keybinds.py | 2 +-
2 files changed, 13 insertions(+), 19 deletions(-)
diff --git a/src/etc/sleex/compositor/keybinds.toml b/src/etc/sleex/compositor/keybinds.toml
index f2d32f62..ca356d53 100644
--- a/src/etc/sleex/compositor/keybinds.toml
+++ b/src/etc/sleex/compositor/keybinds.toml
@@ -5,19 +5,13 @@
#!
##! Basic
-Super-Shift-s = { action = "run-command", arg = 'grim -g "$(slurp -o)" - | wl-copy --type image/png' } # [hidden]
-Super-a = { action = "run-command", arg = "qs -p /usr/share/sleex ipc call overview toggle" } # [hidden]
+Super-Shift-s = { action = "run-command", arg = 'grim -g "$(slurp -o)" - | wl-copy --type image/png' } # Screenshot a selected area and copy to clipboard
#!
##! Multimedia
-XF86AudioRaiseVolume.action = "run-command"
-XF86AudioRaiseVolume.arg = "wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+" # Increase volume by 5%
-XF86AudioRaiseVolume.allow-while-locked = true
-XF86AudioLowerVolume.action = "run-command"
-XF86AudioLowerVolume.arg = "wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%-" # Decrease volume by 5%
-XF86AudioLowerVolume.allow-while-locked = true
-XF86AudioMute.action = "run-command"
-XF86AudioMute.arg = "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle" # Toggle mute
+XF86AudioRaiseVolume = { action = "run-command", arg = "wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+", allow-while-locked = true } # Increase volume by 5%
+XF86AudioLowerVolume = { action = "run-command", arg = "wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%-", allow-while-locked = true } # Decrease volume by 5%
+XF86AudioMute = { action = "run-command", arg = "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle", allow-while-locked = true } # Toggle mute
#!
##! Focus and window management
@@ -87,14 +81,14 @@ Super-Ctrl-9 = { action = "send-to-workspace", arg = 8 } # [hidden]
#!
##! Shell
-Super-d = { action = "global-shortcut", arg = "quickshell:dashboardToggle" }
-Super-Super_L = { action = "global-shortcut", arg = "quickshell:overviewToggleReleaseInterrupt" }
-Super-v = { action = "global-shortcut", arg = "quickshell:overviewClipboardToggle" }
-Super-semicolon = { action = "global-shortcut", arg = "quickshell:overviewEmojiToggle" }
-Super-f1 = { action = "global-shortcut", arg = "quickshell:cheatsheetToggle" }
-Ctrl-Alt-Delete = { action = "global-shortcut", arg = "quickshell:sessionToggle" }
-Super-t = { action = "global-shortcut", arg = "quickshell:wppselectorToggle" }
-Super-l = { action = "global-shortcut", arg = "quickshell:lockScreen" }
+Super-d = { action = "global-shortcut", arg = "quickshell:dashboardToggle" } # Toggle the dashboard
+Super-Super_L = { action = "global-shortcut", arg = "quickshell:overviewToggleReleaseInterrupt" } # Toggle the overview
+Super-v = { action = "global-shortcut", arg = "quickshell:overviewClipboardToggle" } # Toggle the clipboard overview
+Super-semicolon = { action = "global-shortcut", arg = "quickshell:overviewEmojiToggle" } # Toggle the emoji overview
+Super-f1 = { action = "global-shortcut", arg = "quickshell:cheatsheetToggle" } # Toggle the cheatsheet
+Ctrl-Alt-Delete = { action = "global-shortcut", arg = "quickshell:sessionToggle" } # Toggle the session overview
+Super-t = { action = "global-shortcut", arg = "quickshell:wppselectorToggle" } # Toggle the wpp selector
+Super-l = { action = "global-shortcut", arg = "quickshell:lockScreen" } # Lock the screen
# ---------------------------------------------------------
diff --git a/src/share/sleex/scripts/fht/get_keybinds.py b/src/share/sleex/scripts/fht/get_keybinds.py
index fb9c1bb8..fff53775 100755
--- a/src/share/sleex/scripts/fht/get_keybinds.py
+++ b/src/share/sleex/scripts/fht/get_keybinds.py
@@ -88,4 +88,4 @@ def parse_toml_config(path: str):
if __name__ == "__main__":
result = parse_toml_config(args.path)
- print(json.dumps(result, indent=2))
\ No newline at end of file
+ print(json.dumps(result))
\ No newline at end of file
From 47c734ef1bda69383a556310129000e3c1127b1f Mon Sep 17 00:00:00 2001
From: Ardox
Date: Wed, 13 May 2026 12:19:05 +0200
Subject: [PATCH 52/56] update pkgbuild for beta sleex
---
PKGBUILD | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/PKGBUILD b/PKGBUILD
index 4c87324c..3e48c48a 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -1,6 +1,6 @@
pkgname="sleex"
-pkgver="1.31"
-pkgrel="4"
+pkgver="2.0-beta.1"
+pkgrel="1"
pkgdesc="Desktop environment focused on aesthetics and performance"
arch=("x86_64")
depends=(
@@ -23,7 +23,7 @@ depends=(
# Portal
'xdg-desktop-portal'
# Python deps
- 'clang' 'uv' 'gtk4' 'libadwaita' 'libsoup3' 'libportal-gtk4' 'gobject-introspection' 'sassc' 'python-setproctitle' 'python-pywayland'
+ 'clang' 'uv' 'gtk4' 'libadwaita' 'libsoup3' 'libportal-gtk4' 'gobject-introspection' 'sassc' 'python-setproctitle' 'python-pywayland' 'python-toml'
# Screencast/Screenrecord
'ksnip' 'wf-recorder' 'slurp' 'grim' 'tesseract' 'tesseract-data-eng'
# Tools
From c6bca83fcae41aa7c7516325c29976ea490e0770 Mon Sep 17 00:00:00 2001
From: Ardox
Date: Wed, 13 May 2026 12:22:47 +0200
Subject: [PATCH 53/56] fix: correct pkgver format in PKGBUILD
---
PKGBUILD | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/PKGBUILD b/PKGBUILD
index 3e48c48a..2321549c 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -1,5 +1,5 @@
pkgname="sleex"
-pkgver="2.0-beta.1"
+pkgver="2.0_beta.1"
pkgrel="1"
pkgdesc="Desktop environment focused on aesthetics and performance"
arch=("x86_64")
From 91ecb669cec7194d731208cff823247ea21e4bf2 Mon Sep 17 00:00:00 2001
From: Ardox
Date: Wed, 13 May 2026 12:24:30 +0200
Subject: [PATCH 54/56] fix: correct keyboard focus capitalization in
WallpaperSelector.qml
---
src/share/sleex/modules/wallpaperSelector/WallpaperSelector.qml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/share/sleex/modules/wallpaperSelector/WallpaperSelector.qml b/src/share/sleex/modules/wallpaperSelector/WallpaperSelector.qml
index b5c1122c..adbf63ed 100644
--- a/src/share/sleex/modules/wallpaperSelector/WallpaperSelector.qml
+++ b/src/share/sleex/modules/wallpaperSelector/WallpaperSelector.qml
@@ -30,7 +30,7 @@ Scope {
implicitWidth: 1920
implicitHeight: 200
WlrLayershell.namespace: "quickshell:wppselector"
- WlrLayershell.keyboardFocus: WlrKeyboardFocus.onDemand
+ WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
color: "transparent"
anchors {
From c4324ef20768474ed191317edc0ee0702c53ef50 Mon Sep 17 00:00:00 2001
From: Ardox
Date: Wed, 13 May 2026 12:26:58 +0200
Subject: [PATCH 55/56] Updated readme
---
README.md | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 15851dad..7688cd0c 100644
--- a/README.md
+++ b/README.md
@@ -4,6 +4,10 @@
# [WIP] Sleex with the Fht compositor
-Not yet ready for public use. This branch is for testing the Fht compositor with Sleex (and get rid of Hyprland).
+If you want to test this very very unstable branch, you can build and install the package:
+
+```bash
+makepkg -si
+```
[Fht compositor](https://nferhat.github.io/fht-compositor/) is a new Wayland compositor written in Rust. It is designed to be fast, lightweight, and easy to use. It is also highly customizable, allowing users to create their own unique desktop experience.
\ No newline at end of file
From 73368ca736b7ed98039ec15a45140ecc79db16ef Mon Sep 17 00:00:00 2001
From: Ardox
Date: Wed, 13 May 2026 12:36:42 +0200
Subject: [PATCH 56/56] fix: remove unnecessary installation of libalpm hooks
in package function
---
PKGBUILD | 3 ---
1 file changed, 3 deletions(-)
diff --git a/PKGBUILD b/PKGBUILD
index 2321549c..c1efda87 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -75,7 +75,4 @@ package() {
mkdir -p "$pkgdir/usr/share/wayland-sessions"
cp -r "$srcdir/share/wayland-sessions/"* "$pkgdir/usr/share/wayland-sessions/"
-
- mkdir -p "$pkgdir/usr/libalpm/hooks"
- cp -r "$srcdir/share/libalpm/hooks/"* "$pkgdir/usr/libalpm/hooks/"
}