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

Forwarding EV_UINPUT events for FF is difficult due to undocumented behaviour #199

Closed
human9 opened this issue Oct 10, 2023 · 4 comments
Closed

Comments

@human9
Copy link
Contributor

human9 commented Oct 10, 2023

I am unsure if this is really an issue of python-evdev but I've discovered it using python-evdev, so I'll document it here.

I am in the situation of creating a ff-capable uinput device, and forwarding ff events from this device back to an original one. So, this and this in the same code.

Forwarding events directly will fail. This is because an effect id of -1 is never sent to the uinput device under UI_FF_UPLOAD, and as per kernel ff documention an id of -1 is required to allocate the new effect.

Why is a value of -1 never sent through? Because the -1 is actually modified in place by the kernel's handling in ff-core.c.

So, what to do? My workaround is to read the old field that is set to NULL here. That seems a reliable indicator of when an allocation has been requested. In a python program, the relevant snippet might look like:

# Handling EV_UINPUT events from our uinput_dev here
if event.code == e.UI_FF_UPLOAD:
	
    upload = uinput_dev.begin_upload(event.value)
    
    # Testing if upload.old was set to NULL
    # It's not a null pointer here but at some point the null makes this all zeros
    if all(x == 0 for x in bytearray(upload.old)):
        upload.effect.id = -1    

    # Before uploading to a real device
    real_dev.upload_effect(effect)
    
    upload.retval = 0
    uinput_dev.end_upload(upload)

Handling it this way results in fully functional forwarding of all FF events.

Happy to submit a pull request for documentation if desired. Although, I'm not sure if this is intended behaviour or a bug in the way uinput handles this.. It really seems like it should forward the -1 id values, so I might report upstream.

@human9
Copy link
Contributor Author

human9 commented Dec 4, 2023

I asked upstream and this is apparently all working as intended. Below you can find a test script that should give a functional rumble. It checks for whether an unseen effect id comes through to know when to ask for a new effect to be allocated (done by setting id to -1). See upstream discussion.

import evdev
from evdev import ecodes as e

input_device = evdev.InputDevice("/dev/input/event12") # replace with whatever your FF-capable device is
uinput = evdev.UInput.from_device(input_device, filtered_types=[e.EV_SYN])

# Keeps track of which effects have been uploaded
effects = set()

for event in uinput.read_loop():
    
    # Handle the special uinput events
    if event.type == e.EV_UINPUT:

        if event.code == e.UI_FF_UPLOAD:
            upload = uinput.begin_upload(event.value)

            if upload.effect.id not in effects:
                effects.add(upload.effect.id)
                upload.effect.id = -1

            input_device.upload_effect(upload.effect)
            upload.retval = 0
            uinput.end_upload(upload)
            
        elif event.code == e.UI_FF_ERASE:
            erase = uinput.begin_erase(event.value)
            erase.retval = 0
            input_device.erase_effect(erase.effect_id)
            effects.remove(erase.effect_id)
            uinput.end_erase(erase)
    
    # Forward writes to actual rumble device.
    elif event.type == e.EV_FF:
        input_device.write(event.type, event.code, event.value)

I haven't been able to find an example on the internet of anyone actually doing this correctly, so it would be great if it could be documented somewhere.

@sezanzeb
Copy link
Collaborator

sezanzeb commented Dec 5, 2023

it would be great if it could be documented somewhere.

Your code works for me. How about appending it to the tutorials section? https://github.com/gvalkov/python-evdev/blob/main/docs/tutorial.rst

@human9
Copy link
Contributor Author

human9 commented Dec 6, 2023

Happy to do so. Will draft a pull request in a bit.

@human9
Copy link
Contributor Author

human9 commented Dec 14, 2023

Closing as docs were added in #198

@human9 human9 closed this as completed Dec 14, 2023
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