From dd42a25720d9d711137e84a449319fc197b2639f Mon Sep 17 00:00:00 2001 From: Andrew Church Date: Mon, 25 Feb 2019 11:23:12 +1100 Subject: [PATCH] dinput: Allow reconnecting to disconnected joysticks Wine-bug: https://bugs.winehq.org/show_bug.cgi?id=34297 --- dlls/dinput/joystick_linuxinput.c | 148 +++++++++++++++++++++++------- 1 file changed, 113 insertions(+), 35 deletions(-) diff --git a/dlls/dinput/joystick_linuxinput.c b/dlls/dinput/joystick_linuxinput.c index 98283a1eedd..439e7d84bd4 100644 --- a/dlls/dinput/joystick_linuxinput.c +++ b/dlls/dinput/joystick_linuxinput.c @@ -84,6 +84,13 @@ struct wine_input_absinfo { LONG flat; }; +enum wine_joystick_linuxinput_fd_state { + WINE_FD_STATE_CLOSED = 0, /* No device has been opened yet */ + WINE_FD_STATE_OK, /* File descriptor is open and ready for reading */ + WINE_FD_STATE_DISCONNECTED, /* Read error occurred; might be able to reopen later */ + WINE_FD_STATE_INVALID, /* Device is no longer available at original pathname */ +}; + /* implemented in effect_linuxinput.c */ HRESULT linuxinput_create_effect(int* fd, REFGUID rguid, struct list *parent_list_entry, LPDIRECTINPUTEFFECT* peff); HRESULT linuxinput_get_info_A(int fd, REFGUID rguid, LPDIEFFECTINFOA info); @@ -123,6 +130,7 @@ struct JoystickImpl /* joystick private */ int joyfd; + enum wine_joystick_linuxinput_fd_state joyfd_state; int dev_axes_to_di[ABS_MAX]; POINTL povs[4]; @@ -477,6 +485,7 @@ static JoystickImpl *alloc_device(REFGUID rguid, IDirectInputImpl *dinput, unsig newDevice->generic.base.queue_len * sizeof(DIDEVICEOBJECTDATA)); newDevice->generic.joy_polldev = joy_polldev; newDevice->joyfd = -1; + newDevice->joyfd_state = WINE_FD_STATE_CLOSED; newDevice->joydev = &joydevs[index]; newDevice->generic.name = newDevice->joydev->name; list_init(&newDevice->ff_effects); @@ -684,38 +693,15 @@ static HRESULT joydev_create_device(IDirectInputImpl *dinput, REFGUID rguid, REF return DIERR_DEVICENOTREG; } - -const struct dinput_device joystick_linuxinput_device = { - "Wine Linux-input joystick driver", - joydev_enum_deviceA, - joydev_enum_deviceW, - joydev_create_device -}; - -/****************************************************************************** - * Acquire : gets exclusive control of the joystick - */ -static HRESULT WINAPI JoystickWImpl_Acquire(LPDIRECTINPUTDEVICE8W iface) +static int joydev_open_evdev(JoystickImpl *This) { - JoystickImpl *This = impl_from_IDirectInputDevice8W(iface); - HRESULT res; - - TRACE("(this=%p)\n",This); + int fd; - if ((res = IDirectInputDevice2WImpl_Acquire(iface)) != DI_OK) + if ((fd = open(This->joydev->device, O_RDWR)) == -1) { - WARN("Failed to acquire: %x\n", res); - return res; - } - - if ((This->joyfd = open(This->joydev->device, O_RDWR)) == -1) - { - if ((This->joyfd = open(This->joydev->device, O_RDONLY)) == -1) + if ((fd = open(This->joydev->device, O_RDONLY)) == -1) { /* Couldn't open the device at all */ - ERR("Failed to open device %s: %d %s\n", This->joydev->device, errno, strerror(errno)); - IDirectInputDevice2WImpl_Unacquire(iface); - return DIERR_NOTFOUND; } else { @@ -730,18 +716,53 @@ static HRESULT WINAPI JoystickWImpl_Acquire(LPDIRECTINPUTDEVICE8W iface) event.type = EV_FF; event.code = FF_GAIN; event.value = This->ff_gain; - if (write(This->joyfd, &event, sizeof(event)) == -1) + if (write(fd, &event, sizeof(event)) == -1) ERR("Failed to set gain (%i): %d %s\n", This->ff_gain, errno, strerror(errno)); if (!This->ff_autocenter) { /* Disable autocenter. */ event.code = FF_AUTOCENTER; event.value = 0; - if (write(This->joyfd, &event, sizeof(event)) == -1) + if (write(fd, &event, sizeof(event)) == -1) ERR("Failed disabling autocenter: %d %s\n", errno, strerror(errno)); } } + return fd; +} + + +const struct dinput_device joystick_linuxinput_device = { + "Wine Linux-input joystick driver", + joydev_enum_deviceA, + joydev_enum_deviceW, + joydev_create_device +}; + +/****************************************************************************** + * Acquire : gets exclusive control of the joystick + */ +static HRESULT WINAPI JoystickWImpl_Acquire(LPDIRECTINPUTDEVICE8W iface) +{ + JoystickImpl *This = impl_from_IDirectInputDevice8W(iface); + HRESULT res; + + TRACE("(this=%p)\n",This); + + if ((res = IDirectInputDevice2WImpl_Acquire(iface)) != DI_OK) + { + WARN("Failed to acquire: %x\n", res); + return res; + } + + if ((This->joyfd = joydev_open_evdev(This)) == -1) + { + ERR("Failed to open device %s: %d %s\n", This->joydev->device, errno, strerror(errno)); + IDirectInputDevice2WImpl_Unacquire(iface); + return DIERR_NOTFOUND; + } + + This->joyfd_state = WINE_FD_STATE_OK; return DI_OK; } @@ -779,6 +800,7 @@ static HRESULT WINAPI JoystickWImpl_Unacquire(LPDIRECTINPUTDEVICE8W iface) close(This->joyfd); This->joyfd = -1; + This->joyfd_state = WINE_FD_STATE_CLOSED; } return res; } @@ -823,23 +845,79 @@ static void joy_polldev(LPDIRECTINPUTDEVICE8A iface) struct input_event ie; JoystickImpl *This = impl_from_IDirectInputDevice8A(iface); - if (This->joyfd==-1) - return; + if (This->joyfd == -1) + { + int fd; + char namebuf[MAX_PATH + 8]; /* 8 == strlen(EVDEVDRIVER) */ + + if (This->joyfd_state != WINE_FD_STATE_DISCONNECTED) + return; + /* Try to reconnect to the device. */ + fd = joydev_open_evdev(This); + if (fd == -1) + return; + namebuf[sizeof(namebuf) - strlen(EVDEVDRIVER) - 1] = 0; + if (ioctl(fd, EVIOCGNAME(sizeof(namebuf) - strlen(EVDEVDRIVER) - 1), namebuf) == -1) + { + /* Couldn't get the name; assume it's a different device. */ + ERR("EVIOCGNAME(%s) failed: %d %s", This->joydev->device, errno, strerror(errno)); + This->joyfd_state = WINE_FD_STATE_INVALID; + return; + } + strcat(namebuf, EVDEVDRIVER); /* Guaranteed to be safe. */ + if (strcmp(namebuf, This->joydev->name) != 0) + { + ERR("Device %s changed from \"%s\" to \"%s\"! Can't reconnect.\n", This->joydev->device, This->joydev->name, namebuf); + This->joyfd_state = WINE_FD_STATE_INVALID; + return; + } + if (InterlockedCompareExchange(&This->joyfd, fd, -1) == -1) + { + This->joyfd_state = WINE_FD_STATE_OK; + TRACE("Reconnected to \"%s\" on %s", This->joydev->name, This->joydev->device); + } + else + { + /* Somebody beat us to it! Throw away our fd and use theirs. */ + close(fd); + } + } while (1) { LONG value = 0; int inst_id = -1; + int result; plfd.fd = This->joyfd; plfd.events = POLLIN; - if (poll(&plfd,1,0) != 1) - return; + result = poll(&plfd,1,0); + if (result != 1) + { + if (result == -1) + { + ERR("poll failed: %d %s\n", errno, strerror(errno)); + close(This->joyfd); + This->joyfd = -1; + This->joyfd_state = WINE_FD_STATE_DISCONNECTED; + } + return; + } /* we have one event, so we can read */ - if (sizeof(ie)!=read(This->joyfd,&ie,sizeof(ie))) - return; + result = read(This->joyfd,&ie,sizeof(ie)); + if (result != sizeof(ie)) + { + if (result == -1) + { + ERR("read failed: %d %s\n", errno, strerror(errno)); + close(This->joyfd); + This->joyfd = -1; + This->joyfd_state = WINE_FD_STATE_DISCONNECTED; + } + return; + } TRACE("input_event: type %d, code %d, value %d\n",ie.type,ie.code,ie.value); switch (ie.type) { -- 2.24.1