mirror of
https://github.com/lhns/steam-frame-nix.git
synced 2026-10-06 06:00:22 +02:00
- pinned-desktop (VERSION 3): drop the stop() of a predecessor global that never existed in this repo; log errors as "sfui <NAME>:" through one guard helper, like the grid patch. - steamCloseButton.enable is a mkEnableOption like the other feature switches; enable descriptions no longer end in a stray newline. - launcher-menu.nix header lists show-all and no longer claims the patches use no webpack module; hiddenApps mentions showAllApps. - Patch comments: frame-controls ring timing matches the code (half of longPressMs, at most 1 s), window-curvature names frame-controls as what moves its control into the bar, steam-close-button no longer refers to an unpublished version.
252 lines
11 KiB
Nix
252 lines
11 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. Runtime patches of
|
|
# Steam's UI (steam-ui-patches.nix, evaluated in SharedJSContext), each
|
|
# registered only when its option is set:
|
|
# - 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;
|
|
# - 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);
|
|
# - grid/: restyles the programs section as a grid of tiles (icon, name
|
|
# below), optionally limited to maxRows visible rows;
|
|
# - show-all/: empties the list of programs Steam hides without Developer
|
|
# Mode (the webpack module holding it is found by signature).
|
|
# Options reach the patches through mkPatch's `opts`. All are reverted when
|
|
# turned off (next switch). Besides that one module, they depend on Steam UI
|
|
# internals such as React props, popup names and the scroll fade's classes
|
|
# and CSS; scripts/check-signatures.mjs checks all of them after a Steam
|
|
# update (lib/signatures.json). Tested with Steam client 1790377368.
|
|
#
|
|
# iconFallbacks is not a patch: Steam's scan of host programs resolves a
|
|
# desktop entry's Icon= name only in the hicolor icon theme (and pixmaps),
|
|
# so programs whose icon exists only in the desktop's Breeze theme (SteamOS'
|
|
# Konsole and KDE System Settings) have no icon in the menu. On every switch,
|
|
# launcher-menu/icon-fallbacks.sh looks through the desktop entries Steam
|
|
# sees and links the missing icons that nixpkgs' Breeze has into
|
|
# ~/.local/share/icons/hicolor (see the script). Until 2026-09 iconFallbacks
|
|
# was a list of names; setting a list now fails with a message pointing to
|
|
# enable/extra.
|
|
{ config, lib, pkgs, ... }:
|
|
let
|
|
cfg = config.steamFrame.launcherMenu;
|
|
inherit (import ./lib { inherit pkgs; }) mkPatch;
|
|
sharedJSContext = { title = "SharedJSContext"; };
|
|
|
|
iconFallbacks = pkgs.writeShellApplication {
|
|
name = "steam-frame-icon-fallbacks";
|
|
runtimeInputs = [ pkgs.coreutils pkgs.findutils pkgs.gawk ];
|
|
text = builtins.readFile ./launcher-menu/icon-fallbacks.sh;
|
|
};
|
|
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).
|
|
'';
|
|
};
|
|
grid = {
|
|
enable = lib.mkEnableOption ''
|
|
the "+" menu's programs as a grid of tiles (large icon, name below)
|
|
instead of a list. Runtime patch of Steam's UI (steamFrame.uiPatches)'';
|
|
columns = lib.mkOption {
|
|
type = lib.types.ints.between 1 8;
|
|
default = 4;
|
|
description = ''
|
|
Tiles per row. The menu popup is a fixed 300 px wide: 3 columns give
|
|
tiles of about 92 px, 4 about 68 px, 5 about 53 px.
|
|
'';
|
|
};
|
|
maxRows = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.ints.positive;
|
|
default = null;
|
|
example = 4;
|
|
description = ''
|
|
Rows of tiles visible at once: the menu shrinks to that many rows and
|
|
the rest scrolls. null: the grid fills up to the menu's stock maximum
|
|
height (600 px).
|
|
'';
|
|
};
|
|
};
|
|
showAllApps = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = false;
|
|
description = ''
|
|
List all programs in the "+" menu without Steam's Developer Mode.
|
|
Without it, Steam hides a fixed list (konsole, systemsettings,
|
|
dolphin, plasma-discover, vlc, firewall-config, cmake-gui, qrenderdoc,
|
|
lxterminal, sh). Only that list is emptied; the Developer Mode setting
|
|
itself (used by other settings pages) is untouched. Hide individual
|
|
programs with steamFrame.hiddenApps. Runtime patch of Steam's UI
|
|
(steamFrame.uiPatches).
|
|
'';
|
|
};
|
|
iconFallbacks = lib.mkOption {
|
|
default = { };
|
|
description = ''
|
|
Hicolor fallbacks for program icons only the desktop's Breeze theme
|
|
has, so the "+" menu shows them: Steam resolves program icons only in
|
|
the hicolor theme.
|
|
'';
|
|
# The option used to be a list of icon names: keep a list from being
|
|
# silently misread and fail with a message instead (see the assertion).
|
|
type = lib.types.coercedTo (lib.types.listOf lib.types.str)
|
|
(names: { legacyList = names; })
|
|
(lib.types.submodule {
|
|
options = {
|
|
enable = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = true;
|
|
description = ''
|
|
On every switch, look through the desktop entries Steam sees
|
|
(XDG data dirs, shadowed and Hidden/NoDisplay entries skipped)
|
|
and, for each Icon= name no hicolor theme dir (or pixmaps)
|
|
has but nixpkgs' Breeze app icons do (SteamOS: Konsole's
|
|
utilities-terminal, KDE System Settings' preferences-system),
|
|
link the Breeze SVG as
|
|
~/.local/share/icons/hicolor/scalable/apps/<name>.svg. Links
|
|
no longer needed are removed; only links made by this option
|
|
(listed in ~/.local/state/steam-frame-nix/icon-fallbacks) are
|
|
ever touched. false removes them all.
|
|
'';
|
|
};
|
|
extra = lib.mkOption {
|
|
type = lib.types.listOf (lib.types.strMatching "[A-Za-z0-9._+-]+");
|
|
default = [ ];
|
|
example = [ "system-file-manager" ];
|
|
description = ''
|
|
Icon names to provide even if no desktop entry the scan sees
|
|
uses them (still only if hicolor lacks them). A name Breeze
|
|
has no app icon for is reported on switch and skipped.
|
|
'';
|
|
};
|
|
legacyList = lib.mkOption {
|
|
type = lib.types.nullOr (lib.types.listOf lib.types.str);
|
|
default = null;
|
|
internal = true;
|
|
visible = false;
|
|
};
|
|
};
|
|
});
|
|
};
|
|
};
|
|
|
|
config.assertions = [ {
|
|
assertion = cfg.iconFallbacks.legacyList == null;
|
|
message = ''
|
|
steamFrame.launcherMenu.iconFallbacks is no longer a list of icon names
|
|
(set to ${builtins.toJSON cfg.iconFallbacks.legacyList}). Use
|
|
iconFallbacks.enable = true; # default: utilities-terminal, preferences-system
|
|
iconFallbacks.extra = [ ... ]; # further names
|
|
or iconFallbacks.enable = false; for none (what [ ] used to mean).
|
|
'';
|
|
} ];
|
|
|
|
# After installPackages, so the new profile's desktop entries and icons count.
|
|
# Disabled, the script removes its links (no BREEZE_APPS).
|
|
config.home.activation.steamFrameIconFallbacks =
|
|
lib.hm.dag.entryAfter [ "writeBoundary" "installPackages" ] (
|
|
if cfg.iconFallbacks.enable then ''
|
|
run env BREEZE_APPS=${pkgs.kdePackages.breeze-icons}/share/icons/breeze/apps \
|
|
${lib.getExe iconFallbacks} ${lib.escapeShellArgs cfg.iconFallbacks.extra}
|
|
'' else ''
|
|
run ${lib.getExe iconFallbacks}
|
|
'');
|
|
|
|
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 = mkPatch {
|
|
name = "launcher-menu-pinned-desktop";
|
|
src = ./launcher-menu/pinned-desktop/patch.js;
|
|
opts.position = cfg.pinDesktop;
|
|
};
|
|
unpatch = ./launcher-menu/pinned-desktop/unpatch.js;
|
|
}
|
|
++ lib.optional (cfg.closeOnLaunch || cfg.launchDebounce > 0) {
|
|
name = "launcher-menu-launch";
|
|
target = sharedJSContext;
|
|
patch = mkPatch {
|
|
name = "launcher-menu-launch";
|
|
src = ./launcher-menu/launch/patch.js;
|
|
opts = {
|
|
inherit (cfg) closeOnLaunch;
|
|
debounceSeconds = cfg.launchDebounce;
|
|
};
|
|
};
|
|
unpatch = ./launcher-menu/launch/unpatch.js;
|
|
}
|
|
++ lib.optional cfg.grid.enable {
|
|
name = "launcher-menu-grid";
|
|
target = sharedJSContext;
|
|
patch = mkPatch {
|
|
name = "launcher-menu-grid";
|
|
src = ./launcher-menu/grid/patch.js;
|
|
opts = { inherit (cfg.grid) columns maxRows; };
|
|
};
|
|
unpatch = ./launcher-menu/grid/unpatch.js;
|
|
}
|
|
++ lib.optional cfg.showAllApps {
|
|
name = "launcher-menu-show-all";
|
|
target = sharedJSContext;
|
|
patch = mkPatch {
|
|
name = "launcher-menu-show-all";
|
|
src = ./launcher-menu/show-all/patch.js;
|
|
};
|
|
unpatch = ./launcher-menu/show-all/unpatch.js;
|
|
};
|
|
}
|