Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Zero division when trying detect joysticks #986

Closed
HeuristicPerson opened this issue Oct 31, 2023 · 3 comments
Closed

Zero division when trying detect joysticks #986

HeuristicPerson opened this issue Oct 31, 2023 · 3 comments

Comments

@HeuristicPerson
Copy link

HeuristicPerson commented Oct 31, 2023

Hi

I'm using pyglet 2.0.9 under Linux Mint and I'm having an issue when trying to initialise and read joystick inputs. I have physically only one PS3 DualShock pad connected through Bluetooth but somehow the OS recognises it as /dev/input/js0 and /dev/input/js1; only the latter is showing any activity when I calibrate it.

I'm using the code below which is pretty much straight from the documentation with the exception that I'm sub-classing pyglet.Window:

class MainWindow(pyglet.window.Window):
    def __init__(self):
        super(MainWindow, self).__init__(width=640,
                                         height=480,
                                         caption='Joystick Test Window',
                                         resizable=True,
                                         vsync=True)

        # Basic reading of controllers
        controllers = pyglet.input.get_controllers()
        if controllers:
            controller = controllers[0]
            controller.open()

    def on_draw(self, **px_args):
        self.clear()

    def schedule_updater(self, pf_delta_t):
        pass


# Main code
#==========
my_window = MainWindow()

pyglet.clock.schedule_interval(my_window.schedule_updater, 0.2)
pyglet.app.run(1/30)

When I run it I'm getting the following error:

/home/xxx/.local/lib/python3.10/site-packages/pyglet/input/linux/evdev.py:643: UserWarning: Warning: EvdevDevice(name=input-remapper gamepad) (GUID: 03000000010000000100000001000000) has no controller mappings. Update the mappings in the Controller DB.
Auto-detecting as defined by the 'Linux gamepad specification'
  warnings.warn(f"Warning: {device} (GUID: {device.get_guid()}) "
Traceback (most recent call last):
  File "/home/xxx/xxx@xxx.lan/projects/coding/python/emulauncher/dev/libs/joystick.py", line 58, in <module>
    my_window = MainWindow()
  File "/home/xxx/xxx@xxx.lan/projects/coding/python/emulauncher/dev/libs/joystick.py", line 25, in __init__
    controllers = pyglet.input.get_controllers()
  File "/home/xxx/.local/lib/python3.10/site-packages/pyglet/input/linux/evdev.py", line 656, in get_controllers
    [_create_controller(device) for device in get_devices(display)] if controller]
  File "/home/xxx/.local/lib/python3.10/site-packages/pyglet/input/linux/evdev.py", line 656, in <listcomp>
    [_create_controller(device) for device in get_devices(display)] if controller]
  File "/home/xxx/.local/lib/python3.10/site-packages/pyglet/input/linux/evdev.py", line 651, in _create_controller
    return Controller(device, mapping)
  File "/home/xxx/.local/lib/python3.10/site-packages/pyglet/input/base.py", line 608, in __init__
    self._initialize_controls()
  File "/home/xxx/.local/lib/python3.10/site-packages/pyglet/input/base.py", line 738, in _initialize_controls
    add_axis(self._axis_controls[relation.index], name)
  File "/home/xxx/.local/lib/python3.10/site-packages/pyglet/input/base.py", line 613, in add_axis
    tscale = 1.0 / (control.max - control.min)
ZeroDivisionError: float division by zero

Any idea on what's the problem or what I'm doing wrong?

@benmoran56
Copy link
Member

It doesn't look like you're doing anything wrong, but the controller is reporting invalid values.
The min and max range for the axis is somehow cancelling out to 0, meaning that it has no range. I suspect that either the driver is buggy, or that your calibration attempt has set bogus values. We expect something like this:

>>> js = pyglet.input.get_joysticks()[0]
>>> js.x_control.min
0
>>> js.z_control.max
255

pyglet uses evdev directly, not the /dev/input/jsX interface. Ie: something like /dev/input/event21
I would first recommend using a cli utility such as evtest to see a raw printout of your devices capabilities. You should expect to see lots of info like this for a Dualshock 3. You can see the min/max are 0 and 255:

...clip...
  Event type 3 (EV_ABS)
    Event code 0 (ABS_X)
      Value    129
      Min        0
      Max      255
    Event code 1 (ABS_Y)
      Value    125
      Min        0
      Max      255
...clip...

pyglet's Controller and Joystick interfaces will always normalize input values between -1, 1. The lower level Devices interface will not, and you can also use that to check the raw values that the inputs return. For example:

>>> for device in pyglet.input.get_devices():
...    print(device)
... 
EvdevDevice(name=Wireless Controller)
XInputDevice(name=Virtual core pointer)
XInputDevice(name=Virtual core keyboard)
XInputDevice(name=Virtual core XTEST pointer)
XInputDevice(name=Virtual core XTEST keyboard)
XInputDevice(name=xwayland-pointer:15)
XInputDevice(name=xwayland-relative-pointer:15)
XInputDevice(name=xwayland-pointer-gestures:15)
XInputDevice(name=xwayland-keyboard:15)
>>> 
>>> device = pyglet.input.get_devices()[0]
>>> device.controls
[AbsoluteAxis(name=x, raw_name=ABS_X), AbsoluteAxis(name=y, raw_name=ABS_Y), AbsoluteAxis(name=z, raw_name=ABS_Z), AbsoluteAxis(name=rx, raw_name=ABS_RX), AbsoluteAxis(name=ry, raw_name=ABS_RY), AbsoluteAxis(name=rz, raw_name=ABS_RZ), AbsoluteAxis(name=hat_x, raw_name=ABS_HAT0X), AbsoluteAxis(name=hat_y, raw_name=ABS_HAT0Y), Button(raw_name=BTN_A), Button(raw_name=BTN_B), Button(raw_name=BTN_X), Button(raw_name=BTN_Y), Button(raw_name=BTN_TL), Button(raw_name=BTN_TR), Button(raw_name=BTN_TL2), Button(raw_name=BTN_TR2), Button(raw_name=BTN_SELECT), Button(raw_name=BTN_START), Button(raw_name=BTN_MODE), Button(raw_name=BTN_THUMBL), Button(raw_name=BTN_THUMBR)]
>>> device.controls[0].min
0
>>> device.controls[0].max
255

@HeuristicPerson
Copy link
Author

Many thanks, benmoran56, you gave me to figure out what was going on.

I was getting the error even with no joystick connected and using evtest I noticed everything was correct with the PS3 game pad. The problem was in the "dummy" device which in the end was created by Input Remapper (https://github.com/sezanzeb/input-remapper/tree/main), a tool that I had forgotten I installed like one or two years ago to solve an issue with a logitech mouse. It seems that even with no remapping defined, the tool keeps alive a dummy device not properly defined (min-max axis values are equal). After removing the tool, I can normally read pad inputs with pyglet.

Many thanks again for your help all pyglet team!

@benmoran56
Copy link
Member

I'm glad you found the root cause. I've added a small check in the control enumeration code, so that any axis with min/max both set to 0 will be skipped. At least this way it won't crash end user code. If you have a chance to test that utility again, it would be useful feedback, but no obligation. I'll close this ticket for now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants