mirror of
https://github.com/lhns/steam-frame-nix.git
synced 2026-10-06 03:00:13 +02:00
firefox: prefs as default prefs through the systemconfig extension
prefs and vrFullscreenFix become default prefs (pref()) in defaults/pref/steam-frame-nix.js of /app/etc/firefox, the mount point of the org.mozilla.firefox.systemconfig extension point. home-manager provides the extension as a user "unmaintained extension" link ($XDG_DATA_HOME/flatpak/extension/<id>/aarch64/stable) to a store dir, which Flatpak mounts itself: the sandbox doesn't see /nix. Default prefs are never written to prefs.js, so removing one leaves nothing behind. The desktop profile undoes the fullscreen fix with a user.js linked to /app/etc/firefox/steam-frame-nix-desktop-user.js. The sync (on switch, and before each launch) removes the user.js copies and store links of older versions and takes their values out of prefs.js, for each profile not in use (Firefox holds .parentlock open).
This commit is contained in:
1 parent
fd97347e18
commit
4320a69b89
3 files changed
+142
-45
No files matched your search
@@ -257,7 +257,8 @@ without it.
|
|||||||
| `steamFrame.clipboardSync.enable` | bool | `true` | Clipboard bridge between the Steam session and the nested desktop. |
|
| `steamFrame.clipboardSync.enable` | bool | `true` | Clipboard bridge between the Steam session and the nested desktop. |
|
||||||
| `steamFrame.clipboardSync.package` | package | built from `dnut/clipboard-sync` | The clipboard-sync package. |
|
| `steamFrame.clipboardSync.package` | package | built from `dnut/clipboard-sync` | The clipboard-sync package. |
|
||||||
| `steamFrame.firefox.enable` | bool | `false` | Launcher for the Flathub Firefox Flatpak with the fixes below. |
|
| `steamFrame.firefox.enable` | bool | `false` | Launcher for the Flathub Firefox Flatpak with the fixes below. |
|
||||||
| `steamFrame.firefox.vrFullscreenFix` | bool | `true` | Link a `user.js` with `full-screen-api.ignore-widgets` into profiles. |
|
| `steamFrame.firefox.vrFullscreenFix` | bool | `true` | Default `full-screen-api.ignore-widgets` to `true` (not in the desktop profile). |
|
||||||
|
| `steamFrame.firefox.prefs` | attrs of bool, int or str | `{ }` | `about:config` default values for every profile, e.g. `{ "media.av1.enabled" = false; }`. |
|
||||||
| `steamFrame.firefox.desktopProfile` | null or str | `"desktop"` | Separate profile for the nested desktop; `null`: none. |
|
| `steamFrame.firefox.desktopProfile` | null or str | `"desktop"` | Separate profile for the nested desktop; `null`: none. |
|
||||||
| `steamFrame.jellyfin.hardwareDecoding.enable` | bool | `false` | Hardware video decoding in the Jellyfin Desktop Flatpak, see [Jellyfin](#jellyfin-hardware-decoding-jellyfinhardwaredecoding). |
|
| `steamFrame.jellyfin.hardwareDecoding.enable` | bool | `false` | Hardware video decoding in the Jellyfin Desktop Flatpak, see [Jellyfin](#jellyfin-hardware-decoding-jellyfinhardwaredecoding). |
|
||||||
| `steamFrame.jellyfin.hardwareDecoding.hwdec` | str | `"v4l2m2m-copy,auto-copy"` | mpv `hwdec` used instead of Jellyfin's automatic one. |
|
| `steamFrame.jellyfin.hardwareDecoding.hwdec` | str | `"v4l2m2m-copy,auto-copy"` | mpv `hwdec` used instead of Jellyfin's automatic one. |
|
||||||
@@ -807,14 +808,35 @@ launcher shadows the Flatpak's own entry (same ID), so default-browser
|
|||||||
associations keep working.
|
associations keep working.
|
||||||
|
|
||||||
- **`vrFullscreenFix`:** gamescope never shows fullscreen windows, so
|
- **`vrFullscreenFix`:** gamescope never shows fullscreen windows, so
|
||||||
Firefox looks frozen. A `user.js` with `full-screen-api.ignore-widgets`
|
Firefox looks frozen. `full-screen-api.ignore-widgets` makes fullscreen
|
||||||
makes fullscreen fill just the window; it is linked into every profile
|
fill just the window. **Remove when** gamescope shows fullscreen X11
|
||||||
(existing non-symlink `user.js` files are left alone). **Remove when**
|
windows in VR.
|
||||||
gamescope shows fullscreen X11 windows in VR.
|
- **`prefs`:** `about:config` values for every profile. E.g.
|
||||||
|
`"media.av1.enabled" = false`: the Frame's decoder has no AV1, so YouTube
|
||||||
|
and co. fall back to VP9/H.264, which it decodes in hardware.
|
||||||
- **`desktopProfile`:** the sessions can't see each other's Firefox, so a
|
- **`desktopProfile`:** the sessions can't see each other's Firefox, so a
|
||||||
second instance stops at the locked profile; in the nested desktop the
|
second instance stops at the locked profile; in the nested desktop the
|
||||||
launcher uses this separate profile.
|
launcher uses this separate profile.
|
||||||
|
|
||||||
|
`prefs` and the fullscreen fix are *default* values (`pref()`), not user
|
||||||
|
values: `about:config` can still change them per profile, and nothing is
|
||||||
|
written to `prefs.js`, so removing one leaves nothing behind. Firefox reads
|
||||||
|
default prefs from `defaults/pref/*.js` in its system config dir, which in
|
||||||
|
the Flatpak is `/app/etc/firefox`, the mount point of the
|
||||||
|
`org.mozilla.firefox.systemconfig` extension. home-manager provides that
|
||||||
|
extension as a link from
|
||||||
|
`~/.local/share/flatpak/extension/org.mozilla.firefox.systemconfig/aarch64/stable`
|
||||||
|
to a store directory; Flatpak mounts it itself, so the sandbox doesn't get
|
||||||
|
`/nix`. (A systemconfig extension of your own would conflict with it.) The
|
||||||
|
desktop profile undoes the fullscreen fix with a `user.js` linked to
|
||||||
|
`/app/etc/firefox/steam-frame-nix-desktop-user.js` (a sandbox path).
|
||||||
|
Changes take effect at the next start of Firefox.
|
||||||
|
|
||||||
|
Older versions copied a `user.js` into every profile; the copies and the
|
||||||
|
values they left in `prefs.js` are removed on switch and before each launch,
|
||||||
|
for each profile not in use at that moment. `user.js` files of your own are
|
||||||
|
left alone.
|
||||||
|
|
||||||
### Jellyfin hardware decoding (`jellyfin.hardwareDecoding.*`)
|
### Jellyfin hardware decoding (`jellyfin.hardwareDecoding.*`)
|
||||||
|
|
||||||
For the Flathub [Jellyfin Desktop](https://github.com/jellyfin/jellyfin-desktop)
|
For the Flathub [Jellyfin Desktop](https://github.com/jellyfin/jellyfin-desktop)
|
||||||
|
|||||||
+113
-40
@@ -1,30 +1,106 @@
|
|||||||
# Firefox Flatpak (org.mozilla.firefox) launcher.
|
# Firefox Flatpak (org.mozilla.firefox) launcher.
|
||||||
|
# - prefs and vrFullscreenFix are default prefs (pref(), the default branch:
|
||||||
|
# never written to prefs.js, so removing one leaves nothing behind). Firefox
|
||||||
|
# reads defaults/pref/*.js from its system config dir, /app/etc/firefox in
|
||||||
|
# the Flatpak, the mount point of the org.mozilla.firefox.systemconfig
|
||||||
|
# extension. The user installation's "unmaintained extension" dir
|
||||||
|
# ($XDG_DATA_HOME/flatpak/extension/<id>/<arch>/<branch>) provides it: a
|
||||||
|
# home-manager link to a store dir, which Flatpak mounts itself (the
|
||||||
|
# sandbox needn't see /nix).
|
||||||
# - vrFullscreenFix: in the Steam session gamescope focuses a fullscreen X11
|
# - vrFullscreenFix: in the Steam session gamescope focuses a fullscreen X11
|
||||||
# window but never shows it (Firefox looks frozen). ignore-widgets keeps
|
# window but never shows it (Firefox looks frozen). ignore-widgets keeps
|
||||||
# fullscreen inside the window. Profile names are random and $HOME isn't
|
# fullscreen inside the window.
|
||||||
# readable at eval time, so user.js is written into each profile on switch
|
|
||||||
# (a copy: /nix isn't visible in the Flatpak sandbox, a store link dangles).
|
|
||||||
# - desktopProfile: the sessions have separate buses/displays, so a second
|
# - desktopProfile: the sessions have separate buses/displays, so a second
|
||||||
# Firefox can't reach the running one and hits the profile lock; the nested
|
# Firefox can't reach the running one and hits the profile lock; the nested
|
||||||
# desktop gets its own profile (no fullscreen fix: fullscreen works there).
|
# desktop gets its own profile. Fullscreen works there, so the fix is undone
|
||||||
# - prefs: about:config values for every profile, desktop one included.
|
# by a user.js in that profile: a link to the extension's
|
||||||
|
# steam-frame-nix-desktop-user.js (a sandbox path; dangling on the host).
|
||||||
|
# - profileSync (on switch and before each launch) keeps those links and
|
||||||
|
# removes what older versions wrote: user.js copies starting with `marker`
|
||||||
|
# and links to *-firefox-*user.js store files, whose user_pref values
|
||||||
|
# Firefox had stored in prefs.js. They are taken out of prefs.js too, which
|
||||||
|
# needs the profile closed; profiles in use are left for the next run.
|
||||||
# The entry shadows the Flatpak's (same ID), keeping MIME associations, and is
|
# The entry shadows the Flatpak's (same ID), keeping MIME associations, and is
|
||||||
# seen by the "+" menu (which reads only ~/.local/share/applications).
|
# seen by the "+" menu (which reads only ~/.local/share/applications).
|
||||||
{ config, pkgs, lib, ... }:
|
{ config, pkgs, lib, ... }:
|
||||||
let
|
let
|
||||||
cfg = config.steamFrame.firefox;
|
cfg = config.steamFrame.firefox;
|
||||||
marker = "// Managed by steam-frame-nix (steamFrame.firefox); rewritten on switch.";
|
|
||||||
toUserJs = name: prefs: pkgs.writeText name (marker + "\n" + lib.concatStrings (lib.mapAttrsToList
|
|
||||||
(k: v: "user_pref(${builtins.toJSON k}, ${builtins.toJSON v});\n") prefs));
|
|
||||||
vrPrefs = cfg.prefs // lib.optionalAttrs cfg.vrFullscreenFix { "full-screen-api.ignore-widgets" = true; };
|
|
||||||
userJs = toUserJs "firefox-user.js" vrPrefs;
|
|
||||||
desktopUserJs = toUserJs "firefox-desktop-user.js" cfg.prefs;
|
|
||||||
ffDir = "$HOME/.var/app/org.mozilla.firefox/config/mozilla/firefox";
|
ffDir = "$HOME/.var/app/org.mozilla.firefox/config/mozilla/firefox";
|
||||||
profileDir = "${ffDir}/${cfg.desktopProfile}";
|
profileDir = "${ffDir}/${cfg.desktopProfile}";
|
||||||
# user.js for a profile dir ($prof): desktop profile -> prefs only.
|
|
||||||
pickUserJs = if cfg.desktopProfile == null then "js=${userJs}" else ''
|
defaultPrefs = cfg.prefs // lib.optionalAttrs cfg.vrFullscreenFix { "full-screen-api.ignore-widgets" = true; };
|
||||||
js=${userJs}
|
desktopFix = cfg.enable && cfg.vrFullscreenFix && cfg.desktopProfile != null;
|
||||||
[ "$(basename "$prof")" = ${lib.escapeShellArg cfg.desktopProfile} ] && js=${desktopUserJs}
|
# Only ever this one pref (profileSync relies on it).
|
||||||
|
desktopKeys = [ "full-screen-api.ignore-widgets" ];
|
||||||
|
desktopUserJsName = "steam-frame-nix-desktop-user.js";
|
||||||
|
|
||||||
|
sysconfig = pkgs.runCommand "steam-frame-nix-firefox-systemconfig" { } (''
|
||||||
|
mkdir -p $out/defaults/pref
|
||||||
|
cp ${pkgs.writeText "steam-frame-nix.js" (lib.concatStrings (lib.mapAttrsToList
|
||||||
|
(k: v: "pref(${builtins.toJSON k}, ${builtins.toJSON v});\n") defaultPrefs))} \
|
||||||
|
$out/defaults/pref/steam-frame-nix.js
|
||||||
|
'' + lib.optionalString desktopFix ''
|
||||||
|
cp ${pkgs.writeText desktopUserJsName (lib.concatMapStrings
|
||||||
|
(k: "user_pref(${builtins.toJSON k}, false);\n") desktopKeys)} $out/${desktopUserJsName}
|
||||||
|
'');
|
||||||
|
|
||||||
|
# Written by versions that copied user.js into every profile.
|
||||||
|
marker = "// Managed by steam-frame-nix (steamFrame.firefox); rewritten on switch.";
|
||||||
|
|
||||||
|
profileSync = pkgs.writeShellScript "firefox-profile-sync" ''
|
||||||
|
PATH=${lib.makeBinPath (with pkgs; [ coreutils diffutils findutils gnugrep gnused ])}
|
||||||
|
ffDir="${ffDir}"
|
||||||
|
desktopJs=/app/etc/firefox/${desktopUserJsName}
|
||||||
|
[ -d "$ffDir" ] || exit 0
|
||||||
|
|
||||||
|
# Firefox holds .parentlock open while it uses a profile (the sandbox
|
||||||
|
# sees the profile at the same path).
|
||||||
|
inUse() { find /proc/[0-9]*/fd -lname "$1/.parentlock" -print -quit 2>/dev/null | grep -q .; }
|
||||||
|
keysOf() { sed -n 's/^[[:space:]]*user_pref(\("[^"]*"\),.*/\1/p' "$1" 2>/dev/null; }
|
||||||
|
|
||||||
|
for prof in "$ffDir"/*/; do
|
||||||
|
prof=''${prof%/}; name=''${prof##*/}
|
||||||
|
[ -f "$prof/prefs.js" ] || [ "$name" = ${lib.escapeShellArg (toString cfg.desktopProfile)} ] || continue
|
||||||
|
want= wantKeys=
|
||||||
|
${lib.optionalString desktopFix ''
|
||||||
|
[ "$name" = ${lib.escapeShellArg cfg.desktopProfile} ] &&
|
||||||
|
want=$desktopJs wantKeys=${lib.escapeShellArg (lib.concatMapStrings (k: builtins.toJSON k + "\n") desktopKeys)}
|
||||||
|
''}
|
||||||
|
userJs=$prof/user.js
|
||||||
|
if [ -L "$userJs" ]; then
|
||||||
|
case $(readlink "$userJs") in
|
||||||
|
"$want") continue ;;
|
||||||
|
"$desktopJs") oldKeys=${lib.escapeShellArg (lib.concatMapStrings (k: builtins.toJSON k + "\n") desktopKeys)} ;;
|
||||||
|
/nix/store/*-firefox-user.js|/nix/store/*-firefox-desktop-user.js) oldKeys=$(keysOf "$userJs") ;;
|
||||||
|
*) oldKeys=foreign ;;
|
||||||
|
esac
|
||||||
|
elif [ -f "$userJs" ]; then
|
||||||
|
if [ "$(head -n1 "$userJs")" = ${lib.escapeShellArg marker} ]; then oldKeys=$(keysOf "$userJs"); else oldKeys=foreign; fi
|
||||||
|
elif [ -e "$userJs" ]; then oldKeys=foreign
|
||||||
|
else oldKeys=
|
||||||
|
fi
|
||||||
|
if [ "$oldKeys" = foreign ]; then
|
||||||
|
[ -z "$want" ] || echo "firefox: skipping $userJs (not managed by steam-frame-nix, move it away to adopt)"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -e "$userJs" ] || [ -L "$userJs" ]; then
|
||||||
|
# Values our user.js set, no longer set by one: out of prefs.js.
|
||||||
|
keys=$(printf '%s\n' "$oldKeys" | grep -vxF -f <(printf '%s\n' "$wantKeys") | grep .)
|
||||||
|
if [ -n "$keys" ]; then
|
||||||
|
if inUse "$prof"; then
|
||||||
|
echo "firefox: $name is in use; its old user.js stays until the next switch or launch with Firefox closed"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
grep -vF "$(sed 's/.*/user_pref(&,/' <<< "$keys")" "$prof/prefs.js" > "$prof/prefs.js.sfn" || true
|
||||||
|
if cmp -s "$prof/prefs.js" "$prof/prefs.js.sfn"; then rm "$prof/prefs.js.sfn"
|
||||||
|
else cat "$prof/prefs.js.sfn" > "$prof/prefs.js"; rm "$prof/prefs.js.sfn"; echo "firefox: $name: removed from prefs.js:" $keys
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
rm -f "$userJs"
|
||||||
|
fi
|
||||||
|
if [ -n "$want" ]; then ln -s "$want" "$userJs"; fi
|
||||||
|
done
|
||||||
'';
|
'';
|
||||||
|
|
||||||
firefox = pkgs.writeShellScript "firefox-launcher" (''
|
firefox = pkgs.writeShellScript "firefox-launcher" (''
|
||||||
@@ -36,6 +112,7 @@ let
|
|||||||
profile=(--profile "${profileDir}")
|
profile=(--profile "${profileDir}")
|
||||||
fi
|
fi
|
||||||
'' + ''
|
'' + ''
|
||||||
|
${profileSync} >&2 || true
|
||||||
exec /usr/bin/flatpak run --branch=stable --arch=aarch64 --command=firefox \
|
exec /usr/bin/flatpak run --branch=stable --arch=aarch64 --command=firefox \
|
||||||
--file-forwarding org.mozilla.firefox "''${profile[@]}" "$@"
|
--file-forwarding org.mozilla.firefox "''${profile[@]}" "$@"
|
||||||
'');
|
'');
|
||||||
@@ -48,8 +125,8 @@ in {
|
|||||||
type = lib.types.bool;
|
type = lib.types.bool;
|
||||||
default = true;
|
default = true;
|
||||||
description = ''
|
description = ''
|
||||||
Set full-screen-api.ignore-widgets in every profile except the
|
Default full-screen-api.ignore-widgets to true (except in the desktop
|
||||||
desktop one, so fullscreen fills the window instead of freezing in the
|
profile), so fullscreen fills the window instead of freezing in the
|
||||||
Steam session.
|
Steam session.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
@@ -59,9 +136,11 @@ in {
|
|||||||
example = { "media.av1.enabled" = false; };
|
example = { "media.av1.enabled" = false; };
|
||||||
description = ''
|
description = ''
|
||||||
about:config preferences for every profile (desktop one included),
|
about:config preferences for every profile (desktop one included),
|
||||||
set through a linked user.js on each switch. E.g. media.av1.enabled =
|
as default values: about:config can still change them for a profile,
|
||||||
|
and removing one here leaves nothing behind. E.g. media.av1.enabled =
|
||||||
false: the Frame's decoder has no AV1, so sites like YouTube fall back
|
false: the Frame's decoder has no AV1, so sites like YouTube fall back
|
||||||
to VP9/H.264, which it decodes in hardware.
|
to VP9/H.264, which it decodes in hardware. Take effect at the next
|
||||||
|
start of Firefox.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
desktopProfile = lib.mkOption {
|
desktopProfile = lib.mkOption {
|
||||||
@@ -74,27 +153,19 @@ in {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkMerge [
|
||||||
# Written into every existing profile (profile names are random); a
|
{
|
||||||
# user.js with no prefs is harmless and keeps turning prefs off revertible.
|
# Always, so disabling removes our user.js links (and old copies).
|
||||||
# Ours: a symlink (older versions) or a file starting with the marker.
|
home.activation.firefoxProfiles =
|
||||||
home.activation.firefoxUserJs =
|
lib.hm.dag.entryAfter [ "writeBoundary" ] "run ${profileSync}\n";
|
||||||
(lib.hm.dag.entryAfter [ "writeBoundary" ] ''
|
}
|
||||||
for prof in "${ffDir}"/*/; do
|
(lib.mkIf cfg.enable {
|
||||||
[ -f "$prof/prefs.js" ] || continue # only real profiles
|
# stable: the branch the launcher runs (the extension point has no
|
||||||
${pickUserJs}
|
# version, so it takes the app's branch).
|
||||||
target="$prof/user.js"
|
xdg.dataFile = lib.optionalAttrs (defaultPrefs != { } || desktopFix) {
|
||||||
if [ -e "$target" ] && [ ! -L "$target" ] && [ "$(head -n1 "$target")" != ${lib.escapeShellArg marker} ]; then
|
"flatpak/extension/org.mozilla.firefox.systemconfig/aarch64/stable".source = sysconfig;
|
||||||
echo "firefox: skipping $target (not managed by us, move it away to adopt)"
|
} // {
|
||||||
continue
|
"applications/org.mozilla.firefox.desktop".text = ''
|
||||||
fi
|
|
||||||
cmp -s "$js" "$target" && [ ! -L "$target" ] && continue
|
|
||||||
run rm -f "$target"
|
|
||||||
run install -m644 "$js" "$target"
|
|
||||||
done
|
|
||||||
'');
|
|
||||||
|
|
||||||
xdg.dataFile."applications/org.mozilla.firefox.desktop".text = ''
|
|
||||||
[Desktop Entry]
|
[Desktop Entry]
|
||||||
Type=Application
|
Type=Application
|
||||||
Name=Firefox
|
Name=Firefox
|
||||||
@@ -117,4 +188,6 @@ in {
|
|||||||
Exec=${firefox} --private-window @@u %u @@
|
Exec=${firefox} --private-window @@u %u @@
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
})
|
||||||
|
];
|
||||||
}
|
}
|
||||||
@@ -54,6 +54,8 @@
|
|||||||
# frameControls.enable = true;
|
# frameControls.enable = true;
|
||||||
# };
|
# };
|
||||||
# firefox.enable = true; # launcher for the Firefox Flatpak
|
# firefox.enable = true; # launcher for the Firefox Flatpak
|
||||||
|
# # about:config defaults; the Frame decodes VP9/H.264 but not AV1:
|
||||||
|
# firefox.prefs."media.av1.enabled" = false;
|
||||||
# # Hardware video decoding in the Jellyfin Desktop Flatpak (install
|
# # Hardware video decoding in the Jellyfin Desktop Flatpak (install
|
||||||
# # org.jellyfin.JellyfinDesktop yourself); gives it devices=all:
|
# # org.jellyfin.JellyfinDesktop yourself); gives it devices=all:
|
||||||
# jellyfin.hardwareDecoding.enable = true;
|
# jellyfin.hardwareDecoding.enable = true;
|
||||||
|
|||||||
Reference in new issue
Block a user