The attached patch was motivated by my desire to play HalfLife under Wine. I'm weird - I use a stick to move, and the mouse to look. The stick didn't work under Wine, so I fixed it. I had once before submitted something very much like this, but without the extra logic to cache the state of the stick, so it still didn't work. This fix is pretty well tested (as in, I played all of HalfLife - Opposing Force with it). The only nasty in it is the "BODGE_THE_HAT" - HL won't bind an axis to an action, and the Linux driver for my USB stick reports the hat as axises U and V. Hence BODGE_THE_HAT - this code forces the U and V axis to be mapped to buttons. It's a bodge, and if desired I will pull all of it (it is disabled by default - you have to #define BODGE_THE_HAT to get the behavior). Ideally, it would either be a) a parameter to the Linux USB driver (thus removing it from Wine's domain entirely) or a registry entry in Wine. But I wanted to test the rest of the patch (LIE: I wanted to play OpFor ;) ) so I bodged it in.
Index: joystick.c =================================================================== RCS file: /home/wine/wine/dlls/winmm/joystick/joystick.c,v retrieving revision 1.12 diff -u -r1.12 joystick.c --- joystick.c 31 May 2002 23:40:56 -0000 1.12 +++ joystick.c 1 Sep 2002 22:25:00 -0000 @@ -29,7 +29,6 @@ * Thus we should implement the new interface and at most keep the old * routines for backward compatibility. */ - /* * Wolfgang Schwotzer * @@ -37,6 +36,13 @@ * */ +/* + * David Hagood (wowbagger@sktc.net) + * + * 1 Sept 2002 fixed Linux 2.2 event API code + * + */ + #include "config.h" #include <unistd.h> @@ -71,6 +77,24 @@ typedef struct tagWINE_JSTCK { int joyIntf; int in_use; + /* Some extra info we need to make this acutaly work under the + Linux 2.2 event api. + First of all, we cannot keep closing and reopening the device file - + that blows away the state of the stick device, and we lose events. So, we + need to open the low-level device once, and close it when we are done. + + Secondly, the event API only gives us what's changed. However, Windows apps + want the whole state every time, so we have to cache the data. + */ + + int dev; /* Linux level device file descriptor */ + int x; + int y; + int z; + int r; + int u; + int v; + int buttons; } WINE_JSTCK; static WINE_JSTCK JSTCK_Data[MAXJOYSTICK]; @@ -114,6 +138,11 @@ if (jstck == NULL) return 0; jstck->in_use = 0; + if (jstck->dev > 0) + { + close(jstck->dev); + jstck->dev = 0; + } return 1; } @@ -132,13 +161,16 @@ char buf[20]; int flags; + if (jstick->dev > 0) + return jstick->dev; + sprintf(buf, JOYDEV, jstick->joyIntf); #ifdef HAVE_LINUX_22_JOYSTICK_API flags = O_RDONLY | O_NONBLOCK; #else flags = O_RDONLY; #endif - return open(buf, flags); + return (jstick->dev = open(buf, flags)); } /************************************************************************** @@ -159,7 +191,6 @@ return MMSYSERR_NODRIVER; #ifdef HAVE_LINUX_22_JOYSTICK_API - if ((dev = JSTCK_OpenDevice(jstck)) < 0) return JOYERR_PARMS; ioctl(dev, JSIOCGAXES, &nrOfAxes); ioctl(dev, JSIOCGBUTTONS, &nrOfButtons); @@ -177,7 +208,19 @@ lpCaps->wYmax = 0xFFFF; lpCaps->wZmin = 0; lpCaps->wZmax = (nrOfAxes >= 3) ? 0xFFFF : 0; +#ifdef BODGE_THE_HAT + /* HalfLife won't allow you to map an axis event to things like + "next weapon" and "use". Linux reports the hat on my stick as + axis U and V. So, IFF BODGE_THE_HAT is defined, lie through our + teeth and say we have 32 buttons, and we will map the axises to + the high buttons. Really, perhaps this should be a registry entry, + or even a parameter to the Linux joystick driver (which would completely + remove the need for this.) + */ + lpCaps->wNumButtons = 32; +#else lpCaps->wNumButtons = nrOfButtons; +#endif if (dwSize == sizeof(JOYCAPSA)) { /* since we suppose ntOfAxes <= 6 in the following code, do it explicitly */ if (nrOfAxes > 6) nrOfAxes = 6; @@ -203,8 +246,6 @@ JOYCAPS_HASPOV, JOYCAPS_POV4DIR, JOYCAPS_POVCTS */ } } - close(dev); - #else lpCaps->wMid = MM_MICROSOFT; lpCaps->wPid = MM_PC_JOYSTICK; @@ -256,46 +297,41 @@ if ((dev = JSTCK_OpenDevice(jstck)) < 0) return JOYERR_PARMS; #ifdef HAVE_LINUX_22_JOYSTICK_API - /* After opening the device, its state can be - read with JS_EVENT_INIT flag */ while ((read(dev, &ev, sizeof(struct js_event))) > 0) { - if (ev.type == (JS_EVENT_AXIS | JS_EVENT_INIT)) { + if (ev.type == (JS_EVENT_AXIS)) { switch (ev.number) { case 0: - if (lpInfo->dwFlags & JOY_RETURNX) - lpInfo->dwXpos = ev.value + 32767; + jstck->x = ev.value; break; case 1: - if (lpInfo->dwFlags & JOY_RETURNY) - lpInfo->dwYpos = ev.value + 32767; + jstck->y = ev.value; break; case 2: - if (lpInfo->dwFlags & JOY_RETURNZ) - lpInfo->dwZpos = ev.value + 32767; + jstck->z = ev.value; break; case 3: - if (lpInfo->dwFlags & JOY_RETURNR) - lpInfo->dwRpos = ev.value + 32767; + jstck->r = ev.value; + break; case 4: - if (lpInfo->dwFlags & JOY_RETURNU) - lpInfo->dwUpos = ev.value + 32767; + jstck->u = ev.value; + break; case 5: - if (lpInfo->dwFlags & JOY_RETURNV) - lpInfo->dwVpos = ev.value + 32767; + jstck->v = ev.value; break; default: FIXME("Unknown joystick event '%d'\n", ev.number); } - } else if (ev.type == (JS_EVENT_BUTTON | JS_EVENT_INIT)) { - if (lpInfo->dwFlags & JOY_RETURNBUTTONS) { - if (ev.value) { - lpInfo->dwButtons |= (1 << ev.number); + } else if (ev.type == (JS_EVENT_BUTTON)) { + if (ev.value) { + jstck->buttons |= (1 << ev.number); /* FIXME: what to do for this field when * multiple buttons are depressed ? */ - lpInfo->dwButtonNumber = ev.number + 1; + if (lpInfo->dwFlags & JOY_RETURNBUTTONS) + lpInfo->dwButtonNumber = ev.number + 1; } - } + else + jstck->buttons &= ~(1 << ev.number); } } /* EAGAIN is returned when the queue is empty */ @@ -303,10 +339,43 @@ /* FIXME: error should not be ignored */ ERR("Error while reading joystick state (%s)\n", strerror(errno)); } + /* Now, copy the cached values into Window's structure... */ + if (lpInfo->dwFlags & JOY_RETURNBUTTONS) + lpInfo->dwButtons = jstck->buttons; + if (lpInfo->dwFlags & JOY_RETURNX) + lpInfo->dwXpos = jstck->x + 32767; + if (lpInfo->dwFlags & JOY_RETURNY) + lpInfo->dwYpos = jstck->y + 32767; + if (lpInfo->dwFlags & JOY_RETURNZ) + lpInfo->dwZpos = jstck->z + 32767; + if (lpInfo->dwFlags & JOY_RETURNR) + lpInfo->dwRpos = jstck->r + 32767; + #ifdef BODGE_THE_HAT + else if (lpInfo->dwFlags & JOY_RETURNBUTTONS) + { + if (jstck->r > 0) + lpInfo->dwButtons |= 1<<7; + else if (jstck->r < 0) + lpInfo->dwButtons |= 1<<8; + } + #endif + if (lpInfo->dwFlags & JOY_RETURNU) + lpInfo->dwUpos = jstck->u + 32767; + #ifdef BODGE_THE_HAT + else if (lpInfo->dwFlags & JOY_RETURNBUTTONS) + { + if (jstck->u > 0) + lpInfo->dwButtons |= 1<<9; + else if (jstck->u < 0) + lpInfo->dwButtons |= 1<<10; + } + #endif + if (lpInfo->dwFlags & JOY_RETURNV) + lpInfo->dwVpos = jstck->v + 32767; + #else dev_stat = read(dev, &js, sizeof(js)); if (dev_stat != sizeof(js)) { - close(dev); return JOYERR_UNPLUGGED; /* FIXME: perhaps wrong, but what should I return else ? */ } js.x = js.x<<8; @@ -319,13 +388,13 @@ lpInfo->dwButtons = js.buttons; #endif - close(dev); - - TRACE("x: %ld, y: %ld, z: %ld, r: %ld, u: %ld, v: %ld, buttons: 0x%04x, flags: 0x%04x\n", + TRACE("x: %ld, y: %ld, z: %ld, r: %ld, u: %ld, v: %ld, buttons: 0x%04x, flags: 0x%04x (fd %d)\n", lpInfo->dwXpos, lpInfo->dwYpos, lpInfo->dwZpos, lpInfo->dwRpos, lpInfo->dwUpos, lpInfo->dwVpos, (unsigned int)lpInfo->dwButtons, - (unsigned int)lpInfo->dwFlags); + (unsigned int)lpInfo->dwFlags, + dev + ); return JOYERR_NOERROR; }