mirror of
https://github.com/NixOS/nixpkgs.git
synced 2025-06-09 19:13:26 +03:00
271 lines
9.5 KiB
Nix
271 lines
9.5 KiB
Nix
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
...
|
|
}:
|
|
|
|
let
|
|
cfg = config.programs.opengamepadui;
|
|
gamescopeCfg = config.programs.gamescope;
|
|
|
|
opengamepadui-gamescope =
|
|
let
|
|
exports = lib.mapAttrsToList (n: v: "export ${n}=${v}") cfg.gamescopeSession.env;
|
|
in
|
|
# Based on gamescope-session-plus from ChimeraOS
|
|
pkgs.writeShellScriptBin "opengamepadui-gamescope" ''
|
|
${builtins.concatStringsSep "\n" exports}
|
|
|
|
# Enable Mangoapp
|
|
export MANGOHUD_CONFIGFILE=$(mktemp /tmp/mangohud.XXXXXXXX)
|
|
export RADV_FORCE_VRS_CONFIG_FILE=$(mktemp /tmp/radv_vrs.XXXXXXXX)
|
|
|
|
# Plop GAMESCOPE_MODE_SAVE_FILE into $XDG_CONFIG_HOME (defaults to ~/.config).
|
|
export GAMESCOPE_MODE_SAVE_FILE="''${XDG_CONFIG_HOME:-$HOME/.config}/gamescope/modes.cfg"
|
|
export GAMESCOPE_PATCHED_EDID_FILE="''${XDG_CONFIG_HOME:-$HOME/.config}/gamescope/edid.bin"
|
|
|
|
# Make path to gamescope mode save file.
|
|
mkdir -p "$(dirname "$GAMESCOPE_MODE_SAVE_FILE")"
|
|
touch "$GAMESCOPE_MODE_SAVE_FILE"
|
|
|
|
# Make path to Gamescope edid patched file.
|
|
mkdir -p "$(dirname "$GAMESCOPE_PATCHED_EDID_FILE")"
|
|
touch "$GAMESCOPE_PATCHED_EDID_FILE"
|
|
|
|
# Initially write no_display to our config file
|
|
# so we don't get mangoapp showing up before OpenGamepadUI initializes
|
|
# on OOBE and stuff.
|
|
mkdir -p "$(dirname "$MANGOHUD_CONFIGFILE")"
|
|
echo "no_display" >"$MANGOHUD_CONFIGFILE"
|
|
|
|
# Prepare our initial VRS config file
|
|
# for dynamic VRS in Mesa.
|
|
mkdir -p "$(dirname "$RADV_FORCE_VRS_CONFIG_FILE")"
|
|
echo "1x1" >"$RADV_FORCE_VRS_CONFIG_FILE"
|
|
|
|
# To play nice with the short term callback-based limiter for now
|
|
export GAMESCOPE_LIMITER_FILE=$(mktemp /tmp/gamescope-limiter.XXXXXXXX)
|
|
|
|
ulimit -n 524288
|
|
|
|
# Setup socket for gamescope
|
|
# Create run directory file for startup and stats sockets
|
|
tmpdir="$([[ -n ''${XDG_RUNTIME_DIR+x} ]] && mktemp -p "$XDG_RUNTIME_DIR" -d -t gamescope.XXXXXXX)"
|
|
socket="''${tmpdir:+$tmpdir/startup.socket}"
|
|
stats="''${tmpdir:+$tmpdir/stats.pipe}"
|
|
|
|
# Fail early if we don't have a proper runtime directory setup
|
|
if [[ -z $tmpdir || -z ''${XDG_RUNTIME_DIR+x} ]]; then
|
|
echo >&2 "!! Failed to find run directory in which to create stats session sockets (is \$XDG_RUNTIME_DIR set?)"
|
|
exit 0
|
|
fi
|
|
|
|
export GAMESCOPE_STATS="$stats"
|
|
mkfifo -- "$stats"
|
|
mkfifo -- "$socket"
|
|
|
|
# Start gamescope compositor, log it's output and background it
|
|
echo gamescope ${lib.escapeShellArgs cfg.gamescopeSession.args} -R $socket -T $stats >"$HOME"/.gamescope-cmd.log
|
|
gamescope ${lib.escapeShellArgs cfg.gamescopeSession.args} -R $socket -T $stats >"$HOME"/.gamescope-stdout.log 2>&1 &
|
|
gamescope_pid="$!"
|
|
|
|
if read -r -t 3 response_x_display response_wl_display <>"$socket"; then
|
|
export DISPLAY="$response_x_display"
|
|
export GAMESCOPE_WAYLAND_DISPLAY="$response_wl_display"
|
|
# We're done!
|
|
else
|
|
echo "gamescope failed"
|
|
kill -9 "$gamescope_pid"
|
|
wait -n "$gamescope_pid"
|
|
exit 1
|
|
# Systemd or Session manager will have to restart session
|
|
fi
|
|
|
|
# If we have mangoapp binary start it
|
|
if command -v mangoapp >/dev/null; then
|
|
(while true; do
|
|
sleep 1
|
|
mangoapp >"$HOME"/.mangoapp-stdout.log 2>&1
|
|
done) &
|
|
fi
|
|
|
|
# Start OpenGamepadUI
|
|
opengamepadui ${lib.escapeShellArgs cfg.args}
|
|
|
|
# When the client exits, kill gamescope nicely
|
|
kill $gamescope_pid
|
|
'';
|
|
|
|
gamescopeSessionFile =
|
|
(pkgs.writeTextDir "share/wayland-sessions/opengamepadui.desktop" ''
|
|
[Desktop Entry]
|
|
Name=opengamepadui
|
|
Comment=OpenGamepadUI Session
|
|
Exec=${opengamepadui-gamescope}/bin/opengamepadui-gamescope
|
|
Type=Application
|
|
'').overrideAttrs
|
|
(_: {
|
|
passthru.providedSessions = [ "opengamepadui" ];
|
|
});
|
|
in
|
|
{
|
|
options.programs.opengamepadui = {
|
|
enable = lib.mkEnableOption "opengamepadui";
|
|
|
|
args = lib.mkOption {
|
|
type = lib.types.listOf lib.types.str;
|
|
default = [ ];
|
|
description = ''
|
|
Arguments to be passed to OpenGamepadUI
|
|
'';
|
|
};
|
|
|
|
package = lib.mkPackageOption pkgs "OpenGamepadUI" {
|
|
default = [ "opengamepadui" ];
|
|
};
|
|
|
|
extraPackages = lib.mkOption {
|
|
type = lib.types.listOf lib.types.package;
|
|
default = [ ];
|
|
example = lib.literalExpression ''
|
|
with pkgs; [
|
|
gamescope
|
|
]
|
|
'';
|
|
description = ''
|
|
Additional packages to add to the OpenGamepadUI environment.
|
|
'';
|
|
};
|
|
|
|
fontPackages = lib.mkOption {
|
|
type = lib.types.listOf lib.types.package;
|
|
default = config.fonts.packages;
|
|
defaultText = lib.literalExpression "builtins.filter lib.types.package.check config.fonts.packages";
|
|
example = lib.literalExpression "with pkgs; [ source-han-sans ]";
|
|
description = ''
|
|
Font packages to use in OpenGamepadUI.
|
|
|
|
Defaults to system fonts, but could be overridden to use other fonts — useful for users who would like to customize CJK fonts used in opengamepadui. According to the [upstream issue](https://github.com/ValveSoftware/opengamepadui-for-linux/issues/10422#issuecomment-1944396010), opengamepadui only follows the per-user fontconfig configuration.
|
|
'';
|
|
};
|
|
|
|
gamescopeSession = lib.mkOption {
|
|
description = "Run a GameScope driven OpenGamepadUI session from your display-manager";
|
|
default = { };
|
|
type = lib.types.submodule {
|
|
options = {
|
|
enable = lib.mkEnableOption "GameScope Session";
|
|
args = lib.mkOption {
|
|
type = lib.types.listOf lib.types.str;
|
|
default = [
|
|
"--prefer-output"
|
|
"*,eDP-1"
|
|
"--xwayland-count"
|
|
"2"
|
|
"--default-touch-mode"
|
|
"4"
|
|
"--hide-cursor-delay"
|
|
"3000"
|
|
"--fade-out-duration"
|
|
"200"
|
|
"--steam"
|
|
];
|
|
description = ''
|
|
Arguments to be passed to GameScope for the session.
|
|
'';
|
|
};
|
|
|
|
env = lib.mkOption {
|
|
type = lib.types.attrsOf lib.types.str;
|
|
default = { };
|
|
description = ''
|
|
Environmental variables to be passed to GameScope for the session.
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
inputplumber.enable = lib.mkEnableOption ''
|
|
Run InputPlumber service for input management and gamepad configuration.
|
|
'';
|
|
|
|
powerstation.enable = lib.mkEnableOption ''
|
|
Run PowerStation service for TDP control and performance settings.
|
|
'';
|
|
};
|
|
|
|
config = lib.mkIf cfg.enable {
|
|
hardware.graphics = {
|
|
# this fixes the "glXChooseVisual failed" bug, context: https://github.com/NixOS/nixpkgs/issues/47932
|
|
enable = true;
|
|
enable32Bit = pkgs.stdenv.hostPlatform.isx86_64;
|
|
};
|
|
|
|
security.wrappers = lib.mkIf (cfg.gamescopeSession.enable && gamescopeCfg.capSysNice) {
|
|
# needed or steam plugin fails
|
|
bwrap = {
|
|
owner = "root";
|
|
group = "root";
|
|
source = lib.getExe pkgs.bubblewrap;
|
|
setuid = true;
|
|
};
|
|
};
|
|
|
|
programs.opengamepadui.extraPackages = cfg.fontPackages;
|
|
|
|
programs.gamescope.enable = true;
|
|
services.displayManager.sessionPackages = lib.mkIf cfg.gamescopeSession.enable [
|
|
gamescopeSessionFile
|
|
];
|
|
|
|
programs.opengamepadui.gamescopeSession.env = {
|
|
# Fix intel color corruption
|
|
# might come with some performance degradation but is better than a corrupted
|
|
# color image
|
|
INTEL_DEBUG = "norbc";
|
|
mesa_glthread = "true";
|
|
# This should be used by default by gamescope. Cannot hurt to force it anyway.
|
|
# Reported better framelimiting with this enabled
|
|
ENABLE_GAMESCOPE_WSI = "1";
|
|
# Force Qt applications to run under xwayland
|
|
QT_QPA_PLATFORM = "xcb";
|
|
# Some environment variables by default (taken from Deck session)
|
|
SDL_VIDEO_MINIMIZE_ON_FOCUS_LOSS = "0";
|
|
# There is no way to set a color space for an NV12
|
|
# buffer in Wayland. And the color management protocol that is
|
|
# meant to let this happen is missing the color range...
|
|
# So just workaround this with an ENV var that Remote Play Together
|
|
# and Gamescope will use for now.
|
|
GAMESCOPE_NV12_COLORSPACE = "k_EStreamColorspace_BT601";
|
|
# Workaround older versions of vkd3d-proton setting this
|
|
# too low (desc.BufferCount), resulting in symptoms that are potentially like
|
|
# swapchain starvation.
|
|
VKD3D_SWAPCHAIN_LATENCY_FRAMES = "3";
|
|
# To expose vram info from radv
|
|
WINEDLLOVERRIDES = "dxgi=n";
|
|
# Don't wait for buffers to idle on the client side before sending them to gamescope
|
|
vk_xwayland_wait_ready = "false";
|
|
# Temporary crutch until dummy plane interactions / etc are figured out
|
|
GAMESCOPE_DISABLE_ASYNC_FLIPS = "1";
|
|
};
|
|
|
|
# optionally enable 32bit pulseaudio support if pulseaudio is enabled
|
|
services.pulseaudio.support32Bit = config.services.pulseaudio.enable;
|
|
services.pipewire.alsa.support32Bit = config.services.pipewire.alsa.enable;
|
|
|
|
hardware.steam-hardware.enable = true;
|
|
|
|
services.inputplumber.enable = lib.mkDefault cfg.inputplumber.enable;
|
|
services.powerstation.enable = lib.mkDefault cfg.powerstation.enable;
|
|
|
|
environment.pathsToLink = [ "/share" ];
|
|
|
|
environment.systemPackages = [
|
|
cfg.package
|
|
] ++ lib.optional cfg.gamescopeSession.enable opengamepadui-gamescope;
|
|
};
|
|
|
|
meta.maintainers = with lib.maintainers; [ shadowapex ];
|
|
}
|