Header: Image

Qt C++: Launch app at start up

2025-05-06

This article provides comprehensive code examples to implement autostart functionality in Qt applications across Windows, macOS, and Linux (including Flatpak).

 

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());
    }
}
c++

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:

#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;
        }
    }
}
objc++

Add required frameworks in CMake:

target_link_libraries(app PRIVATE
    "-framework ServiceManagement"
    "-framework CoreFoundation"
    "-framework AppKit"
)
cmake

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";
        }
    }
}
c++

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
ini

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();
    }
}
c++

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:

Leave your comments and reactions on GitHub