Moved hardware related handling to dlls/winmm/joystick driver.
Got rid of joySendMessages() hack (now implementation with a timer).

diff --git a/dlls/winmm/joystick/joystick.c b/dlls/winmm/joystick/joystick.c
new file mode 100644
index 0000000..e1846fc
--- /dev/null
+++ b/dlls/winmm/joystick/joystick.c
@@ -0,0 +1,370 @@
+/* -*- tab-width: 8; c-basic-offset: 4 -*- */
+/*
+ * joystick functions
+ *
+ * Copyright 1997 Andreas Mohr
+ *
+ * nearly all joystick functions can be regarded as obsolete,
+ * as Linux (2.1.x) now supports extended joysticks
+ * with a completely new joystick driver interface
+ * new driver's docu says:
+ * "For backward compatibility the old interface is still included,
+ * but will be dropped in the future."
+ * Thus we should implement the new interface and at most keep the old
+ * routines for backward compatibility.
+ */
+
+/*
+ * Wolfgang Schwotzer
+ *
+ *    01/2000    added support for new joystick driver
+ *
+ */
+
+#include "config.h"
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#ifdef HAVE_LINUX_JOYSTICK_H
+#include <linux/joystick.h>
+#define JOYDEV "/dev/js%d"
+#endif
+#ifdef HAVE_SYS_ERRNO_H
+#include <sys/errno.h>
+#endif
+#include "windef.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "mmddk.h"
+#include "driver.h"
+#include "debugtools.h"
+
+DEFAULT_DEBUG_CHANNEL(joystick);
+
+#ifdef HAVE_LINUX_JOYSTICK_H
+
+#define MAXJOYSTICK	(JOYSTICKID2 + 1)
+
+static	struct dummy_struct* JSTCK_Dummy = NULL;
+
+/**************************************************************************
+ * 				JSTCK_drvOpen			[internal]	
+ */
+static	DWORD	JSTCK_drvOpen(LPSTR str)
+{
+    if (JSTCK_Dummy)
+	return 0;
+    
+    /* I know, this is ugly, but who cares... */
+    JSTCK_Dummy = (struct dummy_struct*)1;
+    return 1;
+}
+
+/**************************************************************************
+ * 				JSTCK_drvClose			[internal]	
+ */
+static	DWORD	JSTCK_drvClose(DWORD dwDevID)
+{
+    if (JSTCK_Dummy) {
+	JSTCK_Dummy = NULL;
+	return 1;
+    }
+    return 0;
+}
+
+struct js_status
+{
+    int buttons;
+    int x;
+    int y;
+};
+
+/**************************************************************************
+ *                              JSTCK_OpenDevice           [internal]
+ */
+static	int	JSTCK_OpenDevice(WORD wID)
+{
+    char	buf[20];
+    int		flags;
+    
+    if (wID >= MAXJOYSTICK) return -1;
+
+    sprintf(buf, JOYDEV, wID);
+#ifdef HAVE_LINUX_22_JOYSTICK_API
+    flags = O_RDONLY | O_NONBLOCK;
+#else
+    flags = O_RDONLY;
+#endif
+    return open(buf, flags);
+}
+
+/**************************************************************************
+ * 				JoyGetDevCaps		[MMSYSTEM.102]
+ */
+static	LONG	JSTCK_GetDevCaps(DWORD dwDevID, LPJOYCAPSA lpCaps, DWORD dwSize)
+{
+#ifdef HAVE_LINUX_22_JOYSTICK_API
+    int		dev;
+    char	nrOfAxes;
+    char	nrOfButtons;
+    char	identString[MAXPNAMELEN];
+    int		driverVersion;
+#endif
+
+    if (dwDevID >= MAXJOYSTICK) return MMSYSERR_NODRIVER;
+
+#ifdef HAVE_LINUX_22_JOYSTICK_API
+    
+    if ((dev = JSTCK_OpenDevice(dwDevID)) < 0) return JOYERR_PARMS;
+    ioctl(dev, JSIOCGAXES, &nrOfAxes);
+    ioctl(dev, JSIOCGBUTTONS, &nrOfButtons);
+    ioctl(dev, JSIOCGVERSION, &driverVersion);
+    ioctl(dev, JSIOCGNAME(sizeof(identString)), &identString);
+    TRACE("Driver: 0x%06x, Name: %s, #Axes: %d, #Buttons: %d\n",
+	  driverVersion, identString, nrOfAxes, nrOfButtons);
+    lpCaps->wMid = MM_MICROSOFT;
+    lpCaps->wPid = MM_PC_JOYSTICK;
+    strncpy(lpCaps->szPname, identString, MAXPNAMELEN);
+    lpCaps->szPname[MAXPNAMELEN-1] = '\0';
+    lpCaps->wXmin = 0;
+    lpCaps->wXmax = 0xFFFF;
+    lpCaps->wYmin = 0;
+    lpCaps->wYmax = 0xFFFF;
+    lpCaps->wZmin = 0;
+    lpCaps->wZmax = (nrOfAxes >= 3) ? 0xFFFF : 0;
+    lpCaps->wNumButtons = nrOfButtons;
+    if (dwSize == sizeof(JOYCAPSA)) {
+	/* since we supose ntOfAxes <= 6 in the following code, do it explicitely */
+	if (nrOfAxes > 6) nrOfAxes = 6;
+	/* complete 95 structure */
+	lpCaps->wRmin = 0;
+	lpCaps->wRmax = nrOfAxes >= 4 ? 0xFFFF : 0;
+	lpCaps->wUmin = 0;
+	lpCaps->wUmax = nrOfAxes >= 5 ? 0xFFFF : 0;
+	lpCaps->wVmin = 0;
+	lpCaps->wVmax = nrOfAxes >= 6 ? 0xFFFF : 0;
+	lpCaps->wMaxAxes = 6; /* same as MS Joystick Driver */
+	lpCaps->wNumAxes = nrOfAxes; /* nr of axes in use */
+	lpCaps->wMaxButtons = 32; /* same as MS Joystick Driver */
+	strcpy(lpCaps->szRegKey, "");
+	strcpy(lpCaps->szOEMVxD, "");
+	lpCaps->wCaps = 0;
+	switch(nrOfAxes) {
+	case 6: lpCaps->wCaps |= JOYCAPS_HASV;
+	case 5: lpCaps->wCaps |= JOYCAPS_HASU;
+	case 4: lpCaps->wCaps |= JOYCAPS_HASR;
+	case 3: lpCaps->wCaps |= JOYCAPS_HASZ;
+	    /* FIXME: don't know how to detect for
+	       JOYCAPS_HASPOV, JOYCAPS_POV4DIR, JOYCAPS_POVCTS */
+	}
+    }
+    close(dev);
+
+#else
+    lpCaps->wMid = MM_MICROSOFT;
+    lpCaps->wPid = MM_PC_JOYSTICK;
+    strcpy(lpCaps->szPname, "WineJoy"); /* joystick product name */
+    lpCaps->wXmin = 0;
+    lpCaps->wXmax = 0xFFFF;
+    lpCaps->wYmin = 0;
+    lpCaps->wYmax = 0xFFFF;
+    lpCaps->wZmin = 0;
+    lpCaps->wZmax = 0;
+    lpCaps->wNumButtons = 2;
+    if (dwSize == sizeof(JOYCAPSA)) {
+	/* complete 95 structure */
+	lpCaps->wRmin = 0;
+	lpCaps->wRmax = 0;
+	lpCaps->wUmin = 0;
+	lpCaps->wUmax = 0;
+	lpCaps->wVmin = 0;
+	lpCaps->wVmax = 0;
+	lpCaps->wCaps = 0;
+	lpCaps->wMaxAxes = 2;
+	lpCaps->wNumAxes = 2;
+	lpCaps->wMaxButtons = 4;
+	strcpy(lpCaps->szRegKey,"");
+	strcpy(lpCaps->szOEMVxD,"");
+    }
+#endif
+
+    return JOYERR_NOERROR;
+}
+
+/**************************************************************************
+ * 				JSTCK_GetPos			[internal]
+ */
+static LONG	JSTCK_GetPosEx(DWORD dwDevID, LPJOYINFOEX lpInfo)
+{
+    int			dev;
+#ifdef HAVE_LINUX_22_JOYSTICK_API
+    struct js_event 	ev;
+#else
+    struct js_status 	js;
+    int    		dev_stat;
+#endif
+    
+    if ((dev = JSTCK_OpenDevice(dwDevID)) < 0) return JOYERR_PARMS;
+
+#ifdef HAVE_LINUX_22_JOYSTICK_API
+    /* After opening the device it's 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)) {
+	    switch (ev.number) {
+	    case 0:
+		if (lpInfo->dwFlags & JOY_RETURNX)
+		    lpInfo->dwXpos   = ev.value + 32767;
+		break;
+	    case 1: 
+		if (lpInfo->dwFlags & JOY_RETURNY)
+		    lpInfo->dwYpos   = ev.value + 32767;
+		break;
+	    case 2:
+		if (lpInfo->dwFlags & JOY_RETURNZ)
+		    lpInfo->dwZpos   = ev.value + 32767;
+		break;
+	    case 3: 
+		if (lpInfo->dwFlags & JOY_RETURNR)
+		    lpInfo->dwRpos   = ev.value + 32767;
+	    case 4: 
+		if (lpInfo->dwFlags & JOY_RETURNU)
+		    lpInfo->dwUpos   = ev.value + 32767;
+	    case 5: 
+		if (lpInfo->dwFlags & JOY_RETURNV)
+		    lpInfo->dwVpos   = ev.value + 32767;
+		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);
+		    /* FIXME: what to do for this field when 
+		     * multiple buttons are depressed ?
+		     */
+		    lpInfo->dwButtonNumber = ev.number + 1;
+		}
+	    }
+	}
+    }
+    /* EAGAIN is returned when the queue is empty */
+    if (errno != EAGAIN) {
+	/* FIXME: error should not be ignored */
+	ERR("Error while reading joystick state (%d)\n", errno);
+    }
+#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;
+    js.y = js.y<<8;
+    if (lpInfo->dwFlags & JOY_RETURNX)
+	lpInfo->dwXpos = js.x;   /* FIXME: perhaps multiply it somehow ? */
+    if (lpInfo->dwFlags & JOY_RETURNY)
+	lpInfo->dwYpos = js.y;
+    if (lpInfo->dwFlags & JOY_RETURNBUTTONS)
+	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", 
+	  lpInfo->dwXpos, lpInfo->dwYpos, lpInfo->dwZpos,
+	  lpInfo->dwRpos, lpInfo->dwUpos, lpInfo->dwVpos,
+	  (unsigned int)lpInfo->dwButtons,
+	  (unsigned int)lpInfo->dwFlags);
+
+    return JOYERR_NOERROR;
+}
+
+/**************************************************************************
+ * 				JSTCK_GetPos			[internal]
+ */
+static LONG	JSTCK_GetPos(DWORD dwDevID, LPJOYINFO lpInfo)
+{
+    JOYINFOEX	ji;
+    LONG	ret;
+
+    memset(&ji, 0, sizeof(ji));
+
+    ji.dwSize = sizeof(ji);
+    ji.dwFlags = JOY_RETURNX | JOY_RETURNY | JOY_RETURNZ | JOY_RETURNBUTTONS;
+    ret = JSTCK_GetPosEx(dwDevID, &ji);
+    if (ret == JOYERR_NOERROR)	{
+	lpInfo->wXpos    = ji.dwXpos;
+	lpInfo->wYpos    = ji.dwYpos;
+	lpInfo->wZpos    = ji.dwZpos;
+	lpInfo->wButtons = ji.dwButtons;
+    }
+
+    return ret;
+}
+
+/**************************************************************************
+ * 				JSTCK_DriverProc		[internal]
+ */
+LONG CALLBACK	JSTCK_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg, 
+				  DWORD dwParam1, DWORD dwParam2)
+{
+    /* EPP     TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n",  */
+    /* EPP 	  dwDevID, hDriv, wMsg, dwParam1, dwParam2); */
+    
+    switch(wMsg) {
+    case DRV_LOAD:		return 1;
+    case DRV_FREE:		return 1;
+    case DRV_OPEN:		return JSTCK_drvOpen((LPSTR)dwParam1);
+    case DRV_CLOSE:		return JSTCK_drvClose(dwDevID);
+    case DRV_ENABLE:		return 1;
+    case DRV_DISABLE:		return 1;
+    case DRV_QUERYCONFIGURE:	return 1;
+    case DRV_CONFIGURE:		MessageBoxA(0, "JoyStick MultiMedia Driver !", "JoyStick Driver", MB_OK);	return 1;
+    case DRV_INSTALL:		return DRVCNF_RESTART;
+    case DRV_REMOVE:		return DRVCNF_RESTART;
+
+    case JDD_GETNUMDEVS:	return MAXJOYSTICK;
+    case JDD_GETDEVCAPS:	return JSTCK_GetDevCaps(dwDevID, (LPJOYCAPSA)dwParam1, dwParam2);
+    case JDD_GETPOS:		return JSTCK_GetPos(dwDevID, (LPJOYINFO)dwParam1);
+    case JDD_SETCALIBRATION:	
+    case JDD_CONFIGCHANGED:	return JOYERR_NOCANDO;
+    case JDD_GETPOSEX:		return JSTCK_GetPosEx(dwDevID, (LPJOYINFOEX)dwParam1);
+    default:
+	return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
+    }
+}
+
+#else
+
+/**************************************************************************
+ * 				JSTCK_DriverProc		[internal]
+ */
+LONG CALLBACK	JSTCK_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg, 
+				  DWORD dwParam1, DWORD dwParam2)
+{
+    /* EPP     TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n",  */
+    /* EPP 	  dwDevID, hDriv, wMsg, dwParam1, dwParam2); */
+    
+    switch(wMsg) {
+    case DRV_LOAD:		
+    case DRV_FREE:		
+    case DRV_OPEN:		
+    case DRV_CLOSE:		
+    case DRV_ENABLE:		
+    case DRV_DISABLE:		
+    case DRV_QUERYCONFIGURE:	return 0;
+    case DRV_CONFIGURE:		MessageBoxA(0, "JoyStick MultiMedia Driver !", "JoyStick Driver", MB_OK);	return 1;
+    case DRV_INSTALL:		return DRVCNF_RESTART;
+    case DRV_REMOVE:		return DRVCNF_RESTART;
+    default:
+	return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
+    }
+}
+
+#endif