Skip to content

Starting at login

An app can register itself to start when people log in. Sabine writes the entry each desktop uses for this, and leaves it off when people turn it off in their system settings.

Pass an AutostartEntry to .autostart(...). Its id must be the app id, so uninstalling the app removes the entry; a different id stops the launch with an error.

use sabine::prelude::*;
use sabine::AutostartEntry;
fn main() {
SabineWindow::main(|window| {
let executable = std::env::current_exe()
.map_err(|error| SabineError::CreationFailed { message: error.to_string() })?;
Ok(window
.tray_app()
.app_id("com.example.sync")
.title("Sync")
.autostart(AutostartEntry {
id: "com.example.sync".into(),
name: "Sync".into(),
command: format!("\"{}\" --background", executable.display()),
enabled: true,
}))
});
}
Field Meaning
id The app id.
name The name the desktop shows for the entry.
command The command to run at login, in the platform’s command-line syntax.
enabled true writes the entry; false removes it.

Sabine applies the entry each time the app launches. With enabled: false, the entry is removed; an error removing it, such as a file that can’t be deleted, stops the launch and names the problem.

Each launch rewrites the entry only when its contents changed, so desktops that announce new login items don’t tell people about the app on every launch.

Development runs, such as sabine dev, register no autostart entry.

A setting in your app can turn the entry on and off at once. From the page:

import { app } from "@lantharos/sabine";
await app.setAutostart(true);

From Rust, with the process handle, for example from a tray checkbox:

use sabine::{PlatformEvent, TrayActivation};
window.on_desktop_event(|app, event| {
if let PlatformEvent::Tray(TrayActivation { item_id: Some(item), checked: Some(checked), .. }) = event
&& item == "start-at-login"
&& let Err(error) = app.set_autostart(*checked)
{
eprintln!("could not change the login item: {error}");
}
})

Both write the entry from .autostart(...) with its enabled replaced, and fail when the app has no autostart entry. Development runs accept the change without touching the desktop. Keep the choice in your app’s settings and pass it as enabled when you build the window, so the next launch applies the same choice.

The command uses the target platform’s own command-line syntax:

  • Linux: the Exec syntax of desktop entries. Quote paths with spaces in double quotes.
  • Windows: a Windows command line, the same as a shortcut target. Quote the executable path.
  • macOS: POSIX shell quoting, which Sabine splits into the LaunchAgent’s arguments without running a shell. Quoted empty arguments and escaped characters are kept, and malformed quoting stops the launch with an error.

Quoting the executable path, as in the example, works on all three.

People can turn login items off in their system settings without going through your app. Sabine keeps that choice: a later launch with enabled: true, or setAutostart(true), doesn’t turn the entry back on.

The entry is an XDG autostart desktop entry at ~/.config/autostart/<app id>.desktop (under $XDG_CONFIG_HOME when it is set), hidden from launchers.

Desktops turn an entry off by marking it rather than deleting it. Sabine leaves an entry marked Hidden=true or X-GNOME-Autostart-enabled=false as it is.

Desktops that offer their own “start at login” switch for installed apps write a login entry themselves. Name the arguments they should add, such as a flag that starts the app in the background, with autostart_arguments in Sabine.toml:

[app]
autostart_arguments = "--background"

Linux desktop entries carry them as X-Sabine-Autostart-Arguments, and desktops that read the key add them to the command in the login entry they write.

sabine install --autostart also registers an autostart entry for the installation it creates:

Terminal window
sabine install --bundle --autostart .

sabine uninstall and the uninstallers of Windows .exe and MSI packages remove the app’s autostart entry along with the rest of its registration. Upgrading an MSI package keeps it.

  • Tray icons covers the tray_app recipe used above.
  • Installing covers sabine install and sabine uninstall.