Options regrouped: session.*, keyboard.*, launcherMenu.hiddenApps

- runtimeDir, userBus, outerBusEnv, userServices.*, portalFix.enable
  -> session.{runtimeDir, bus, busEnv, services.*, portalFix.enable}
- keyboardLayout/keyboardVariant -> keyboard.layout/variant;
  steamKeyboardPatch.enable -> keyboard.vr.extraKeys.enable
- hiddenApps -> launcherMenu.hiddenApps
- launcherMenu.launchDebounce -> launchDebounceSeconds (also the patch's
  option key; launcher-menu-launch VERSION 3)
- "Steam UI patch" in option descriptions

Old names keep working with a rename warning (outerBusEnv is read-only:
reading it warns).
This commit is contained in:
Pierre Kisters committed 2026-09-28 03:36:38 +02:00
1 parent 36fcb207bd
commit a0c3e0846f
8 files changed
+124 -63

No files matched your search

+11 -3
View File
@@ -1,15 +1,23 @@
# Hide desktop entries from the "+" menu and the KDE menu: a user entry with # Hide desktop entries from the "+" menu and the KDE menu: a user entry with
# Hidden=true masks the system one. # Hidden=true masks the system one.
{ config, lib, ... }: { { config, lib, ... }: {
options.steamFrame.hiddenApps = lib.mkOption { imports = [
(lib.mkRenamedOptionModule
[ "steamFrame" "hiddenApps" ] [ "steamFrame" "launcherMenu" "hiddenApps" ])
];
options.steamFrame.launcherMenu.hiddenApps = lib.mkOption {
type = lib.types.listOf lib.types.str; type = lib.types.listOf lib.types.str;
default = [ ]; default = [ ];
example = [ "lxterminal" "cmake-gui" "firewall-config" "renderdoc" ]; example = [ "lxterminal" "cmake-gui" "firewall-config" "renderdoc" ];
description = "Desktop entry ids (without .desktop) to hide from the \"+\" menu and the KDE menu."; description = ''
Desktop entry ids (without .desktop) to hide from the "+" menu and the
KDE menu.
'';
}; };
config.xdg.dataFile = lib.genAttrs config.xdg.dataFile = lib.genAttrs
(map (id: "applications/${id}.desktop") config.steamFrame.hiddenApps) (map (id: "applications/${id}.desktop") config.steamFrame.launcherMenu.hiddenApps)
(_: { (_: {
text = '' text = ''
[Desktop Entry] [Desktop Entry]
+14 -9
View File
@@ -4,10 +4,15 @@
# (next session start). # (next session start).
{ config, lib, ... }: { config, lib, ... }:
let let
cfg = config.steamFrame; cfg = config.steamFrame.keyboard;
in { in {
options.steamFrame = { imports = [
keyboardLayout = lib.mkOption { (lib.mkRenamedOptionModule [ "steamFrame" "keyboardLayout" ] [ "steamFrame" "keyboard" "layout" ])
(lib.mkRenamedOptionModule [ "steamFrame" "keyboardVariant" ] [ "steamFrame" "keyboard" "variant" ])
];
options.steamFrame.keyboard = {
layout = lib.mkOption {
type = lib.types.nullOr lib.types.str; type = lib.types.nullOr lib.types.str;
default = null; default = null;
example = "de"; example = "de";
@@ -15,7 +20,7 @@ in {
XKB layout for the Steam session (XKB_DEFAULT_LAYOUT); null = US. XKB layout for the Steam session (XKB_DEFAULT_LAYOUT); null = US.
''; '';
}; };
keyboardVariant = lib.mkOption { variant = lib.mkOption {
type = lib.types.nullOr lib.types.str; type = lib.types.nullOr lib.types.str;
default = null; default = null;
example = "nodeadkeys"; example = "nodeadkeys";
@@ -23,12 +28,12 @@ in {
}; };
}; };
config = lib.mkIf (cfg.keyboardLayout != null || cfg.keyboardVariant != null) { config = lib.mkIf (cfg.layout != null || cfg.variant != null) {
xdg.configFile."systemd/user/gamescope-session.service.d/keyboard.conf".text = xdg.configFile."systemd/user/gamescope-session.service.d/keyboard.conf".text =
"[Service]\n" "[Service]\n"
+ lib.optionalString (cfg.keyboardLayout != null) + lib.optionalString (cfg.layout != null)
"Environment=XKB_DEFAULT_LAYOUT=${cfg.keyboardLayout}\n" "Environment=XKB_DEFAULT_LAYOUT=${cfg.layout}\n"
+ lib.optionalString (cfg.keyboardVariant != null) + lib.optionalString (cfg.variant != null)
"Environment=XKB_DEFAULT_VARIANT=${cfg.keyboardVariant}\n"; "Environment=XKB_DEFAULT_VARIANT=${cfg.variant}\n";
}; };
} }
+18 -15
View File
@@ -1,5 +1,5 @@
# The VR dashboard's "+" menu (non-Steam programs). Steam lists them in GLib # The VR dashboard's "+" menu (non-Steam programs). Steam lists them in GLib
# hash-table order (random-looking). Runtime patches in SharedJSContext, each # hash-table order (random-looking). Steam UI patches in SharedJSContext, each
# registered only when its option is set, reverted when unset (next switch): # registered only when its option is set, reverted when unset (next switch):
# - order/: sorts ScanForInstalledNonSteamApps() by name; # - order/: sorts ScanForInstalledNonSteamApps() by name;
# - pinned-desktop/: hides Desktop in the list, pins a proxy above/below it; # - pinned-desktop/: hides Desktop in the list, pins a proxy above/below it;
@@ -26,14 +26,19 @@ let
text = builtins.readFile ./launcher-menu/icon-fallbacks.sh; text = builtins.readFile ./launcher-menu/icon-fallbacks.sh;
}; };
in { in {
imports = [ ./steam-ui-patches.nix ]; imports = [
./steam-ui-patches.nix
(lib.mkRenamedOptionModule
[ "steamFrame" "launcherMenu" "launchDebounce" ]
[ "steamFrame" "launcherMenu" "launchDebounceSeconds" ])
];
options.steamFrame.launcherMenu = { options.steamFrame.launcherMenu = {
sort = lib.mkOption { sort = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
default = false; default = false;
description = '' description = ''
Sort the "+" menu (non-Steam programs) by name. Runtime patch. Sort the "+" menu (non-Steam programs) by name. Steam UI patch.
''; '';
}; };
pinDesktop = lib.mkOption { pinDesktop = lib.mkOption {
@@ -43,7 +48,7 @@ in {
description = '' description = ''
Pin "Desktop" (the nested Plasma session) above ("top") or below Pin "Desktop" (the nested Plasma session) above ("top") or below
("bottom") the "+" menu's scrolling list, so it is always visible; ("bottom") the "+" menu's scrolling list, so it is always visible;
null = normal list entry. Runtime patch. null = normal list entry. Steam UI patch.
''; '';
}; };
closeOnLaunch = lib.mkOption { closeOnLaunch = lib.mkOption {
@@ -51,23 +56,24 @@ in {
default = false; default = false;
description = '' description = ''
Close the "+" menu as soon as a program is launched (stock: it stays Close the "+" menu as soon as a program is launched (stock: it stays
open until the window appears, inviting double launches). Runtime patch. open until the window appears, inviting double launches). Steam UI
patch.
''; '';
}; };
launchDebounce = lib.mkOption { launchDebounceSeconds = lib.mkOption {
type = lib.types.ints.unsigned; type = lib.types.ints.unsigned;
default = 0; default = 0;
example = 10; example = 10;
description = '' description = ''
Ignore another "+" menu launch of the same command line within this Ignore another "+" menu launch of the same command line within this
many seconds of the last one that went through (logged); 0 = off. many seconds of the last one that went through (logged); 0 = off.
Runtime patch. Steam UI patch.
''; '';
}; };
grid = { grid = {
enable = lib.mkEnableOption '' enable = lib.mkEnableOption ''
the "+" menu's programs as a grid of tiles (icon, name below) instead the "+" menu's programs as a grid of tiles (icon, name below) instead
of a list. Runtime patch''; of a list. Steam UI patch'';
columns = lib.mkOption { columns = lib.mkOption {
type = lib.types.ints.between 1 8; type = lib.types.ints.between 1 8;
default = 4; default = 4;
@@ -93,8 +99,8 @@ in {
List all programs in the "+" menu without Developer Mode (Steam List all programs in the "+" menu without Developer Mode (Steam
otherwise hides konsole, systemsettings, dolphin, plasma-discover, vlc, otherwise hides konsole, systemsettings, dolphin, plasma-discover, vlc,
firewall-config, cmake-gui, qrenderdoc, lxterminal, sh). Developer Mode firewall-config, cmake-gui, qrenderdoc, lxterminal, sh). Developer Mode
itself is untouched; hide single programs with steamFrame.hiddenApps. itself is untouched; hide single programs with
Runtime patch. steamFrame.launcherMenu.hiddenApps. Steam UI patch.
''; '';
}; };
iconFallbacks = lib.mkOption { iconFallbacks = lib.mkOption {
@@ -179,16 +185,13 @@ in {
}; };
unpatch = ./launcher-menu/pinned-desktop/unpatch.js; unpatch = ./launcher-menu/pinned-desktop/unpatch.js;
} }
++ lib.optional (cfg.closeOnLaunch || cfg.launchDebounce > 0) { ++ lib.optional (cfg.closeOnLaunch || cfg.launchDebounceSeconds > 0) {
name = "launcher-menu-launch"; name = "launcher-menu-launch";
target = sharedJSContext; target = sharedJSContext;
patch = mkPatch { patch = mkPatch {
name = "launcher-menu-launch"; name = "launcher-menu-launch";
src = ./launcher-menu/launch/patch.js; src = ./launcher-menu/launch/patch.js;
opts = { opts = { inherit (cfg) closeOnLaunch launchDebounceSeconds; };
inherit (cfg) closeOnLaunch;
debounceSeconds = cfg.launchDebounce;
};
}; };
unpatch = ./launcher-menu/launch/unpatch.js; unpatch = ./launcher-menu/launch/unpatch.js;
} }
+4 -4
View File
@@ -1,11 +1,11 @@
// launch: what activating a program in the VR dashboard's "+" menu does. // launch: what activating a program in the VR dashboard's "+" menu does.
// mkPatch patch (see lib/default.nix); opts: { closeOnLaunch, debounceSeconds }. // mkPatch patch (see lib/default.nix); opts: { closeOnLaunch, launchDebounceSeconds }.
// //
// Stock, an item only calls SteamClient.Apps.LaunchNonSteamApp(cmdline) and // Stock, an item only calls SteamClient.Apps.LaunchNonSteamApp(cmdline) and
// the popup stays open until the new window appears, so users click twice. // the popup stays open until the new window appears, so users click twice.
// That call is its only UI use and is looked up at call time, so it is wrapped // That call is its only UI use and is looked up at call time, so it is wrapped
// here in SharedJSContext: // here in SharedJSContext:
// - debounceSeconds > 0: the same command line within that many seconds of // - launchDebounceSeconds > 0: the same command line within that many seconds of
// the last accepted launch is ignored (console.info; reported in the // the last accepted launch is ignored (console.info; reported in the
// steam-ui-patches journal on the next re-injection); // steam-ui-patches journal on the next re-injection);
// - closeOnLaunch: afterwards the "+" popup is closed via the bar button's // - closeOnLaunch: afterwards the "+" popup is closed via the bar button's
@@ -16,10 +16,10 @@
// Covers every activation path (pointer, controller, pinned Desktop copy). // Covers every activation path (pointer, controller, pinned Desktop copy).
// Original kept as __sfuiOrig for unpatch.js; bump VERSION on changes. // Original kept as __sfuiOrig for unpatch.js; bump VERSION on changes.
((find, sigs, opts) => { ((find, sigs, opts) => {
const VERSION = 2; const VERSION = 3;
const NAME = 'launcher-menu-launch'; const NAME = 'launcher-menu-launch';
const CLOSE = opts.closeOnLaunch === true; const CLOSE = opts.closeOnLaunch === true;
const DEBOUNCE_MS = Math.max(0, Number(opts.debounceSeconds) || 0) * 1000; const DEBOUNCE_MS = Math.max(0, Number(opts.launchDebounceSeconds) || 0) * 1000;
const Apps = window.SteamClient?.Apps; const Apps = window.SteamClient?.Apps;
const cur = Apps?.LaunchNonSteamApp; const cur = Apps?.LaunchNonSteamApp;
if (typeof cur !== 'function') return 'SteamClient.Apps.LaunchNonSteamApp missing'; if (typeof cur !== 'function') return 'SteamClient.Apps.LaunchNonSteamApp missing';
+8 -2
View File
@@ -8,7 +8,13 @@ let
sys = "/usr/share/xdg-desktop-portal/gamescope-portals"; sys = "/usr/share/xdg-desktop-portal/gamescope-portals";
dir = "xdg-desktop-portal/gamescope-portals"; dir = "xdg-desktop-portal/gamescope-portals";
in { in {
options.steamFrame.portalFix.enable = lib.mkOption { imports = [
(lib.mkRenamedOptionModule
[ "steamFrame" "portalFix" "enable" ]
[ "steamFrame" "session" "portalFix" "enable" ])
];
options.steamFrame.session.portalFix.enable = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
default = true; default = true;
description = '' description = ''
@@ -17,7 +23,7 @@ in {
''; '';
}; };
config = lib.mkIf config.steamFrame.portalFix.enable { config = lib.mkIf config.steamFrame.session.portalFix.enable {
xdg.dataFile."${dir}/holo.portal".source = xdg.dataFile."${dir}/holo.portal".source =
config.lib.file.mkOutOfStoreSymlink "${sys}/holo.portal"; config.lib.file.mkOutOfStoreSymlink "${sys}/holo.portal";
xdg.dataFile."${dir}/gamescope.portal".source = xdg.dataFile."${dir}/gamescope.portal".source =
+52 -20
View File
@@ -5,66 +5,98 @@
# and private bus. `switch` usually runs from the nested desktop, where # and private bus. `switch` usually runs from the nested desktop, where
# home-manager's reloadSystemd is skipped ("User systemd daemon not running"), # home-manager's reloadSystemd is skipped ("User systemd daemon not running"),
# so `steamFrameUserServices` does it against the outer session: always # so `steamFrameUserServices` does it against the outer session: always
# daemon-reload, then start/stop/restart the units in `userServices`. # daemon-reload, then start/stop/restart the units in `session.services`.
{ config, lib, ... }: { config, lib, ... }:
let let
cfg = config.steamFrame; cfg = config.steamFrame.session;
units = lib.escapeShellArgs; units = lib.escapeShellArgs;
rename = from: to: lib.mkRenamedOptionModule ([ "steamFrame" ] ++ from) [ "steamFrame" "session" to ];
in { in {
options.steamFrame = { imports = [
(rename [ "runtimeDir" ] "runtimeDir")
(rename [ "userBus" ] "bus")
] ++ map (n: lib.mkRenamedOptionModule
[ "steamFrame" "userServices" n ] [ "steamFrame" "session" "services" n ])
[ "start" "restart" "stop" ];
# Read-only, so no mkRenamedOptionModule (its alias definition would be a
# second one); reading the old name warns.
options.steamFrame.outerBusEnv = lib.mkOption {
type = lib.types.str;
readOnly = true;
visible = false;
default = lib.warn
"The option `steamFrame.outerBusEnv' has been renamed to `steamFrame.session.busEnv'."
cfg.busEnv;
defaultText = lib.literalExpression "config.steamFrame.session.busEnv";
description = "Alias of {option}`steamFrame.session.busEnv`.";
};
options.steamFrame.session = {
runtimeDir = lib.mkOption { runtimeDir = lib.mkOption {
type = lib.types.str; type = lib.types.str;
default = "/run/user/1000"; default = "/run/user/1000";
description = "XDG_RUNTIME_DIR of the outer (Steam/VR) session."; description = "XDG_RUNTIME_DIR of the outer (Steam/VR) session.";
}; };
userBus = lib.mkOption { bus = lib.mkOption {
type = lib.types.str; type = lib.types.str;
default = "unix:path=${cfg.runtimeDir}/bus"; default = "unix:path=${cfg.runtimeDir}/bus";
defaultText = lib.literalExpression ''"unix:path=''${config.steamFrame.runtimeDir}/bus"''; defaultText = lib.literalExpression
''"unix:path=''${config.steamFrame.session.runtimeDir}/bus"'';
description = '' description = ''
Outer session D-Bus address; the only bus that reaches the user Outer session D-Bus address; the only bus that reaches the user
systemd manager and the running kwalletd6. systemd manager and the running kwalletd6.
''; '';
}; };
outerBusEnv = lib.mkOption { busEnv = lib.mkOption {
type = lib.types.str; type = lib.types.str;
readOnly = true; readOnly = true;
default = "env DBUS_SESSION_BUS_ADDRESS=${cfg.userBus}"; default = "env DBUS_SESSION_BUS_ADDRESS=${cfg.bus}";
defaultText = lib.literalExpression ''"env DBUS_SESSION_BUS_ADDRESS=''${config.steamFrame.userBus}"''; defaultText = lib.literalExpression
''"env DBUS_SESSION_BUS_ADDRESS=''${config.steamFrame.session.bus}"'';
description = '' description = ''
Exec= prefix for launchers that must use the outer bus, e.g. so apps Exec= prefix for launchers that must use the outer bus, e.g. so apps
in the nested desktop use the running kwalletd6 instead of a second one. in the nested desktop use the running kwalletd6 instead of a second
one.
''; '';
}; };
userServices = { services = {
start = lib.mkOption { start = lib.mkOption {
type = lib.types.listOf lib.types.str; type = lib.types.listOf lib.types.str;
default = [ ]; default = [ ];
example = [ "docker.service" ]; example = [ "docker.service" ];
description = "User units started on switch if not already running (outer user manager)."; description = ''
User units started on switch if not already running (outer user
manager).
'';
}; };
restart = lib.mkOption { restart = lib.mkOption {
type = lib.types.listOf lib.types.str; type = lib.types.listOf lib.types.str;
default = [ ]; default = [ ];
example = [ "steam-keyboard-patch.service" ]; example = [ "steam-keyboard-patch.service" ];
description = "User units restarted on every switch (outer user manager)."; description = ''
User units restarted on every switch (outer user manager).
'';
}; };
stop = lib.mkOption { stop = lib.mkOption {
type = lib.types.listOf lib.types.str; type = lib.types.listOf lib.types.str;
default = [ ]; default = [ ];
description = "User units stopped on switch if running (e.g. of a just-disabled feature)."; description = ''
User units stopped on switch if running (e.g. of a just-disabled
feature).
'';
}; };
}; };
}; };
config.home.activation.steamFrameUserServices = lib.hm.dag.entryAfter [ "reloadSystemd" ] ('' config.home.activation.steamFrameUserServices = lib.hm.dag.entryAfter [ "reloadSystemd" ] (''
export XDG_RUNTIME_DIR=${lib.escapeShellArg cfg.runtimeDir} DBUS_SESSION_BUS_ADDRESS=${lib.escapeShellArg cfg.userBus} export XDG_RUNTIME_DIR=${lib.escapeShellArg cfg.runtimeDir} DBUS_SESSION_BUS_ADDRESS=${lib.escapeShellArg cfg.bus}
run /usr/bin/systemctl --user daemon-reload run /usr/bin/systemctl --user daemon-reload
'' + lib.optionalString (cfg.userServices.start != [ ]) '' '' + lib.optionalString (cfg.services.start != [ ]) ''
run /usr/bin/systemctl --user start ${units cfg.userServices.start} run /usr/bin/systemctl --user start ${units cfg.services.start}
'' + lib.optionalString (cfg.userServices.stop != [ ]) '' '' + lib.optionalString (cfg.services.stop != [ ]) ''
run /usr/bin/systemctl --user stop ${units cfg.userServices.stop} || true run /usr/bin/systemctl --user stop ${units cfg.services.stop} || true
'' + lib.optionalString (cfg.userServices.restart != [ ]) '' '' + lib.optionalString (cfg.services.restart != [ ]) ''
run /usr/bin/systemctl --user restart ${units cfg.userServices.restart} run /usr/bin/systemctl --user restart ${units cfg.services.restart}
''); '');
} }
+13 -7
View File
@@ -13,16 +13,22 @@ let
name = "steam-keyboard-patch"; name = "steam-keyboard-patch";
src = ./steam-keyboard-patch/patch.js; src = ./steam-keyboard-patch/patch.js;
}; };
cfg = config.steamFrame.keyboard.vr.extraKeys;
in { in {
imports = [ ./session.nix ]; imports = [
./session.nix
(lib.mkRenamedOptionModule
[ "steamFrame" "steamKeyboardPatch" "enable" ]
[ "steamFrame" "keyboard" "vr" "extraKeys" "enable" ])
];
options.steamFrame.steamKeyboardPatch.enable = lib.mkEnableOption '' options.steamFrame.keyboard.vr.extraKeys.enable = lib.mkEnableOption ''
Esc/Ctrl/Alt, arrow keys, Ctrl/Alt chords (held while toggled) and Esc/Ctrl/Alt, arrow keys, Ctrl/Alt chords (held while toggled) and
AltGr/non-ASCII characters on Steam's VR keyboard (runtime patch plus an AltGr/non-ASCII characters on Steam's VR keyboard (Steam UI patch plus an
xdotool helper service); off reverts it on the next switch''; xdotool helper service); off reverts it on the next switch'';
config = lib.mkMerge [ config = lib.mkMerge [
(lib.mkIf config.steamFrame.steamKeyboardPatch.enable { (lib.mkIf cfg.enable {
systemd.user.services.steam-keyboard-patch = { systemd.user.services.steam-keyboard-patch = {
Unit.Description = "Modifier keys for Steam's VR keyboard (CEF patch + xdotool)"; Unit.Description = "Modifier keys for Steam's VR keyboard (CEF patch + xdotool)";
Service = { Service = {
@@ -41,11 +47,11 @@ in {
}; };
# Restart on switch to re-inject a changed patch. # Restart on switch to re-inject a changed patch.
steamFrame.userServices.restart = [ "steam-keyboard-patch.service" ]; steamFrame.session.services.restart = [ "steam-keyboard-patch.service" ];
}) })
# Disabled: stopping the helper reverts the patch right away. # Disabled: stopping the helper reverts the patch right away.
(lib.mkIf (!config.steamFrame.steamKeyboardPatch.enable) { (lib.mkIf (!cfg.enable) {
steamFrame.userServices.stop = [ "steam-keyboard-patch.service" ]; steamFrame.session.services.stop = [ "steam-keyboard-patch.service" ];
}) })
]; ];
} }
+4 -3
View File
@@ -23,7 +23,8 @@ let
default = "http://127.0.0.1:8080"; default = "http://127.0.0.1:8080";
example = "http://127.0.0.1:8087"; example = "http://127.0.0.1:8087";
description = '' description = ''
DevTools base URL (its /json/list is polled): Steam 8080, SteamVR 8087. DevTools base URL (its /json/list is polled): Steam 8080, SteamVR
8087.
''; '';
}; };
target = { target = {
@@ -134,11 +135,11 @@ in {
# Restart on switch: re-inject changed patches; the old instance reverts # Restart on switch: re-inject changed patches; the old instance reverts
# removed ones. # removed ones.
steamFrame.userServices.restart = [ "steam-ui-patches.service" ]; steamFrame.session.services.restart = [ "steam-ui-patches.service" ];
}) })
# No patches: stopping the injector reverts them right away. # No patches: stopping the injector reverts them right away.
(lib.mkIf (cfg.patches == [ ]) { (lib.mkIf (cfg.patches == [ ]) {
steamFrame.userServices.stop = [ "steam-ui-patches.service" ]; steamFrame.session.services.stop = [ "steam-ui-patches.service" ];
}) })
]; ];
} }