Skip to content
  • Peter Hutterer's avatar
    Split mixed pointer/keyboard devices into two separate X devices · 1f43f392
    Peter Hutterer authored
    The server struggles with devices that are both, the protocol (especially XI2)
    requires a fairly strict separation of pointer vs keyboard devices. Though the
    server has a couple of hacks to route events correctly, mixed
    devices still experience bugs like [1].
    
    Instead of advertising the device as a single mixed device, split the device
    into two X devices, one with only a pointer/touch component, one with only a
    keyboard component. This ensures that the device is effectively attached to
    both the VCP and the VCK, something the XI2 protocol doesn't really allow.
    
    This patch drops the keyboard capability on a mixed device, duplicates the
    input options and attributes and queues a NewInputDeviceRequest call. The new
    device only has the keyboard capability but is otherwise unchanged. The
    wacom driver has used this approach for years.
    
    The WorkProc is necessary to avoid inconsistent state, the server doesn't
    handle a NewInputDeviceRequest during PreInit well.
    
    The approach:
    During pre-init we create a struct xf86libinput_device with the
    libinput_device and a unique ID. The child device has that ID added to the
    options and will look for the other device during its pre-init. The two
    devices then share the xf86libinput_device struct.
    
    We only have a single epollfd for all devices  and the server calls read_input
    on the first device in the list with the epollfd as pInfo->fd. That shared
    struct is used as the userdata on the libinput_device we get back from the
    event, and each device is in the xorg_list device_list of that shared struct.
    We loop through those to find the ones with the right capabilities and
    post the event through that device.
    
    Since devices can be enabled and disabled independently, the rest of the code
    makes sure that we only ever add the device to libinput when the first shared
    device is enabled, and remove it accordingly.
    
    The server uses pInfo->major/minor to detect if another device is using the
    same path for a logind-controlled fd. If so, it reuses that device's
    pInfo->fd and sets the "fd" option to that value. That pInfo->fd is the
    libinput epollfd though, not the actual device fd.
    
    This doesn't matter for us, since we manage the fds largely ourselves and the
    pInfo->fd we use is the epollfd anyway. On unplug however, the udev code
    triggers a device removal for all devices, including the duplicated ones. When
    we disable device, we restore the pInfo->fd from the "fd" option so that the
    server can request logind to close the fd.
    
    That only works if the "fd" option is correct, otherwise the server asks
    logind to close the epollfd and everyone is unhappy.
    
    [1] https://bugs.freedesktop.org/show_bug.cgi?id=49950
    
    https://bugs.freedesktop.org/show_bug.cgi?id=92896
    
    
    
    Signed-off-by: default avatarPeter Hutterer <peter.hutterer@who-t.net>
    Reviewed-by: default avatarHans de Goede <hdegoede@redhat.com>
    1f43f392