Parallel-Launcher Joystick detection issues (N64 emulator)

Hello,

This is the first time I write a derivation for a package. My host system is Manjaro.

I created a derivation for “Parallel Launcher” (https://parallel-launcher.ca/), which launches N64 emulators, but joystick detection doesn’t work. In the joystick configuration dialog (GUI), there’s a red text saying “Input driver failed to initialize due to an unexpected error”.

You can try it yourself, this is my current derivation.nix for Parallel-Launcher:

{ pkgs, stdenv, fetchFromGitLab, lib }:
stdenv.mkDerivation rec {
  name = "parallel-launcher";
  version = "7.6.0";

  meta = {
    description = "A simple easy-to-use launcher for the ParallelN64 emulator";
    homepage = "https://parallel-launcher.ca";
    license = lib.licenses.gpl3Only;
    platforms = [ "x86_64-linux" ];
  };

  src = fetchFromGitLab {
    owner = "parallel-launcher";
    repo = "parallel-launcher";
    rev = "v7.6-0";
    hash = "sha256-slIfZ4/3hGCvKH2HtlsrYWO9h5YQ+er4MBj2nr468R0=";
  };

  nativeBuildInputs = [
    pkgs.gnumake
    pkgs.qt5.qtdeclarative
    pkgs.qt5.qttools
    pkgs.qt5.wrapQtAppsHook
    pkgs.qt5.qtbase
  ];

  buildInputs = [
    pkgs.qt5.qtbase
    pkgs.qt5.qtsvg
    pkgs.findutils
    pkgs.xdg-utils
    pkgs.SDL2
    pkgs.polkit
    pkgs.sqlite
    pkgs.libgcrypt
    pkgs.dosfstools
    pkgs.coreutils
    pkgs.p7zip
  ];

  #optdepends=(
  #	'noto-fonts: intended font'
  #	'udisks2: mounting emulated SD cards'
  #)

  buildPhase = ''
    ./qmake-release.sh
    make -j $NIX_BUILD_CORES
  '';

  installPhase = ''
    mkdir -p $out/bin
    mkdir -p $out/share/applications
    mkdir -p $out/share/metainfo
    mkdir -p $out/share/icons/hicolor/scalable/apps
    mkdir -p $out/share/parallel-launcher
    cp parallel-launcher                                  $out/bin/
    cp ca.parallel_launcher.ParallelLauncher.desktop      $out/share/applications/
    cp ca.parallel_launcher.ParallelLauncher.metainfo.xml $out/share/metainfo/
    cp data/appicon.svg                $out/share/icons/hicolor/scalable/apps/ca.parallel_launcher.ParallelLauncher.svg
    cp bps-mime.xml                    $out/share/parallel-launcher/
    cp lang/*                          $out/share/parallel-launcher/
    cp parallel-launcher-sdl-relay     $out/share/parallel-launcher/
    cp parallel-launcher-lsjs          $out/share/parallel-launcher/
  '';
}

When I click on the top right controller icon, with the SDL2 input driver selected, I see a red “Input driver failed to initialize” error.

My first guess was that SDL2 joystick detection doesn’t work. The package even has a small separate SDL2 joystick detection executable, it’s just a few lines of C code. When I compiled that on my Manjaro host system (without nix), it works fine and prints the GUID of both my XBox360 controller and a GameCube controller connected via a Mayflash adapter connected to my system which uses a custom udev rule. But when I compile it with nix-build, it only prints a GUID when my XBox360 controller is connected, and that GUID is different than the two previous GUIDs. Not sure where to go from here.

If you want to toy with the SDL2 controller detection alone, here’s the joystick detection C code:

// src/main.cpp
#include <SDL2/SDL.h>
#include <stdio.h>

typedef unsigned char ubyte;

static void writeHex( char *out, const ubyte *data, size_t numBytes ) {
	for( size_t i = 0; i < numBytes; i++ ) {
		const ubyte upperNibble = data[i] >> 4;
		const ubyte lowerNibble = data[i] & 0xF;
		out[i*2] = upperNibble + (upperNibble > 9 ? ('a' - (char)10) : '0');
		out[i*2+1] = lowerNibble + (lowerNibble > 9 ? ('a' - (char)10) : '0');
	}
}

static inline void writeUuid( const ubyte *data, char *buffer ) {
	writeHex( buffer, data, 4 );
	writeHex( &buffer[9], &data[4], 2 );
	writeHex( &buffer[14], &data[6], 2 );
	writeHex( &buffer[19], &data[8], 2 );
	writeHex( &buffer[24], &data[10], 6 );
}

int main( int argc, char *argv[] ) {
	SDL_Init( SDL_INIT_JOYSTICK );
	SDL_JoystickUpdate();
	
	char uuid[37] = "00000000-0000-0000-0000-000000000000";
	const int numConnected = SDL_NumJoysticks();
	for( int i = 0; i < numConnected; i++ ) {
		writeUuid( SDL_JoystickGetDeviceGUID( i ).data, uuid );
		printf( "%s\n", uuid );
	}
	
	SDL_Quit();
	return 0;
}

and here’s the corresponding default.nix build file, placed in the same directory where the “src” folder lives:

with import <nixpkgs> {};

stdenv.mkDerivation {
  name = "sdl-sample";
  src = ./src;
  buildInputs = [ SDL2 ];
  buildPhase = "c++ -o main main.cpp -lSDL2";

  installPhase = ''
    mkdir -p $out/bin
    cp main $out/bin/
  '';
}

Then run nix-build and run ./result/bin/main to print the GUIDs of the connected controllers found by SDL2.

Figured out why the input driver failed to initialize: Parallel-Launcher stores the SDL joystick detection helper binary inside /usr/shar/parallel-launcher, which isn’t the Nix way. I patched the source via:

  patchPhase = ''
    substituteInPlace src/polyfill/base-directory.cpp --replace "/usr/share/parallel-launcher" "$out/share/parallel-launcher"
  '';

Now my XBox360 controller is recognized and works!

Remaining problem: My Gamecube controller isn’t detected yet, it’s connected via a Mayflash adapter and uses a custom udev rule. (Edit: Nix’s dolphin-emu and the Manjaro native parallel-launcher both recognize my Gamecube controller)

Wondering what the proper way to develop Nix native applications is that want to access an equivalent of “/usr/share”, because patching it in the installer is ugly. Maybe passing the /nix/store/hash-parallel-launcher directory to the build system? Or pass it as an environment variable to the running application?

I think the problem is that SDL2 isn’t compiled with HIDAPI support, and I want to compile it with HIDAPI support.

But how do I compile Nix’s SDL2 package locally? Copy-pasting the SDL2/default.nix file and trying to nix-build it didn’t work. The package file is here: nixpkgs/pkgs/development/libraries/SDL2/default.nix at 9d29cd266cebf80234c98dd0b87256b6be0af44e · NixOS/nixpkgs · GitHub

Reasons why I think missing HIDAPI support is the issue:

  • The hidapi package isn’t in SDL’s dependencies
  • I don’t see the HID_API_LIBUSB cmake flag being enabled like in the ArchLinux package: PKGBUILD · main · Arch Linux / Packaging / Packages / sdl2 · GitLab
  • strace results also show that the controller detection utility above opens my controller device file under /dev/bus/usb/ when compiled in the Manjaro environment, but doesn’t even look at /dev/bus/ when compiled in Nix’s environment