Files
lhns--steam-frame-nix/modules/launcher-menu.nix
T
Pierre Kisters dd6f3602aa launcher-menu: closeOnLaunch and launchDebounce options
Stock, clicking a program in the VR dashboard's "+" menu only calls
SteamClient.Apps.LaunchNonSteamApp; the menu stays open until the
program's window appears, so users click again and launch it twice.

New launch/ patch (SharedJSContext) wraps LaunchNonSteamApp, whose only
caller is this menu: closeOnLaunch closes the menu through the "+" bar
button's own popup handle right after a launch; launchDebounce ignores
repeated launches of the same command line within N seconds (logged to
the steam-ui-patches journal). Both opt-in, like the other launcherMenu
options.
2026-09-27 19:45:09 +02:00

98 lines
4.2 KiB
Nix

# The VR dashboard's "+" menu (non-Steam programs, #VRDashboard_LaunchNonSteamApp)
# lists programs in the order SteamClient.Apps.ScanForInstalledNonSteamApps()
# returns them: GLib hash-table order, effectively random, with "Desktop" (the
# nested Plasma session) somewhere in a scrolling list. Two runtime patches of
# Steam's UI (steam-ui-patches.nix, evaluated in SharedJSContext) fix that:
# - order/: wraps ScanForInstalledNonSteamApps to sort the list by name;
# - pinned-desktop/: hides Desktop in the scrolling list and pins a copy above
# or below it, with a separator; clicking the copy clicks the hidden
# original. The position is passed by calling the patch's function.
# - launch/: wraps SteamClient.Apps.LaunchNonSteamApp (only called by this
# menu) to close the menu right after a program is started, and/or to
# ignore repeated launches of the same program within a few seconds (stock,
# the menu stays open until the program's window appears, inviting double
# launches). Options are passed by calling the patch's function.
# All are reverted when turned off (next switch).
# Depends on Steam UI internals; tested with Steam client 1790377368.
{ config, lib, pkgs, ... }:
let
cfg = config.steamFrame.launcherMenu;
sharedJSContext = { title = "SharedJSContext"; };
in {
imports = [ ./steam-ui-patches.nix ];
options.steamFrame.launcherMenu = {
sort = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Sort the VR dashboard's "+" menu (non-Steam programs) alphabetically
instead of Steam's random-looking order. Runtime patch of Steam's UI
(steamFrame.uiPatches).
'';
};
pinDesktop = lib.mkOption {
type = lib.types.nullOr (lib.types.enum [ "top" "bottom" ]);
default = null;
example = "bottom";
description = ''
Pin "Desktop" (the nested Plasma session) above ("top", below the
menu heading) or below ("bottom") the scrolling program list of the
"+" menu, separated by a thin line, so it is always visible; it is
hidden in the list itself. null leaves it a normal list entry. Runtime
patch of Steam's UI (steamFrame.uiPatches).
'';
};
closeOnLaunch = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Close the "+" menu as soon as a program in it is activated (pointer,
controller or the pinned Desktop entry). Stock, it stays open until the
program's window appears, so it looks as if the click did nothing and
programs get launched twice. Runtime patch of Steam's UI
(steamFrame.uiPatches); off by default like the other launcherMenu
options, so nothing is injected unless asked for.
'';
};
launchDebounce = lib.mkOption {
type = lib.types.ints.unsigned;
default = 0;
example = 10;
description = ''
Seconds during which another launch of the same program (same command
line) from the "+" menu is ignored, counted from the last launch that
went through; ignored launches are logged (steam-ui-patches journal).
0 disables it. Runtime patch of Steam's UI (steamFrame.uiPatches).
'';
};
};
config.steamFrame.uiPatches.patches =
lib.optional cfg.sort {
name = "launcher-menu-order";
target = sharedJSContext;
patch = ./launcher-menu/order/patch.js;
unpatch = ./launcher-menu/order/unpatch.js;
}
++ lib.optional (cfg.pinDesktop != null) {
name = "launcher-menu-pinned-desktop";
target = sharedJSContext;
patch = pkgs.writeText "launcher-menu-pinned-desktop.js" ''
(${builtins.readFile ./launcher-menu/pinned-desktop/patch.js})(${builtins.toJSON { position = cfg.pinDesktop; }})
'';
unpatch = ./launcher-menu/pinned-desktop/unpatch.js;
}
++ lib.optional (cfg.closeOnLaunch || cfg.launchDebounce > 0) {
name = "launcher-menu-launch";
target = sharedJSContext;
patch = pkgs.writeText "launcher-menu-launch.js" ''
(${builtins.readFile ./launcher-menu/launch/patch.js})(${builtins.toJSON {
inherit (cfg) closeOnLaunch;
debounceSeconds = cfg.launchDebounce;
}})
'';
unpatch = ./launcher-menu/launch/unpatch.js;
};
}