Skip to content

Commit

Permalink
opengamepadui: init at 0.35.1
Browse files Browse the repository at this point in the history
  • Loading branch information
ShadowApex committed Dec 24, 2024
1 parent de61daf commit 09d3f7a
Show file tree
Hide file tree
Showing 4 changed files with 452 additions and 0 deletions.
2 changes: 2 additions & 0 deletions nixos/doc/manual/release-notes/rl-2505.section.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@

- [nvidia-gpu](https://github.com/utkuozdemir/nvidia_gpu_exporter), a Prometheus exporter that scrapes `nvidia-smi` for GPU metrics. Available as [services.prometheus.exporters.nvidia-gpu](#opt-services.prometheus.exporters.nvidia-gpu.enable).

- [OpenGamepadUI](https://github.com/ShadowBlip/OpenGamepadUI/), an open source gamepad-native game launcher and overlay for Linux. Available as [programs.opengamepadui](#opt-programs.opengamepadui.enable).

- [InputPlumber](https://github.com/ShadowBlip/InputPlumber/), an open source input router and remapper daemon for Linux. Available as [services.inputplumber](#opt-services.inputplumber.enable).

- [Buffyboard](https://gitlab.postmarketos.org/postmarketOS/buffybox/-/tree/master/buffyboard), a framebuffer on-screen keyboard. Available as [services.buffyboard](option.html#opt-services.buffyboard).
Expand Down
1 change: 1 addition & 0 deletions nixos/modules/module-list.nix
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@
./programs/ns-usbloader.nix
./programs/oblogout.nix
./programs/oddjobd.nix
./programs/opengamepadui.nix
./programs/openvpn3.nix
./programs/obs-studio.nix
./programs/partition-manager.nix
Expand Down
295 changes: 295 additions & 0 deletions nixos/modules/programs/opengamepadui.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,295 @@
{
config,
lib,
pkgs,
...
}:

let
cfg = config.programs.opengamepadui;
gamescopeCfg = config.programs.gamescope;

opengamepadui-gamescope =
let
exports = builtins.attrValues (
builtins.mapAttrs (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 ${builtins.toString cfg.gamescopeSession.args} -R $socket -T $stats >"$HOME"/.gamescope-cmd.log
gamescope ${builtins.toString 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 ${builtins.toString 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.mkOption {
type = lib.types.package;
default = pkgs.opengamepadui;
defaultText = lib.literalExpression "pkgs.opengamepadui";
example = lib.literalExpression ''
pkgs.opengamepadui.override {
extraEnv = {
MANGOHUD = true;
OBS_VKCAPTURE = true;
RADV_TEX_ANISO = 16;
};
extraLibraries = p: with p; [
atk
];
}
'';

description = ''
The OpenGamepadUI package to use. Additional libraries are added from the system
configuration to ensure graphics work properly.
Use this option to customise the OpenGamepadUI package rather than adding your
custom opengamepadui to {option}`environment.systemPackages` yourself.
'';
};

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;
# `fonts.packages` is a list of paths now, filter out which are not packages
default = builtins.filter lib.types.package.check 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 = {
# 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";
};
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 and performance control.
'';
};

config = lib.mkIf cfg.enable {
hardware.graphics = {
# this fixes the "glXChooseVisual failed" bug, context: https://github.com/NixOS/nixpkgs/issues/47932
enable = true;
enable32Bit = true;
};

security.wrappers = lib.mkIf (cfg.gamescopeSession.enable && gamescopeCfg.capSysNice) {
# needed or steam plugin fails
bwrap = {
owner = "root";
group = "root";
source = "${pkgs.bubblewrap}/bin/bwrap";
setuid = true;
};
};

programs.opengamepadui.extraPackages = cfg.fontPackages;

programs.gamescope.enable = lib.mkForce cfg.gamescopeSession.enable;
services.displayManager.sessionPackages = lib.mkIf cfg.gamescopeSession.enable [
gamescopeSessionFile
];

# optionally enable 32bit pulseaudio support if pulseaudio is enabled
hardware.pulseaudio.support32Bit = config.hardware.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
cfg.package.run
] ++ lib.optional cfg.gamescopeSession.enable opengamepadui-gamescope;
};

meta.maintainers = with lib.maintainers; [ shadowapex ];
}
Loading

0 comments on commit 09d3f7a

Please sign in to comment.