Double joystick gamepad seen as one

I have a gamepad with two joystick on it and it function perfectly if I use windows, but when I plug into Linux the two joysticks functions as one (it will take the same direction whatever of the two I move) and right pad buttons do not work (it has a total of 20 buttons, 10 for each side).

It’s surely not Manjaro specific, but anyway there is some suggestion on how to try to solve it?

Some informations on the device: it’s recognized with “usb ID 0810:e001 Personal Communication Systems, Inc. Twin controller”, and evemu-record is showing:
Available devices:
/dev/input/event21: Twin USB Gamepad
Select the device event number [0-21]: 21
EVEMU 1.3
Kernel: 5.10.89-1-MANJARO
DMI: dmi:bvnAmericanMegatrendsInternational,LLC.:bvrF14f:bd10/13/2021:br5.17:svnGigabyteTechnologyCo.,Ltd.:pnB550MS2H:pvrDefaultstring:rvnGigabyteTechnologyCo.,Ltd.:rnB550MS2H:rvrDefaultstring:cvnDefaultstring:ct3:cvrDefaultstring:
Input device name: "Twin USB Gamepad "
Input device ID: bus 0x03 vendor 0x810 product 0xe001 version 0x110
Supported events:
Event type 0 (EV_SYN)
Event code 0 (SYN_REPORT)
Event code 1 (SYN_CONFIG)
Event code 2 (SYN_MT_REPORT)
Event code 3 (SYN_DROPPED)
Event code 4 ((null))
Event code 5 ((null))
Event code 6 ((null))
Event code 7 ((null))
Event code 8 ((null))
Event code 9 ((null))
Event code 10 ((null))
Event code 11 ((null))
Event code 12 ((null))
Event code 13 ((null))
Event code 14 ((null))
Event code 15 (SYN_MAX)
Event type 1 (EV_KEY)
Event code 288 (BTN_TRIGGER)
Event code 289 (BTN_THUMB)
Event code 290 (BTN_THUMB2)
Event code 291 (BTN_TOP)
Event code 292 (BTN_TOP2)
Event code 293 (BTN_PINKIE)
Event code 294 (BTN_BASE)
Event code 295 (BTN_BASE2)
Event code 296 (BTN_BASE3)
Event code 297 (BTN_BASE4)
Event type 3 (EV_ABS)
Event code 0 (ABS_X)
Value 127
Min 0
Max 255
Fuzz 0
Flat 15
Resolution 0
Event code 1 (ABS_Y)
Value 127
Min 0
Max 255
Fuzz 0
Flat 15
Resolution 0
Event type 4 (EV_MSC)
Event code 4 (MSC_SCAN)
Properties:
N: Twin USB Gamepad
I: 0003 0810 e001 0110
P: 00 00 00 00 00 00 00 00
B: 00 0b 00 00 00 00 00 00 00
B: 01 00 00 00 00 00 00 00 00
B: 01 00 00 00 00 00 00 00 00
B: 01 00 00 00 00 00 00 00 00
B: 01 00 00 00 00 00 00 00 00
B: 01 00 00 00 00 ff 03 00 00
B: 01 00 00 00 00 00 00 00 00
B: 01 00 00 00 00 00 00 00 00
B: 01 00 00 00 00 00 00 00 00
B: 01 00 00 00 00 00 00 00 00
B: 01 00 00 00 00 00 00 00 00
B: 01 00 00 00 00 00 00 00 00
B: 01 00 00 00 00 00 00 00 00
B: 02 00 00 00 00 00 00 00 00
B: 03 03 00 00 00 00 00 00 00
B: 04 10 00 00 00 00 00 00 00
B: 05 00 00 00 00 00 00 00 00
B: 11 00 00 00 00 00 00 00 00
B: 12 00 00 00 00 00 00 00 00
B: 14 00 00 00 00 00 00 00 00
B: 15 00 00 00 00 00 00 00 00
B: 15 00 00 00 00 00 00 00 00
A: 00 0 255 0 15 0
A: 01 0 255 0 15 0

jstest /dev/input/js0 is confirming the behaviours:
Driver version is 2.1.0.
Joystick (Twin USB Gamepad ) has 2 axes (X, Y)
and 10 buttons (Trigger, ThumbBtn, ThumbBtn2, TopBtn, TopBtn2, PinkieBtn, BaseBtn, BaseBtn2, BaseBtn3, BaseBtn4).
Testing … (interrupt to exit)
Axes: 0: 0 1: 0 Buttons: 0:off 1:off 2:off 3:off 4:off 5:off 6:off 7:off 8:off 9:off

usbhid-dump descriptor dump:
003:004:000:DESCRIPTOR 1642028605.396474
05 01 09 04 A1 01 85 01 A1 02 75 08 95 02 15 00
26 FF 00 35 00 46 FF 00 95 02 81 01 95 02 09 30
09 31 81 02 65 00 75 01 95 04 25 01 45 01 81 01
75 01 95 0A 25 01 45 01 05 09 19 01 29 0A 81 02
75 01 95 02 81 01 06 00 FF 75 01 95 08 25 01 45
01 45 01 09 01 81 02 C0 C0 05 01 09 04 A1 01 85
02 A1 02 75 08 95 02 15 00 26 FF 00 35 00 46 FF
00 95 02 81 01 95 02 09 30 09 31 81 02 65 00 75
01 95 04 25 01 45 01 81 01 75 01 95 0A 25 01 45
01 05 09 19 01 29 0A 81 02 75 01 95 02 81 01 06
00 FF 75 01 95 08 25 01 45 01 45 01 09 01 81 02
C0 C0

and the dump of me moving joysticks and pressing all buttons usbhid-dump events

Looking on the internet seems I’m not alone and problem is not new, just willing to understand if there is a way to address this problem.

Hi, well, I have bought today a ShanWan Twin gamepad having the same problem.
I am now in the process of:

I’ve apparently just met a problem in:
sudo ./start_shanwan.sh
Found joystick on /dev/hidraw0
File “/opt/ShanWanTwin_2-4Ghz_Linux/shanwan-joystick.py”, line 97
firstID = ord(firstID)
TabError: inconsistent use of tabs and spaces in indentation

If someone has a swifter solution to this issue or is able to upgrade the device driver would be so nice.
Besides, these 2.4Gh USB controllers are nice, cheap (13-17USD shipping and VAT included). Same we cannot play all together right from the plugging!

I probably won’t be much help since I have no experience with gamepads etc, but I do know what the error is about. The indentation on line 97 is a single tab but should be 8 spaces.

# replace this
	firstID = ord(firstID)
# with this
        firstID = ord(firstID)

Apart from the indentation I haven’t looked at the code so I don’t know if it’ll work.

Thank You very much! A step further
Now uinput problem:
Traceback (most recent call last): ─╯
File “/opt/ShanWanTwin_2-4Ghz_Linux/shanwan-joystick.py”, line 21, in
import uinput
ModuleNotFoundError: No module named ‘uinput’

It is strange: shoudn’t be part of the linux kernel?

Anyway, i did:
sudo pip install python-uinput

to get to next trouble:

─ sudo ./start_shanwan.sh ─╯
Found joystick on /dev/hidraw0

Traceback (most recent call last): ─╯
File “/opt/ShanWanTwin_2-4Ghz_Linux/shanwan-joystick.py”, line 195, in
cJoysticks.getEventChanges()
File “/opt/ShanWanTwin_2-4Ghz_Linux/shanwan-joystick.py”, line 89, in getEventChanges
self.interpretEvents(self.buf[0])
File “/opt/ShanWanTwin_2-4Ghz_Linux/shanwan-joystick.py”, line 97, in interpretEvents
firstID = ord(firstID)
TypeError: ord() expected string of length 1, but int found

Original thread code and sources are here: https://www.linuxquestions.org/questions/linux-hardware-18/separate-two-wireless-twin-gamepads-4175572775/

No, it’s a python module, it has nothing to do with the kernel.

The script expects self.buf to be a string but it’s actually a bytestring. So we need to remove all of the ord()s, I’ve done so below. Unfortunately I have no gamepad to test with… :man_shrugging:

# shanwan-joystick.py - Userspace script to split controller input for
# the Shanwan Wireless Twin controllers
#
# Copyright (C) 2016 Joost van den Brandt
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.


import sys
import uinput
import time
from collections import defaultdict
#import struct

# Shanwan Twin USB hidraw driver.

# Read 32 bytes from hidraw. They remain static if you don't press
# anything on the joystick, but occasionally it flips from having
# joystick id 1 to 2 at the start and vice-versa. We'll just test the
# first byte then we'll know which set of 8 bytes to look at for each
# joystick.

class TwinUSB:

    def __init__(self, devfile):
        try:
            self.file = open( devfile, "rb" );
        except:
            print ("Could not open", devfile)
            return
        self.buf = None
        self.oldBuf = None

        self.hatDir = defaultdict(list)
        self.oldHatDir = defaultdict(list)

        # Will hold the state of all of the buttons and axes.
        # Each gamepad has 12 buttons, 1 dpad and 2 analog sticks.
        # The buttons are either on or off, the dpad is either in
        # some direction or none.
        self.eventState = defaultdict(list)

        self.events = (uinput.BTN_TRIGGER, uinput.BTN_THUMB, uinput.BTN_THUMB2, \
              uinput.BTN_TOP, uinput.BTN_TOP2, uinput.BTN_PINKIE, \
              uinput.BTN_BASE, uinput.BTN_BASE2, uinput.BTN_BASE3, \
              uinput.BTN_BASE4,  uinput.BTN_BASE5,  uinput.BTN_BASE6,
              uinput.ABS_HAT0X + (-1, 1, 0, 0), \
              uinput.ABS_HAT0Y + (-1, 1, 0, 0), \
              uinput.ABS_X + (0, 255, 0, 0), uinput.ABS_Y + (0, 255, 0, 0),\
              uinput.ABS_Z + (0, 255, 0, 0), uinput.ABS_RZ + (0, 255, 0, 0))

        self.axisEvents = [uinput.ABS_HAT0X, uinput.ABS_HAT0Y, uinput.ABS_X, \
                  uinput.ABS_Y, uinput.ABS_Z, uinput.ABS_RZ]

        # Hat directions from top clockwise
        self.hatDirs = [[0,-1], [1, -1], [1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1]]

        self.gamepadIds = []
        self.devices = defaultdict(lambda : uinput.device([]))

        time.sleep(1)


    def getEventChanges(self):
        """Read the raw controller input data into a buffer and interpret
           the data if it's different from the last read"""

        # Note: for buttons being released, the event state for the button
        # will be set to -1. (t.e. if it was previously pressed and now is
        # not pressed, it will be set to -1) The button release event will
        # be emitted once then the event state for the button will be reset
        # to 0.

        self.buf = self.file.read(32)
        if self.buf != self.oldBuf:
            self.oldBuf = self.buf
            # Interpret the events
            self.interpretEvents(self.buf[0])



    def interpretEvents(self, firstID):
        """Interpret the raw controller input data and update the event state"""
        # FirstID is the joystick id that is in the first byte of the
        # buffer, either 1 or 2. If it's anything else then something
        # went wrong.

        if firstID in self.gamepadIds:
            for dev in self.gamepadIds:
                # 6th byte is shape buttons
                btn = self.buf[((firstID - 1) * 8) + (dev * 8) + 5]
                # Buttons 1 to 4:
                self.eventState[dev][0] = -1 if self.eventState[dev][0] and not btn & 16 else btn & 16
                self.eventState[dev][1] = -1 if self.eventState[dev][1] and not btn & 32 else btn & 32
                self.eventState[dev][2] = -1 if self.eventState[dev][2] and not btn & 64 else btn & 64
                self.eventState[dev][3] = -1 if self.eventState[dev][3] and not btn & 128 else btn & 128

                # Buttons 5 to 12:
                btn = self.buf[((firstID - 1) * 8) + (dev * 8) + 6]
                self.eventState[dev][4] = -1 if self.eventState[dev][4] and not btn & 1 else btn & 1
                self.eventState[dev][5] = -1 if self.eventState[dev][5] and not btn & 2 else btn & 2
                self.eventState[dev][6] = -1 if self.eventState[dev][6] and not btn & 4 else btn & 4
                self.eventState[dev][7] = -1 if self.eventState[dev][7] and not btn & 5 else btn & 8
                self.eventState[dev][8] = -1 if self.eventState[dev][8] and not btn & 16 else btn & 16
                self.eventState[dev][9] = -1 if self.eventState[dev][9] and not btn & 32 else btn & 32
                self.eventState[dev][10] = -1 if self.eventState[dev][10] and not btn & 64 else btn & 64
                self.eventState[dev][11] = -1 if self.eventState[dev][11] and not btn & 128 else btn & 128

                # Left stick (eventstate 14-15) (byte 3 = x 4 = y))
                axisX = self.buf[((firstID - 1) * 8) + (dev * 8) + 3]
                axisY = self.buf[((firstID - 1) * 8) + (dev * 8) + 4]
                self.eventState[dev][12] = axisX
                self.eventState[dev][13] = axisY

                # Right stick (eventstate 12-13) (byte 1 = x 2 = y))
                axisX = self.buf[((firstID - 1) * 8) + (dev * 8) + 1]
                axisY = self.buf[((firstID - 1) * 8) + (dev * 8) + 2]
                self.eventState[dev][14] = axisX
                self.eventState[dev][15] = axisY

                # D-pad (first 4 bits of byte 5 is the hat direction.
                # value is 0 to 7 for the 8 directions starting from
                # the top and going clockwise. Neutral position = 15.)
                # these must be converted into -1, 0, 1 event values for
                # the x and y hat axes.

                # Mask off the last 4 bits
                self.hatDir[dev] = self.buf[((firstID - 1) * 8) + (dev * 8) + 5] & 0xf
                if self.hatDir[dev] == 15:
                    self.eventState[dev][16] = 0
                    self.eventState[dev][17] = 0
                else:
                    self.eventState[dev][16] = self.hatDirs[self.hatDir[dev]][0]
                    self.eventState[dev][17] = self.hatDirs[self.hatDir[dev]][1]
        else:
            self.gamepadIds.append(firstID)
            self.devices[firstID] =(uinput.Device(self.events, "ShanWan Gamepad "+str(firstID)))
            self.eventState[firstID] = ([0, 0, 0, 0, 0, 0, 0 ,0 ,0 ,0 ,0 ,0, 127, 127, 127, 127, 0, 0])
            self.hatDir[firstID] = 15
            print ("ShanWan controller with ID "+str(firstID)+" added!")

    def emitEvents(self):
        """Emit input events based on the current event state"""
        for i in self.gamepadIds:
            # Button events:
            for j in range(0, 12):
                # If this button was just released then emit a release event
                # and reset to 0.
                if self.eventState[i][j] == -1:
                    self.devices[i].emit(self.events[j], 0)
                    self.eventState[i][j] = 0
                elif self.eventState[i][j] > 0:
                    self.devices[i].emit(self.events[j], 1)

            # Axis events
            self.devices[i].emit(self.axisEvents[2], self.eventState[i][12])
            self.devices[i].emit(self.axisEvents[3], self.eventState[i][13])
            self.devices[i].emit(self.axisEvents[4], self.eventState[i][14])
            self.devices[i].emit(self.axisEvents[5], self.eventState[i][15])

            # D=pad
            # if the current hat direction is different to oldHat
            # then send an event on both axes
            if self.oldHatDir[i] != self.hatDir[i]:
                self.devices[i].emit(self.axisEvents[0], self.eventState[i][16])
                self.devices[i].emit(self.axisEvents[1], self.eventState[i][17])

            self.oldHatDir[i] = self.hatDir[i]


"""Main proecess"""
cJoysticks = None
try:
    if len(sys.argv) != 2:
        print ("Usage: joystick.py [hidraw-file]")
        sys.exit()
    if not sys.argv[1].startswith("/dev/"):
        print("You must enter a hidraw file path, e.g. /dev/hidraw0")
        sys.exit()
    cJoysticks = TwinUSB(sys.argv[1])
    # see if the file was opened
    if hasattr(cJoysticks, 'file'):
        running = 1
        while (running):
            cJoysticks.getEventChanges()
            cJoysticks.emitEvents()
except KeyboardInterrupt:
    running = 0
    print ("Bye")
finally:
    if cJoysticks:
        if hasattr(cJoysticks, 'file'):
            cJoysticks.file.close()

Thank You very much. It now seems to work properly with Your amends to the script!

At launch executes splitting up the two controllers and two new events dirs do appear in /dev/input/event*

By testing with evtest-qt the two gamepad controllers do actually respond separately.

To sum up for later reference:

I am putting up a UDEV rule in /etc/udev/rules/90-shanwan.rules (althought it does not seem to work properly):
ACTION==“add|remove”, SUBSYSTEM==“usb”, ATTRS{idVendor}==“2563”, ATTRS{idProduct}==“0555”, MODE=“0660”, TAG+=“uaccess”, RUN+=“/opt/gamepadShanWanTwin/shanwan-start.sh”

It all means a new driver patch is now available: wouldn’t it now make sense to make a AUR pkg out of it, so that this code will be available to the community?

I’ve never done something like this but I am slowly coming up with this (seems to work):

Maintainer: alimurgia <infos[at]firemail[dot]de>

Contributer: alimurgia <infos[at]firemail[dot]de>

pkgname=gamepadShanWanTwin
_pkgname=gamepadShanWanTwin
pkgver=“0.0.1”
pkgrel=“1”
pkgdesc=“ShanWanTwin 2.4Ghz USB gamepad (2563:0555) is a nice and cheap controller. Its shortcoming under Linux is that it is loaded as a single controller. This script splits the incoming data so that Linux can see both of them. Depends on Python 2.7 and python-uinput module (Install using pip: pip install python-uinput)”
url=“GitHub - JoostvdB94/ShanWanTwin_2-4Ghz_Linux: Userspace script to split controller input for the Shanwan Wireless Twin controllers (2563:0555)
arch=(‘any’)
license=(‘GPLv3’)
depends=(‘python>=3’)
optdepends=()
makedepends=()
conflicts=()
replaces=()
backup=()
source=(“src/shanwan-joystick3.py”
“src/shanwan-start.sh”
“src/default.cfg”
“src/90-shanwan.rules”
“src/README.md”
“src/LICENSE”
)
md5sums=(‘423acf73df05498ab3dbf6dd4d485bd5’
‘aabf78c89b21c96ad717ffbd7bba3068’
‘c79ef93cc3c97fba15c5a2652d46d8d7’
‘b049f8a8132b7e06459169c52e014f27’
‘9905604d49974931d7b13fafb87b369b’
‘d32239bcb673463ab874e80d47fae504’)

package() {
mkdir -p “${pkgdir}/opt/gamepadShanWanTwin”
install -Dm755 “shanwan-joystick3.py” “${pkgdir}/opt/gamepadShanWanTwin/shanwan-joystick3.py”
install -Dm755 “shanwan-start.sh” “${pkgdir}/opt/gamepadShanWanTwin/shanwan-start.sh”
install -Dm755 “default.cfg” “${pkgdir}/opt/gamepadShanWanTwin/default.cfg”
install -Dm755 “README.md” “${pkgdir}/opt/gamepadShanWanTwin/README.md”
install -Dm755 “LICENSE” “${pkgdir}/opt/gamepadShanWanTwin/LICENSE”
install -Dm755 “90-shanwan.rules” “${pkgdir}/etc/udev/rules.d/90-shanwan.rules”
}

I now have a gamepadShanWanTwin-0.0.1-1-any.pkg.tar.zst package.
Can I submit it to the Manjaro community?

You’re welcome. :slight_smile:

How doesn’t it work properly?

I’m not sure if the MODE or TAG are needed.

I’m not great with udev rules, but yours would launch another instance every time the device is plugged/unplugged. Ideally we’d close the driver when it’s unplugged, we can do this with the startup script.

#!/bin/bash
HIDRAW_DEVICE=$(dmesg | grep -oP '(?<=2563:0555\.\d{4}: input,)\w{6}\d')
SCRIPT_PATH=/opt/ShanWanTwin_2-4Ghz_Linux/shanwan-joystick.py

# kill any instances
killall ${SCRIPT_PATH##*/} > /dev/null

if [ -n "$HIDRAW_DEVICE" ]; then
    # start it up only if the device is connected
	echo "Found joystick on /dev/$HIDRAW_DEVICE"
	exec "sudo" "python" "$SCRIPT_PATH" "/dev/$HIDRAW_DEVICE" &
fi

You can submit your PKGBUILD to the AUR, or you can host it all on github/gitlab, or both.

You are actually right: without this piece of code, by plugging in the USB key, it adds up a couple of event dirs more. But, when I restart the system, udev does not execute the starting script.
Permission issues?
How can I check/debug them?

As for AUR, where shoud I post them to?

Moreover, according to the Manjaro package guidelines, packages should go into /bin dirs but I put them in /opt since it is a script and not a binary. But /opt is devised for bulky codes according to the guidelines. Which dir is right then?

Your udev rule triggers on add and remove, if it’s plugged in at startup it may not trigger. Removing the ACTION part may or may not help.

You may need to run mkinitpio -P to include your rule in the boot environment.

https://unix.stackexchange.com/questions/187995/udev-not-running-rule-at-boot

I remembered you’re not meant to launch long running processes using RUN. I’m not sure if this counts, but it may be better to use a systemd service file.

ACTION==“add|remove”, SUBSYSTEM==“usb”, ATTRS{idVendor}==“2563”, ATTRS{idProduct}==“0555”, TAG+="systemd", ENV{SYSTEMD_WANTS}+=“shanwan.service”
# if installing manually
# /etc/systemd/system/shanwan.service

# if installing from a pkg
# /usr/lib/systemd/system/shanwan.service

[Unit]
Description=Start driver for shanwan gamepad

[Service]
Type=oneshot
ExecStart=/opt/ShanWanTwin_2-4Ghz_Linux/shanwan-start.sh

[Install]
WantedBy=default.target

I’m not sure it’ll work, but if it does you should be able to sudo systemctl enable shanwan.service for it to run at startup. :crossed_fingers:

Information can be found starting here, but you’ll have to follow a few links. You might be better starting with github/gitlab, you could fork the original project and then make the changes.

/opt is usually used for packages that don’t match the packaging guidelines they tend to expect their files to be in a single directory.

/usr/bin is normally where executables go.

I’d probably put them in /usr/bin, since we have at least 1 file elsewhere. Config files go in /etc, the rest should go in /usr/share/shanwan or similar.

I moved everything to /bin as suggested but then I was met with this error I cannot understand at installing the PKG: conflicting files:

  • shanwangamepads: /bin already exists in filesystem (owned by filesystem)

PKGBUILD was this:
package() {
cd “/bin”
mkdir -p “${pkgdir}/gamepadShanWanTwin”
install -Dm755 “shanwan-joystick3.py” “${pkgdir}/bin/gamepadShanWanTwin/shanwan-joystick3.py”
install -Dm755 “shanwan-start.sh” “${pkgdir}/bin/gamepadShanWanTwin/shanwan-start.sh”
install -Dm755 “shanwanMAME.cfg” “${pkgdir}/bin/gamepadShanWanTwin/shanwanMAME.cfg”
install -Dm755 “README.md” “${pkgdir}/bin/gamepadShanWanTwin/README.md”
install -Dm755 “shanwan.service” “${pkgdir}/usr/lib/systemd/system/gamepadShanWanTwin/shanwan.service”
install -Dm755 “90-shanwangamepadboot.rules” “${pkgdir}/etc/udev/rules.d/90-shanwangamepadboot.rules”
install -Dm755 “90-shanwangamepadplugin.rules” “${pkgdir}/etc/udev/rules.d/90-shanwangamepadplugin.rules”
install -D -m644 “LICENSE” “${pkgdir}/usr/share/licenses/${pkgname}/LICENSE”
chmod +x “${pkgdir}/bin/gamepadShanWanTwin/shanwan-start.sh”

Have you got a github/gitlab repo set up? It would make it easier to view the files and keep track of changes etc.

The files shouldn’t exist prior to installing the package, unless you’ve installed it before. However in this case you seem to be trying to replace /bin, which you shouldn’t do.

Sorry I wasn’t clear, manually installed executables go in places like /usr/local/bin etc. Executables from a pkg go in /usr/bin. Other files should go in different directories depending on what they do, eg systemwide config in /etc or /etc/${pkgname}. Application data goes in /usr/share/${pkgname}.

Speaking of ${pkgname}, I think I’d go with something a little shorter and all lowercase like shanwan or shanwan-gamepad.

It’s been a while since I made a PKGBUILD but if I remember correctly, it should be something like this.

package() {
  cd ${srcdir}
  install -Dm755 “shanwan-joystick3.py” “${pkgdir}/usr/bin/shanwan-joystick3.py” # maybe /usr/lib/${pkgname}
  install -Dm755 “shanwan-start.sh” “${pkgdir}/usr/bin/shanwan-start.sh”
  install -Dm644 “shanwanMAME.cfg” “${pkgdir}/usr/lib/mame/ctrlr/shanwanMAME.cfg” 
  install -Dm644 “README.md” “${pkgdir}/usr/share/${pkgname}/README.md” # not sure I'd include this, especially since it's just a single file in this dir
  install -Dm644 “shanwan.service” “${pkgdir}/usr/lib/systemd/system/shanwan.service”
  install -Dm644 “90-shanwangamepadboot.rules” “${pkgdir}/etc/udev/rules.d/90-shanwangamepadboot.rules”
  install -Dm644 “90-shanwangamepadplugin.rules” “${pkgdir}/etc/udev/rules.d/90-shanwangamepadplugin.rules”
  install -Dm644 “LICENSE” “${pkgdir}/usr/share/licenses/${pkgname}/LICENSE” 
}

Your original depends doesn’t include python-uinput (the other modules are part of the std library so we’re ok there), have you added it in your latest version? I can’t tell since you only posted the package() part.

However I’m no expert on packaging (or anything really).


Pre-formatted text is much easier to read. You can press Ctrl +E and this will appear.

```
type or paste code here
```

which will look like this:

type or paste code here

You’ve probably already seen these, but just in case.

PKGBUILD - ArchWiki
Arch package guidelines - ArchWiki
Creating packages - ArchWiki
Filesystem Hierarchy Standard - Wikipedia

EDIT:
I forked the original repo, and made a few changes including a working PKGBUILD. I’m not sure if everything else will work for you since I don’t know everything you’ve done. Feel free to fork it and make changes, assuming you haven’t already created your own.

[Deleted link before it becomes unusable]

EDIT:
Did you get default.cfg from here? As far as I can tell from a quick search it should go in /usr/lib/mame/ctrlr, but I may be wrong. Added it to the repo.

Sorry for all the edits everyone.