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 @@ AxOS

-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). -| | -|------| -| ![preview](preview.mp4) | - -## 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/" }