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 25, 2024
1 parent 0697226 commit 9e79e57
Show file tree
Hide file tree
Showing 4 changed files with 398 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
273 changes: 273 additions & 0 deletions nixos/modules/programs/opengamepadui.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
{
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 ${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;
# `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 = lib.getExe pkgs.bubblewrap;
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 9e79e57

Please sign in to comment.