mirror of
https://github.com/quickshell-mirror/quickshell.git
synced 2026-10-28 02:55:05 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f508be500 | ||
|
|
11ca60be22 | ||
|
|
8fd0e25844 | ||
|
|
5d5d49873f | ||
|
|
41651d7dcd | ||
|
|
4594680eb4 | ||
|
|
25da30db02 | ||
|
|
fae96f1a5b | ||
|
|
c6a516096d | ||
|
|
86b4275879 | ||
|
|
9b4b206961 | ||
|
|
92cb8909eb | ||
|
|
2d3b3e9c70 | ||
|
|
ec0be5b36f | ||
|
|
fd116897cf | ||
|
|
916a0dd90c | ||
|
|
0f9939ca4f | ||
|
|
052059fb7c | ||
|
|
39e14d3627 | ||
|
|
9f59d0a05d | ||
|
|
afb2c27cd6 | ||
|
|
0fed22a2c4 |
No files matched your search
@@ -0,0 +1,31 @@
|
||||
<!--
|
||||
Read https://github.com/quickshell-mirror/quickshell/blob/master/CONTRIBUTING.md before opening a PR.
|
||||
|
||||
Failing to do so may result in a ban from opening PRs.
|
||||
-->
|
||||
|
||||
### Describe your changes
|
||||
<!-- High level overview of what you are changing and why. AI may not be used to write this. -->
|
||||
|
||||
### Details and descisions
|
||||
<!--
|
||||
Put details of why you took a given approach to problems here if its not obvious.
|
||||
Reviewing PRs is easier if we don't have to reverse engineer your reasoning.
|
||||
|
||||
Consider if things you put here should be code comments instead.
|
||||
|
||||
If an AI was heavily used to make the change and its context might be useful,
|
||||
put it in a spoiler under your own reasoning.
|
||||
-->
|
||||
|
||||
### Open concerns
|
||||
<!-- Anything you're not sure of or want help with? -->
|
||||
|
||||
### Participation
|
||||
<!--
|
||||
With the advent of AI, we get a lot more drive-bys that don't care about participating
|
||||
in a traditional review process, and just want to get a change in.
|
||||
|
||||
If you care about your code or learning how the project works to contribute in the future,
|
||||
please say so. If not, we'll assume you don't particularly care.
|
||||
-->
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
project(quickshell VERSION "0.3.1" LANGUAGES CXX C)
|
||||
project(quickshell VERSION "0.3.2" LANGUAGES CXX C)
|
||||
|
||||
set(UNRELEASED_FEATURES)
|
||||
|
||||
|
||||
@@ -9,6 +9,9 @@ To ensure nobody's time is wasted, please follow the rules below.
|
||||
your change works, do not submit it to be merged. You must be able to explain your reasoning
|
||||
for every change.
|
||||
|
||||
- Change descriptions MUST NOT be written by AI. Summarize what you changed in your own words.
|
||||
If you think an AI summary of a chat session etc may provide useful context, include it in a spoiler.
|
||||
|
||||
- Changes MUST be submitted by a human who will be responsible for them. Changes submitted without
|
||||
a human in the loop such as automated tooling and AI Agents are **strictly disallowed**. Accounts
|
||||
responsible for such contribution attempts **will be banned**.
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
## Other Changes
|
||||
|
||||
- Added support for Qt 6.12
|
||||
- Added support for Hyprland's new workspace address format.
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- Fixed main process crashes on pam subprocess misbehavior.
|
||||
- Fixed crashes when attempting to create or modify session locks reentrantly.
|
||||
- Fixed networking state breaking after restarting NetworkManager.
|
||||
- Fixed accessibility information not being provided for Quickshell windows.
|
||||
- Fixed `WlSessionLockSurface.screen` being null and never updating with a real screen on monitor plug.
|
||||
- Fixed `NotificationAction.text` not accepting text updates.
|
||||
- Fixed PipeWire route volume writes for devices without a reported volume step.
|
||||
- Fixed a crash when quitting or reloading while a `Loader` was still loading asynchronously.
|
||||
- Fixed future `Socket` connection attempts after a failed connection attempt.
|
||||
- Fixed Open wifi networks showing as Unknown security.
|
||||
+1
-1
@@ -56,7 +56,7 @@
|
||||
|
||||
unwrapped = stdenv.mkDerivation {
|
||||
pname = "quickshell${lib.optionalString debug "-debug"}";
|
||||
version = "0.3.1";
|
||||
version = "0.3.2";
|
||||
src = nix-gitignore.gitignoreSource "/default.nix\n" ./.;
|
||||
|
||||
dontWrapQtApps = true; # see wrappers
|
||||
|
||||
@@ -29,6 +29,10 @@ if (X11)
|
||||
add_subdirectory(x11)
|
||||
endif()
|
||||
|
||||
if (I3)
|
||||
add_subdirectory(x11/i3)
|
||||
endif()
|
||||
|
||||
add_subdirectory(services)
|
||||
|
||||
if (BLUETOOTH)
|
||||
|
||||
+32
-19
@@ -11,6 +11,7 @@
|
||||
#include <qlist.h>
|
||||
#include <qlogging.h>
|
||||
#include <qloggingcategory.h>
|
||||
#include <qnamespace.h>
|
||||
#include <qobject.h>
|
||||
#include <qqmlcontext.h>
|
||||
#include <qqmlengine.h>
|
||||
@@ -62,6 +63,10 @@ EngineGeneration::EngineGeneration(const QDir& rootPath, QmlScanner scanner)
|
||||
EngineGeneration::EngineGeneration(): EngineGeneration(QDir(), QmlScanner()) {}
|
||||
|
||||
EngineGeneration::~EngineGeneration() {
|
||||
for (auto* extension: this->extensions.values()) {
|
||||
delete extension;
|
||||
}
|
||||
|
||||
if (this->engine != nullptr) {
|
||||
qFatal() << this << "destroyed without calling destroy()";
|
||||
}
|
||||
@@ -78,30 +83,37 @@ void EngineGeneration::destroy() {
|
||||
this->watcher = nullptr;
|
||||
}
|
||||
|
||||
for (auto* extension: this->extensions.values()) {
|
||||
delete extension;
|
||||
}
|
||||
|
||||
if (this->root != nullptr) {
|
||||
// prevent further js execution during teardown of the root's children, gc and the engine.
|
||||
QObject::connect(this->root, &QObject::destroyed, this, [this]() {
|
||||
// prevent further js execution between garbage collection and engine destruction.
|
||||
this->engine->setInterrupted(true);
|
||||
|
||||
g_generations.remove(this->engine);
|
||||
|
||||
// Garbage is not collected during engine destruction.
|
||||
this->engine->collectGarbage();
|
||||
|
||||
delete this->engine;
|
||||
this->engine = nullptr;
|
||||
|
||||
auto terminate = this->shouldTerminate;
|
||||
auto code = this->exitCode;
|
||||
delete this;
|
||||
|
||||
if (terminate) QCoreApplication::exit(code);
|
||||
});
|
||||
|
||||
// QObject::destroyed is emitted before the root's children are destroyed.
|
||||
// The engine must outlive them, as objects such as Loaders with in-flight
|
||||
// incubations access it in their destructors.
|
||||
QObject::connect(
|
||||
this->root,
|
||||
&QObject::destroyed,
|
||||
this,
|
||||
[this]() {
|
||||
g_generations.remove(this->engine);
|
||||
|
||||
// Garbage is not collected during engine destruction.
|
||||
this->engine->collectGarbage();
|
||||
|
||||
delete this->engine;
|
||||
this->engine = nullptr;
|
||||
|
||||
auto terminate = this->shouldTerminate;
|
||||
auto code = this->exitCode;
|
||||
delete this;
|
||||
|
||||
if (terminate) QCoreApplication::exit(code);
|
||||
},
|
||||
Qt::QueuedConnection
|
||||
);
|
||||
|
||||
this->root->deleteLater();
|
||||
this->root = nullptr;
|
||||
} else {
|
||||
@@ -198,6 +210,7 @@ void EngineGeneration::setWatchingFiles(bool watching) {
|
||||
}
|
||||
} else {
|
||||
if (this->watcher != nullptr) {
|
||||
QObject::disconnect(this->watcher, nullptr, this, nullptr);
|
||||
this->watcher->deleteLater();
|
||||
this->watcher = nullptr;
|
||||
}
|
||||
|
||||
+17
-19
@@ -335,28 +335,33 @@ void LogManager::init(
|
||||
|
||||
qInstallMessageHandler(&LogManager::messageHandler);
|
||||
|
||||
qCDebug(logLogging) << "Creating offthread logger...";
|
||||
auto* thread = new QThread();
|
||||
instance->threadProxy.moveToThread(thread);
|
||||
thread->start();
|
||||
|
||||
QMetaObject::invokeMethod(
|
||||
&instance->threadProxy,
|
||||
&LoggingThreadProxy::initInThread,
|
||||
Qt::BlockingQueuedConnection
|
||||
);
|
||||
instance->threadLogging = new ThreadLogging(instance);
|
||||
instance->threadLogging->init();
|
||||
|
||||
qCDebug(logLogging) << "Logger initialized.";
|
||||
}
|
||||
|
||||
void LogManager::initThreadLogging() {
|
||||
auto* instance = LogManager::instance();
|
||||
|
||||
qCDebug(logLogging) << "Moving logger to dedicated thread...";
|
||||
|
||||
auto* thread = new QThread();
|
||||
instance->threadLogging->setParent(nullptr);
|
||||
instance->threadLogging->moveToThread(thread);
|
||||
thread->start();
|
||||
|
||||
qCDebug(logLogging) << "Logger thread initialized.";
|
||||
}
|
||||
|
||||
void initLogCategoryLevel(const char* name, QtMsgType defaultLevel) {
|
||||
LogManager::instance()->defaultLevels.insert(QLatin1StringView(name), defaultLevel);
|
||||
}
|
||||
|
||||
void LogManager::initFs() {
|
||||
QMetaObject::invokeMethod(
|
||||
&LogManager::instance()->threadProxy,
|
||||
"initFs",
|
||||
LogManager::instance()->threadLogging,
|
||||
&ThreadLogging::initFs,
|
||||
Qt::BlockingQueuedConnection
|
||||
);
|
||||
}
|
||||
@@ -369,13 +374,6 @@ CategoryFilter LogManager::getFilter(QLatin1StringView category) {
|
||||
return this->allFilters.value(category);
|
||||
}
|
||||
|
||||
void LoggingThreadProxy::initInThread() {
|
||||
this->logging = new ThreadLogging(this);
|
||||
this->logging->init();
|
||||
}
|
||||
|
||||
void LoggingThreadProxy::initFs() { this->logging->initFs(); }
|
||||
|
||||
void ThreadLogging::init() {
|
||||
auto logMfd = memfd_create("quickshell:logs", 0);
|
||||
|
||||
|
||||
+2
-15
@@ -55,20 +55,6 @@ size_t qHash(const LogMessage& message);
|
||||
|
||||
class ThreadLogging;
|
||||
|
||||
class LoggingThreadProxy: public QObject {
|
||||
Q_OBJECT;
|
||||
|
||||
public:
|
||||
explicit LoggingThreadProxy() = default;
|
||||
|
||||
public slots:
|
||||
void initInThread();
|
||||
void initFs();
|
||||
|
||||
private:
|
||||
ThreadLogging* logging = nullptr;
|
||||
};
|
||||
|
||||
namespace qt_logging_registry {
|
||||
class QLoggingRule;
|
||||
}
|
||||
@@ -104,6 +90,7 @@ public:
|
||||
const QString& prefix = ""
|
||||
);
|
||||
|
||||
static void initThreadLogging();
|
||||
static void initFs();
|
||||
static LogManager* instance();
|
||||
|
||||
@@ -137,7 +124,7 @@ private:
|
||||
|
||||
QTextStream stdoutStream;
|
||||
QMutex stdoutMutex;
|
||||
LoggingThreadProxy threadProxy;
|
||||
ThreadLogging* threadLogging = nullptr;
|
||||
|
||||
friend void initLogCategoryLevel(const char* name, QtMsgType defaultLevel);
|
||||
};
|
||||
|
||||
@@ -114,9 +114,11 @@ public:
|
||||
explicit ThreadLogging(QObject* parent): QObject(parent) {}
|
||||
|
||||
void init();
|
||||
void initFs();
|
||||
void setupFileLogging();
|
||||
|
||||
public slots:
|
||||
void initFs();
|
||||
|
||||
private slots:
|
||||
void onMessage(const LogMessage& msg, bool showInSparse);
|
||||
|
||||
|
||||
+14
-6
@@ -88,6 +88,8 @@ QuickshellTracked::QuickshellTracked() {
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Some callers can potentially pass a freed QScreen here which is why we don't init a new one.
|
||||
// The QuickshellTracked::init() in launch.cpp makes sure our screenAdded handler runs first for now.
|
||||
QuickshellScreenInfo* QuickshellTracked::screenInfo(QScreen* screen) const {
|
||||
for (auto* info: this->screens) {
|
||||
if (info->screen == screen) return info;
|
||||
@@ -96,13 +98,19 @@ QuickshellScreenInfo* QuickshellTracked::screenInfo(QScreen* screen) const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
namespace {
|
||||
static QuickshellTracked* qsTrackedInstance = nullptr; // NOLINT
|
||||
}
|
||||
|
||||
void QuickshellTracked::init() {
|
||||
if (qsTrackedInstance) qFatal() << "Tried to reinitialize QuickshellTracked";
|
||||
qsTrackedInstance = new QuickshellTracked();
|
||||
QJSEngine::setObjectOwnership(qsTrackedInstance, QJSEngine::CppOwnership);
|
||||
}
|
||||
|
||||
QuickshellTracked* QuickshellTracked::instance() {
|
||||
static QuickshellTracked* instance = nullptr; // NOLINT
|
||||
if (instance == nullptr) {
|
||||
QJSEngine::setObjectOwnership(instance, QJSEngine::CppOwnership);
|
||||
instance = new QuickshellTracked();
|
||||
}
|
||||
return instance;
|
||||
if (!qsTrackedInstance) qFatal() << "Tried to get QuickshellTracked instance before init";
|
||||
return qsTrackedInstance;
|
||||
}
|
||||
|
||||
void QuickshellTracked::updateScreens() {
|
||||
|
||||
@@ -70,6 +70,7 @@ public:
|
||||
QVector<QuickshellScreenInfo*> screens;
|
||||
QuickshellScreenInfo* screenInfo(QScreen* screen) const;
|
||||
|
||||
static void init();
|
||||
static QuickshellTracked* instance();
|
||||
|
||||
private slots:
|
||||
|
||||
@@ -74,6 +74,7 @@ void QsMenuAnchor::onClosed() {
|
||||
this->mOpen = false;
|
||||
|
||||
if (this->platformMenu) {
|
||||
QObject::disconnect(this->platformMenu, nullptr, this, nullptr);
|
||||
this->platformMenu->deleteLater();
|
||||
this->platformMenu = nullptr;
|
||||
}
|
||||
@@ -101,7 +102,12 @@ void QsMenuAnchor::setMenu(QsMenuHandle* menu) {
|
||||
if (menu == this->mMenu) return;
|
||||
|
||||
if (this->mMenu != nullptr) {
|
||||
if (this->platformMenu != nullptr) this->platformMenu->deleteLater();
|
||||
if (this->platformMenu != nullptr) {
|
||||
QObject::disconnect(this->platformMenu, nullptr, this, nullptr);
|
||||
this->platformMenu->deleteLater();
|
||||
this->platformMenu = nullptr;
|
||||
}
|
||||
|
||||
QObject::disconnect(this->mMenu, nullptr, this, nullptr);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,14 @@ bool qjsValueStructuralEq(
|
||||
const RecursionPair* activePair = nullptr
|
||||
) {
|
||||
if (a.strictlyEquals(b)) return true;
|
||||
if (!a.isObject() || !b.isObject() || a.isArray() != b.isArray()) return false;
|
||||
|
||||
const auto jsValueType = QMetaType::fromType<QJSValue>();
|
||||
const auto aIsNative = a.toVariant(QJSValue::RetainJSObjects).metaType() != jsValueType;
|
||||
const auto bIsNative = b.toVariant(QJSValue::RetainJSObjects).metaType() != jsValueType;
|
||||
if (aIsNative || bIsNative) return false;
|
||||
|
||||
if (!a.isObject() || !b.isObject() || a.isCallable() || b.isCallable()) return false;
|
||||
if (a.isArray() != b.isArray()) return false;
|
||||
|
||||
for (const auto* pair = activePair; pair != nullptr; pair = pair->previous) {
|
||||
if (a.strictlyEquals(*pair->a) || b.strictlyEquals(*pair->b)) return false;
|
||||
|
||||
@@ -272,6 +272,7 @@ void Process::onStarted() {
|
||||
}
|
||||
|
||||
void Process::onFinished(qint32 exitCode, QProcess::ExitStatus exitStatus) {
|
||||
QObject::disconnect(this->process, nullptr, this, nullptr);
|
||||
this->process->deleteLater();
|
||||
this->process = nullptr;
|
||||
if (this->mStdoutParser) this->mStdoutParser->streamEnded(this->stdoutBuffer);
|
||||
@@ -290,6 +291,7 @@ void Process::onErrorOccurred(QProcess::ProcessError error) {
|
||||
if (error == QProcess::FailedToStart) { // other cases should be covered by other events
|
||||
qWarning() << "Process failed to start, likely because the binary could not be found. Command:"
|
||||
<< this->mCommand;
|
||||
QObject::disconnect(this->process, nullptr, this, nullptr);
|
||||
this->process->deleteLater();
|
||||
this->process = nullptr;
|
||||
emit this->runningChanged();
|
||||
|
||||
+14
-1
@@ -17,7 +17,11 @@
|
||||
QS_LOGGING_CATEGORY(logSocket, "quickshell.io.socket", QtWarningMsg);
|
||||
|
||||
void Socket::setSocket(QLocalSocket* socket) {
|
||||
if (this->socket != nullptr) this->socket->deleteLater();
|
||||
if (this->socket != nullptr) {
|
||||
QObject::disconnect(this->socket, nullptr, this, nullptr);
|
||||
this->socket->deleteLater();
|
||||
}
|
||||
|
||||
this->socket = socket;
|
||||
|
||||
if (socket != nullptr) {
|
||||
@@ -57,6 +61,7 @@ void Socket::onSocketDisconnected() {
|
||||
qCDebug(logSocket) << "Socket disconnected:" << this;
|
||||
this->connected = false;
|
||||
this->disconnecting = false;
|
||||
QObject::disconnect(this->socket, nullptr, this, nullptr);
|
||||
this->socket->deleteLater();
|
||||
this->socket = nullptr;
|
||||
this->buffer.clear();
|
||||
@@ -68,6 +73,13 @@ void Socket::onSocketDisconnected() {
|
||||
void Socket::onSocketError(QLocalSocket::LocalSocketError error) {
|
||||
qCWarning(logSocket) << "Socket error for" << this << error;
|
||||
emit this->error(error);
|
||||
|
||||
if (!this->connected) {
|
||||
QObject::disconnect(this->socket, nullptr, this, nullptr);
|
||||
this->socket->deleteLater();
|
||||
this->socket = nullptr;
|
||||
this->disconnecting = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Socket::isConnected() const { return this->connected; }
|
||||
@@ -191,6 +203,7 @@ void SocketServer::disableServer() {
|
||||
}
|
||||
|
||||
this->mSockets.clear();
|
||||
QObject::disconnect(this->server, nullptr, this, nullptr);
|
||||
this->server->close();
|
||||
this->server->deleteLater();
|
||||
this->server = nullptr;
|
||||
|
||||
@@ -62,6 +62,7 @@ IpcServerConnection::IpcServerConnection(QLocalSocket* socket, IpcServer* server
|
||||
|
||||
void IpcServerConnection::onDisconnected() {
|
||||
qCInfo(logIpc) << "IPC connection disconnected" << this;
|
||||
QObject::disconnect(this->socket, nullptr, this, nullptr);
|
||||
this->deleteLater();
|
||||
}
|
||||
|
||||
|
||||
+16
-10
@@ -2,6 +2,7 @@
|
||||
#include <array>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
#include <qconfig.h>
|
||||
@@ -465,7 +466,7 @@ int ipcCommand(CommandState& cmd) {
|
||||
});
|
||||
}
|
||||
|
||||
int launchFromCommand(CommandState& cmd, QCoreApplication* coreApplication) {
|
||||
int launchFromCommand(CommandState& cmd) {
|
||||
QString configPath;
|
||||
|
||||
auto r = locateConfigFile(cmd, configPath);
|
||||
@@ -485,14 +486,13 @@ int launchFromCommand(CommandState& cmd, QCoreApplication* coreApplication) {
|
||||
.debugPort = cmd.debug.port,
|
||||
.waitForDebug = cmd.debug.wait,
|
||||
},
|
||||
cmd.exec.argv,
|
||||
coreApplication
|
||||
cmd.exec.argv
|
||||
);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int runCommand(int argc, char** argv, QCoreApplication* coreApplication) {
|
||||
int runCommand(int argc, char** argv) {
|
||||
auto state = CommandState();
|
||||
if (auto ret = parseCommand(argc, argv, state); ret != 65535) return ret;
|
||||
|
||||
@@ -555,20 +555,24 @@ int runCommand(int argc, char** argv, QCoreApplication* coreApplication) {
|
||||
);
|
||||
}
|
||||
|
||||
std::function<int(CommandState&)> continuation;
|
||||
|
||||
if (state.misc.printVersion) {
|
||||
if (state.log.verbosity == 0) {
|
||||
qCInfo(logBare).noquote() << "Quickshell" << qs::debuginfo::qsVersion();
|
||||
} else {
|
||||
qCInfo(logBare).noquote() << qs::debuginfo::combinedInfo();
|
||||
}
|
||||
|
||||
return 0;
|
||||
} else if (*state.subcommand.log) {
|
||||
return readLogFile(state);
|
||||
continuation = readLogFile;
|
||||
} else if (*state.subcommand.list) {
|
||||
return listInstances(state);
|
||||
continuation = listInstances;
|
||||
} else if (*state.subcommand.kill) {
|
||||
return killInstances(state);
|
||||
continuation = killInstances;
|
||||
} else if (*state.subcommand.msg || *state.ipc.ipc) {
|
||||
return ipcCommand(state);
|
||||
continuation = ipcCommand;
|
||||
} else {
|
||||
if (strcmp(qVersion(), QT_VERSION_STR) != 0) {
|
||||
qWarning() << "\033[31mQuickshell was built against Qt" << QT_VERSION_STR
|
||||
@@ -577,10 +581,12 @@ int runCommand(int argc, char** argv, QCoreApplication* coreApplication) {
|
||||
"the quickshell package must be rebuilt.\n";
|
||||
}
|
||||
|
||||
return launchFromCommand(state, coreApplication);
|
||||
return launchFromCommand(state);
|
||||
}
|
||||
|
||||
return 0;
|
||||
int qArgc = 1;
|
||||
auto coreApp = QCoreApplication(qArgc, argv);
|
||||
return continuation(state);
|
||||
}
|
||||
|
||||
QString getDisplayConnection() {
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "../core/logging.hpp"
|
||||
#include "../core/paths.hpp"
|
||||
#include "../core/plugin.hpp"
|
||||
#include "../core/qmlglobal.hpp"
|
||||
#include "../core/rootwrapper.hpp"
|
||||
#include "../ipc/ipc.hpp"
|
||||
#include "build.hpp"
|
||||
@@ -57,7 +58,7 @@ QString base36Encode(T number) {
|
||||
|
||||
} // namespace
|
||||
|
||||
int launch(const LaunchArgs& args, char** argv, QCoreApplication* coreApplication) {
|
||||
int launch(const LaunchArgs& args, char** argv) {
|
||||
auto pathId = QCryptographicHash::hash(args.configPath.toUtf8(), QCryptographicHash::Md5).toHex();
|
||||
auto shellId = QString(pathId);
|
||||
|
||||
@@ -171,7 +172,6 @@ int launch(const LaunchArgs& args, char** argv, QCoreApplication* coreApplicatio
|
||||
QsPaths::init(shellId, pathId, pragmas.dataDir, pragmas.stateDir, pragmas.cacheDir);
|
||||
QsPaths::instance()->linkRunDir();
|
||||
QsPaths::instance()->linkPathDir();
|
||||
LogManager::initFs();
|
||||
|
||||
Common::INITIAL_ENVIRONMENT = QProcessEnvironment::systemEnvironment();
|
||||
|
||||
@@ -279,8 +279,6 @@ int launch(const LaunchArgs& args, char** argv, QCoreApplication* coreApplicatio
|
||||
|
||||
QGuiApplication::setDesktopSettingsAware(pragmas.desktopSettingsAware);
|
||||
|
||||
delete coreApplication;
|
||||
|
||||
QGuiApplication* app = nullptr;
|
||||
auto qArgC = 0;
|
||||
|
||||
@@ -290,6 +288,9 @@ int launch(const LaunchArgs& args, char** argv, QCoreApplication* coreApplicatio
|
||||
app = new QGuiApplication(qArgC, argv);
|
||||
}
|
||||
|
||||
LogManager::initThreadLogging();
|
||||
LogManager::initFs();
|
||||
|
||||
QGuiApplication::setDesktopFileName(appId);
|
||||
|
||||
if (args.debugPort != -1) {
|
||||
@@ -299,6 +300,10 @@ int launch(const LaunchArgs& args, char** argv, QCoreApplication* coreApplicatio
|
||||
QQmlDebuggingEnabler::startTcpDebugServer(args.debugPort, wait);
|
||||
}
|
||||
|
||||
// This needs to run early to get the first connection to QGuiApplication::screenAdded() in Qs.
|
||||
// If we don't do that, attempts to get a QuickshellScreenInfo from a QScreen will fail in screenAdded bindings.
|
||||
QuickshellTracked::init();
|
||||
|
||||
QsEnginePlugin::initPlugins();
|
||||
|
||||
// Base window transparency appears to be additive.
|
||||
|
||||
@@ -107,10 +107,10 @@ struct LaunchArgs {
|
||||
void exitDaemon(int code);
|
||||
|
||||
int parseCommand(int argc, char** argv, CommandState& state);
|
||||
int runCommand(int argc, char** argv, QCoreApplication* coreApplication);
|
||||
int runCommand(int argc, char** argv);
|
||||
|
||||
QString getDisplayConnection();
|
||||
|
||||
int launch(const LaunchArgs& args, char** argv, QCoreApplication* coreApplication);
|
||||
int launch(const LaunchArgs& args, char** argv);
|
||||
|
||||
} // namespace qs::launch
|
||||
+4
-7
@@ -24,7 +24,7 @@ namespace qs::launch {
|
||||
|
||||
namespace {
|
||||
|
||||
void checkCrashRelaunch(char** argv, QCoreApplication* coreApplication) {
|
||||
void checkCrashRelaunch(char** argv) {
|
||||
#if CRASH_HANDLER
|
||||
auto lastInfoFdStr = qEnvironmentVariable("__QUICKSHELL_CRASH_INFO_FD");
|
||||
auto dumpPid = qEnvironmentVariable("__QUICKSHELL_CRASH_DUMP_PID").toInt();
|
||||
@@ -71,7 +71,7 @@ void checkCrashRelaunch(char** argv, QCoreApplication* coreApplication) {
|
||||
} else {
|
||||
qCritical() << "Quickshell has been restarted.";
|
||||
|
||||
launch({.configPath = info.instance.configPath}, argv, coreApplication);
|
||||
launch({.configPath = info.instance.configPath}, argv);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -123,11 +123,8 @@ int main(int argc, char** argv) {
|
||||
qsCheckCrash(argc, argv);
|
||||
#endif
|
||||
|
||||
auto qArgC = 1;
|
||||
auto* coreApplication = new QCoreApplication(qArgC, argv);
|
||||
|
||||
checkCrashRelaunch(argv, coreApplication);
|
||||
auto code = runCommand(argc, argv, coreApplication);
|
||||
checkCrashRelaunch(argv);
|
||||
auto code = runCommand(argc, argv);
|
||||
|
||||
exitDaemon(code);
|
||||
return code;
|
||||
|
||||
+40
-12
@@ -6,6 +6,7 @@
|
||||
#include <qdbusmetatype.h>
|
||||
#include <qdbuspendingcall.h>
|
||||
#include <qdbuspendingreply.h>
|
||||
#include <qdbusservicewatcher.h>
|
||||
#include <qlist.h>
|
||||
#include <qlogging.h>
|
||||
#include <qloggingcategory.h>
|
||||
@@ -42,6 +43,17 @@ NetworkManager::NetworkManager(QObject* parent): NetworkBackend(parent) {
|
||||
return;
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
QObject::connect(&this->serviceWatcher, &QDBusServiceWatcher::serviceRegistered, this, &NetworkManager::onServiceRegistered);
|
||||
QObject::connect(&this->serviceWatcher, &QDBusServiceWatcher::serviceUnregistered, this, &NetworkManager::onServiceUnregistered);
|
||||
// clang-format on
|
||||
|
||||
this->serviceWatcher.setWatchMode(
|
||||
QDBusServiceWatcher::WatchForRegistration | QDBusServiceWatcher::WatchForUnregistration
|
||||
);
|
||||
this->serviceWatcher.addWatchedService("org.freedesktop.NetworkManager");
|
||||
this->serviceWatcher.setConnection(bus);
|
||||
|
||||
this->proxy = new DBusNetworkManagerProxy(
|
||||
"org.freedesktop.NetworkManager",
|
||||
"/org/freedesktop/NetworkManager",
|
||||
@@ -49,25 +61,20 @@ NetworkManager::NetworkManager(QObject* parent): NetworkBackend(parent) {
|
||||
this
|
||||
);
|
||||
|
||||
if (!this->proxy->isValid()) {
|
||||
qCDebug(
|
||||
logNetworkManager
|
||||
) << "NetworkManager is not currently running. This network backend will not work";
|
||||
} else {
|
||||
this->init();
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkManager::init() {
|
||||
// clang-format off
|
||||
QObject::connect(this->proxy, &DBusNetworkManagerProxy::DeviceAdded, this, &NetworkManager::onDevicePathAdded);
|
||||
QObject::connect(this->proxy, &DBusNetworkManagerProxy::DeviceRemoved, this, &NetworkManager::onDevicePathRemoved);
|
||||
// clang-format on
|
||||
|
||||
this->dbusProperties.setInterface(this->proxy);
|
||||
this->dbusProperties.updateAllViaGetAll();
|
||||
|
||||
this->registerDevices();
|
||||
if (!this->proxy->isValid()) {
|
||||
qCDebug(
|
||||
logNetworkManager
|
||||
) << "NetworkManager is not currently running. This network backend will not work";
|
||||
} else {
|
||||
this->onServiceRegistered();
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkManager::checkConnectivity() {
|
||||
@@ -230,6 +237,27 @@ void NetworkManager::setWifiEnabled(bool enabled) {
|
||||
this->pWifiEnabled.write();
|
||||
}
|
||||
|
||||
void NetworkManager::onServiceRegistered() {
|
||||
qCDebug(logNetworkManager) << "NetworkManager service registered";
|
||||
this->dbusProperties.updateAllViaGetAll();
|
||||
this->registerDevices();
|
||||
}
|
||||
|
||||
void NetworkManager::onServiceUnregistered() {
|
||||
qCDebug(logNetworkManager) << "NetworkManager service unregistered";
|
||||
|
||||
// Clear all remaining devices
|
||||
const auto devices = this->mDevices;
|
||||
this->mDevices.clear();
|
||||
|
||||
for (auto* device: devices) {
|
||||
if (!device) continue;
|
||||
qCDebug(logNetworkManager) << "Device removed:" << device->path();
|
||||
emit this->deviceRemoved(device->frontend());
|
||||
delete device;
|
||||
}
|
||||
}
|
||||
|
||||
bool NetworkManager::isAvailable() const { return this->proxy && this->proxy->isValid(); };
|
||||
|
||||
} // namespace qs::network
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <qdbusextratypes.h>
|
||||
#include <qdbusservicewatcher.h>
|
||||
#include <qhash.h>
|
||||
#include <qobject.h>
|
||||
#include <qproperty.h>
|
||||
@@ -59,6 +60,8 @@ public slots:
|
||||
private slots:
|
||||
void onDevicePathAdded(const QDBusObjectPath& path);
|
||||
void onDevicePathRemoved(const QDBusObjectPath& path);
|
||||
void onServiceRegistered();
|
||||
void onServiceUnregistered();
|
||||
void activateConnection(const QDBusObjectPath& connPath, const QDBusObjectPath& devPath);
|
||||
void addAndActivateConnection(
|
||||
const NMSettingsMap& settings,
|
||||
@@ -67,7 +70,6 @@ private slots:
|
||||
);
|
||||
|
||||
private:
|
||||
void init();
|
||||
void registerDevices();
|
||||
void registerDevice(const QString& path);
|
||||
|
||||
@@ -88,6 +90,7 @@ private:
|
||||
QS_DBUS_PROPERTY_BINDING(NetworkManager, pConnectivityCheckEnabled, bConnectivityCheckEnabled, dbusProperties, "ConnectivityCheckEnabled");
|
||||
// clang-format on
|
||||
DBusNetworkManagerProxy* proxy = nullptr;
|
||||
QDBusServiceWatcher serviceWatcher;
|
||||
};
|
||||
|
||||
} // namespace qs::network
|
||||
@@ -186,10 +186,10 @@ NMWirelessNetwork::NMWirelessNetwork(const QString& ssid, NetworkDevice* device,
|
||||
, bSecurity(WifiSecurityType::Unknown) {
|
||||
|
||||
auto updateSecurity = [this]() {
|
||||
if (NMSettings* settings = this->bReferenceSettings) {
|
||||
this->bSecurity.setBinding([settings]() { return securityFromSettingsMap(settings->map()); });
|
||||
} else if (NMAccessPoint* ap = this->bReferenceAp) {
|
||||
if (NMAccessPoint* ap = this->bReferenceAp) {
|
||||
this->bSecurity.setBinding([ap]() { return ap->security(); });
|
||||
} else if (NMSettings* settings = this->bReferenceSettings) {
|
||||
this->bSecurity.setBinding([settings]() { return securityFromSettingsMap(settings->map()); });
|
||||
} else {
|
||||
this->bSecurity = WifiSecurityType::Unknown;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace qs::network {
|
||||
|
||||
WifiSecurityType::Enum securityFromSettingsMap(const NMSettingsMap& settings) {
|
||||
const QString mapName = "802-11-wireless-security";
|
||||
if (!settings.contains(mapName)) return WifiSecurityType::Unknown;
|
||||
if (!settings.contains(mapName)) return WifiSecurityType::Open;
|
||||
const QVariantMap& security = settings.value(mapName);
|
||||
if (security.isEmpty()) return WifiSecurityType::Open;
|
||||
|
||||
|
||||
@@ -124,7 +124,8 @@ void NMWirelessDevice::onSettingsLoaded(NMSettings* settings) {
|
||||
const auto ssid = map["802-11-wireless"]["ssid"].toString();
|
||||
const auto mode = map["802-11-wireless"]["mode"].toString();
|
||||
|
||||
if (mode == "infrastructure") {
|
||||
// Omitted mode is assumed to be "infrastructure".
|
||||
if (mode.isEmpty() || mode == "infrastructure") {
|
||||
auto* net = this->mNetworks.value(ssid);
|
||||
if (!net) net = this->registerNetwork(ssid);
|
||||
net->addSettings(settings);
|
||||
|
||||
@@ -65,8 +65,10 @@ void MprisWatcher::onServiceRegistered(const QString& service) {
|
||||
|
||||
void MprisWatcher::onServiceUnregistered(const QString& service) {
|
||||
if (auto* player = this->mPlayers.value(service)) {
|
||||
player->deleteLater();
|
||||
QObject::disconnect(player, nullptr, this, nullptr);
|
||||
this->mPlayers.remove(service);
|
||||
this->readyPlayers.removeObject(player);
|
||||
player->deleteLater();
|
||||
qCDebug(logMprisWatcher) << "Unregistered MprisPlayer" << service;
|
||||
} else {
|
||||
qCWarning(logMprisWatcher) << "Got service unregister event for untracked service" << service;
|
||||
|
||||
@@ -56,7 +56,7 @@ void NotificationAction::invoke() {
|
||||
}
|
||||
|
||||
void NotificationAction::setText(const QString& text) {
|
||||
if (text != this->mText) return;
|
||||
if (text == this->mText) return;
|
||||
|
||||
this->mText = text;
|
||||
emit this->textChanged();
|
||||
|
||||
@@ -201,6 +201,6 @@ int PamSubprocess::conversation(
|
||||
return PAM_SUCCESS;
|
||||
|
||||
fail:
|
||||
free(responseArray); // NOLINT
|
||||
free(responses); // NOLINT
|
||||
_exit(1);
|
||||
}
|
||||
@@ -102,6 +102,7 @@ void PwConnection::startSocketWatcher() {
|
||||
void PwConnection::stopSocketWatcher() {
|
||||
if (this->socketWatcher == nullptr) return;
|
||||
|
||||
QObject::disconnect(this->socketWatcher, nullptr, this, nullptr);
|
||||
this->socketWatcher->deleteLater();
|
||||
this->socketWatcher = nullptr;
|
||||
}
|
||||
|
||||
@@ -486,8 +486,8 @@ void PwNodeBoundAudio::setVolumes(const QVector<float>& volumes) {
|
||||
<< "via device";
|
||||
this->waitingVolumes = realVolumes;
|
||||
} else {
|
||||
auto significantChange = this->mServerVolumes.isEmpty() || this->volumeStep == -1;
|
||||
if (this->volumeStep != -1) {
|
||||
auto significantChange = this->mServerVolumes.isEmpty();
|
||||
for (auto i = 0; i < this->mServerVolumes.length(); i++) {
|
||||
auto serverVolume = this->mServerVolumes.value(i);
|
||||
auto targetVolume = realVolumes.value(i);
|
||||
@@ -496,25 +496,25 @@ void PwNodeBoundAudio::setVolumes(const QVector<float>& volumes) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (significantChange) {
|
||||
qCInfo(logNode) << "Changing volumes of" << this->node << "to" << realVolumes
|
||||
<< "via device";
|
||||
if (!this->node->device->setVolumes(this->node->routeDevice, realVolumes)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->mDeviceVolumes = realVolumes;
|
||||
this->node->device->waitForDevice();
|
||||
} else {
|
||||
// Insignificant changes won't cause an info event on the device, leaving qs hung in the
|
||||
// "waiting for acknowledgement" state forever.
|
||||
qCInfo(logNode).nospace()
|
||||
<< "Ignoring volume change for " << this->node << " to " << realVolumes << " from "
|
||||
<< this->mServerVolumes
|
||||
<< " as it is a device node and the change is too small (min step: "
|
||||
<< this->volumeStep << ").";
|
||||
if (significantChange) {
|
||||
qCInfo(logNode) << "Changing volumes of" << this->node << "to" << realVolumes
|
||||
<< "via device";
|
||||
if (!this->node->device->setVolumes(this->node->routeDevice, realVolumes)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->mDeviceVolumes = realVolumes;
|
||||
this->node->device->waitForDevice();
|
||||
} else {
|
||||
// Insignificant changes won't cause an info event on the device, leaving qs hung in the
|
||||
// "waiting for acknowledgement" state forever.
|
||||
qCInfo(logNode).nospace()
|
||||
<< "Ignoring volume change for " << this->node << " to " << realVolumes << " from "
|
||||
<< this->mServerVolumes
|
||||
<< " as it is a device node and the change is too small (min step: " << this->volumeStep
|
||||
<< ").";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -8,18 +8,12 @@
|
||||
#include <qvector.h>
|
||||
|
||||
#include "node.hpp"
|
||||
#include "qml.hpp"
|
||||
|
||||
namespace qs::service::pipewire {
|
||||
|
||||
class PwNodeIface;
|
||||
class PwPeakStream;
|
||||
|
||||
} // namespace qs::service::pipewire
|
||||
|
||||
Q_DECLARE_OPAQUE_POINTER(qs::service::pipewire::PwNodeIface*);
|
||||
|
||||
namespace qs::service::pipewire {
|
||||
|
||||
///! Monitors peak levels of an audio node.
|
||||
/// Tracks volume peaks for a node across all its channels.
|
||||
///
|
||||
|
||||
@@ -43,6 +43,8 @@ void PolkitAgentImpl::cancelAllRequests(const QString& reason) {
|
||||
|
||||
auto* flow = this->bActiveFlow.value();
|
||||
if (flow) {
|
||||
QObject::disconnect(flow, nullptr, this, nullptr);
|
||||
this->bActiveFlow = nullptr;
|
||||
flow->cancelAuthenticationRequest();
|
||||
flow->deleteLater();
|
||||
}
|
||||
@@ -169,12 +171,12 @@ void PolkitAgentImpl::finishAuthenticationRequest() {
|
||||
qCDebug(logPolkit) << "finishing authentication request for action"
|
||||
<< this->bActiveFlow.value()->actionId();
|
||||
|
||||
QObject::disconnect(this->bActiveFlow.value(), nullptr, this, nullptr);
|
||||
this->bActiveFlow.value()->deleteLater();
|
||||
this->bActiveFlow = nullptr;
|
||||
|
||||
if (!this->queuedRequests.empty()) {
|
||||
this->activateAuthenticationRequest();
|
||||
} else {
|
||||
this->bActiveFlow = nullptr;
|
||||
}
|
||||
}
|
||||
} // namespace qs::service::polkit
|
||||
@@ -103,7 +103,12 @@ StatusNotifierItem::StatusNotifierItem(const QString& address, QObject* parent)
|
||||
return this->imageHandle.url() % "/" % QString::number(this->pixmapIndex);
|
||||
});
|
||||
|
||||
this->bHasMenu.setBinding([this]() { return !this->bMenuPath.value().path().isEmpty(); });
|
||||
this->bHasMenu.setBinding([this]() {
|
||||
// Could not find any documentation about No_DBUSMENU but KDE checks for it
|
||||
// and it was requested.
|
||||
return !this->bMenuPath.value().path().isEmpty()
|
||||
&& this->bMenuPath.value().path() != "/NO_DBUSMENU";
|
||||
});
|
||||
|
||||
QObject::connect(
|
||||
this->item,
|
||||
@@ -270,11 +275,13 @@ void StatusNotifierItem::scroll(qint32 delta, bool horizontal) const {
|
||||
void StatusNotifierItem::updatePixmapIndex() { this->pixmapIndex = this->pixmapIndex + 1; }
|
||||
|
||||
DBusMenuHandle* StatusNotifierItem::menuHandle() {
|
||||
return this->bMenuPath.value().path().isEmpty() ? nullptr : &this->mMenuHandle;
|
||||
return !this->bHasMenu ? nullptr : &this->mMenuHandle;
|
||||
}
|
||||
|
||||
void StatusNotifierItem::onMenuPathChanged() {
|
||||
this->mMenuHandle.setAddress(this->item->service(), this->bMenuPath.value().path());
|
||||
auto path = this->bMenuPath.value().path();
|
||||
if (!this->bHasMenu) path = "";
|
||||
this->mMenuHandle.setAddress(this->item->service(), path);
|
||||
}
|
||||
|
||||
void StatusNotifierItem::onGetAllFinished() {
|
||||
|
||||
@@ -37,6 +37,18 @@ QS_LOGGING_CATEGORY(logHyprlandIpc, "quickshell.hyprland.ipc", QtWarningMsg);
|
||||
QS_LOGGING_CATEGORY(logHyprlandIpcEvents, "quickshell.hyprland.ipc.events", QtWarningMsg);
|
||||
} // namespace
|
||||
|
||||
QBindable<HyprlandMonitor*> HyprlandIpc::bindableFocusedMonitor() const {
|
||||
return &this->bFocusedMonitor;
|
||||
}
|
||||
|
||||
QBindable<HyprlandWorkspace*> HyprlandIpc::bindableFocusedWorkspace() const {
|
||||
return &this->bFocusedWorkspace;
|
||||
}
|
||||
|
||||
QBindable<HyprlandToplevel*> HyprlandIpc::bindableActiveToplevel() const {
|
||||
return &this->bActiveToplevel;
|
||||
}
|
||||
|
||||
HyprlandIpc::HyprlandIpc() {
|
||||
auto his = qEnvironmentVariable("HYPRLAND_INSTANCE_SIGNATURE");
|
||||
if (his.isEmpty()) {
|
||||
@@ -187,11 +199,13 @@ void HyprlandIpc::makeRequest(
|
||||
requestSocket->flush();
|
||||
};
|
||||
|
||||
auto errorCallback = [=](QLocalSocket::LocalSocketError error) {
|
||||
qCWarning(logHyprlandIpc) << "Error making request:" << error << "request:" << request;
|
||||
requestSocket->deleteLater();
|
||||
callback(false, {});
|
||||
};
|
||||
auto errorCallback =
|
||||
[this, requestSocket, request, callback](QLocalSocket::LocalSocketError error) {
|
||||
qCWarning(logHyprlandIpc) << "Error making request:" << error << "request:" << request;
|
||||
QObject::disconnect(requestSocket, nullptr, this, nullptr);
|
||||
requestSocket->deleteLater();
|
||||
callback(false, {});
|
||||
};
|
||||
|
||||
QObject::connect(requestSocket, &QLocalSocket::connected, this, connectedCallback);
|
||||
QObject::connect(requestSocket, &QLocalSocket::errorOccurred, this, errorCallback);
|
||||
@@ -305,7 +319,7 @@ void HyprlandIpc::onEvent(HyprlandIpcEvent* event) {
|
||||
const auto& mList = this->mMonitors.valueList();
|
||||
auto name = QString::fromUtf8(event->data);
|
||||
|
||||
auto monitorIter = std::ranges::find_if(mList, [name](HyprlandMonitor* m) {
|
||||
auto monitorIter = std::ranges::find_if(mList, [&](HyprlandMonitor* m) {
|
||||
return m->bindableName().value() == name;
|
||||
});
|
||||
|
||||
@@ -330,10 +344,10 @@ void HyprlandIpc::onEvent(HyprlandIpcEvent* event) {
|
||||
} else if (event->name == "createworkspacev2") {
|
||||
auto args = event->parseView(2);
|
||||
|
||||
auto id = args.at(0).toInt();
|
||||
auto address = QString::fromUtf8(args.at(0));
|
||||
auto name = QString::fromUtf8(args.at(1));
|
||||
|
||||
qCDebug(logHyprlandIpc) << "Workspace created with id" << id << "name" << name;
|
||||
qCDebug(logHyprlandIpc) << "Workspace created with address" << address << "name" << name;
|
||||
|
||||
auto* workspace = this->findWorkspaceByName(name, false);
|
||||
auto existed = workspace != nullptr;
|
||||
@@ -342,7 +356,7 @@ void HyprlandIpc::onEvent(HyprlandIpcEvent* event) {
|
||||
workspace = new HyprlandWorkspace(this);
|
||||
}
|
||||
|
||||
workspace->updateInitial(id, name);
|
||||
workspace->updateInitial(address, name);
|
||||
|
||||
if (!existed) {
|
||||
this->refreshWorkspaces(false);
|
||||
@@ -351,17 +365,17 @@ void HyprlandIpc::onEvent(HyprlandIpcEvent* event) {
|
||||
} else if (event->name == "destroyworkspacev2") {
|
||||
auto args = event->parseView(2);
|
||||
|
||||
auto id = args.at(0).toInt();
|
||||
auto address = QString::fromUtf8(args.at(0));
|
||||
auto name = QString::fromUtf8(args.at(1));
|
||||
|
||||
const auto& mList = this->mWorkspaces.valueList();
|
||||
|
||||
auto workspaceIter = std::ranges::find_if(mList, [id](HyprlandWorkspace* m) {
|
||||
return m->bindableId().value() == id;
|
||||
auto workspaceIter = std::ranges::find_if(mList, [&](HyprlandWorkspace* m) {
|
||||
return m->bindableAddress().value() == address;
|
||||
});
|
||||
|
||||
if (workspaceIter == mList.end()) {
|
||||
qCWarning(logHyprlandIpc) << "Got removal for workspace id" << id << "name" << name
|
||||
qCWarning(logHyprlandIpc) << "Got removal for workspace id" << address << "name" << name
|
||||
<< "which was not previously tracked.";
|
||||
return;
|
||||
}
|
||||
@@ -369,7 +383,7 @@ void HyprlandIpc::onEvent(HyprlandIpcEvent* event) {
|
||||
auto index = workspaceIter - mList.begin();
|
||||
auto* workspace = *workspaceIter;
|
||||
|
||||
qCDebug(logHyprlandIpc) << "Workspace removed with id" << id << "name" << name;
|
||||
qCDebug(logHyprlandIpc) << "Workspace removed with id" << address << "name" << name;
|
||||
this->mWorkspaces.removeAt(index);
|
||||
|
||||
// workspaces have not been observed to be referenced after deletion
|
||||
@@ -398,10 +412,10 @@ void HyprlandIpc::onEvent(HyprlandIpcEvent* event) {
|
||||
this->setFocusedMonitor(monitor);
|
||||
monitor->setActiveWorkspace(workspace);
|
||||
qCDebug(logHyprlandIpc) << "Monitor" << name << "focused with workspace"
|
||||
<< (workspace ? workspace->bindableId().value() : -1);
|
||||
<< (workspace ? workspace->bindableAddress().value() : "<unk>");
|
||||
} else if (event->name == "workspacev2") {
|
||||
auto args = event->parseView(2);
|
||||
auto id = args.at(0).toInt();
|
||||
auto id = QString::fromUtf8(args.at(0));
|
||||
auto name = QString::fromUtf8(args.at(1));
|
||||
|
||||
if (this->bFocusedMonitor != nullptr) {
|
||||
@@ -412,7 +426,7 @@ void HyprlandIpc::onEvent(HyprlandIpcEvent* event) {
|
||||
}
|
||||
} else if (event->name == "moveworkspacev2") {
|
||||
auto args = event->parseView(3);
|
||||
auto id = args.at(0).toInt();
|
||||
auto id = QString::fromUtf8(args.at(0));
|
||||
auto name = QString::fromUtf8(args.at(1));
|
||||
auto monitorName = QString::fromUtf8(args.at(2));
|
||||
|
||||
@@ -423,18 +437,18 @@ void HyprlandIpc::onEvent(HyprlandIpcEvent* event) {
|
||||
workspace->setMonitor(monitor);
|
||||
} else if (event->name == "renameworkspace") {
|
||||
auto args = event->parseView(2);
|
||||
auto id = args.at(0).toInt();
|
||||
auto address = QString::fromUtf8(args.at(0));
|
||||
auto name = QString::fromUtf8(args.at(1));
|
||||
|
||||
const auto& mList = this->mWorkspaces.valueList();
|
||||
|
||||
auto workspaceIter = std::ranges::find_if(mList, [id](HyprlandWorkspace* m) {
|
||||
return m->bindableId().value() == id;
|
||||
auto workspaceIter = std::ranges::find_if(mList, [&](HyprlandWorkspace* m) {
|
||||
return m->bindableAddress().value() == address;
|
||||
});
|
||||
|
||||
if (workspaceIter == mList.end()) return;
|
||||
|
||||
qCDebug(logHyprlandIpc) << "Workspace with id" << id << "renamed from"
|
||||
qCDebug(logHyprlandIpc) << "Workspace with address" << address << "renamed from"
|
||||
<< (*workspaceIter)->bindableName().value() << "to" << name;
|
||||
|
||||
(*workspaceIter)->bindableName().setValue(name);
|
||||
@@ -486,7 +500,7 @@ void HyprlandIpc::onEvent(HyprlandIpcEvent* event) {
|
||||
if (!ok) return;
|
||||
|
||||
const auto& mList = this->mToplevels.valueList();
|
||||
auto toplevelIter = std::ranges::find_if(mList, [windowAddress](HyprlandToplevel* m) {
|
||||
auto toplevelIter = std::ranges::find_if(mList, [&](HyprlandToplevel* m) {
|
||||
return m->address() == windowAddress;
|
||||
});
|
||||
|
||||
@@ -578,14 +592,17 @@ void HyprlandIpc::onEvent(HyprlandIpcEvent* event) {
|
||||
}
|
||||
}
|
||||
|
||||
HyprlandWorkspace*
|
||||
HyprlandIpc::findWorkspaceByName(const QString& name, bool createIfMissing, qint32 id) {
|
||||
HyprlandWorkspace* HyprlandIpc::findWorkspaceByName(
|
||||
const QString& name,
|
||||
bool createIfMissing,
|
||||
const QString& address
|
||||
) {
|
||||
const auto& mList = this->mWorkspaces.valueList();
|
||||
HyprlandWorkspace* workspace = nullptr;
|
||||
|
||||
if (id != -1) {
|
||||
if (!address.isEmpty()) {
|
||||
auto workspaceIter = std::ranges::find_if(mList, [&](HyprlandWorkspace* m) {
|
||||
return m->bindableId().value() == id;
|
||||
return m->bindableAddress().value() == address;
|
||||
});
|
||||
|
||||
workspace = workspaceIter == mList.end() ? nullptr : *workspaceIter;
|
||||
@@ -603,10 +620,11 @@ HyprlandIpc::findWorkspaceByName(const QString& name, bool createIfMissing, qint
|
||||
return workspace;
|
||||
} else if (createIfMissing) {
|
||||
qCDebug(logHyprlandIpc) << "Workspace" << name
|
||||
<< "requested before creation, performing early init with id" << id;
|
||||
<< "requested before creation, performing early init with address"
|
||||
<< address;
|
||||
|
||||
auto* workspace = new HyprlandWorkspace(this);
|
||||
workspace->updateInitial(id, name);
|
||||
workspace->updateInitial(address, name);
|
||||
this->mWorkspaces.insertObjectSorted(workspace, &HyprlandIpc::compareWorkspaces);
|
||||
return workspace;
|
||||
} else {
|
||||
@@ -626,15 +644,16 @@ void HyprlandIpc::refreshWorkspaces(bool canCreate) {
|
||||
auto json = QJsonDocument::fromJson(resp).array();
|
||||
|
||||
const auto& mList = this->mWorkspaces.valueList();
|
||||
auto ids = QVector<quint32>();
|
||||
auto addresses = QVector<QString>();
|
||||
|
||||
for (auto entry: json) {
|
||||
auto object = entry.toObject().toVariantMap();
|
||||
|
||||
auto id = object.value("id").toInt();
|
||||
auto address = object.value("address").toString();
|
||||
if (address.isEmpty()) address = QString::number(object.value("id").toInt());
|
||||
|
||||
auto workspaceIter = std::ranges::find_if(mList, [&](HyprlandWorkspace* m) {
|
||||
return m->bindableId().value() == id;
|
||||
return m->bindableAddress().value() == address;
|
||||
});
|
||||
|
||||
// Only fall back to name-based filtering as a last resort, for workspaces where
|
||||
@@ -643,7 +662,7 @@ void HyprlandIpc::refreshWorkspaces(bool canCreate) {
|
||||
auto name = object.value("name").toString();
|
||||
|
||||
workspaceIter = std::ranges::find_if(mList, [&](HyprlandWorkspace* m) {
|
||||
return m->bindableId().value() == -1 && m->bindableName().value() == name;
|
||||
return m->bindableAddress().value().isEmpty() && m->bindableName().value() == name;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -661,14 +680,14 @@ void HyprlandIpc::refreshWorkspaces(bool canCreate) {
|
||||
this->mWorkspaces.insertObjectSorted(workspace, &HyprlandIpc::compareWorkspaces);
|
||||
}
|
||||
|
||||
ids.push_back(id);
|
||||
addresses.push_back(address);
|
||||
}
|
||||
|
||||
if (canCreate) {
|
||||
auto removedWorkspaces = QVector<HyprlandWorkspace*>();
|
||||
|
||||
for (auto* workspace: mList) {
|
||||
if (!ids.contains(workspace->bindableId().value())) {
|
||||
if (!addresses.contains(workspace->bindableAddress().value())) {
|
||||
removedWorkspaces.push_back(workspace);
|
||||
}
|
||||
}
|
||||
@@ -750,7 +769,7 @@ HyprlandMonitor*
|
||||
HyprlandIpc::findMonitorByName(const QString& name, bool createIfMissing, qint32 id) {
|
||||
const auto& mList = this->mMonitors.valueList();
|
||||
|
||||
auto monitorIter = std::ranges::find_if(mList, [name](HyprlandMonitor* m) {
|
||||
auto monitorIter = std::ranges::find_if(mList, [&](HyprlandMonitor* m) {
|
||||
return m->bindableName().value() == name;
|
||||
});
|
||||
|
||||
@@ -818,7 +837,7 @@ void HyprlandIpc::refreshMonitors(bool canCreate) {
|
||||
auto object = entry.toObject().toVariantMap();
|
||||
auto name = object.value("name").toString();
|
||||
|
||||
auto monitorIter = std::ranges::find_if(mList, [name](HyprlandMonitor* m) {
|
||||
auto monitorIter = std::ranges::find_if(mList, [&](HyprlandMonitor* m) {
|
||||
return m->bindableName().value() == name;
|
||||
});
|
||||
|
||||
|
||||
@@ -25,9 +25,9 @@ class HyprlandToplevel;
|
||||
|
||||
} // namespace qs::hyprland::ipc
|
||||
|
||||
Q_DECLARE_OPAQUE_POINTER(qs::hyprland::ipc::HyprlandWorkspace*);
|
||||
Q_DECLARE_OPAQUE_POINTER(qs::hyprland::ipc::HyprlandMonitor*);
|
||||
Q_DECLARE_OPAQUE_POINTER(qs::hyprland::ipc::HyprlandToplevel*);
|
||||
Q_MOC_INCLUDE("hyprland_toplevel.hpp")
|
||||
Q_MOC_INCLUDE("monitor.hpp")
|
||||
Q_MOC_INCLUDE("workspace.hpp")
|
||||
|
||||
namespace qs::hyprland::ipc {
|
||||
|
||||
@@ -83,17 +83,9 @@ public:
|
||||
|
||||
[[nodiscard]] QBindable<bool> bindableUsingLua() const { return &this->bUsingLua; }
|
||||
|
||||
[[nodiscard]] QBindable<HyprlandMonitor*> bindableFocusedMonitor() const {
|
||||
return &this->bFocusedMonitor;
|
||||
}
|
||||
|
||||
[[nodiscard]] QBindable<HyprlandWorkspace*> bindableFocusedWorkspace() const {
|
||||
return &this->bFocusedWorkspace;
|
||||
}
|
||||
|
||||
[[nodiscard]] QBindable<HyprlandToplevel*> bindableActiveToplevel() const {
|
||||
return &this->bActiveToplevel;
|
||||
}
|
||||
[[nodiscard]] QBindable<HyprlandMonitor*> bindableFocusedMonitor() const;
|
||||
[[nodiscard]] QBindable<HyprlandWorkspace*> bindableFocusedWorkspace() const;
|
||||
[[nodiscard]] QBindable<HyprlandToplevel*> bindableActiveToplevel() const;
|
||||
|
||||
void setFocusedMonitor(HyprlandMonitor* monitor);
|
||||
|
||||
@@ -102,7 +94,11 @@ public:
|
||||
[[nodiscard]] ObjectModel<HyprlandToplevel>* toplevels();
|
||||
|
||||
// No byId because these preemptively create objects. The given id is set if created.
|
||||
HyprlandWorkspace* findWorkspaceByName(const QString& name, bool createIfMissing, qint32 id = -1);
|
||||
HyprlandWorkspace* findWorkspaceByName(
|
||||
const QString& name,
|
||||
bool createIfMissing,
|
||||
const QString& address = QString()
|
||||
);
|
||||
HyprlandMonitor* findMonitorByName(const QString& name, bool createIfMissing, qint32 id = -1);
|
||||
HyprlandToplevel* findToplevelByAddress(quint64 address, bool createIfMissing);
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "../../toplevel/qml.hpp"
|
||||
#include "connection.hpp"
|
||||
#include "monitor.hpp"
|
||||
#include "toplevel_mapping.hpp"
|
||||
#include "workspace.hpp"
|
||||
|
||||
@@ -15,6 +16,10 @@ using namespace qs::wayland::toplevel;
|
||||
|
||||
namespace qs::hyprland::ipc {
|
||||
|
||||
QBindable<HyprlandWorkspace*> HyprlandToplevel::bindableWorkspace() { return &this->bWorkspace; }
|
||||
|
||||
QBindable<HyprlandMonitor*> HyprlandToplevel::bindableMonitor() { return &this->bMonitor; }
|
||||
|
||||
HyprlandToplevel::HyprlandToplevel(HyprlandIpc* ipc): QObject(ipc), ipc(ipc) {
|
||||
this->bMonitor.setBinding([this]() {
|
||||
return this->bWorkspace ? this->bWorkspace->bindableMonitor().value() : nullptr;
|
||||
|
||||
@@ -81,10 +81,10 @@ public:
|
||||
return &this->bLastIpcObject;
|
||||
};
|
||||
|
||||
[[nodiscard]] QBindable<HyprlandWorkspace*> bindableWorkspace() { return &this->bWorkspace; }
|
||||
[[nodiscard]] QBindable<HyprlandWorkspace*> bindableWorkspace();
|
||||
void setWorkspace(HyprlandWorkspace* workspace);
|
||||
|
||||
[[nodiscard]] QBindable<HyprlandMonitor*> bindableMonitor() { return &this->bMonitor; }
|
||||
[[nodiscard]] QBindable<HyprlandMonitor*> bindableMonitor();
|
||||
|
||||
signals:
|
||||
void addressChanged();
|
||||
|
||||
@@ -13,6 +13,10 @@ namespace qs::hyprland::ipc {
|
||||
|
||||
QVariantMap HyprlandMonitor::lastIpcObject() const { return this->mLastIpcObject; }
|
||||
|
||||
QBindable<HyprlandWorkspace*> HyprlandMonitor::bindableActiveWorkspace() const {
|
||||
return &this->bActiveWorkspace;
|
||||
}
|
||||
|
||||
void HyprlandMonitor::updateInitial(qint32 id, const QString& name, const QString& description) {
|
||||
Qt::beginPropertyUpdateGroup();
|
||||
this->bId = id;
|
||||
@@ -28,7 +32,9 @@ void HyprlandMonitor::updateInitial(qint32 id, const QString& name, const QStrin
|
||||
|
||||
void HyprlandMonitor::updateFromObject(QVariantMap object) {
|
||||
auto activeWorkspaceObj = object.value("activeWorkspace").value<QVariantMap>();
|
||||
auto activeWorkspaceAddress = activeWorkspaceObj.value("address").value<QString>();
|
||||
auto activeWorkspaceId = activeWorkspaceObj.value("id").value<qint32>();
|
||||
if (activeWorkspaceAddress.isEmpty()) activeWorkspaceAddress = QString::number(activeWorkspaceId);
|
||||
auto activeWorkspaceName = activeWorkspaceObj.value("name").value<QString>();
|
||||
auto focused = object.value("focused").value<bool>();
|
||||
|
||||
@@ -46,7 +52,8 @@ void HyprlandMonitor::updateFromObject(QVariantMap object) {
|
||||
if (this->bActiveWorkspace == nullptr
|
||||
|| this->bActiveWorkspace->bindableName().value() != activeWorkspaceName)
|
||||
{
|
||||
auto* workspace = this->ipc->findWorkspaceByName(activeWorkspaceName, true, activeWorkspaceId);
|
||||
auto* workspace =
|
||||
this->ipc->findWorkspaceByName(activeWorkspaceName, true, activeWorkspaceAddress);
|
||||
workspace->setMonitor(this);
|
||||
this->setActiveWorkspace(workspace);
|
||||
}
|
||||
|
||||
@@ -54,9 +54,7 @@ public:
|
||||
[[nodiscard]] QBindable<qint32> bindableHeight() { return &this->bHeight; }
|
||||
[[nodiscard]] QBindable<qreal> bindableScale() { return &this->bScale; }
|
||||
|
||||
[[nodiscard]] QBindable<HyprlandWorkspace*> bindableActiveWorkspace() const {
|
||||
return &this->bActiveWorkspace;
|
||||
}
|
||||
[[nodiscard]] QBindable<HyprlandWorkspace*> bindableActiveWorkspace() const;
|
||||
|
||||
[[nodiscard]] QBindable<bool> bindableFocused() const { return &this->bFocused; }
|
||||
|
||||
|
||||
@@ -15,14 +15,23 @@ namespace qs::hyprland::ipc {
|
||||
|
||||
QVariantMap HyprlandWorkspace::lastIpcObject() const { return this->mLastIpcObject; }
|
||||
|
||||
QBindable<HyprlandMonitor*> HyprlandWorkspace::bindableMonitor() { return &this->bMonitor; }
|
||||
|
||||
HyprlandWorkspace::HyprlandWorkspace(HyprlandIpc* ipc): QObject(ipc), ipc(ipc) {
|
||||
Qt::beginPropertyUpdateGroup();
|
||||
|
||||
this->bActive.setBinding([this]() {
|
||||
this->bId.setBinding([this] {
|
||||
auto idOk = false;
|
||||
auto id = this->bAddress.value().toInt(&idOk);
|
||||
if (!idOk) id = -1;
|
||||
return id;
|
||||
});
|
||||
|
||||
this->bActive.setBinding([this] {
|
||||
return this->bMonitor.value() && this->bMonitor->bindableActiveWorkspace().value() == this;
|
||||
});
|
||||
|
||||
this->bFocused.setBinding([this]() {
|
||||
this->bFocused.setBinding([this] {
|
||||
return this->ipc->bindableFocusedWorkspace().value() == this;
|
||||
});
|
||||
|
||||
@@ -35,9 +44,9 @@ HyprlandWorkspace::HyprlandWorkspace(HyprlandIpc* ipc): QObject(ipc), ipc(ipc) {
|
||||
Qt::endPropertyUpdateGroup();
|
||||
}
|
||||
|
||||
void HyprlandWorkspace::updateInitial(qint32 id, const QString& name) {
|
||||
void HyprlandWorkspace::updateInitial(const QString& address, const QString& name) {
|
||||
Qt::beginPropertyUpdateGroup();
|
||||
this->bId = id;
|
||||
this->bAddress = address;
|
||||
this->bName = name;
|
||||
Qt::endPropertyUpdateGroup();
|
||||
}
|
||||
@@ -47,11 +56,14 @@ void HyprlandWorkspace::updateFromObject(QVariantMap object) {
|
||||
auto monitorName = object.value("monitor").value<QString>();
|
||||
auto hasFullscreen = object.value("hasfullscreen").value<bool>();
|
||||
|
||||
auto initial = this->bId == -1;
|
||||
auto initial = this->bAddress.value().isEmpty();
|
||||
|
||||
// ID cannot be updated after creation
|
||||
// Address cannot be updated after creation
|
||||
if (initial) {
|
||||
this->bId = object.value("id").value<qint32>();
|
||||
auto address = object.value("address").value<QString>();
|
||||
auto id = object.value("id").value<qint32>();
|
||||
if (address.isEmpty()) address = QString::number(id);
|
||||
this->bAddress = address;
|
||||
}
|
||||
|
||||
// No events we currently handle give a workspace id but not a name,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <qbytearrayview.h>
|
||||
#include <qcontainerfwd.h>
|
||||
#include <qjsonobject.h>
|
||||
#include <qobject.h>
|
||||
#include <qproperty.h>
|
||||
@@ -18,6 +19,13 @@ class HyprlandMonitor;
|
||||
class HyprlandWorkspace: public QObject {
|
||||
Q_OBJECT;
|
||||
// clang-format off
|
||||
// > [!NOTE] On Hyprland versions prior to 0.57, this property is set to @@id as a string for compatibility.
|
||||
// >
|
||||
// > Prefer this property over @@id for new code.
|
||||
Q_PROPERTY(QString address READ default NOTIFY addressChanged BINDABLE bindableAddress);
|
||||
// > [!NOTE] On Hyprland versions 0.57 and later, this property is set to @@address as an integer for compatibility.
|
||||
// >
|
||||
// > After 0.57, this property will equal `-1` for non-numbered workspaces.
|
||||
Q_PROPERTY(qint32 id READ default NOTIFY idChanged BINDABLE bindableId);
|
||||
Q_PROPERTY(QString name READ default NOTIFY nameChanged BINDABLE bindableName);
|
||||
/// If this workspace is currently active on its monitor. See also @@focused.
|
||||
@@ -47,7 +55,7 @@ class HyprlandWorkspace: public QObject {
|
||||
public:
|
||||
explicit HyprlandWorkspace(HyprlandIpc* ipc);
|
||||
|
||||
void updateInitial(qint32 id, const QString& name);
|
||||
void updateInitial(const QString& address, const QString& name);
|
||||
void updateFromObject(QVariantMap object);
|
||||
|
||||
/// Activate the workspace.
|
||||
@@ -58,13 +66,14 @@ public:
|
||||
/// > ```
|
||||
Q_INVOKABLE void activate();
|
||||
|
||||
[[nodiscard]] QBindable<QString> bindableAddress() { return &this->bAddress; }
|
||||
[[nodiscard]] QBindable<qint32> bindableId() { return &this->bId; }
|
||||
[[nodiscard]] QBindable<QString> bindableName() { return &this->bName; }
|
||||
[[nodiscard]] QBindable<bool> bindableActive() { return &this->bActive; }
|
||||
[[nodiscard]] QBindable<bool> bindableFocused() { return &this->bFocused; }
|
||||
[[nodiscard]] QBindable<bool> bindableUrgent() { return &this->bUrgent; }
|
||||
[[nodiscard]] QBindable<bool> bindableHasFullscreen() { return &this->bHasFullscreen; }
|
||||
[[nodiscard]] QBindable<HyprlandMonitor*> bindableMonitor() { return &this->bMonitor; }
|
||||
[[nodiscard]] QBindable<HyprlandMonitor*> bindableMonitor();
|
||||
[[nodiscard]] ObjectModel<HyprlandToplevel>* toplevels() { return &this->mToplevels; }
|
||||
|
||||
[[nodiscard]] QVariantMap lastIpcObject() const;
|
||||
@@ -75,7 +84,9 @@ public:
|
||||
void removeToplevel(HyprlandToplevel* toplevel);
|
||||
|
||||
signals:
|
||||
void addressChanged();
|
||||
void idChanged();
|
||||
void typeChanged();
|
||||
void nameChanged();
|
||||
void activeChanged();
|
||||
void focusedChanged();
|
||||
@@ -97,6 +108,7 @@ private:
|
||||
ObjectModel<HyprlandToplevel> mToplevels {this};
|
||||
|
||||
// clang-format off
|
||||
Q_OBJECT_BINDABLE_PROPERTY(HyprlandWorkspace, QString, bAddress, &HyprlandWorkspace::addressChanged);
|
||||
Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(HyprlandWorkspace, qint32, bId, -1, &HyprlandWorkspace::idChanged);
|
||||
Q_OBJECT_BINDABLE_PROPERTY(HyprlandWorkspace, QString, bName, &HyprlandWorkspace::nameChanged);
|
||||
Q_OBJECT_BINDABLE_PROPERTY(HyprlandWorkspace, bool, bActive, &HyprlandWorkspace::activeChanged);
|
||||
|
||||
@@ -16,7 +16,7 @@ FloatingWindow {
|
||||
color: "lightgray"
|
||||
|
||||
ColumnLayout {
|
||||
Text { text: `Workspace ${wsDelegate.modelData.id} on ${wsDelegate.modelData.monitor} | urgent: ${wsDelegate.modelData.urgent}`}
|
||||
Text { text: `Workspace (addr ${wsDelegate.modelData.address}; id ${wsDelegate.modelData.id}; name: ${wsDelegate.modelData.name}) on ${wsDelegate.modelData.monitor} | urgent: ${wsDelegate.modelData.urgent}`}
|
||||
|
||||
ColumnLayout {
|
||||
Repeater {
|
||||
|
||||
@@ -22,6 +22,15 @@
|
||||
#include "../window/proxywindow.hpp"
|
||||
#include "session_lock/session_lock.hpp"
|
||||
|
||||
WlSessionLock::~WlSessionLock() {
|
||||
if (this->isLocked()) {
|
||||
qCritical() << "Session lock object was destroyed without unlocking. The session will stay "
|
||||
"locked in case this was accidental.";
|
||||
qCritical() << "Component.onDestruction may be used if this behavior is required, but it may "
|
||||
"result in a lock failing open.";
|
||||
}
|
||||
}
|
||||
|
||||
void WlSessionLock::onReload(QObject* oldInstance) {
|
||||
auto* old = qobject_cast<WlSessionLock*>(oldInstance);
|
||||
|
||||
@@ -113,70 +122,84 @@ void WlSessionLock::updateSurfaces(bool show, WlSessionLock* old) {
|
||||
}
|
||||
|
||||
void WlSessionLock::realizeLockTarget(WlSessionLock* old) {
|
||||
if (this->lockTarget) {
|
||||
if (!SessionLockManager::lockAvailable()) {
|
||||
qCritical() << "Cannot start session lock: The current compositor does not support the "
|
||||
"ext-session-lock-v1 protocol.";
|
||||
this->unlock();
|
||||
return;
|
||||
if (this->realizing || !this->manager) {
|
||||
this->awaitingRealization = true;
|
||||
return;
|
||||
}
|
||||
|
||||
this->realizing = true;
|
||||
|
||||
[&, this]() {
|
||||
if (this->lockTarget) {
|
||||
if (!SessionLockManager::lockAvailable()) {
|
||||
qCritical() << "Cannot start session lock: The current compositor does not support the "
|
||||
"ext-session-lock-v1 protocol.";
|
||||
this->unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->mSurfaceComponent == nullptr) {
|
||||
qWarning() << "WlSessionLock.surface is null. Aborting lock.";
|
||||
this->unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
// preload initial surfaces to make the chance of the compositor displaying a blank
|
||||
// frame before the lock surfaces are shown as low as possible.
|
||||
this->updateSurfaces(false);
|
||||
|
||||
auto wasLocked = this->isLocked();
|
||||
if (!this->manager->lock()) {
|
||||
qWarning() << "Failed to acquire session lock.";
|
||||
this->unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
this->updateSurfaces(true, old);
|
||||
if (!wasLocked) emit this->lockStateChanged();
|
||||
} else {
|
||||
this->unlock(); // emits lockStateChanged
|
||||
}
|
||||
}();
|
||||
|
||||
if (this->mSurfaceComponent == nullptr) {
|
||||
qWarning() << "WlSessionLock.surface is null. Aborting lock.";
|
||||
this->unlock();
|
||||
return;
|
||||
}
|
||||
this->realizing = false;
|
||||
|
||||
// preload initial surfaces to make the chance of the compositor displaying a blank
|
||||
// frame before the lock surfaces are shown as low as possible.
|
||||
this->updateSurfaces(false);
|
||||
|
||||
if (!this->manager->lock()) this->lockTarget = false;
|
||||
|
||||
this->updateSurfaces(true, old);
|
||||
} else {
|
||||
this->unlock(); // emits lockStateChanged
|
||||
if (this->awaitingRealization) {
|
||||
this->awaitingRealization = false;
|
||||
this->realizeLockTarget();
|
||||
}
|
||||
}
|
||||
|
||||
void WlSessionLock::unlock() {
|
||||
if (this->isLocked()) {
|
||||
this->lockTarget = false;
|
||||
this->manager->unlock();
|
||||
this->lockTarget = false;
|
||||
|
||||
for (auto* surface: this->surfaces) {
|
||||
surface->deleteLater();
|
||||
}
|
||||
|
||||
this->surfaces.clear();
|
||||
|
||||
emit this->lockStateChanged();
|
||||
for (auto* surface: this->surfaces) {
|
||||
surface->deleteLater();
|
||||
}
|
||||
|
||||
this->surfaces.clear();
|
||||
|
||||
if (this->manager) this->manager->unlock();
|
||||
if (this->isLocked()) emit this->lockStateChanged();
|
||||
}
|
||||
|
||||
void WlSessionLock::onScreensChanged() {
|
||||
if (this->manager != nullptr && this->manager->isLocked()) {
|
||||
// Theoretically screens shouldnt be invalidated while realizing, instead in later event loop cycles.
|
||||
if (this->isLocked() && !this->realizing) {
|
||||
this->updateSurfaces(true);
|
||||
}
|
||||
}
|
||||
|
||||
bool WlSessionLock::isLocked() const {
|
||||
return this->manager == nullptr ? this->lockTarget : this->manager->isLocked();
|
||||
}
|
||||
bool WlSessionLock::isLocked() const { return this->manager && this->manager->isLocked(); }
|
||||
|
||||
bool WlSessionLock::isSecure() const {
|
||||
return this->manager != nullptr && SessionLockManager::isSecure();
|
||||
}
|
||||
|
||||
void WlSessionLock::setLocked(bool locked) {
|
||||
if (this->isLocked() == locked) return;
|
||||
if (this->lockTarget == locked) return;
|
||||
this->lockTarget = locked;
|
||||
|
||||
if (this->manager == nullptr) {
|
||||
emit this->lockStateChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
this->realizeLockTarget();
|
||||
}
|
||||
|
||||
|
||||
@@ -73,6 +73,8 @@ class WlSessionLock: public Reloadable {
|
||||
|
||||
public:
|
||||
explicit WlSessionLock(QObject* parent = nullptr): Reloadable(parent) {}
|
||||
~WlSessionLock() override;
|
||||
Q_DISABLE_COPY_MOVE(WlSessionLock);
|
||||
|
||||
void onReload(QObject* oldInstance) override;
|
||||
|
||||
@@ -101,6 +103,8 @@ private:
|
||||
QQmlComponent* mSurfaceComponent = nullptr;
|
||||
QMap<QScreen*, WlSessionLockSurface*> surfaces;
|
||||
bool lockTarget = false;
|
||||
bool realizing = false;
|
||||
bool awaitingRealization = false;
|
||||
|
||||
friend class WlSessionLockSurface;
|
||||
};
|
||||
|
||||
@@ -25,7 +25,8 @@ QSWaylandSessionLockManager* manager() {
|
||||
bool SessionLockManager::lockAvailable() { return manager()->isActive(); }
|
||||
|
||||
bool SessionLockManager::lock() {
|
||||
if (this->isLocked() || SessionLockManager::sessionLocked()) return false;
|
||||
if (this->isLocked()) return true;
|
||||
if (SessionLockManager::sessionLocked()) return false;
|
||||
this->mLock = manager()->acquireLock();
|
||||
this->mLock->setParent(this);
|
||||
|
||||
|
||||
@@ -11,10 +11,6 @@ qt_add_qml_module(quickshell-x11
|
||||
DEPENDENCIES QtQuick
|
||||
)
|
||||
|
||||
if(I3)
|
||||
add_subdirectory(i3)
|
||||
endif()
|
||||
|
||||
install_qml_module(quickshell-x11)
|
||||
|
||||
add_library(quickshell-x11-init OBJECT init.cpp)
|
||||
|
||||
@@ -31,6 +31,14 @@ QS_LOGGING_CATEGORY(logI3Ipc, "quickshell.I3.ipc", QtWarningMsg);
|
||||
QS_LOGGING_CATEGORY(logI3IpcEvents, "quickshell.I3.ipc.events", QtWarningMsg);
|
||||
} // namespace
|
||||
|
||||
QBindable<I3Monitor*> I3IpcController::bindableFocusedMonitor() const {
|
||||
return &this->bFocusedMonitor;
|
||||
}
|
||||
|
||||
QBindable<I3Workspace*> I3IpcController::bindableFocusedWorkspace() const {
|
||||
return &this->bFocusedWorkspace;
|
||||
}
|
||||
|
||||
I3IpcController::I3IpcController(): I3Ipc({"workspace", "output"}) {
|
||||
// bind focused workspace to focused monitor's active workspace
|
||||
this->bFocusedWorkspace.setBinding([this]() -> I3Workspace* {
|
||||
|
||||
@@ -20,8 +20,8 @@ class I3Workspace;
|
||||
class I3Monitor;
|
||||
} // namespace qs::i3::ipc
|
||||
|
||||
Q_DECLARE_OPAQUE_POINTER(qs::i3::ipc::I3Workspace*);
|
||||
Q_DECLARE_OPAQUE_POINTER(qs::i3::ipc::I3Monitor*);
|
||||
Q_MOC_INCLUDE("monitor.hpp")
|
||||
Q_MOC_INCLUDE("workspace.hpp")
|
||||
|
||||
namespace qs::i3::ipc {
|
||||
|
||||
@@ -43,13 +43,8 @@ public:
|
||||
|
||||
I3Monitor* monitorFor(QuickshellScreenInfo* screen);
|
||||
|
||||
[[nodiscard]] QBindable<I3Monitor*> bindableFocusedMonitor() const {
|
||||
return &this->bFocusedMonitor;
|
||||
};
|
||||
|
||||
[[nodiscard]] QBindable<I3Workspace*> bindableFocusedWorkspace() const {
|
||||
return &this->bFocusedWorkspace;
|
||||
};
|
||||
[[nodiscard]] QBindable<I3Monitor*> bindableFocusedMonitor() const;
|
||||
[[nodiscard]] QBindable<I3Workspace*> bindableFocusedWorkspace() const;
|
||||
|
||||
[[nodiscard]] ObjectModel<I3Monitor>* monitors();
|
||||
[[nodiscard]] ObjectModel<I3Workspace>* workspaces();
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
|
||||
namespace qs::i3::ipc {
|
||||
|
||||
QBindable<I3Workspace*> I3Monitor::bindableActiveWorkspace() const {
|
||||
return &this->bActiveWorkspace;
|
||||
}
|
||||
|
||||
I3Monitor::I3Monitor(I3IpcController* ipc): QObject(ipc), ipc(ipc) {
|
||||
// clang-format off
|
||||
this->bFocused.setBinding([this]() { return this->ipc->bindableFocusedMonitor().value() == this; });
|
||||
|
||||
@@ -55,9 +55,7 @@ public:
|
||||
[[nodiscard]] QBindable<qreal> bindableScale() { return &this->bScale; }
|
||||
[[nodiscard]] QBindable<bool> bindableFocused() { return &this->bFocused; }
|
||||
|
||||
[[nodiscard]] QBindable<I3Workspace*> bindableActiveWorkspace() const {
|
||||
return &this->bActiveWorkspace;
|
||||
}
|
||||
[[nodiscard]] QBindable<I3Workspace*> bindableActiveWorkspace() const;
|
||||
|
||||
[[nodiscard]] QVariantMap lastIpcObject() const;
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
|
||||
namespace qs::i3::ipc {
|
||||
|
||||
QBindable<I3Monitor*> I3Workspace::bindableMonitor() const { return &this->bMonitor; }
|
||||
|
||||
I3Workspace::I3Workspace(I3IpcController* ipc): QObject(ipc), ipc(ipc) {
|
||||
Qt::beginPropertyUpdateGroup();
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ public:
|
||||
[[nodiscard]] QBindable<bool> bindableActive() { return &this->bActive; }
|
||||
[[nodiscard]] QBindable<bool> bindableFocused() { return &this->bFocused; }
|
||||
[[nodiscard]] QBindable<bool> bindableUrgent() { return &this->bUrgent; }
|
||||
[[nodiscard]] QBindable<I3Monitor*> bindableMonitor() const { return &this->bMonitor; }
|
||||
[[nodiscard]] QBindable<I3Monitor*> bindableMonitor() const;
|
||||
[[nodiscard]] QVariantMap lastIpcObject() const;
|
||||
|
||||
void updateFromObject(const QVariantMap& obj);
|
||||
|
||||
Reference in new issue
Block a user