Compare commits

..

4 commits

8 changed files with 123 additions and 31 deletions

View file

@ -43,6 +43,8 @@ set shell id.
- Added `QS_DISABLE_CRASH_HANDLER` environment variable to disable crash handling. - Added `QS_DISABLE_CRASH_HANDLER` environment variable to disable crash handling.
- Added `QS_CRASHREPORT_URL` environment variable to allow overriding the crash reporter link. - Added `QS_CRASHREPORT_URL` environment variable to allow overriding the crash reporter link.
- Added `AppId` pragma and `QS_APP_ID` environment variable to allow overriding the desktop application ID. - Added `AppId` pragma and `QS_APP_ID` environment variable to allow overriding the desktop application ID.
- Added `DropExpensiveFonts` pragma and `QS_DROP_EXPENSIVE_FONTS` environment variable which avoids loading fonts which may cause lag and excessive memory usage if many variants are used.
- Unrecognized pragmas are no longer a hard error for future backward compatibility.
## Bug Fixes ## Bug Fixes
@ -71,6 +73,7 @@ set shell id.
- Fixed JsonAdapter sending unnecessary property changes for primitive values. - Fixed JsonAdapter sending unnecessary property changes for primitive values.
- Fixed JsonAdapter serialization for lists. - Fixed JsonAdapter serialization for lists.
- Fixed pipewire crashes after hotplugging devices and changing default outputs. - Fixed pipewire crashes after hotplugging devices and changing default outputs.
- Fixed launches failing for `--daemonize` on some systems.
## Packaging Changes ## Packaging Changes

View file

@ -77,6 +77,7 @@ int launch(const LaunchArgs& args, char** argv, QCoreApplication* coreApplicatio
QString iconTheme = qEnvironmentVariable("QS_ICON_THEME"); QString iconTheme = qEnvironmentVariable("QS_ICON_THEME");
QHash<QString, QString> envOverrides; QHash<QString, QString> envOverrides;
QString appId = qEnvironmentVariable("QS_APP_ID"); QString appId = qEnvironmentVariable("QS_APP_ID");
bool dropExpensiveFonts = false;
QString dataDir; QString dataDir;
QString stateDir; QString stateDir;
QString cacheDir; QString cacheDir;
@ -92,6 +93,7 @@ int launch(const LaunchArgs& args, char** argv, QCoreApplication* coreApplicatio
else if (pragma == "NativeTextRendering") pragmas.nativeTextRendering = true; else if (pragma == "NativeTextRendering") pragmas.nativeTextRendering = true;
else if (pragma == "IgnoreSystemSettings") pragmas.desktopSettingsAware = false; else if (pragma == "IgnoreSystemSettings") pragmas.desktopSettingsAware = false;
else if (pragma == "RespectSystemStyle") pragmas.useSystemStyle = true; else if (pragma == "RespectSystemStyle") pragmas.useSystemStyle = true;
else if (pragma == "DropExpensiveFonts") pragmas.dropExpensiveFonts = true;
else if (pragma.startsWith("IconTheme ")) pragmas.iconTheme = pragma.sliced(10); else if (pragma.startsWith("IconTheme ")) pragmas.iconTheme = pragma.sliced(10);
else if (pragma.startsWith("Env ")) { else if (pragma.startsWith("Env ")) {
auto envPragma = pragma.sliced(4); auto envPragma = pragma.sliced(4);
@ -116,8 +118,7 @@ int launch(const LaunchArgs& args, char** argv, QCoreApplication* coreApplicatio
} else if (pragma.startsWith("CacheDir ")) { } else if (pragma.startsWith("CacheDir ")) {
pragmas.cacheDir = pragma.sliced(9).trimmed(); pragmas.cacheDir = pragma.sliced(9).trimmed();
} else { } else {
qCritical() << "Unrecognized pragma" << pragma; qWarning() << "Unrecognized pragma" << pragma;
return -1;
} }
} else if (line.startsWith("import")) break; } else if (line.startsWith("import")) break;
} }
@ -177,6 +178,48 @@ int launch(const LaunchArgs& args, char** argv, QCoreApplication* coreApplicatio
qputenv(var.toUtf8(), val.toUtf8()); qputenv(var.toUtf8(), val.toUtf8());
} }
pragmas.dropExpensiveFonts |= qEnvironmentVariableIntValue("QS_DROP_EXPENSIVE_FONTS") == 1;
if (pragmas.dropExpensiveFonts) {
if (auto* runDir = QsPaths::instance()->instanceRunDir()) {
auto baseConfigPath = qEnvironmentVariable("FONTCONFIG_FILE");
if (baseConfigPath.isEmpty()) baseConfigPath = "/etc/fonts/fonts.conf";
auto filterPath = runDir->filePath("fonts-override.conf");
auto filterFile = QFile(filterPath);
if (filterFile.open(QFile::WriteOnly | QFile::Truncate | QFile::Text)) {
auto filterTemplate = QStringLiteral(R"(<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd">
<fontconfig>
<include ignore_missing="no">%1</include>
<selectfont>
<rejectfont>
<pattern>
<patelt name="fontwrapper">
<string>woff</string>
</patelt>
</pattern>
<pattern>
<patelt name="fontwrapper">
<string>woff2</string>
</patelt>
</pattern>
</rejectfont>
</selectfont>
</fontconfig>
)");
QTextStream(&filterFile) << filterTemplate.arg(baseConfigPath);
filterFile.close();
qputenv("FONTCONFIG_FILE", filterPath.toUtf8());
} else {
qCritical() << "Could not write fontconfig filter to" << filterPath;
}
} else {
qCritical() << "Could not create fontconfig filter: instance run directory unavailable";
}
}
// The qml engine currently refuses to cache non file (qsintercept) paths. // The qml engine currently refuses to cache non file (qsintercept) paths.
// if (auto* cacheDir = QsPaths::instance()->cacheDir()) { // if (auto* cacheDir = QsPaths::instance()->cacheDir()) {

View file

@ -84,21 +84,29 @@ void exitDaemon(int code) {
close(DAEMON_PIPE); close(DAEMON_PIPE);
close(STDIN_FILENO); auto fd = open("/dev/null", O_RDWR);
close(STDOUT_FILENO); if (fd == -1) {
close(STDERR_FILENO); qCritical().nospace() << "Failed to open /dev/null for daemon stdio" << errno << ": "
<< qt_error_string();
if (open("/dev/null", O_RDONLY) != STDIN_FILENO) { // NOLINT return;
qFatal() << "Failed to open /dev/null on stdin";
} }
if (open("/dev/null", O_WRONLY) != STDOUT_FILENO) { // NOLINT if (dup2(fd, STDIN_FILENO) != STDIN_FILENO) { // NOLINT
qFatal() << "Failed to open /dev/null on stdout"; qCritical().nospace() << "Failed to set daemon stdin to /dev/null" << errno << ": "
<< qt_error_string();
} }
if (open("/dev/null", O_WRONLY) != STDERR_FILENO) { // NOLINT if (dup2(fd, STDOUT_FILENO) != STDOUT_FILENO) { // NOLINT
qFatal() << "Failed to open /dev/null on stderr"; qCritical().nospace() << "Failed to set daemon stdout to /dev/null" << errno << ": "
<< qt_error_string();
} }
if (dup2(fd, STDERR_FILENO) != STDERR_FILENO) { // NOLINT
qCritical().nospace() << "Failed to set daemon stderr to /dev/null" << errno << ": "
<< qt_error_string();
}
close(fd);
} }
int main(int argc, char** argv) { int main(int argc, char** argv) {

View file

@ -276,7 +276,7 @@ void I3IpcController::handleWorkspaceEvent(I3IpcEvent* event) {
if (newWorkspace->bindableMonitor().value()) { if (newWorkspace->bindableMonitor().value()) {
auto* monitor = newWorkspace->bindableMonitor().value(); auto* monitor = newWorkspace->bindableMonitor().value();
monitor->setFocusedWorkspace(newWorkspace); monitor->setActiveWorkspace(newWorkspace);
this->bFocusedMonitor = monitor; this->bFocusedMonitor = monitor;
} }
} else if (change == "empty") { } else if (change == "empty") {
@ -286,13 +286,7 @@ void I3IpcController::handleWorkspaceEvent(I3IpcEvent* event) {
if (oldWorkspace != nullptr) { if (oldWorkspace != nullptr) {
qCInfo(logI3Ipc) << "Deleting" << oldWorkspace->bindableId().value() << name; qCInfo(logI3Ipc) << "Deleting" << oldWorkspace->bindableId().value() << name;
if (this->bFocusedWorkspace == oldWorkspace) {
this->bFocusedMonitor->setFocusedWorkspace(nullptr);
}
this->workspaces()->removeObject(oldWorkspace); this->workspaces()->removeObject(oldWorkspace);
delete oldWorkspace; delete oldWorkspace;
} else { } else {
qCInfo(logI3Ipc) << "Workspace" << name << "has already been deleted"; qCInfo(logI3Ipc) << "Workspace" << name << "has already been deleted";

View file

@ -40,21 +40,37 @@ void I3Monitor::updateFromObject(const QVariantMap& obj) {
this->bHeight = rect.value("height").value<qint32>(); this->bHeight = rect.value("height").value<qint32>();
this->bScale = obj.value("scale").value<qreal>(); this->bScale = obj.value("scale").value<qreal>();
if (!this->bActiveWorkspace auto* activeWorkspace = this->bActiveWorkspace.value();
|| activeWorkspaceName != this->bActiveWorkspace->bindableName().value()) if (!activeWorkspace || activeWorkspaceName != activeWorkspace->bindableName().value()) {
{
if (activeWorkspaceName.isEmpty()) { if (activeWorkspaceName.isEmpty()) {
this->bActiveWorkspace = nullptr; activeWorkspace = nullptr;
} else { } else {
this->bActiveWorkspace = this->ipc->findWorkspaceByName(activeWorkspaceName); activeWorkspace = this->ipc->findWorkspaceByName(activeWorkspaceName);
} }
}; };
this->setActiveWorkspace(activeWorkspace);
Qt::endPropertyUpdateGroup(); Qt::endPropertyUpdateGroup();
} }
void I3Monitor::updateInitial(const QString& name) { this->bName = name; } void I3Monitor::updateInitial(const QString& name) { this->bName = name; }
void I3Monitor::setFocusedWorkspace(I3Workspace* workspace) { this->bActiveWorkspace = workspace; }; void I3Monitor::setActiveWorkspace(I3Workspace* workspace) {
auto* oldWorkspace = this->bActiveWorkspace.value();
if (oldWorkspace == workspace) return;
if (oldWorkspace) {
QObject::disconnect(oldWorkspace, nullptr, this, nullptr);
}
if (workspace) {
QObject::connect(workspace, &QObject::destroyed, this, &I3Monitor::onActiveWorkspaceDestroyed);
}
this->bActiveWorkspace = workspace;
}
void I3Monitor::onActiveWorkspaceDestroyed() { this->bActiveWorkspace = nullptr; }
} // namespace qs::i3::ipc } // namespace qs::i3::ipc

View file

@ -55,7 +55,7 @@ public:
[[nodiscard]] QBindable<qreal> bindableScale() { return &this->bScale; } [[nodiscard]] QBindable<qreal> bindableScale() { return &this->bScale; }
[[nodiscard]] QBindable<bool> bindableFocused() { return &this->bFocused; } [[nodiscard]] QBindable<bool> bindableFocused() { return &this->bFocused; }
[[nodiscard]] QBindable<I3Workspace*> bindableActiveWorkspace() { [[nodiscard]] QBindable<I3Workspace*> bindableActiveWorkspace() const {
return &this->bActiveWorkspace; return &this->bActiveWorkspace;
} }
@ -64,7 +64,7 @@ public:
void updateFromObject(const QVariantMap& obj); void updateFromObject(const QVariantMap& obj);
void updateInitial(const QString& name); void updateInitial(const QString& name);
void setFocusedWorkspace(I3Workspace* workspace); void setActiveWorkspace(I3Workspace* workspace);
signals: signals:
void idChanged(); void idChanged();
@ -79,6 +79,9 @@ signals:
void lastIpcObjectChanged(); void lastIpcObjectChanged();
void focusedChanged(); void focusedChanged();
private slots:
void onActiveWorkspaceDestroyed();
private: private:
I3IpcController* ipc; I3IpcController* ipc;

View file

@ -43,14 +43,17 @@ void I3Workspace::updateFromObject(const QVariantMap& obj) {
auto monitorName = obj.value("output").value<QString>(); auto monitorName = obj.value("output").value<QString>();
if (!this->bMonitor || monitorName != this->bMonitor->bindableName().value()) { auto* monitor = this->bMonitor.value();
if (!monitor || monitorName != monitor->bindableName().value()) {
if (monitorName.isEmpty()) { if (monitorName.isEmpty()) {
this->bMonitor = nullptr; monitor = nullptr;
} else { } else {
this->bMonitor = this->ipc->findMonitorByName(monitorName, true); monitor = this->ipc->findMonitorByName(monitorName, true);
} }
} }
this->setMonitor(monitor);
Qt::endPropertyUpdateGroup(); Qt::endPropertyUpdateGroup();
} }
@ -58,4 +61,21 @@ void I3Workspace::activate() {
this->ipc->dispatch(QString("workspace number %1").arg(this->bNumber.value())); this->ipc->dispatch(QString("workspace number %1").arg(this->bNumber.value()));
} }
void I3Workspace::setMonitor(I3Monitor* monitor) {
auto* oldMonitor = this->bMonitor.value();
if (oldMonitor == monitor) return;
if (oldMonitor) {
QObject::disconnect(oldMonitor, nullptr, this, nullptr);
}
if (monitor) {
QObject::connect(monitor, &QObject::destroyed, this, &I3Workspace::onMonitorDestroyed);
}
this->bMonitor = monitor;
}
void I3Workspace::onMonitorDestroyed() { this->bMonitor = nullptr; }
} // namespace qs::i3::ipc } // namespace qs::i3::ipc

View file

@ -57,11 +57,13 @@ public:
[[nodiscard]] QBindable<bool> bindableActive() { return &this->bActive; } [[nodiscard]] QBindable<bool> bindableActive() { return &this->bActive; }
[[nodiscard]] QBindable<bool> bindableFocused() { return &this->bFocused; } [[nodiscard]] QBindable<bool> bindableFocused() { return &this->bFocused; }
[[nodiscard]] QBindable<bool> bindableUrgent() { return &this->bUrgent; } [[nodiscard]] QBindable<bool> bindableUrgent() { return &this->bUrgent; }
[[nodiscard]] QBindable<I3Monitor*> bindableMonitor() { return &this->bMonitor; } [[nodiscard]] QBindable<I3Monitor*> bindableMonitor() const { return &this->bMonitor; }
[[nodiscard]] QVariantMap lastIpcObject() const; [[nodiscard]] QVariantMap lastIpcObject() const;
void updateFromObject(const QVariantMap& obj); void updateFromObject(const QVariantMap& obj);
void setMonitor(I3Monitor* monitor);
signals: signals:
void idChanged(); void idChanged();
void nameChanged(); void nameChanged();
@ -72,6 +74,9 @@ signals:
void monitorChanged(); void monitorChanged();
void lastIpcObjectChanged(); void lastIpcObjectChanged();
private slots:
void onMonitorDestroyed();
private: private:
I3IpcController* ipc; I3IpcController* ipc;