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

diff --git a/Make.rules.in b/Make.rules.in
index 3f760f7..e2ecbdfc 100644
--- a/Make.rules.in
+++ b/Make.rules.in
@@ -93,6 +93,7 @@
 	icmp \
 	imagehlp \
 	imm32 \
+	joystick.drv \
 	lz32 \
 	mcianim.drv \
 	mciavi.drv \
diff --git a/configure b/configure
index 3baf0a2..9413897 100755
--- a/configure
+++ b/configure
@@ -6215,6 +6215,7 @@
 dlls/windebug/Makefile
 dlls/wing/Makefile
 dlls/winmm/Makefile
+dlls/winmm/joystick/Makefile
 dlls/winmm/mcianim/Makefile
 dlls/winmm/mciavi/Makefile
 dlls/winmm/mcicda/Makefile
@@ -6427,6 +6428,7 @@
 dlls/windebug/Makefile
 dlls/wing/Makefile
 dlls/winmm/Makefile
+dlls/winmm/joystick/Makefile
 dlls/winmm/mcianim/Makefile
 dlls/winmm/mciavi/Makefile
 dlls/winmm/mcicda/Makefile
diff --git a/configure.in b/configure.in
index 429d0ef..3f1c70d 100644
--- a/configure.in
+++ b/configure.in
@@ -977,6 +977,7 @@
 dlls/windebug/Makefile
 dlls/wing/Makefile
 dlls/winmm/Makefile
+dlls/winmm/joystick/Makefile
 dlls/winmm/mcianim/Makefile
 dlls/winmm/mciavi/Makefile
 dlls/winmm/mcicda/Makefile
diff --git a/dlls/Makefile.in b/dlls/Makefile.in
index 00b51b7..3c68904 100644
--- a/dlls/Makefile.in
+++ b/dlls/Makefile.in
@@ -37,6 +37,7 @@
 	win87em/libwin87em.@LIBEXT@ \
 	windebug/libwindebug.@LIBEXT@ \
 	wing/libwing.@LIBEXT@ \
+	winmm/joystick/libjoystick.drv.@LIBEXT@ \
 	winmm/mcianim/libmcianim.drv.@LIBEXT@ \
 	winmm/mciavi/libmciavi.drv.@LIBEXT@ \
 	winmm/mcicda/libmcicda.drv.@LIBEXT@ \
@@ -93,6 +94,10 @@
 	$(RM) $@
 	$(LN_S) imm32/libimm32.@LIBEXT@ $@
 
+libjoystick.drv.@LIBEXT@: winmm/joystick/libjoystick.drv.@LIBEXT@
+	$(RM) $@
+	$(LN_S) winmm/joystick/libjoystick.drv.@LIBEXT@ $@
+
 liblz32.@LIBEXT@: lzexpand/liblz32.@LIBEXT@
 	$(RM) $@
 	$(LN_S) lzexpand/liblz32.@LIBEXT@ $@
diff --git a/dlls/winmm/Makefile.in b/dlls/winmm/Makefile.in
index 9fc2ca8..a02e076 100644
--- a/dlls/winmm/Makefile.in
+++ b/dlls/winmm/Makefile.in
@@ -28,6 +28,7 @@
 all: check_wrc $ $(MODULE).o
 
 SUBDIRS = \
+	joystick \
 	mcianim \
 	mciavi \
 	mcicda \
diff --git a/dlls/winmm/joystick.c b/dlls/winmm/joystick.c
index e773639..22d2093 100644
--- a/dlls/winmm/joystick.c
+++ b/dlls/winmm/joystick.c
@@ -1,23 +1,10 @@
+/* -*- 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
- *
+ *	     2000 Wolfgang Schwotzer
+ *                Eric Pouech
  */
 
 #include "config.h"
@@ -28,18 +15,11 @@
 #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 "winbase.h"
 #include "wingdi.h"
 #include "winuser.h"
-#include "winbase.h"
-#include "mmsystem.h"
+#include "driver.h"
+#include "winemm.h"
 #include "debugtools.h"
 
 DEFAULT_DEBUG_CHANNEL(mmsys);
@@ -48,178 +28,79 @@
 #define JOY_PERIOD_MIN	(10)	/* min Capture time period */
 #define JOY_PERIOD_MAX	(1000)	/* max Capture time period */
 
-static int count_use[MAXJOYSTICK] = {0, 0};
-static int joy_nr_open = 0;
-static BOOL16 joyCaptured = FALSE;
-static HWND16 CaptureWnd[MAXJOYSTICK] = {0, 0};
-static int joy_dev[MAXJOYSTICK] = {-1, -1};
-static JOYINFO16 joyCapData[MAXJOYSTICK];
-static unsigned int joy_threshold[MAXJOYSTICK] = {0, 0};
+typedef struct tagWINE_JOYSTICK {
+    JOYINFO	ji;
+    HWND	hCapture;
+    UINT	wTimer;
+    DWORD	threshold;
+    BOOL	bChanged;
+} WINE_JOYSTICK;
 
-struct js_status
-{
-    int buttons;
-    int x;
-    int y;
-};
-
+static	WINE_JOYSTICK	JOY_Sticks[MAXJOYSTICK];
+static	HDRVR		JOY_Driver;
 
 /**************************************************************************
- *                              joyOpenDriver           [internal]
+ * 				JOY_LoadDriver		[internal]
  */
-BOOL16 joyOpenDriver(WORD wID)
+static	BOOL JOY_LoadDriver(void)
 {
-	char	buf[20];
-	int	flags;
+    if (JOY_Driver)
+	return TRUE;
 
-	if (wID>=MAXJOYSTICK) return FALSE;
-	if (joy_dev[wID] >= 0) return TRUE; /* was already open */
-#ifdef HAVE_LINUX_JOYSTICK_H
-        sprintf(buf,JOYDEV,wID);
-	flags = O_RDONLY;
-#ifdef HAVE_LINUX_22_JOYSTICK_API
-	flags |= O_NONBLOCK;
-#endif
-        if ((joy_dev[wID] = open(buf, flags)) >= 0) {
-		joy_nr_open++;
-		return TRUE;
-	} else
-		return FALSE;
-#else
-	return FALSE;
-#endif /* HAVE_LINUX_JOYSTICK_H */
+    return JOY_Driver = OpenDriverA("joystick.drv", 0, 0);
 }
 
 /**************************************************************************
- *                              joyCloseDriver           [internal]
+ * 				JOY_Timer		[internal]
  */
-void joyCloseDriver(WORD wID)
+static	void	CALLBACK	JOY_Timer(HWND hWnd, UINT wMsg, UINT wTimer, DWORD dwTime)
 {
-	if (joy_dev[wID] >= 0) {
-		close(joy_dev[wID]);
-		joy_dev[wID] = -1;
-		joy_nr_open--;
+    int			i;
+    WINE_JOYSTICK*	joy;
+    JOYINFO		ji;
+    LONG		pos;
+    unsigned 		buttonChange;
+    
+    for (i = 0; i < MAXJOYSTICK; i++) {
+	joy = &JOY_Sticks[i];
+	
+	if (joy->hCapture != hWnd) continue;	
+	
+	joyGetPos(i, &ji);
+	pos = MAKELONG(ji.wXpos, ji.wYpos);
+	
+	if (!joy->bChanged ||
+	    abs(joy->ji.wXpos - ji.wXpos) > joy->threshold || 
+	    abs(joy->ji.wYpos - ji.wYpos) > joy->threshold) {
+	    SendMessageA(joy->hCapture, MM_JOY1MOVE + i, ji.wButtons, pos);
+	    joy->ji.wXpos = ji.wXpos;
+	    joy->ji.wYpos = ji.wYpos;
 	}
-}
-
-/**************************************************************************
- *                              joyUpdateCaptureData           [internal]
- */
-#ifdef HAVE_LINUX_22_JOYSTICK_API
-void joyUpdateCaptureData(WORD wID)
-{
-	struct 		js_event ev;
-	unsigned int	ButtonState;
-	int		messageType = 0;
-
-	if (joyOpenDriver(wID) == FALSE) return;
-	while (read(joy_dev[wID], &ev, sizeof(struct js_event)) > 0) {
-       		if (ev.type & JS_EVENT_AXIS) {
-			if (ev.number == 0)
-				joyCapData[wID].wXpos = ev.value + 32767;
-			if (ev.number == 1)
-				joyCapData[wID].wYpos = ev.value + 32767;
-			if (ev.number <= 1) /* Message for X, Y-Axis */
-			   SendMessageA(CaptureWnd[wID], MM_JOY1MOVE + wID,
-				joyCapData[wID].wButtons,
-				MAKELONG(joyCapData[wID].wXpos,
-					joyCapData[wID].wYpos));
-			if (ev.number == 2) { /* Message for Z-axis */
-				joyCapData[wID].wZpos = ev.value + 32767;
-			   SendMessageA(CaptureWnd[wID], MM_JOY1ZMOVE + wID,
-				joyCapData[wID].wButtons,
-				joyCapData[wID].wZpos);
-			}
-		} else if (ev.type & JS_EVENT_BUTTON) {
-			if (ev.value) {
-                        	joyCapData[wID].wButtons |= (1 << ev.number);
-				messageType = MM_JOY1BUTTONDOWN;
-                	} else {
-                        	joyCapData[wID].wButtons &= ~(1 << ev.number);
-				messageType = MM_JOY1BUTTONUP;
-			}
-			/* create button state with changed buttons and 
-			   pressed buttons */
-			ButtonState = ((1 << (ev.number + 8)) & 0xFF00) |
-					(joyCapData[wID].wButtons & 0xFF);
-			SendMessageA(CaptureWnd[wID], messageType + wID,
-				ButtonState,
-				MAKELONG(joyCapData[wID].wXpos,
-					joyCapData[wID].wYpos));
-		}
+	if (!joy->bChanged ||
+	    abs(joy->ji.wZpos - ji.wZpos) > joy->threshold) {
+	    SendMessageA(joy->hCapture, MM_JOY1ZMOVE + i, ji.wButtons, pos);
+	    joy->ji.wZpos = ji.wZpos;
 	}
-	/* EAGAIN is returned when the queue is empty */
-	if (errno != EAGAIN) {
-		/* FIXME: error should not be ignored */
+	if ((buttonChange = joy->ji.wButtons ^ ji.wButtons) != 0) {
+	    if (ji.wButtons & buttonChange)
+		SendMessageA(joy->hCapture, MM_JOY1BUTTONDOWN + i, 
+			     (buttonChange << 8) | (ji.wButtons & buttonChange), pos);
+	    if (joy->ji.wButtons & buttonChange)
+		SendMessageA(joy->hCapture, MM_JOY1BUTTONUP + i, 
+			     (buttonChange << 8) | (joy->ji.wButtons & buttonChange), pos);
+	    joy->ji.wButtons = ji.wButtons;
 	}
-}
-#endif
-
-/**************************************************************************
- *                              joySendMessages           [internal]
- */
-void joySendMessages(void)
-{
-	int joy;
-#ifndef HAVE_LINUX_22_JOYSTICK_API
-        struct js_status js;
-#endif
-	if (joy_nr_open)
-        {
-            for (joy=0; joy < MAXJOYSTICK; joy++) 
-		if (joy_dev[joy] >= 0) {
-			if (count_use[joy] > 250) {
-				joyCloseDriver(joy);
-				count_use[joy] = 0;
-			}
-			count_use[joy]++;
-		} else
-			return;
-        }
-        if (joyCaptured == FALSE) return;
-
-	TRACE(" --\n");
-
-#ifdef HAVE_LINUX_22_JOYSTICK_API
-       	for (joy=0; joy < MAXJOYSTICK; joy++)
-		joyUpdateCaptureData(joy);
-	return;
-#else
-        for (joy=0; joy < MAXJOYSTICK; joy++) {
-		int dev_stat;
-
-		if (joyOpenDriver(joy) == FALSE) continue;
-                dev_stat = read(joy_dev[joy], &js, sizeof(js));
-                if (dev_stat == sizeof(js)) {
-                        js.x = js.x<<8;
-                        js.y = js.y<<8;
-                        if ((joyCapData[joy].wXpos != js.x) || (joyCapData[joy].wYpos != js.y)) {
-                                SendMessageA(CaptureWnd[joy], MM_JOY1MOVE + joy, js.buttons, MAKELONG(js.x, js.y));
-                                joyCapData[joy].wXpos = js.x;
-                                joyCapData[joy].wYpos = js.y;
-                        }
-                        if (joyCapData[joy].wButtons != js.buttons) {
-				unsigned int ButtonChanged = (WORD)(joyCapData[joy].wButtons ^ js.buttons)<<8;
-                                if (joyCapData[joy].wButtons < js.buttons)
-                                SendMessageA(CaptureWnd[joy], MM_JOY1BUTTONDOWN + joy, ButtonChanged, MAKELONG(js.x, js.y));
-				else
-                                if (joyCapData[joy].wButtons > js.buttons)
-                                SendMessageA(CaptureWnd[joy], MM_JOY1BUTTONUP
-+ joy, ButtonChanged, MAKELONG(js.x, js.y));
-                                joyCapData[joy].wButtons = js.buttons;
-                        }
-                }
-        }
-#endif
+    }
 }
 
-
 /**************************************************************************
- * 				JoyGetNumDevs		[MMSYSTEM.101]
+ * 				JoyGetNumDevs		[WINMM.30]
  */
 UINT WINAPI joyGetNumDevs(void)
 {
-	return joyGetNumDevs16();
+    if (!JOY_LoadDriver())	return MMSYSERR_NODRIVER;
+
+    return SendDriverMessage(JOY_Driver, JDD_GETNUMDEVS, 0L, 0L);
 }
 
 /**************************************************************************
@@ -227,307 +108,152 @@
  */
 UINT16 WINAPI joyGetNumDevs16(void)
 {
-/*    int joy;
-    UINT16 joy_cnt = 0;
-
-    for (joy=0; joy<MAXJOYSTICK; joy++)
-	if (joyOpenDriver(joy) == TRUE) {		
-		joyCloseDriver(joy);
-		joy_cnt++;
-    }
-    TRACE("returning %d\n", joy_cnt);
-    if (!joy_cnt) ERR("No joystick found - "
-			  "perhaps get joystick-0.8.0.tar.gz and load"
-			  "it as module or use Linux >= 2.1.45 to be "
-			  "able to use joysticks.\n");
-    return joy_cnt;*/
-
-/* simply return the max. nr. of supported joysticks. The rest
-   will be done with joyGetPos or joyGetDevCaps. Look at
-   MS Joystick Driver */
-
-    return MAXJOYSTICK;
+    return joyGetNumDevs();
 }
 
 /**************************************************************************
  * 				JoyGetDevCaps		[WINMM.27]
  */
-MMRESULT WINAPI joyGetDevCapsA(UINT wID, LPJOYCAPSA lpCaps,UINT wSize)
+MMRESULT WINAPI joyGetDevCapsA(UINT wID, LPJOYCAPSA lpCaps, UINT wSize)
 {
-	JOYCAPS16	jc16;
-	MMRESULT16	ret = joyGetDevCaps16(wID,&jc16,sizeof(jc16));
+    if (wID >= MAXJOYSTICK)	return JOYERR_PARMS;
+    if (!JOY_LoadDriver())	return MMSYSERR_NODRIVER;
 
-	if (ret != JOYERR_NOERROR) return ret;
-	lpCaps->wMid = jc16.wMid;
-	lpCaps->wPid = jc16.wPid;
-	strcpy(lpCaps->szPname,jc16.szPname);
-        lpCaps->wXmin = jc16.wXmin;
-        lpCaps->wXmax = jc16.wXmax;
-        lpCaps->wYmin = jc16.wYmin;
-        lpCaps->wYmax = jc16.wYmax;
-        lpCaps->wZmin = jc16.wZmin;
-        lpCaps->wZmax = jc16.wZmax;
-        lpCaps->wNumButtons = jc16.wNumButtons;
-        lpCaps->wPeriodMin = jc16.wPeriodMin;
-        lpCaps->wPeriodMax = jc16.wPeriodMax;
+    lpCaps->wPeriodMin = JOY_PERIOD_MIN; /* FIXME */
+    lpCaps->wPeriodMax = JOY_PERIOD_MAX; /* FIXME (same as MS Joystick Driver) */
 
-        lpCaps->wRmin = jc16.wRmin;
-        lpCaps->wRmax = jc16.wRmax;
-        lpCaps->wUmin = jc16.wUmin;
-        lpCaps->wUmax = jc16.wUmax;
-        lpCaps->wVmin = jc16.wVmin;
-        lpCaps->wVmax = jc16.wVmax;
-        lpCaps->wCaps = jc16.wCaps;
-        lpCaps->wMaxAxes = jc16.wMaxAxes;
-        lpCaps->wNumAxes = jc16.wNumAxes;
-        lpCaps->wMaxButtons = jc16.wMaxButtons;
-        strcpy(lpCaps->szRegKey,jc16.szRegKey);
-        strcpy(lpCaps->szOEMVxD,jc16.szOEMVxD);
-	return ret;
-}
+    return SendDriverMessage(JOY_Driver, JDD_GETDEVCAPS, (DWORD)lpCaps, wSize);
+}   
 
 /**************************************************************************
  * 				JoyGetDevCaps		[WINMM.28]
  */
-MMRESULT WINAPI joyGetDevCapsW(UINT wID, LPJOYCAPSW lpCaps,UINT wSize)
+MMRESULT WINAPI joyGetDevCapsW(UINT wID, LPJOYCAPSW lpCaps, UINT wSize)
 {
-	JOYCAPS16	jc16;
-	MMRESULT16	ret = joyGetDevCaps16(wID,&jc16,sizeof(jc16));
-
-	if (ret != JOYERR_NOERROR) return ret;
-	lpCaps->wMid = jc16.wMid;
-	lpCaps->wPid = jc16.wPid;
-	lstrcpyAtoW(lpCaps->szPname,jc16.szPname);
-        lpCaps->wXmin = jc16.wXmin;
-        lpCaps->wXmax = jc16.wXmax;
-        lpCaps->wYmin = jc16.wYmin;
-        lpCaps->wYmax = jc16.wYmax;
-        lpCaps->wZmin = jc16.wZmin;
-        lpCaps->wZmax = jc16.wZmax;
-        lpCaps->wNumButtons = jc16.wNumButtons;
-        lpCaps->wPeriodMin = jc16.wPeriodMin;
-        lpCaps->wPeriodMax = jc16.wPeriodMax;
-
-        lpCaps->wRmin = jc16.wRmin;
-        lpCaps->wRmax = jc16.wRmax;
-        lpCaps->wUmin = jc16.wUmin;
-        lpCaps->wUmax = jc16.wUmax;
-        lpCaps->wVmin = jc16.wVmin;
-        lpCaps->wVmax = jc16.wVmax;
-        lpCaps->wCaps = jc16.wCaps;
-        lpCaps->wMaxAxes = jc16.wMaxAxes;
-        lpCaps->wNumAxes = jc16.wNumAxes;
-        lpCaps->wMaxButtons = jc16.wMaxButtons;
-        lstrcpyAtoW(lpCaps->szRegKey,jc16.szRegKey);
-        lstrcpyAtoW(lpCaps->szOEMVxD,jc16.szOEMVxD);
-	return ret;
+    JOYCAPSA	jca;
+    MMRESULT	ret = joyGetDevCapsA(wID, &jca, sizeof(jca));
+    
+    if (ret != JOYERR_NOERROR) return ret;
+    lpCaps->wMid = jca.wMid;
+    lpCaps->wPid = jca.wPid;
+    lstrcpyAtoW(lpCaps->szPname, jca.szPname);
+    lpCaps->wXmin = jca.wXmin;
+    lpCaps->wXmax = jca.wXmax;
+    lpCaps->wYmin = jca.wYmin;
+    lpCaps->wYmax = jca.wYmax;
+    lpCaps->wZmin = jca.wZmin;
+    lpCaps->wZmax = jca.wZmax;
+    lpCaps->wNumButtons = jca.wNumButtons;
+    lpCaps->wPeriodMin = jca.wPeriodMin;
+    lpCaps->wPeriodMax = jca.wPeriodMax;
+    
+    if (wSize >= sizeof(JOYCAPSW)) { /* Win95 extensions ? */
+	lpCaps->wRmin = jca.wRmin;
+	lpCaps->wRmax = jca.wRmax;
+	lpCaps->wUmin = jca.wUmin;
+	lpCaps->wUmax = jca.wUmax;
+	lpCaps->wVmin = jca.wVmin;
+	lpCaps->wVmax = jca.wVmax;
+	lpCaps->wCaps = jca.wCaps;
+	lpCaps->wMaxAxes = jca.wMaxAxes;
+	lpCaps->wNumAxes = jca.wNumAxes;
+	lpCaps->wMaxButtons = jca.wMaxButtons;
+	lstrcpyAtoW(lpCaps->szRegKey, jca.szRegKey);
+	lstrcpyAtoW(lpCaps->szOEMVxD, jca.szOEMVxD);
+    }
+    
+    return ret;
 }
+
 /**************************************************************************
  * 				JoyGetDevCaps		[MMSYSTEM.102]
  */
 MMRESULT16 WINAPI joyGetDevCaps16(UINT16 wID, LPJOYCAPS16 lpCaps, UINT16 wSize)
 {
-    TRACE("(%04X, %p, %d);\n",
-            wID, lpCaps, wSize);
-    if (wID >= MAXJOYSTICK) return MMSYSERR_NODRIVER;
-#ifdef HAVE_LINUX_22_JOYSTICK_API
-    if (joyOpenDriver(wID) == TRUE) {
-	char	nrOfAxes;
-	char	nrOfButtons;
-	char	identString[MAXPNAMELEN];
-	int	driverVersion;
-
-	ioctl(joy_dev[wID], JSIOCGAXES, &nrOfAxes);
-        ioctl(joy_dev[wID], JSIOCGBUTTONS, &nrOfButtons);
-        ioctl(joy_dev[wID], JSIOCGVERSION, &driverVersion);
-        ioctl(joy_dev[wID], 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;
-       	lpCaps->wPeriodMin = JOY_PERIOD_MIN; /* FIXME */
-       	lpCaps->wPeriodMax = JOY_PERIOD_MAX; /* FIXME (same as MS Joystick Driver */
-	if (wSize == sizeof(JOYCAPS16)) {
-		/* 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 */
-		}
-	}
-	joyCloseDriver(wID);
-        return JOYERR_NOERROR;
-    } else
-    	return JOYERR_PARMS;
-#else
-    if (joyOpenDriver(wID) == TRUE) {
-        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;
-        lpCaps->wPeriodMin = JOY_PERIOD_MIN; /* FIXME */
-        lpCaps->wPeriodMax = JOY_PERIOD_MAX; /* FIXME end */
-	if (wSize == sizeof(JOYCAPS16)) {
-		/* 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,"");
-	}
-	joyCloseDriver(wID);
-        return JOYERR_NOERROR;
-    } else
-    	return JOYERR_PARMS;
-#endif
+    JOYCAPSA	jca;
+    MMRESULT	ret = joyGetDevCapsA(wID, &jca, sizeof(jca));
+    
+    if (ret != JOYERR_NOERROR) return ret;
+    lpCaps->wMid = jca.wMid;
+    lpCaps->wPid = jca.wPid;
+    strcpy(lpCaps->szPname, jca.szPname);
+    lpCaps->wXmin = jca.wXmin;
+    lpCaps->wXmax = jca.wXmax;
+    lpCaps->wYmin = jca.wYmin;
+    lpCaps->wYmax = jca.wYmax;
+    lpCaps->wZmin = jca.wZmin;
+    lpCaps->wZmax = jca.wZmax;
+    lpCaps->wNumButtons = jca.wNumButtons;
+    lpCaps->wPeriodMin = jca.wPeriodMin;
+    lpCaps->wPeriodMax = jca.wPeriodMax;
+    
+    if (wSize >= sizeof(JOYCAPS16)) { /* Win95 extensions ? */
+	lpCaps->wRmin = jca.wRmin;
+	lpCaps->wRmax = jca.wRmax;
+	lpCaps->wUmin = jca.wUmin;
+	lpCaps->wUmax = jca.wUmax;
+	lpCaps->wVmin = jca.wVmin;
+	lpCaps->wVmax = jca.wVmax;
+	lpCaps->wCaps = jca.wCaps;
+	lpCaps->wMaxAxes = jca.wMaxAxes;
+	lpCaps->wNumAxes = jca.wNumAxes;
+	lpCaps->wMaxButtons = jca.wMaxButtons;
+	strcpy(lpCaps->szRegKey, jca.szRegKey);
+	strcpy(lpCaps->szOEMVxD, jca.szOEMVxD);
+    }
+    
+    return ret;
 }
 
 /**************************************************************************
- *                              JoyGetPosEx             [WINMM.31]
+ *                              JoyGetPosEx             [WINMM.32]
  */
 MMRESULT WINAPI joyGetPosEx(UINT wID, LPJOYINFOEX lpInfo)
 {
-	return joyGetPosEx16(wID, lpInfo);
+    TRACE("(%d, %p);\n", wID, lpInfo);
+    
+    if (wID >= MAXJOYSTICK)	return JOYERR_PARMS;
+    if (!JOY_LoadDriver())	return MMSYSERR_NODRIVER;
+    
+    lpInfo->dwXpos = 0;
+    lpInfo->dwYpos = 0;
+    lpInfo->dwZpos = 0;
+    lpInfo->dwRpos = 0;
+    lpInfo->dwUpos = 0;
+    lpInfo->dwVpos = 0;
+    lpInfo->dwButtons = 0;
+    lpInfo->dwButtonNumber = 0;
+    lpInfo->dwPOV = 0;
+    lpInfo->dwReserved1 = 0;
+    lpInfo->dwReserved2 = 0;
+
+    return SendDriverMessage(JOY_Driver, JDD_GETPOSEX, (DWORD)lpInfo, 0L);
 }
 
 /**************************************************************************
- *                              JoyGetPosEx             [WINMM.31]
+ *                              JoyGetPosEx16           [MMSYSTEM.]
  */
 MMRESULT16 WINAPI joyGetPosEx16(UINT16 wID, LPJOYINFOEX lpInfo)
 {
-
-    TRACE("(%04X, %p)\n", wID, lpInfo);
-
-    if (wID < MAXJOYSTICK) {
-#ifdef HAVE_LINUX_22_JOYSTICK_API
-	struct js_event ev;
-
-	joyCloseDriver(wID);
-       	if (joyOpenDriver(wID) == FALSE) return JOYERR_PARMS;
-	lpInfo->dwSize = sizeof(JOYINFOEX);
-	lpInfo->dwXpos = lpInfo->dwYpos =lpInfo->dwZpos = 0;
-	lpInfo->dwButtons = lpInfo->dwFlags = 0;
-	/* After opening the device it's state can be
-	   read with JS_EVENT_INIT flag */
-	while ((read(joy_dev[wID], &ev, sizeof(struct js_event))) > 0) {
-       		if (ev.type == (JS_EVENT_AXIS | JS_EVENT_INIT)) {
-			switch (ev.number) {
-			 case 0: lpInfo->dwXpos   = ev.value + 32767;
-				 lpInfo->dwFlags |= JOY_RETURNX; break;
-			 case 1: lpInfo->dwYpos   = ev.value + 32767;
-				 lpInfo->dwFlags |= JOY_RETURNY; break;
-			 case 2: lpInfo->dwZpos   = ev.value + 32767;
-				 lpInfo->dwFlags |= JOY_RETURNZ; break;
-			 case 3: lpInfo->dwRpos   = ev.value + 32767;
-				 lpInfo->dwFlags |= JOY_RETURNR; break;
-			 case 4: lpInfo->dwUpos   = ev.value + 32767;
-				 lpInfo->dwFlags |= JOY_RETURNU; break;
-			 case 5: lpInfo->dwVpos   = ev.value + 32767;
-				 lpInfo->dwFlags |= JOY_RETURNV; break;
-			}
-		} else if (ev.type ==
-			(JS_EVENT_BUTTON | JS_EVENT_INIT)) {
-			if (ev.value)
-                       		lpInfo->dwButtons |= (1 << ev.number);
-               		else
-                       		lpInfo->dwButtons &= ~(1 << ev.number);
-			lpInfo->dwFlags |= JOY_RETURNBUTTONS;
-		}
-	}
-	/* EAGAIN is returned when the queue is empty */
-	if (errno != EAGAIN) {
-		/* FIXME: error should not be ignored */
-	}
-	joyCloseDriver(wID);
-	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;
-#else
-        struct js_status js;
-       	int    dev_stat;
- 
-	if (joyOpenDriver(wID) == FALSE) return JOYERR_UNPLUGGED;
-	dev_stat = read(joy_dev[wID], &js, sizeof(js));
-	if (dev_stat != sizeof(js)) {
-		joyCloseDriver(wID);
-		return JOYERR_UNPLUGGED; /* FIXME: perhaps wrong, but what should I return else ? */
-	}
-	count_use[wID] = 0;
-	js.x = js.x<<8;
-	js.y = js.y<<8;
-	lpInfo->dwXpos = js.x;   /* FIXME: perhaps multiply it somehow ? */
-	lpInfo->dwYpos = js.y;
-	lpInfo->dwZpos = 0;
-	lpInfo->dwButtons = js.buttons;
-	lpInfo->dwFlags = JOY_RETURNX | JOY_RETURNY | JOY_RETURNBUTTONS;
-	TRACE("x: %ld, y: %ld, buttons: 0x%04x, flags: 0x%04x\n",
-		lpInfo->dwXpos, lpInfo->dwYpos,
-		(unsigned int)lpInfo->dwButtons,
-		(unsigned int)lpInfo->dwFlags);
-	return JOYERR_NOERROR;
-#endif
-    } else
-    	return JOYERR_PARMS;
+    return joyGetPosEx(wID, lpInfo);
 }
 
 /**************************************************************************
- * 				JoyGetPos	       	[WINMM.30]
+ * 				JoyGetPos	       	[WINMM.31]
  */
 MMRESULT WINAPI joyGetPos(UINT wID, LPJOYINFO lpInfo)
 {
-	JOYINFOEX	ji;
-	MMRESULT	ret;
+    TRACE("(%d, %p);\n", wID, lpInfo);
+    
+    if (wID >= MAXJOYSTICK)	return JOYERR_PARMS;
+    if (!JOY_LoadDriver())	return MMSYSERR_NODRIVER;
+    
+    lpInfo->wXpos = 0;
+    lpInfo->wYpos = 0;
+    lpInfo->wZpos = 0;
+    lpInfo->wButtons = 0;
 
-        TRACE("(%d, %p);\n", wID, lpInfo);
-
-	ret = joyGetPosEx16(wID,&ji);
-	lpInfo->wXpos = ji.dwXpos;
-	lpInfo->wYpos = ji.dwYpos;
-	lpInfo->wZpos = ji.dwZpos;
-	lpInfo->wButtons = ji.dwButtons;
-	return ret;
+    return SendDriverMessage(JOY_Driver, JDD_GETPOS, (DWORD)lpInfo, 0L);
 }
 
 /**************************************************************************
@@ -535,29 +261,31 @@
  */
 MMRESULT16 WINAPI joyGetPos16(UINT16 wID, LPJOYINFO16 lpInfo)
 {
-	JOYINFOEX	ji;
-	MMRESULT16	ret;
-
-        TRACE("(%d, %p);\n", wID, lpInfo);
-
-	ret = joyGetPosEx16(wID,&ji);
-	lpInfo->wXpos = ji.dwXpos;
-	lpInfo->wYpos = ji.dwYpos;
-	lpInfo->wZpos = ji.dwZpos;
-	lpInfo->wButtons = ji.dwButtons;
-	return ret;
+    JOYINFO	ji;
+    MMRESULT	ret;
+    
+    TRACE("(%d, %p);\n", wID, lpInfo);
+    
+    if ((ret = joyGetPos(wID, &ji)) == JOYERR_NOERROR) {
+	lpInfo->wXpos = ji.wXpos;
+	lpInfo->wYpos = ji.wYpos;
+	lpInfo->wZpos = ji.wZpos;
+	lpInfo->wButtons = ji.wButtons;
+    }
+    return ret;
 }
 
 /**************************************************************************
- * 				JoyGetThreshold		[WINMM.32]
+ * 				JoyGetThreshold		[WINMM.36]
  */
 MMRESULT WINAPI joyGetThreshold(UINT wID, LPUINT lpThreshold)
 {
-	UINT16		thresh;
-	MMRESULT16	ret = joyGetThreshold16(wID,&thresh);
-
-	*lpThreshold = thresh;
-	return ret;
+    TRACE("(%04X, %p);\n", wID, lpThreshold);
+    
+    if (wID >= MAXJOYSTICK)	return JOYERR_PARMS;
+    
+    *lpThreshold = JOY_Sticks[wID].threshold;
+    return JOYERR_NOERROR;
 }
 
 /**************************************************************************
@@ -566,17 +294,29 @@
 MMRESULT16 WINAPI joyGetThreshold16(UINT16 wID, LPUINT16 lpThreshold)
 {
     TRACE("(%04X, %p);\n", wID, lpThreshold);
-    if (wID >= MAXJOYSTICK) return MMSYSERR_INVALPARAM;
-    *lpThreshold = joy_threshold[wID];
+    
+    if (wID >= MAXJOYSTICK)	return JOYERR_PARMS;
+    
+    *lpThreshold = JOY_Sticks[wID].threshold;
     return JOYERR_NOERROR;
 }
 
 /**************************************************************************
- * 				JoyReleaseCapture	[WINMM.33]
+ * 				JoyReleaseCapture	[WINMM.34]
  */
 MMRESULT WINAPI joyReleaseCapture(UINT wID)
 {
-	return joyReleaseCapture16(wID);
+    TRACE("(%04X);\n", wID);
+
+    if (wID >= MAXJOYSTICK)		return JOYERR_PARMS;
+    if (!JOY_LoadDriver())		return MMSYSERR_NODRIVER;
+    if (!JOY_Sticks[wID].hCapture)	return JOYERR_NOCANDO;
+
+    KillTimer(JOY_Sticks[wID].hCapture, JOY_Sticks[wID].wTimer);
+    JOY_Sticks[wID].hCapture = 0;
+    JOY_Sticks[wID].wTimer = 0;
+
+    return JOYERR_NOERROR;
 }
 
 /**************************************************************************
@@ -584,64 +324,63 @@
  */
 MMRESULT16 WINAPI joyReleaseCapture16(UINT16 wID)
 {
-    TRACE("(%04X);\n", wID);
-    if (wID >= MAXJOYSTICK) return MMSYSERR_INVALPARAM;
-    joyCaptured = FALSE;
-    joyCloseDriver(wID);
-    joy_dev[wID] = -1;
-    CaptureWnd[wID] = 0;
+    return joyReleaseCapture(wID);
+}
+
+/**************************************************************************
+ * 				JoySetCapture		[WINMM.35]
+ */
+MMRESULT WINAPI joySetCapture(HWND hWnd, UINT wID, UINT wPeriod, BOOL bChanged)
+{
+    TRACE("(%04X, %04X, %d, %d);\n",  hWnd, wID, wPeriod, bChanged);
+
+    if (wID >= MAXJOYSTICK || hWnd == 0) return JOYERR_PARMS;
+    if (wPeriod<JOY_PERIOD_MIN || wPeriod>JOY_PERIOD_MAX) return JOYERR_PARMS;
+    if (!JOY_LoadDriver()) return MMSYSERR_NODRIVER;
+
+    if (JOY_Sticks[wID].hCapture || !IsWindow(hWnd))
+	return JOYERR_NOCANDO; /* FIXME: what should be returned ? */
+
+    if (joyGetPos(wID, &JOY_Sticks[wID].ji) != JOYERR_NOERROR)
+	return JOYERR_UNPLUGGED;
+
+    if ((JOY_Sticks[wID].wTimer = SetTimer(hWnd, 0, wPeriod, JOY_Timer)) == 0)
+	return JOYERR_NOCANDO;
+
+    JOY_Sticks[wID].hCapture = hWnd;
+    JOY_Sticks[wID].bChanged = bChanged;
+    
     return JOYERR_NOERROR;
 }
 
 /**************************************************************************
  * 				JoySetCapture		[MMSYSTEM.106]
  */
-MMRESULT WINAPI joySetCapture(HWND hWnd,UINT wID,UINT wPeriod,BOOL bChanged)
+MMRESULT16 WINAPI joySetCapture16(HWND16 hWnd, UINT16 wID, UINT16 wPeriod, BOOL16 bChanged)
 {
-	return joySetCapture16(hWnd,wID,wPeriod,bChanged);
+    return joySetCapture16(hWnd, wID, wPeriod, bChanged);    
 }
 
 /**************************************************************************
- * 				JoySetCapture		[MMSYSTEM.106]
- */
-MMRESULT16 WINAPI joySetCapture16(HWND16 hWnd,UINT16 wID,UINT16 wPeriod,BOOL16 bChanged)
-{
-
-    TRACE("(%04X, %04X, %d, %d);\n",
-	    hWnd, wID, wPeriod, bChanged);
-    if (wID >= MAXJOYSTICK) return JOYERR_PARMS;
-    if (hWnd == 0) return JOYERR_PARMS;
-    if (wPeriod<JOY_PERIOD_MIN || wPeriod>JOY_PERIOD_MAX) return JOYERR_PARMS;
-    if (!CaptureWnd[wID]) {
-	if (joyOpenDriver(wID) == FALSE) return JOYERR_PARMS;
-	joyCaptured = TRUE;
-	CaptureWnd[wID] = hWnd;
-	joyCapData[wID].wXpos = 0;
-	joyCapData[wID].wYpos = 0;
-	joyCapData[wID].wZpos = 0;
-	joyCapData[wID].wButtons = 0;
-	return JOYERR_NOERROR;
-    } else
-    return JOYERR_NOCANDO; /* FIXME: what should be returned ? */
-}
-
-/**************************************************************************
- * 				JoySetThreshold		[WINMM.35]
+ * 				JoySetThreshold		[WINMM.36]
  */
 MMRESULT WINAPI joySetThreshold(UINT wID, UINT wThreshold)
 {
-	return joySetThreshold16(wID,wThreshold);
+    TRACE("(%04X, %d);\n", wID, wThreshold);
+    
+    if (wID >= MAXJOYSTICK) return MMSYSERR_INVALPARAM;
+
+    JOY_Sticks[wID].threshold = wThreshold;
+
+    return JOYERR_NOERROR;
 }
+
 /**************************************************************************
  * 				JoySetThreshold		[MMSYSTEM.107]
  */
 MMRESULT16 WINAPI joySetThreshold16(UINT16 wID, UINT16 wThreshold)
 {
-    TRACE("(%04X, %d);\n", wID, wThreshold);
-
-    if (wID >= MAXJOYSTICK) return MMSYSERR_INVALPARAM;
-    joy_threshold[wID] = wThreshold;
-    return JOYERR_NOERROR;
+    return joySetThreshold16(wID,wThreshold);
 }
 
 /**************************************************************************
diff --git a/dlls/winmm/joystick/.cvsignore b/dlls/winmm/joystick/.cvsignore
new file mode 100644
index 0000000..00cf59e
--- /dev/null
+++ b/dlls/winmm/joystick/.cvsignore
@@ -0,0 +1,3 @@
+*.spec.c
+Makefile
+libjoystick.drv.so.1.0
diff --git a/dlls/winmm/joystick/Makefile.in b/dlls/winmm/joystick/Makefile.in
new file mode 100644
index 0000000..d21e08f
--- /dev/null
+++ b/dlls/winmm/joystick/Makefile.in
@@ -0,0 +1,23 @@
+DEFS      = @DLLFLAGS@ -D__WINE__
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = ../../..
+SRCDIR    = @srcdir@
+VPATH     = @srcdir@
+LIBEXT    = @LIBEXT@
+MODULE    = joystick.drv
+SOVERSION = 1.0
+
+SPEC_SRCS = \
+	joystick.spec
+
+C_SRCS = \
+	joystick.c
+
+all: lib$(MODULE).$(LIBEXT)
+
+@MAKE_RULES@
+
+clean::
+	$(RM) lib$(MODULE).$(LIBEXT).$(SOVERSION)
+
+### Dependencies:
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
diff --git a/dlls/winmm/joystick/joystick.spec b/dlls/winmm/joystick/joystick.spec
new file mode 100644
index 0000000..bea6d1a
--- /dev/null
+++ b/dlls/winmm/joystick/joystick.spec
@@ -0,0 +1,5 @@
+name joystick
+file joystick.drv
+type win32
+
+  1 stdcall DriverProc(long long long long long) JSTCK_DriverProc
diff --git a/include/debugdefs.h b/include/debugdefs.h
index 86b8b6a..3d08eba 100644
--- a/include/debugdefs.h
+++ b/include/debugdefs.h
@@ -71,6 +71,7 @@
 char dbch_int31[] = "\003int31";
 char dbch_io[] = "\003io";
 char dbch_ipaddress[] = "\003ipaddress";
+char dbch_joystick[] = "\003joystick";
 char dbch_key[] = "\003key";
 char dbch_keyboard[] = "\003keyboard";
 char dbch_ldt[] = "\003ldt";
@@ -169,7 +170,7 @@
 char dbch_x11[] = "\003x11";
 char dbch_x11drv[] = "\003x11drv";
 
-#define DEBUG_CHANNEL_COUNT 162
+#define DEBUG_CHANNEL_COUNT 163
 
 static char * const debug_channels[DEBUG_CHANNEL_COUNT] = {
     dbch_accel,
@@ -237,6 +238,7 @@
     dbch_int31,
     dbch_io,
     dbch_ipaddress,
+    dbch_joystick,
     dbch_key,
     dbch_keyboard,
     dbch_ldt,
diff --git a/include/message.h b/include/message.h
index fd86651..2c789e4 100644
--- a/include/message.h
+++ b/include/message.h
@@ -42,6 +42,4 @@
 
 extern HWND EVENT_Capture( HWND, INT16 );
 
-extern void joySendMessages(void);
-
 #endif  /* __WINE_MESSAGE_H */
diff --git a/include/mmddk.h b/include/mmddk.h
index 6af547a..a1de5d4 100644
--- a/include/mmddk.h
+++ b/include/mmddk.h
@@ -132,12 +132,16 @@
 #define MIDM_RESET       	62
 
 
-#define AUXM_INIT             DRVM_INIT
+#define AUXM_INIT             	DRVM_INIT
 #define AUXDM_GETNUMDEVS    	3
 #define AUXDM_GETDEVCAPS    	4
 #define AUXDM_GETVOLUME     	5
 #define AUXDM_SETVOLUME     	6
 
+#define MXDM_INIT		DRVM_INIT
+#define MXDM_USER               DRVM_USER
+#define MXDM_MAPPER             DRVM_MAPPER
+
 #define	MXDM_GETNUMDEVS		1
 #define	MXDM_GETDEVCAPS		2
 #define	MXDM_OPEN		3
@@ -147,6 +151,144 @@
 #define	MXDM_GETCONTROLDETAILS	7
 #define	MXDM_SETCONTROLDETAILS	8
 
+/* pre-defined joystick types */
+#define JOY_HW_NONE			0
+#define JOY_HW_CUSTOM			1
+#define JOY_HW_2A_2B_GENERIC		2
+#define JOY_HW_2A_4B_GENERIC		3
+#define JOY_HW_2B_GAMEPAD		4
+#define JOY_HW_2B_FLIGHTYOKE		5
+#define JOY_HW_2B_FLIGHTYOKETHROTTLE	6
+#define JOY_HW_3A_2B_GENERIC		7
+#define JOY_HW_3A_4B_GENERIC		8
+#define JOY_HW_4B_GAMEPAD		9
+#define JOY_HW_4B_FLIGHTYOKE		10
+#define JOY_HW_4B_FLIGHTYOKETHROTTLE	11
+#define JOY_HW_LASTENTRY		12
+
+/* calibration flags */
+#define	JOY_ISCAL_XY		0x00000001l	/* XY are calibrated */
+#define	JOY_ISCAL_Z		0x00000002l	/* Z is calibrated */
+#define	JOY_ISCAL_R		0x00000004l	/* R is calibrated */
+#define	JOY_ISCAL_U		0x00000008l	/* U is calibrated */
+#define	JOY_ISCAL_V		0x00000010l	/* V is calibrated */
+#define	JOY_ISCAL_POV		0x00000020l	/* POV is calibrated */
+
+/* point of view constants */
+#define JOY_POV_NUMDIRS          4
+#define JOY_POVVAL_FORWARD       0
+#define JOY_POVVAL_BACKWARD      1
+#define JOY_POVVAL_LEFT          2
+#define JOY_POVVAL_RIGHT         3
+
+/* Specific settings for joystick hardware */
+#define JOY_HWS_HASZ		0x00000001l	/* has Z info? */
+#define JOY_HWS_HASPOV		0x00000002l	/* point of view hat present */
+#define JOY_HWS_POVISBUTTONCOMBOS 0x00000004l	/* pov done through combo of buttons */
+#define JOY_HWS_POVISPOLL	0x00000008l	/* pov done through polling */
+#define JOY_HWS_ISYOKE		0x00000010l	/* joystick is a flight yoke */
+#define JOY_HWS_ISGAMEPAD	0x00000020l	/* joystick is a game pad */
+#define JOY_HWS_ISCARCTRL	0x00000040l	/* joystick is a car controller */
+/* X defaults to J1 X axis */
+#define JOY_HWS_XISJ1Y		0x00000080l	/* X is on J1 Y axis */
+#define JOY_HWS_XISJ2X		0x00000100l	/* X is on J2 X axis */
+#define JOY_HWS_XISJ2Y		0x00000200l	/* X is on J2 Y axis */
+/* Y defaults to J1 Y axis */
+#define JOY_HWS_YISJ1X		0x00000400l	/* Y is on J1 X axis */
+#define JOY_HWS_YISJ2X		0x00000800l	/* Y is on J2 X axis */
+#define JOY_HWS_YISJ2Y		0x00001000l	/* Y is on J2 Y axis */
+/* Z defaults to J2 Y axis */
+#define JOY_HWS_ZISJ1X		0x00002000l	/* Z is on J1 X axis */
+#define JOY_HWS_ZISJ1Y		0x00004000l	/* Z is on J1 Y axis */
+#define JOY_HWS_ZISJ2X		0x00008000l	/* Z is on J2 X axis */
+/* POV defaults to J2 Y axis, if it is not button based */
+#define JOY_HWS_POVISJ1X	0x00010000l	/* pov done through J1 X axis */
+#define JOY_HWS_POVISJ1Y	0x00020000l	/* pov done through J1 Y axis */
+#define JOY_HWS_POVISJ2X	0x00040000l	/* pov done through J2 X axis */
+/* R defaults to J2 X axis */
+#define JOY_HWS_HASR		0x00080000l	/* has R (4th axis) info */
+#define JOY_HWS_RISJ1X		0x00100000l	/* R done through J1 X axis */
+#define JOY_HWS_RISJ1Y		0x00200000l	/* R done through J1 Y axis */
+#define JOY_HWS_RISJ2Y		0x00400000l	/* R done through J2 X axis */
+/* U & V for future hardware */
+#define JOY_HWS_HASU		0x00800000l	/* has U (5th axis) info */
+#define JOY_HWS_HASV		0x01000000l	/* has V (6th axis) info */
+
+/* Usage settings */
+#define JOY_US_HASRUDDER	0x00000001l	/* joystick configured with rudder */
+#define JOY_US_PRESENT		0x00000002l	/* is joystick actually present? */
+#define JOY_US_ISOEM		0x00000004l	/* joystick is an OEM defined type */
+
+
+/* struct for storing x,y, z, and rudder values */
+typedef struct joypos_tag {
+    DWORD	dwX;
+    DWORD	dwY;
+    DWORD	dwZ;
+    DWORD	dwR;
+    DWORD	dwU;
+    DWORD	dwV;
+} JOYPOS, *LPJOYPOS;
+
+/* struct for storing ranges */
+typedef struct joyrange_tag {
+    JOYPOS	jpMin;
+    JOYPOS	jpMax;
+    JOYPOS	jpCenter;
+} JOYRANGE,*LPJOYRANGE;
+
+typedef struct joyreguservalues_tag {
+    DWORD	dwTimeOut;	/* value at which to timeout joystick polling */
+    JOYRANGE	jrvRanges;	/* range of values app wants returned for axes */
+    JOYPOS	jpDeadZone;	/* area around center to be considered
+    				   as "dead". specified as a percentage
+				   (0-100). Only X & Y handled by system driver */
+} JOYREGUSERVALUES, *LPJOYREGUSERVALUES;
+
+typedef struct joyreghwsettings_tag {
+    DWORD	dwFlags;
+    DWORD	dwNumButtons;		/* number of buttons */
+} JOYREGHWSETTINGS, *LPJOYHWSETTINGS;
+
+/* range of values returned by the hardware (filled in by calibration) */
+typedef struct joyreghwvalues_tag {
+    JOYRANGE	jrvHardware;		/* values returned by hardware */
+    DWORD	dwPOVValues[JOY_POV_NUMDIRS];/* POV values returned by hardware */
+    DWORD	dwCalFlags;		/* what has been calibrated */
+} JOYREGHWVALUES, *LPJOYREGHWVALUES;
+
+/* hardware configuration */
+typedef struct joyreghwconfig_tag {
+    JOYREGHWSETTINGS	hws;		/* hardware settings */
+    DWORD		dwUsageSettings;/* usage settings */
+    JOYREGHWVALUES	hwv;		/* values returned by hardware */
+    DWORD		dwType;		/* type of joystick */
+    DWORD		dwReserved;	/* reserved for OEM drivers */
+} JOYREGHWCONFIG, *LPJOYREGHWCONFIG;
+
+/* joystick calibration info structure */
+typedef struct joycalibrate_tag {
+    UINT    wXbase;
+    UINT    wXdelta;
+    UINT    wYbase;
+    UINT    wYdelta;
+    UINT    wZbase;
+    UINT    wZdelta;
+} JOYCALIBRATE;
+typedef JOYCALIBRATE *LPJOYCALIBRATE;
+
+/* prototype for joystick message function */
+typedef UINT (CALLBACK JOYDEVMSGPROC)(DWORD dwID, UINT uMessage, LPARAM lParam1, LPARAM lParam2);
+typedef JOYDEVMSGPROC *LPJOYDEVMSGPROC;
+
+/* messages sent to joystick driver's DriverProc() function */
+#define JDD_GETNUMDEVS          (DRV_RESERVED + 0x0001)
+#define JDD_GETDEVCAPS          (DRV_RESERVED + 0x0002)
+#define JDD_GETPOS              (DRV_RESERVED + 0x0101)
+#define JDD_SETCALIBRATION      (DRV_RESERVED + 0x0102)
+#define JDD_CONFIGCHANGED       (DRV_RESERVED + 0x0103)
+#define JDD_GETPOSEX            (DRV_RESERVED + 0x0104)
+
 #define MCI_MAX_DEVICE_TYPE_LENGTH 80
 
 #define MCI_FALSE                       (MCI_STRING_OFFSET + 19)
diff --git a/windows/message.c b/windows/message.c
index 78f2578..2337bca 100644
--- a/windows/message.c
+++ b/windows/message.c
@@ -577,9 +577,6 @@
     QMSG *nextqmsg, *qmsg = 0;
     BOOL bRet = FALSE;
 
-    /* FIXME: there has to be a better way to do this */
-    joySendMessages();
-
     EnterCriticalSection(&sysMsgQueue->cSection);
 
     /* Loop through the Q and translate the message we wish to process