Compare commits

...

3 commits

Author SHA1 Message Date
outfoxxed
7c5a6c4bd4
core/log: crash if Quickshell's log filter is installed twice
Crashes from recursion inside filterCategories through the old filter
have been observed. Presumably this means the log filter is getting
installed twice somehow. This should catch it.
2026-04-06 00:45:26 -07:00
outfoxxed
5bf6a412b0
core: correctly construct runtime path when XDG_RUNTIME_DIR missing
Fixes a typo of % as $, which broke string substitution.
2026-04-06 00:43:02 -07:00
outfoxxed
13fe9b0d98
services/pipewire: avoid blanket disconnect for default nodes
The same nodes can be both default and default configured nodes. When
the default and default configured node are not changed in unison, a
blanket disconnect will also disconnect the other's destroy handler,
causing a crash if the other is accessed after the node is destroyed.
2026-04-06 00:42:29 -07:00
4 changed files with 51 additions and 11 deletions

View file

@ -70,6 +70,7 @@ set shell id.
- Fixed JsonAdapter crashing and providing bad data on read when using JsonObject.
- Fixed JsonAdapter sending unnecessary property changes for primitive values.
- Fixed JsonAdapter serialization for lists.
- Fixed pipewire crashes after hotplugging devices and changing default outputs.
## Packaging Changes

View file

@ -310,10 +310,15 @@ void LogManager::init(
instance->rules->append(parser.rules());
}
qInstallMessageHandler(&LogManager::messageHandler);
instance->lastCategoryFilter = QLoggingCategory::installFilter(&LogManager::filterCategory);
if (instance->lastCategoryFilter == &LogManager::filterCategory) {
qCFatal(logLogging) << "Quickshell's log filter has been installed twice. This is a bug.";
instance->lastCategoryFilter = nullptr;
}
qInstallMessageHandler(&LogManager::messageHandler);
qCDebug(logLogging) << "Creating offthread logger...";
auto* thread = new QThread();
instance->threadProxy.moveToThread(thread);

View file

@ -64,7 +64,7 @@ QDir* QsPaths::baseRunDir() {
if (this->baseRunState == DirState::Unknown) {
auto runtimeDir = qEnvironmentVariable("XDG_RUNTIME_DIR");
if (runtimeDir.isEmpty()) {
runtimeDir = QString("/run/user/$1").arg(getuid());
runtimeDir = QString("/run/user/%1").arg(getuid());
qCInfo(logPaths) << "XDG_RUNTIME_DIR was not set, defaulting to" << runtimeDir;
}

View file

@ -214,13 +214,24 @@ void PwDefaultTracker::setDefaultSink(PwNode* node) {
qCInfo(logDefaults) << "Default sink changed to" << node;
if (this->mDefaultSink != nullptr) {
QObject::disconnect(this->mDefaultSink, nullptr, this, nullptr);
// Targeted disconnect is used because this can also be the default configured sink.
QObject::disconnect(
this->mDefaultSink,
&PwBindableObject::destroying,
this,
&PwDefaultTracker::onDefaultSinkDestroyed
);
}
this->mDefaultSink = node;
if (node != nullptr) {
QObject::connect(node, &QObject::destroyed, this, &PwDefaultTracker::onDefaultSinkDestroyed);
QObject::connect(
node,
&PwBindableObject::destroying,
this,
&PwDefaultTracker::onDefaultSinkDestroyed
);
}
emit this->defaultSinkChanged();
@ -244,13 +255,24 @@ void PwDefaultTracker::setDefaultSource(PwNode* node) {
qCInfo(logDefaults) << "Default source changed to" << node;
if (this->mDefaultSource != nullptr) {
QObject::disconnect(this->mDefaultSource, nullptr, this, nullptr);
// Targeted disconnect is used because this can also be the default configured source.
QObject::disconnect(
this->mDefaultSource,
&PwBindableObject::destroying,
this,
&PwDefaultTracker::onDefaultSourceDestroyed
);
}
this->mDefaultSource = node;
if (node != nullptr) {
QObject::connect(node, &QObject::destroyed, this, &PwDefaultTracker::onDefaultSourceDestroyed);
QObject::connect(
node,
&PwBindableObject::destroying,
this,
&PwDefaultTracker::onDefaultSourceDestroyed
);
}
emit this->defaultSourceChanged();
@ -274,7 +296,13 @@ void PwDefaultTracker::setDefaultConfiguredSink(PwNode* node) {
qCInfo(logDefaults) << "Default configured sink changed to" << node;
if (this->mDefaultConfiguredSink != nullptr) {
QObject::disconnect(this->mDefaultConfiguredSink, nullptr, this, nullptr);
// Targeted disconnect is used because this can also be the default sink.
QObject::disconnect(
this->mDefaultConfiguredSink,
&PwBindableObject::destroying,
this,
&PwDefaultTracker::onDefaultConfiguredSinkDestroyed
);
}
this->mDefaultConfiguredSink = node;
@ -282,7 +310,7 @@ void PwDefaultTracker::setDefaultConfiguredSink(PwNode* node) {
if (node != nullptr) {
QObject::connect(
node,
&QObject::destroyed,
&PwBindableObject::destroying,
this,
&PwDefaultTracker::onDefaultConfiguredSinkDestroyed
);
@ -309,7 +337,13 @@ void PwDefaultTracker::setDefaultConfiguredSource(PwNode* node) {
qCInfo(logDefaults) << "Default configured source changed to" << node;
if (this->mDefaultConfiguredSource != nullptr) {
QObject::disconnect(this->mDefaultConfiguredSource, nullptr, this, nullptr);
// Targeted disconnect is used because this can also be the default source.
QObject::disconnect(
this->mDefaultConfiguredSource,
&PwBindableObject::destroying,
this,
&PwDefaultTracker::onDefaultConfiguredSourceDestroyed
);
}
this->mDefaultConfiguredSource = node;
@ -317,7 +351,7 @@ void PwDefaultTracker::setDefaultConfiguredSource(PwNode* node) {
if (node != nullptr) {
QObject::connect(
node,
&QObject::destroyed,
&PwBindableObject::destroying,
this,
&PwDefaultTracker::onDefaultConfiguredSourceDestroyed
);