From fab62e2aa3bd597f9ff152d77f2bd24462faa59c Mon Sep 17 00:00:00 2001 From: cmitu <31816814+cmitu@users.noreply.github.com> Date: Tue, 8 Jun 2021 17:37:02 +0100 Subject: [PATCH] joy2key: new version using PySDL2 Added a new `joy2key` implementation, using PySDL2 for joystick event handling. PySDL2 is a python module that wraps SDL2 (and other SDL libraries) using the built-in `ctypes` module. Pros: * event handling is simplified a bit, using SDL's event loop. * (subjective) the code is a bit more structured and easy to follow. * joystick handling is rewritten based on EmulationStation code, movement is smoother and scrolling is improved. * support for input repeat to improve scrolling, just keep the input pressed and scrolling continues Cons: * module is a bit larger (296 LOC vs 224 in current joy2key.py) * needs PySDL2 (which might not be packaged, see the notes below) and SDL2 * arguably, device config to keyboard event mapping is more complex (abstracted as InputDev) Notes: * availability of PySDL2 as a system package is good, but it's not standard in Debian 10 (stable) at the moment. The module is present though (as a backport) in Ubuntu 18.04 (and later) and Raspberry Pi OS (Buster). When it will be universally available, we should probably revisit the code that checks for the version and make it a hard requirement in `get_retropie_depends`, similar to `python3-pyudev`. * added PgUp/PgDown to the default parameters for `joy2key`, they are mapped in a similar fashion to EmulationStation to the shoulder buttons. This doesn't apply to Runcommand's invocation, since it uses a different set of parameters. * added a configuration option in `runcommand` for selecting the joy2key version used. Default is the SDL version (when PySDL2 is installed), with the ability to fallback to the previous version (udev based). --- scriptmodules/helpers.sh | 24 +- scriptmodules/supplementary/runcommand.sh | 27 +- .../supplementary/runcommand/joy2key_sdl.py | 527 ++++++++++++++++++ .../supplementary/runcommand/runcommand.sh | 11 +- scriptmodules/system.sh | 8 + 5 files changed, 582 insertions(+), 15 deletions(-) create mode 100755 scriptmodules/supplementary/runcommand/joy2key_sdl.py diff --git a/scriptmodules/helpers.sh b/scriptmodules/helpers.sh index ad2b266dd2..64fa525753 100644 --- a/scriptmodules/helpers.sh +++ b/scriptmodules/helpers.sh @@ -1213,7 +1213,7 @@ _EOF_ ## @param but2 mapping for button 2 ## @param but3 mapping for button 3 ## @param butX mapping for button X ... -## @brief Start joy2key.py process in background to map joystick presses to keyboard +## @brief Start joy2key process in background to map joystick presses to keyboard ## @details Arguments are curses capability names or hex values starting with '0x' ## see: http://pubs.opengroup.org/onlinepubs/7908799/xcurses/terminfo.html function joy2keyStart() { @@ -1223,18 +1223,30 @@ function joy2keyStart() { local params=("$@") if [[ "${#params[@]}" -eq 0 ]]; then - params=(kcub1 kcuf1 kcuu1 kcud1 0x0a 0x20 0x1b) + # Default button-to-keyboard mappings: + # * cursor keys for axis/dpad + # * enter, space and esc for buttons 'a', 'b' and 'x' + # * page up/page down for buttons 5,6 (shoulder buttons) + params=(kcub1 kcuf1 kcuu1 kcud1 0x0a 0x20 0x1b 0x00 kpp knp) + fi + + # Choose the joy2key implementation here, since `runcommand` may not be installed + local joy2key="joy2key.py" + if hasPackage "python3-sdl2"; then + iniConfig " =" '"' "$configdir/all/runcommand.cfg" + iniGet "joy2key_version" + [[ $ini_value != "0" ]] && joy2key="joy2key_sdl.py" fi # get the first joystick device (if not already set) [[ -c "$__joy2key_dev" ]] || __joy2key_dev="/dev/input/jsX" # if no joystick device, or joy2key is already running exit - [[ -z "$__joy2key_dev" ]] || pgrep -f joy2key.py >/dev/null && return 1 + [[ -z "$__joy2key_dev" ]] || pgrep -f "$joy2key" >/dev/null && return 1 - # if joy2key.py is installed run it with cursor keys for axis/dpad, and enter + space for buttons 0 and 1 - if "$scriptdir/scriptmodules/supplementary/runcommand/joy2key.py" "$__joy2key_dev" "${params[@]}" 2>/dev/null; then - __joy2key_pid=$(pgrep -f joy2key.py) + # if joy2key is installed, run it + if "$scriptdir/scriptmodules/supplementary/runcommand/$joy2key" "$__joy2key_dev" "${params[@]}" 2>/dev/null; then + __joy2key_pid=$(pgrep -f "$joy2key") return 0 fi diff --git a/scriptmodules/supplementary/runcommand.sh b/scriptmodules/supplementary/runcommand.sh index e469bf2838..c22d711bcc 100644 --- a/scriptmodules/supplementary/runcommand.sh +++ b/scriptmodules/supplementary/runcommand.sh @@ -28,11 +28,11 @@ function depends_runcommand() { } function install_bin_runcommand() { - cp "$md_data/runcommand.sh" "$md_inst/" - cp "$md_data/joy2key.py" "$md_inst/" - chmod a+x "$md_inst/runcommand.sh" - chmod a+x "$md_inst/joy2key.py" - python3 -m compileall "$md_inst/joy2key.py" + for file in "runcommand.sh" "joy2key.py" "joy2key_sdl.py"; do + cp "$md_data/$file" "$md_inst/" + chmod +x "$md_inst/$file" + done + python3 -m compileall "$md_inst/joy2key.py" "$md_inst/joy2key_sdl.py" if [[ ! -f "$configdir/all/runcommand.cfg" ]]; then mkUserDir "$configdir/all" iniConfig " = " '"' "$configdir/all/runcommand.cfg" @@ -41,8 +41,16 @@ function install_bin_runcommand() { iniSet "governor" "" iniSet "disable_menu" "0" iniSet "image_delay" "2" + if hasPackage "python3-sdl2"; then + iniSet "joy2key_version" "1" + fi chown $user:$user "$configdir/all/runcommand.cfg" fi + # if PySDL2 is not installed, force the udev version of joy2key + if ! hasPackage "python3-sdl2"; then + iniConfig " = " '"' "$configdir/all/runcommand.cfg" + iniSet "joy2key_version" "0" + fi if [[ ! -f "$configdir/all/runcommand-launch-dialog.cfg" ]]; then dialog --create-rc "$configdir/all/runcommand-launch-dialog.cfg" chown $user:$user "$configdir/all/runcommand-launch-dialog.cfg" @@ -108,6 +116,7 @@ function gui_runcommand() { 'disable_joystick=0' \ 'image_delay=2' \ 'governor=' \ + 'joy2key_version=1' \ )" [[ -z "$governor" ]] && governor="Default: don't change" @@ -135,6 +144,11 @@ function gui_runcommand() { options+=(4 "Launch image delay in seconds (currently $image_delay)") options+=(5 "CPU governor configuration (currently: $governor)") + if [[ "$joy2key_version" -eq 1 ]]; then + options+=(6 "Joy2key version (currently: sdl)") + else + options+=(6 "Joy2key version (currently: udev)") + fi local choice=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty) [[ -z "$choice" ]] && break @@ -157,6 +171,9 @@ function gui_runcommand() { 5) governor_runcommand ;; + 6) + iniSet "joy2key_version" "$((joy2key_version ^ 1))" + ;; esac done } diff --git a/scriptmodules/supplementary/runcommand/joy2key_sdl.py b/scriptmodules/supplementary/runcommand/joy2key_sdl.py new file mode 100755 index 0000000000..a03a105c4a --- /dev/null +++ b/scriptmodules/supplementary/runcommand/joy2key_sdl.py @@ -0,0 +1,527 @@ +#!/usr/bin/env python3 +""" +This file is part of The RetroPie Project + +The RetroPie Project is the legal property of its developers, whose names are +too numerous to list here. Please refer to the COPYRIGHT.md file distributed with this source. + +See the LICENSE.md file at the top-level directory of this distribution and +https://raw.githubusercontent.com/RetroPie/RetroPie-Setup/master/LICENSE.md. + +Command line joystick to keyboard translator, using SDL2 for event handling +Example usage: +