inputs icon indicating copy to clipboard operation
inputs copied to clipboard

get_gamepad seems to block

Open Fuzzyma opened this issue 8 years ago • 12 comments

For my application it is crucial to check if there are new events but if not, proceed with the other code. However: In order to check for new events I need to call get_gamepad which calls internal read on the device. This function seems to block until an event arrives. It would be awesome to add a nonblocking read or an option to specify all reads as nonblocking!

As always: Thanks for your library!

Fuzzyma avatar Oct 07 '16 09:10 Fuzzyma

why was the blocking parameter never added to inputs? I either need to run part of my input handling in a seperate thread or I need to edit inputs to be nonblocking. it reduces my programs fps from 3000 to about 200 with most of the time being spent waiting for inputs.get_gamepad()

Sartek avatar Feb 15 '19 20:02 Sartek

@Sartek not sure. Maybe the author of this lib is short on time?

Fuzzyma avatar Feb 15 '19 21:02 Fuzzyma

yeah, the more I use this library the more I find issues with it that make it really hard to use. even if I were to run it in a seperate thread it would still max out the cpu. if I try to call time.sleep() the input seems to slow down and play at a reduced speed.

Sartek avatar Feb 15 '19 23:02 Sartek

I added this in the _character_device() in the InputDevice class:

self._character_file = io.open(
  self._character_device_path, 'rb')
fd = self._character_file.fileno()
flag = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, flag | os.O_NONBLOCK)

And then calling this in my code:

events = devices.gamepads[0]._do_iter()

To bypass the while True loop in iter(). Not so pretty but hey, it worked for me!

I think adding this flag and also yeilding an empty array in iter() if there is no data would be a nice complete solution but I never tried it.

ErikOrjehag avatar Feb 05 '20 21:02 ErikOrjehag

@ErikOrjehag when I get events like you events = devices.gamepads[0]._do_iter() I get this error:

Exception in thread Thread-2:
Traceback (most recent call last):
  File "/usr/lib/python3.6/threading.py", line 916, in _bootstrap_inner
    self.run()
  File "/home/louis/Documents/IUT/s3a_s01_demange_gsell_noel_nosal/src/pilotage.py", line 84, in run
    for event in events:
TypeError: 'NoneType' object is not iterable

So I use events = get_gamepad()and it works but CPU jump to 100%...

Also I'm new to python so I don't understand everything, I modified the class like that and addedd import fcntl, is this correct ?

@property
def _character_device(self):
    if not self._character_file:
        if WIN:
            self._character_file = io.BytesIO()
            return self._character_file
        try:
            self._character_file = io.open(self._character_device_path, 'rb')
            fd = self._character_file.fileno()
            flag = fcntl.fcntl(fd, fcntl.F_GETFL)
            fcntl.fcntl(fd, fcntl.F_SETFL, flag | os.O_NONBLOCK)
        except PermissionError:
            # Python 3
            raise PermissionError(PERMISSIONS_ERROR_TEXT)
        except IOError as err:
            # Python 2
            if err.errno == 13:
                raise PermissionError(PERMISSIONS_ERROR_TEXT)
            else:
                raise
    return self._character_file

Nekzuris avatar Feb 08 '20 15:02 Nekzuris

@Nekzuris Because _do_iter() returns None when there are no events you need to do it like this:

events = devices.gamepads[0]._do_iter()
if events is not None:
  for event in events:
    print(event)

ErikOrjehag avatar Feb 08 '20 15:02 ErikOrjehag

Thanks! Since I read controller inputs in another thread I added this sleep to reduce load on the CPU

events = devices.gamepads[0]._do_iter()
if events == None:
    time.sleep(0.0001)
else:
    for event in events:
        print(event)

Nekzuris avatar Feb 08 '20 16:02 Nekzuris

For what it is worth, the following gist is a use case why this issue is valid and that @ErikOrjehag's patch works. The application shows two circles that can be moved independently with the two joysticks of a gamepad. It is written with the goocanvas within gtk. Without Erik's patch you can't integrate input.py into gtk's event loop:

See: https://gist.github.com/dov/696db3faa1cc702b5362fd0bbb201cad

dov avatar Dec 01 '20 06:12 dov

Hey, thanks for this thread!

Total beginner at this, sorry if I'm missing something obvious.

I'm having issues too with the blocking aspect of get_gamepad(). I was trying to implement your patch, but the _do_iter() method seems to return only None here.

Furthermore, the try statement in the _character_device definition seems to always fail.

I'm on windows 10, is this a Linux specific patch, and if yes, any idea how to implement in windows?

thanks a lot!

chienMouille avatar Oct 12 '21 13:10 chienMouille

@chienMouille try the linked pr I wrote back then

Fuzzyma avatar Oct 12 '21 14:10 Fuzzyma

@Fuzzyma thank you so much! That pretty much solves it.

chienMouille avatar Oct 12 '21 15:10 chienMouille

I didn't want to patch, so I just used a thread: ` import inputs import threading

eventList = []

def monitorGamepad():
    while True:
        try:
            for e in inputs.get_gamepad():
                eventList.append(e)
        except inputs.UnpluggedError:
            time.sleep(0.5)

gamepadThread = threading.Thread(target=monitorGamepad)
gamepadThread.daemon = True
gamepadThread.start()

def gamepadEvents():
    copy = eventList[:]
    eventList.clear()
    return copy
    
def haveGamepad():
    return len(inputs.devices.gamepads)>0

`

arpruss avatar Dec 18 '21 15:12 arpruss