While Qt offers cross-platform development capabilities, it lacks built-in support for login item management. Developers must therefore implement platform-specific solutions or rely on third-party libraries. Though options like QAutoStart and qautostart exist, neither of them are well maintained or provide optimal ways for autostarting on macOS and Linux Flatpak. Specifically, using osascript
for registering autostart is an overkill and requires extra permissions. Also, Flatpak has its own API for requesting autostart and need some extra coding. Therefore, implementing our own platform-specific solutions is a better approach.
Windows: Registry-Based Autostart #
Windows provides the simplest autostart mechanism through its registry system. By writing to the HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run
key, applications can register themselves without requiring administrative privileges.
The QSettings
class provides convenient access to the Windows registry. When enabled, we store the application's executable path under this key, using the application name as the value identifier.
#include <QDir>
#include <QSettings>
// Register application in Windows Registry
void setAutostart(bool enabled) {
QSettings settings(
"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run",
QSettings::NativeFormat);
if (enabled) {
// Store application path with proper Windows path separators
settings.setValue(
QCoreApplication::applicationName(),
QDir::toNativeSeparators(QCoreApplication::applicationFilePath()));
} else {
// Remove autostart entry when disabled
settings.remove(QCoreApplication::applicationName());
}
}
macOS: Service Management Framework #
macOS 13 introduced a streamlined API through the ServiceManagement
framework that eliminates the need for deprecated helpers like osascript
. The SMAppService
class provides a secure, Apple-approved method for managing login items without requiring special entitlements.
Key requirements:
- Application must be code-signed
- Requires linking against
ServiceManagement
framework
#include <ServiceManagement/ServiceManagement.h>
void setAutostart(bool enabled) {
SMAppService *service = [SMAppService mainAppService];
NSError *error = nil;
if (enabled) {
// Register with system services using modern API
if (![service registerAndReturnError:&error]) {
qWarning() << "Autostart registration failed:" << error.localizedDescription;
}
} else {
// Cleanly remove registration when disabled
if (![service unregisterAndReturnError:&error]) {
qWarning() << "Autostart removal failed:" << error.localizedDescription;
}
}
}
Add required frameworks in CMake:
target_link_libraries(app PRIVATE
"-framework ServiceManagement"
"-framework CoreFoundation"
"-framework AppKit"
)
Linux (Native): Desktop Entry Files #
Traditional Linux systems rely on freedesktop.org standards for autostart management. Applications should install a .desktop
file in the user's ~/.config/autostart
directory. This file contains metadata and execution instructions following the Desktop Entry Specification.
Our implementation copies a pre-configured template from application resources to the target location.
void setAutostart(bool enabled) {
QString configPath = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
QString desktopPath = configPath + "/autostart/app.desktop";
if (enabled) {
// Copy template from resources to autostart directory
if (!QFile::copy(":/app.desktop", desktopPath)) {
qWarning() << "Failed to create autostart desktop file";
}
} else {
// Remove existing autostart file
if (QFile::exists(desktopPath) && !QFile::remove(desktopPath)) {
qWarning() << "Failed to remove autostart file";
}
}
}
Example .desktop
template:
[Desktop Entry]
Type=Application
Categories=Qt;Utility;
Exec=/path/to/your/app
Icon=your-app-icon
Name=Your Application
Comment=Your app description
X-GNOME-Autostart-enabled=true
X-GNOME-Autostart-Delay=2
X-KDE-autostart-phase=2
X-KDE-autostart-after=panel
X-LXQt-Need-Tray=true
Linux (Flatpak): Background Portal #
Flatpak applications operate in sandboxed environments, requiring special handling through the XDG Desktop Portal. The org.freedesktop.portal.Background
interface manages autostart requests while maintaining security boundaries.
This solution uses D-Bus to communicate with the portal, requesting background execution permissions with appropriate metadata. The randomized handle token prevents conflicts during multiple requests.
void setAutostart(bool enabled) {
QDBusConnection bus = QDBusConnection::sessionBus();
// Create D-Bus request with unique handle
QDBusMessage msg = QDBusMessage::createMethodCall(
"org.freedesktop.portal.Desktop",
"/org/freedesktop/portal/desktop",
"org.freedesktop.portal.Background",
"RequestBackground");
int token = QRandomGenerator::global()->bounded(1000, 9999);
QMap<QString, QVariant> options = {
{"autostart", enabled},
{"background", enabled},
{"commandline", QStringList({"app-executable"})},
{"reason", "Automatically launch application at login"},
{"handle_token", QString("com/example/AppNameWithoutDash/%1").arg(token)}
};
msg << "" << options; // Empty parent window ID
QDBusMessage response = bus.call(msg);
// Handle response and connect to signal for final status
if (response.type() == QDBusMessage::ReplyMessage) {
QDBusObjectPath handle = response.arguments().at(0).value<QDBusObjectPath>();
bus.connect("org.freedesktop.portal.Desktop", handle.path(),
"org.freedesktop.portal.Request", "Response",
this, SLOT(handleFlatpakResponse(uint, QVariantMap)));
} else {
qWarning() << "Portal request failed:" << response.errorMessage();
}
}
Not that if you include dashes in AppNameWithoutDash
, the xdg-desktop-portal
may crash. Reading the logs via journalctl --user -u xdg-desktop-portal
can be helpful when debugging.
Full code example #
For full code examples, please refer to: