Launchers: apps' own desktop entries, rewritten; keyring moves into them

steamFrame.launchers.<desktop ID> takes the app's own entry (a Flatpak's
export, a Nix package's or a host file) and rewrites only its command lines
(hostEnv, env, flatpakArgs, args, wrappers) and the keys asked for
(mimeTypes, defaultFor, settings), so name, icon, translations and actions
stay the app's and follow its updates.

- Package entries are rewritten at build time; Flatpak/host file entries at
  runtime by steam-frame-nix-launchers into
  <session.runtimeDir>/steam-frame-nix/applications (tmpfs), on switch, at
  login and when Flatpak installs/updates apps (path unit); the Home Manager
  links point there.
- keyring.{enable,electron} replaces steamFrame.keyring.{flatpaks,programs}
  (clean break: the old options fail with the new form).
- Firefox's launcher is the Flatpak's entry with the profile wrapper in
  front (no --profile for the profile manager); Jellyfin's hardware
  decoding sets flatpakArgs and env.
- cleanup --all removes the tmpfs entries.
- Check on copies of the real Element, KRDC, Firefox, gedit, Jellyfin and
  Claude entries, quoting and generator behaviour.
This commit is contained in:
Pierre Kisters committed 2026-09-29 02:05:06 +02:00
1 parent 5d15902ba1
commit 0d9a238e5a
37 files changed
+4140 -577

No files matched your search

+1
View File
@@ -25,6 +25,7 @@ is reported as "left alone" and never touched:
| `firefox`: `user.js` in Firefox profiles | links to `/app/etc/firefox/steam-frame-nix-desktop-user.js` (left alone while the profile is in use), older links to `*-firefox-*user.js` and copies starting with the steam-frame-nix marker comment, and the values they left in `prefs.js` (only with Firefox closed) |
| `jellyfin` | the hwdec shim entries in the Jellyfin Flatpak's user override `~/.local/share/flatpak/overrides/org.jellyfin.JellyfinDesktop` (nix-flatpak), an empty override file, and the shim copy of earlier versions in `~/.var/app/org.jellyfin.JellyfinDesktop` (marker `~/.local/state/steam-frame-nix/jellyfin-hwdec-shim`) |
| `ui-state`: `~/.local/state/steam-frame-nix/ui-patches/<name>.json` | the dashboard patches' saved choices; `--all` only, never `--orphans`; stray `*.json.tmp` files |
| `launchers`: `/run/user/1000/steam-frame-nix/applications` | the entries of [launchers](launchers.md) (tmpfs); `--all` only (switches remove those of removed launchers themselves) |
| `dirs` | `~/.local/state/steam-frame-nix` and `/run/user/1000/steam-frame-nix` when empty |
### Left by older versions
+12 -5
View File
@@ -53,12 +53,18 @@ modules/
keyboard-layout.nix gamescope-session drop-in with XKB_DEFAULT_* (keyboard.layout)
clipboard-sync.nix app: KDE autostart of clipboard-sync (clipboardSync)
hidden-apps.nix Hidden=true desktop entries (launcherMenu.hiddenApps)
keyring.nix app: launchers sharing the KDE wallet (keyring)
launchers.nix links, MIME defaults, units of the launchers (launchers.<id>)
launchers/
lib.nix the launcher option type, Exec quoting, package entries (build)
rewrite.awk build+service: rewrites a desktop entry's Exec lines and keys
generate.sh service steam-frame-nix-launchers (+ .path), switch: entries
of Flatpaks/host files in <runtimeDir>/steam-frame-nix/applications
check.nix, fixtures/ test: real entries rewritten (fixtures/expected), generator runs
docker.nix service: rootless dockerd (docker)
firefox.nix Flatpak prefs extension and launcher entry (firefox)
firefox/launcher.nix app: launcher script (desktop profile, fullscreen fix)
firefox/check.nix test: launcher against a fake flatpak
jellyfin.nix desktop entry with flatpak run options (jellyfin.hardwareDecoding)
firefox.nix Flatpak prefs extension and launcher (firefox)
firefox/wrapper.nix app: launcher wrapper (desktop profile, fullscreen fix)
firefox/check.nix test: wrapper against a fake flatpak
jellyfin.nix launcher with flatpak run options (jellyfin.hardwareDecoding)
jellyfin/mpv-hwdec-shim.c app: LD_PRELOAD shim, hwdec auto* -> v4l2m2m-copy
jellyfin/shim.nix build: the shim as lib/mpv-hwdec-shim.so
jellyfin/check.nix test: shim ELF and rewriting
@@ -116,6 +122,7 @@ they are kept even where a file name says more:
| `window-curvature` | `dashboard.windowCurvature` | `window-curvature` (SteamVR) | `steam-ui-patches` |
| `frame-controls` | `dashboard.frameControls` | `frame-controls` (SteamVR, state) | `steam-ui-patches` |
| `steamvr-debugger` | `steamvrDebugger` | | `steamvr-webhelper-debugger` |
| `launchers` | `launchers` | | `steam-frame-nix-launchers` (`.service`, `.path`) |
Log of all patches:
`journalctl --user -u steam-ui-patches -u steam-keyboard-patch -u vr-keyboard-relay`.
+12 -9
View File
@@ -14,9 +14,10 @@ browser the portal opens links with the first installed `https` handler
## What you get
A launcher for the Flathub Firefox Flatpak (`org.mozilla.firefox`, stable;
install it yourself). The launcher shadows the Flatpak's own entry (same
ID), so default-browser associations keep working.
A [launcher](launchers.md) for the Flathub Firefox Flatpak
(`org.mozilla.firefox`, stable; install it yourself): the Flatpak's own
entry with a wrapper in front, same ID, so default-browser associations
keep working.
- **`vrFullscreenFix`** (on): `full-screen-api.ignore-widgets` makes
fullscreen fill just the window. Not applied in the desktop profile.
@@ -26,11 +27,11 @@ ID), so default-browser associations keep working.
- **`prefs`:** further `about:config` values for every profile; they can
also override the fixes above.
- **`desktopProfile`** (`"desktop"`): in the nested desktop the launcher
uses this separate profile (a normal Firefox profile with its own browser
uses this separate profile (not for "Open Profile Manager") (a normal Firefox profile with its own browser
data, created on first use). `null`: the default profile in both sessions.
- **`defaultBrowser`** (off): the launcher becomes the default for `http`,
`https` and `text/html` (Home Manager's `xdg.mimeApps`, which then owns
`~/.config/mimeapps.list`).
`https` and `text/html` (the launcher's `defaultFor`: Home Manager's
`xdg.mimeApps`, which then owns `~/.config/mimeapps.list`).
`prefs` and the fixes are *default* values, not user values: `about:config`
can still change them per profile, and removing one leaves nothing behind.
@@ -71,9 +72,11 @@ to a store directory; Flatpak mounts it itself, so the sandbox doesn't get
### Desktop profile
The launcher (a desktop entry with the Flatpak's ID) picks `desktopProfile`
when started in the nested desktop. The desktop profile undoes the
fullscreen fix only while its Firefox runs: the launcher links the
The launcher's wrapper (`modules/firefox/wrapper.nix`) gets the entry's
`flatpak run …` command line and, in the nested desktop, adds `--profile
<desktopProfile>` (not to the profile manager action, `--ProfileManager`).
The desktop profile undoes the fullscreen fix only while its Firefox runs:
the wrapper links the
profile's `user.js` to `/app/etc/firefox/steam-frame-nix-desktop-user.js`
(a sandbox path) right before starting Firefox, waits for it, and once it
has exited and the profile is no longer in use removes the link and the
+12 -9
View File
@@ -19,8 +19,9 @@ which
## What you get
The Jellyfin desktop entry (same ID as the Flatpak's, so the KDE menu and
the "+" menu start it) runs the Flatpak with device access (`devices=all`)
A [launcher](launchers.md) (the Flatpak's own entry, rewritten under its
ID, so the KDE menu and the "+" menu start it) runs the Flatpak with device
access (`devices=all`)
and makes mpv use `hwdec` (default `v4l2m2m-copy,auto-copy`); explicit
values such as `no` stay. mpv tries the listed decoders in order and falls
back to software decoding per stream. With the default, 1080p H.264 plays
@@ -38,7 +39,7 @@ steamFrame.jellyfin.hardwareDecoding.enable = true;
```
From a terminal, start it with the command line of
`steamFrame.jellyfin.hardwareDecoding.command` (or `grep ^Exec=
`steamFrame.jellyfin.hardwareDecoding.command` (or the one of `grep ^Exec=
~/.local/share/applications/org.jellyfin.JellyfinDesktop.desktop`); its
output shows `mpv-hwdec-shim: hwdec "auto-copy" -> "v4l2m2m-copy,auto-copy"`,
then mpv's `Using hardware decoding (v4l2m2m-copy)`.
@@ -63,12 +64,13 @@ option). It is preloaded from the Nix store; only its store path is exposed
(read-only) to the sandbox. It would work for any libmpv app that sets
`hwdec=auto*`, but only Jellyfin Desktop is set up here.
### The desktop entry
### The launcher
Nothing is written to Flatpak's overrides: the entry
(`~/.local/share/applications/org.jellyfin.JellyfinDesktop.desktop`) passes
device access and the shim as `flatpak run` options, so they apply to
launches from that entry and are gone with it:
Nothing is written to Flatpak's overrides: the launcher
`steamFrame.launchers."org.jellyfin.JellyfinDesktop"` passes device access
and the shim as `flatpak run` options (`flatpakArgs`, `env`), so they apply
to launches from the entry and are gone with it. Its command lines (the
entry's and the four actions') become
```sh
flatpak run --branch=stable --arch=aarch64 --command=jellyfin-desktop \
@@ -77,7 +79,8 @@ flatpak run --branch=stable --arch=aarch64 --command=jellyfin-desktop \
--env=SFN_MPV_HWDEC=v4l2m2m-copy,auto-copy org.jellyfin.JellyfinDesktop
```
(`command` is this line with the store path.)
(`command` is about this line, with the store path and without the
entry's `--branch`/`--arch`/`--command`.)
Older versions used a Flatpak override (via nix-flatpak or a Home Manager
link) and a shim copy in `~/.var/app/org.jellyfin.JellyfinDesktop`;
+4 -126
View File
@@ -1,128 +1,6 @@
# Keyring launchers
`keyring.flatpaks.<app ID>`, `keyring.programs.<desktop ID>`, module
`keyring`. Options: [README, Options](../README.md#options).
## Problem
Apps that keep logins or passwords in the KDE wallet lose them between the
Frame's [two sessions](../README.md#two-sessions):
- **A second wallet:** the nested desktop has its own D-Bus. An app started
there starts a second `kwalletd6` on that bus; what it stores there is
invisible to the same app in the Steam session, which talks to the running
`kwalletd6` on the outer bus.
- **Electron in the Steam session:** Electron picks its keyring from
`XDG_CURRENT_DESKTOP`. In the Steam session that is `gamescope`, which it
doesn't know, so it falls back to `basic` (a local, unencrypted store) and
the login made in the desktop (stored in the wallet) is gone.
- **Flatpak permissions:** many Flatpaks may not talk to the wallet at all
(Element), or only to `org.kde.kwalletd6` while their KF6 wallet client
reads through the Secret Service `org.freedesktop.secrets` (KRDC: "Password
not found").
- **Login callbacks:** SSO logins come back through a URL scheme
(`io.element.desktop://`, `claude://`) opened by the portal. Unless the app
is the default and a recommended handler, the portal opens an app chooser,
which isn't shown in VR.
## What you get
For each listed app, a desktop entry in `~/.local/share/applications` with
the app's own desktop ID, so it replaces the Flatpak's or package's entry in
the KDE menu and the "+" menu. It starts the app
- on the outer bus (`session.busEnv`): one `kwalletd6` for both sessions;
- for Flatpaks, with `--talk-name=org.kde.kwalletd6` and
`--talk-name=org.freedesktop.secrets` as `flatpak run` options (not a
Flatpak override: they apply only to launches from this entry and are
gone with it);
- with `electron = true`, with `--password-store=kwallet6`;
- as the default and recommended handler of its `schemeHandlers`
(`xdg.mimeApps`).
Logins then survive switching between the desktop and VR windows. Changes
take effect at the next start of the app.
## Configuration
The Flatpak isn't in the Nix store, so its entry can't be read at build
time: give the fields you want in menus (`name` is required; `icon`
defaults to the app ID, as Flatpaks export it). A Flatpak (installing it is
up to you):
```nix
steamFrame.keyring.flatpaks = {
"im.riot.Riot" = {
name = "Element";
electron = true;
categories = [ "Network" "InstantMessaging" ];
schemeHandlers = [ "element" "io.element.desktop" ]; # SSO callback
};
"org.kde.krdc" = {
name = "KRDC";
fieldCode = "%u";
categories = [ "Qt" "KDE" "Network" "RemoteAccess" ];
mimeTypes = [ "x-scheme-handler/vnc" "x-scheme-handler/rdp" ]; # listed, not made default
};
};
```
A program from a Nix package: use the package's own desktop ID (without
`.desktop`), so the entry replaces the package's:
```nix
{ pkgs, ... }: {
home.packages = [ pkgs.claude-desktop ];
steamFrame.keyring.programs."com.anthropic.Claude" = {
name = "Claude";
executable = "${pkgs.claude-desktop}/bin/claude-desktop";
icon = "claude-desktop";
electron = true;
startupWMClass = "com.anthropic.Claude";
schemeHandlers = [ "claude" ]; # login callback
settings.StartupNotify = "true";
actions.NewChat = {
name = "New Chat";
args = [ ''"claude://claude.ai/new?surface=chat&source=desktop_action"'' ];
};
};
}
```
`args`, `actions.<name>.args` and `settings` are written into the entry as
given (desktop entry syntax: quote yourself). Each app's `command` (read
only) is its command line without arguments, to start it from a terminal
the same way. The fields each entry takes are in the
[options](../README.md#options).
## Caveats
- The wallet must be KDE's (`kwalletd6`, and `ksecretd` for the Secret
Service), as SteamOS ships it.
- Only launches from the entry get the fixes: a Flatpak started with plain
`flatpak run`, or a program from `~/.nix-profile/bin`, doesn't.
- `schemeHandlers` turns on Home Manager's `xdg.mimeApps`, which then owns
`~/.config/mimeapps.list`: set other defaults there too.
- A Flatpak's own entry may have fields not given here (translations,
`Keywords`, actions); add what you need through `settings` and `actions`.
## How it works
For `"im.riot.Riot" = { name = "Element"; electron = true; ... }` the entry's
command line is
```sh
env DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus \
flatpak run --talk-name=org.kde.kwalletd6 --talk-name=org.freedesktop.secrets \
im.riot.Riot --password-store=kwallet6 %U
```
`flatpak run` connects the sandbox's D-Bus proxy to the bus in its own
environment, so the prefix (not `--env=`) moves the app to the outer bus.
`--talk-name` adds to the Flatpak's permissions for this launch only. The
entry also sets `X-Flatpak=<app ID>`. For `programs` the prefix runs
`executable` directly.
`schemeHandlers` go into `xdg.mimeApps.defaultApplications` and
`associations.added` (Added Associations, what the portal treats as
recommended), and into the entry's `MimeType=` with `mimeTypes`.
Moved: apps that keep their logins in the KDE wallet are now
[launchers](launchers.md) with `keyring.enable` (the former
`keyring.flatpaks` / `keyring.programs` options and how to convert them are
described there).
+204
View File
@@ -0,0 +1,204 @@
# Launchers
`launchers.<desktop ID>`, module `launchers`. Options:
[README, Options](../README.md#options).
## Problem
Starting an app the way the Frame needs often means changing its desktop
entry: extra `flatpak run` options, environment, a wrapper script, making it
a default handler. A hand-written entry with the same ID replaces the
original, but loses what the original has (translations, icon, actions,
MIME types) and goes stale when the app changes its entry in an update.
The most common case is the KDE wallet, where apps lose their logins between
the Frame's [two sessions](../README.md#two-sessions):
- **A second wallet:** the nested desktop has its own D-Bus. An app started
there starts a second `kwalletd6` on that bus; what it stores there is
invisible to the same app in the Steam session, which talks to the running
`kwalletd6` on the outer bus.
- **Electron in the Steam session:** Electron picks its keyring from
`XDG_CURRENT_DESKTOP`. In the Steam session that is `gamescope`, which it
doesn't know, so it falls back to `basic` (a local, unencrypted store) and
the login made in the desktop (stored in the wallet) is gone.
- **Flatpak permissions:** many Flatpaks may not talk to the wallet at all
(Element), or only to `org.kde.kwalletd6` while their KF6 wallet client
reads through the Secret Service `org.freedesktop.secrets` (KRDC: "Password
not found").
- **Login callbacks:** SSO logins come back through a URL scheme
(`io.element.desktop://`, `claude://`) opened by the portal. Unless the app
is the default and a recommended handler, the portal opens an app chooser,
which isn't shown in VR.
## What you get
For each `launchers.<desktop ID>`, the app's own desktop entry, rewritten,
under the same ID in `~/.local/share/applications`: it replaces the original
in the KDE menu and the "+" menu (which reads only that directory). Name,
icon, translations, categories, actions and MIME types stay the app's; only
the command lines (the entry's and every action's) and the keys you set
change. When a Flatpak update changes its entry, the launcher follows within
seconds.
- **`keyring.enable`:** the app runs on the outer bus (one `kwalletd6` for
both sessions); a Flatpak may also talk to the wallet
(`--talk-name=org.kde.kwalletd6 --talk-name=org.freedesktop.secrets`).
**`keyring.electron`** adds `--password-store=kwallet6`. Logins then
survive switching between the desktop and VR windows.
- **`defaultFor`:** the app becomes the default and a recommended handler of
these MIME types / URL schemes (login callbacks open without a chooser).
- **`flatpakArgs`, `env`, `hostEnv`, `args`, `wrappers`:** options,
environment and wrapper commands for the launch; Flatpak permissions this
way apply only to launches from the entry (no Flatpak override files).
- **`mimeTypes`, `settings`:** further `MimeType=` entries; set, override or
remove `[Desktop Entry]` keys.
Changes take effect at the next start of the app. [Firefox](firefox.md)
(`firefox.*`) and [Jellyfin](jellyfin.md) (`jellyfin.hardwareDecoding`) are
launchers too.
## Configuration
The desktop ID is the attribute name; the source is the Flatpak with that
app ID unless `source.package` or `source.file` is set. Installing the app
is up to you.
A Flatpak whose logins live in the wallet (Element, an Electron app), with
its SSO callback schemes:
```nix
steamFrame.launchers."im.riot.Riot" = {
keyring = { enable = true; electron = true; };
defaultFor = [ "x-scheme-handler/element" "x-scheme-handler/io.element.desktop" ];
};
steamFrame.launchers."org.kde.krdc".keyring.enable = true;
```
A program from a Nix package, from the package's own entry
(`share/applications/<desktop ID>.desktop`, rewritten at build time):
```nix
{ pkgs, ... }: {
home.packages = [ pkgs.claude-desktop ];
steamFrame.launchers."com.anthropic.Claude" = {
source.package = pkgs.claude-desktop;
keyring = { enable = true; electron = true; };
defaultFor = [ "x-scheme-handler/claude" ]; # login callback
};
}
```
Flatpak permissions and environment for one app (what
[Jellyfin](jellyfin.md) sets):
```nix
steamFrame.launchers."org.jellyfin.JellyfinDesktop" = {
flatpakArgs = [ "--device=all" ];
env.SFN_MPV_HWDEC = "v4l2m2m-copy"; # --env= inside the sandbox
};
```
A wrapper, which gets the whole original command line as its arguments (how
[Firefox](firefox.md) picks its desktop profile), and keys of the entry:
```nix
{ pkgs, ... }: {
steamFrame.launchers."org.chromium.Chromium" = {
wrappers = [ "${pkgs.writeShellScript "no-gpu" ''exec "$@" --disable-gpu''}" ];
settings = { Name = "Chromium (no GPU)"; Keywords = null; }; # null: removed
};
}
```
`args`, `flatpakArgs`, `wrappers` and the environment values are plain
strings, quoted for the entry for you; `settings` values are written as
given (key-file syntax). Each launcher's `command` (read only) is roughly its
command line, to start it the same way from a terminal. A host file as
source: `source.file = "/usr/share/applications/<ID>.desktop"`.
**From `keyring.*` (until 2026-09):** `keyring.flatpaks.<ID>` and
`keyring.programs.<ID>` are gone; using them fails with the new form.
`electron = true` is `keyring = { enable = true; electron = true; }`,
`schemeHandlers = [ "x" ]` is `defaultFor = [ "x-scheme-handler/x" ]`,
`programs.<ID>.executable` becomes `source.package`; name, icon, categories,
actions and field codes come from the app's entry.
## Caveats
- Only launches from the entry get the changes: a Flatpak started with plain
`flatpak run`, or a program from `~/.nix-profile/bin`, doesn't.
- A Flatpak's or host file's launcher exists only while its source does: an
app not installed (or uninstalled) has none. The entry is written at
runtime into `/run/user/1000` (tmpfs); until then, e.g. right after boot
before the Steam session's user services started, the link in
`~/.local/share/applications` points nowhere, and menus show neither the
launcher nor the original entry.
- An entry whose command line isn't recognised (a Flatpak's without
`flatpak run ... <app ID>`, a program's starting with `env -…`) is used
unchanged, with a warning in the journal.
- Edits with KDE's "Edit Application" don't last: use `settings`. An
edited entry is overwritten at the next rewrite; a file that replaced the
link is reported, and Home Manager refuses the next switch until it is
removed. Warnings go to the journal
(`journalctl --user -u steam-frame-nix-launchers`).
- `defaultFor` turns on Home Manager's `xdg.mimeApps`, which then owns
`~/.config/mimeapps.list`: set other defaults there too. Two launchers
claiming the same type fail the build.
- The wallet must be KDE's (`kwalletd6`, and `ksecretd` for the Secret
Service), as SteamOS ships it.
## How it works
The Exec lines are rewritten, everything else is copied line by line
(comments, unknown and localized keys as they are):
| Source | Exec= becomes |
|---|---|
| Flatpak | `<hostEnv> <wrappers> <original up to the app ID> <flatpakArgs> <--env=…> <app ID> <args> <rest>` |
| package, file | `<hostEnv + env> <wrappers> <original up to the program> <args> <rest>` |
For a Flatpak the app ID is the first token equal to `X-Flatpak=` (or the
source ID) after `flatpak run`; for others the program is the first token
after an optional leading `env A=B …`. The rest (`--file-forwarding`,
`@@u %U @@`, `%c`, action arguments) stays. `DBusActivatable=true` becomes
`false` (otherwise the desktop would start the app over D-Bus, skipping
Exec), `defaultFor` and `mimeTypes` are added to `MimeType=`, and
`X-SteamFrameNix-Source=` names the source. Element's entry with
`keyring = { enable = true; electron = true; }`:
```sh
env DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus \
/usr/bin/flatpak run --branch=stable --arch=aarch64 --command=/app/bin/element \
--file-forwarding --talk-name=org.kde.kwalletd6 --talk-name=org.freedesktop.secrets \
im.riot.Riot --password-store=kwallet6 @@u %U @@
```
`flatpak run` connects the sandbox's D-Bus proxy (and portals) to the bus in
its own environment, so `hostEnv` (not `--env=`) moves the app to the outer
bus. `defaultFor` goes into `xdg.mimeApps.defaultApplications` and
`associations.added` (Added Associations, what the portal treats as
recommended).
**When it is written:** a package's entry at build time (a Home Manager link
into the store). Flatpak and host file entries can't be read at build time,
so `steam-frame-nix-launchers` (`modules/launchers/generate.sh`,
`rewrite.awk`) writes them to `/run/user/1000/steam-frame-nix/applications`
(tmpfs, `session.runtimeDir`), where the Home Manager links in
`~/.local/share/applications` point. It runs
- on every switch (Home Manager activation; skipped with a message when the
Steam session isn't running),
- at login (`steam-frame-nix-launchers.service` of the Steam session's user
manager),
- when Flatpak installs, updates or removes apps
(`steam-frame-nix-launchers.path` watches `.changed` of both Flatpak
installations and their exported `applications` directories).
Sources: `~/.local/share/flatpak/exports/share/applications/<ID>.desktop`,
then `/var/lib/flatpak/exports/share/applications/<ID>.desktop`. An entry is
replaced (temp file and rename) only when its content changes; entries of
launchers no longer configured or without a source are removed. If anything
changed, the mtime of `~/.local/share/applications` is updated, so a running
Steam rescans the "+" menu. A hash of the last written entry
(`.<ID>.desktop.sum`) shows edits by others.
+8 -9
View File
@@ -23,18 +23,17 @@ running") and skips `reloadSystemd`.
and applies `session.services.start` / `stop` / `restart`, which other
modules fill (you can add your own units).
**Configuration:** apps that keep secrets in the wallet get launchers from
[keyring](keyring.md). Another launcher that must reach the outer session
(its bus, services or wallet) uses the prefix:
**Configuration:** apps that keep secrets in the wallet get a
[launcher](launchers.md) with `keyring.enable`. Another app that must reach
the outer session (its bus, services or wallet) gets the bus through its
launcher's `hostEnv`; a hand-written entry uses the prefix `session.busEnv`:
```nix
{ config, ... }: {
xdg.dataFile."applications/org.example.App.desktop".text = ''
[Desktop Entry]
Type=Application
Name=Example
Exec=${config.steamFrame.session.busEnv} flatpak run org.example.App %U
'';
steamFrame.launchers."org.example.App".hostEnv.DBUS_SESSION_BUS_ADDRESS =
config.steamFrame.session.bus;
# or, in an entry of your own:
# Exec=${config.steamFrame.session.busEnv} flatpak run org.example.App %U
}
```