4 * Copyright 1997 Andreas Mohr
5 * Copyright 2000 Wolfgang Schwotzer
6 * Copyright 2002 David Hagood
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * - nearly all joystick functions can be regarded as obsolete,
25 * as Linux (2.1.x) now supports extended joysticks with a completely
26 * new joystick driver interface
27 * New driver's docu says:
28 * "For backward compatibility the old interface is still included,
29 * but will be dropped in the future."
30 * Thus we should implement the new interface and at most keep the old
31 * routines for backward compatibility.
32 * - better support of enhanced joysticks (Linux 2.2 interface is available)
33 * - support more joystick drivers (like the XInput extension)
34 * - should load joystick DLL as any other driver (instead of hardcoding)
35 * the driver's name, and load it as any low lever driver.
48 #ifdef HAVE_SYS_IOCTL_H
49 #include <sys/ioctl.h>
51 #ifdef HAVE_LINUX_IOCTL_H
52 #include <linux/ioctl.h>
54 #ifdef HAVE_LINUX_JOYSTICK_H
55 #include <linux/joystick.h>
56 #define JOYDEV "/dev/js%d"
58 #ifdef HAVE_SYS_ERRNO_H
59 #include <sys/errno.h>
68 #include "wine/debug.h"
70 WINE_DEFAULT_DEBUG_CHANNEL(joystick);
72 #ifdef HAVE_LINUX_JOYSTICK_H
74 #define MAXJOYSTICK (JOYSTICKID2 + 1)
76 typedef struct tagWINE_JSTCK {
79 /* Some extra info we need to make this acutaly work under the
81 First of all, we cannot keep closing and reopening the device file -
82 that blows away the state of the stick device, and we lose events. So, we
83 need to open the low-level device once, and close it when we are done.
85 Secondly, the event API only gives us what's changed. However, Windows apps
86 want the whole state every time, so we have to cache the data.
89 int dev; /* Linux level device file descriptor */
99 static WINE_JSTCK JSTCK_Data[MAXJOYSTICK];
101 /**************************************************************************
102 * JSTCK_drvGet [internal]
104 static WINE_JSTCK* JSTCK_drvGet(DWORD dwDevID)
108 if ((dwDevID - (DWORD)JSTCK_Data) % sizeof(JSTCK_Data[0]) != 0)
110 p = (dwDevID - (DWORD)JSTCK_Data) / sizeof(JSTCK_Data[0]);
111 if (p < 0 || p >= MAXJOYSTICK || !((WINE_JSTCK*)dwDevID)->in_use)
114 return (WINE_JSTCK*)dwDevID;
117 /**************************************************************************
118 * JSTCK_drvOpen [internal]
120 static DWORD JSTCK_drvOpen(LPSTR str, DWORD dwIntf)
122 if (dwIntf >= MAXJOYSTICK || JSTCK_Data[dwIntf].in_use)
125 JSTCK_Data[dwIntf].joyIntf = dwIntf;
126 JSTCK_Data[dwIntf].in_use = 1;
127 return (DWORD)&JSTCK_Data[dwIntf];
130 /**************************************************************************
131 * JSTCK_drvClose [internal]
133 static DWORD JSTCK_drvClose(DWORD dwDevID)
135 WINE_JSTCK* jstck = JSTCK_drvGet(dwDevID);
155 /**************************************************************************
156 * JSTCK_OpenDevice [internal]
158 static int JSTCK_OpenDevice(WINE_JSTCK* jstick)
166 sprintf(buf, JOYDEV, jstick->joyIntf);
167 #ifdef HAVE_LINUX_22_JOYSTICK_API
168 flags = O_RDONLY | O_NONBLOCK;
172 return (jstick->dev = open(buf, flags));
176 /**************************************************************************
177 * JoyGetDevCaps [MMSYSTEM.102]
179 static LONG JSTCK_GetDevCaps(DWORD dwDevID, LPJOYCAPSW lpCaps, DWORD dwSize)
182 #ifdef HAVE_LINUX_22_JOYSTICK_API
186 char identString[MAXPNAMELEN];
189 static const WCHAR ini[] = {'W','i','n','e',' ','J','o','y','s','t','i','c','k',' ','D','r','i','v','e','r',0};
192 if ((jstck = JSTCK_drvGet(dwDevID)) == NULL)
193 return MMSYSERR_NODRIVER;
195 #ifdef HAVE_LINUX_22_JOYSTICK_API
196 if ((dev = JSTCK_OpenDevice(jstck)) < 0) return JOYERR_PARMS;
197 ioctl(dev, JSIOCGAXES, &nrOfAxes);
198 ioctl(dev, JSIOCGBUTTONS, &nrOfButtons);
199 ioctl(dev, JSIOCGVERSION, &driverVersion);
200 ioctl(dev, JSIOCGNAME(sizeof(identString)), &identString);
201 TRACE("Driver: 0x%06x, Name: %s, #Axes: %d, #Buttons: %d\n",
202 driverVersion, identString, nrOfAxes, nrOfButtons);
203 lpCaps->wMid = MM_MICROSOFT;
204 lpCaps->wPid = MM_PC_JOYSTICK;
205 MultiByteToWideChar(CP_ACP, 0, identString, -1, lpCaps->szPname, MAXPNAMELEN);
206 lpCaps->szPname[MAXPNAMELEN-1] = '\0';
208 lpCaps->wXmax = 0xFFFF;
210 lpCaps->wYmax = 0xFFFF;
212 lpCaps->wZmax = (nrOfAxes >= 3) ? 0xFFFF : 0;
214 /* HalfLife won't allow you to map an axis event to things like
215 "next weapon" and "use". Linux reports the hat on my stick as
216 axis U and V. So, IFF BODGE_THE_HAT is defined, lie through our
217 teeth and say we have 32 buttons, and we will map the axises to
218 the high buttons. Really, perhaps this should be a registry entry,
219 or even a parameter to the Linux joystick driver (which would completely
220 remove the need for this.)
222 lpCaps->wNumButtons = 32;
224 lpCaps->wNumButtons = nrOfButtons;
226 if (dwSize == sizeof(JOYCAPSW)) {
227 /* since we suppose ntOfAxes <= 6 in the following code, do it explicitly */
228 if (nrOfAxes > 6) nrOfAxes = 6;
229 /* complete 95 structure */
231 lpCaps->wRmax = nrOfAxes >= 4 ? 0xFFFF : 0;
233 lpCaps->wUmax = nrOfAxes >= 5 ? 0xFFFF : 0;
235 lpCaps->wVmax = nrOfAxes >= 6 ? 0xFFFF : 0;
236 lpCaps->wMaxAxes = 6; /* same as MS Joystick Driver */
237 lpCaps->wNumAxes = nrOfAxes; /* nr of axes in use */
238 lpCaps->wMaxButtons = 32; /* same as MS Joystick Driver */
239 lpCaps->szRegKey[0] = 0;
240 lpCaps->szOEMVxD[0] = 0;
243 case 6: lpCaps->wCaps |= JOYCAPS_HASV;
244 case 5: lpCaps->wCaps |= JOYCAPS_HASU;
245 case 4: lpCaps->wCaps |= JOYCAPS_HASR;
246 case 3: lpCaps->wCaps |= JOYCAPS_HASZ;
247 /* FIXME: don't know how to detect for
248 JOYCAPS_HASPOV, JOYCAPS_POV4DIR, JOYCAPS_POVCTS */
252 lpCaps->wMid = MM_MICROSOFT;
253 lpCaps->wPid = MM_PC_JOYSTICK;
254 strcpyW(lpCaps->szPname, ini); /* joystick product name */
256 lpCaps->wXmax = 0xFFFF;
258 lpCaps->wYmax = 0xFFFF;
261 lpCaps->wNumButtons = 2;
262 if (dwSize == sizeof(JOYCAPSW)) {
263 /* complete 95 structure */
271 lpCaps->wMaxAxes = 2;
272 lpCaps->wNumAxes = 2;
273 lpCaps->wMaxButtons = 4;
274 lpCaps->szRegKey[0] = 0;
275 lpCaps->szOEMVxD[0] = 0;
279 return JOYERR_NOERROR;
282 /**************************************************************************
283 * JSTCK_GetPos [internal]
285 static LONG JSTCK_GetPosEx(DWORD dwDevID, LPJOYINFOEX lpInfo)
289 #ifdef HAVE_LINUX_22_JOYSTICK_API
296 if ((jstck = JSTCK_drvGet(dwDevID)) == NULL)
297 return MMSYSERR_NODRIVER;
299 if ((dev = JSTCK_OpenDevice(jstck)) < 0) return JOYERR_PARMS;
301 #ifdef HAVE_LINUX_22_JOYSTICK_API
302 while ((read(dev, &ev, sizeof(struct js_event))) > 0) {
303 if (ev.type == (JS_EVENT_AXIS)) {
324 FIXME("Unknown joystick event '%d'\n", ev.number);
326 } else if (ev.type == (JS_EVENT_BUTTON)) {
328 jstck->buttons |= (1 << ev.number);
329 /* FIXME: what to do for this field when
330 * multiple buttons are depressed ?
332 if (lpInfo->dwFlags & JOY_RETURNBUTTONS)
333 lpInfo->dwButtonNumber = ev.number + 1;
336 jstck->buttons &= ~(1 << ev.number);
339 /* EAGAIN is returned when the queue is empty */
340 if (errno != EAGAIN) {
341 /* FIXME: error should not be ignored */
342 ERR("Error while reading joystick state (%s)\n", strerror(errno));
344 /* Now, copy the cached values into Window's structure... */
345 if (lpInfo->dwFlags & JOY_RETURNBUTTONS)
346 lpInfo->dwButtons = jstck->buttons;
347 if (lpInfo->dwFlags & JOY_RETURNX)
348 lpInfo->dwXpos = jstck->x + 32767;
349 if (lpInfo->dwFlags & JOY_RETURNY)
350 lpInfo->dwYpos = jstck->y + 32767;
351 if (lpInfo->dwFlags & JOY_RETURNZ)
352 lpInfo->dwZpos = jstck->z + 32767;
353 if (lpInfo->dwFlags & JOY_RETURNR)
354 lpInfo->dwRpos = jstck->r + 32767;
356 else if (lpInfo->dwFlags & JOY_RETURNBUTTONS)
359 lpInfo->dwButtons |= 1<<7;
360 else if (jstck->r < 0)
361 lpInfo->dwButtons |= 1<<8;
364 if (lpInfo->dwFlags & JOY_RETURNU)
365 lpInfo->dwUpos = jstck->u + 32767;
367 else if (lpInfo->dwFlags & JOY_RETURNBUTTONS)
370 lpInfo->dwButtons |= 1<<9;
371 else if (jstck->u < 0)
372 lpInfo->dwButtons |= 1<<10;
375 if (lpInfo->dwFlags & JOY_RETURNV)
376 lpInfo->dwVpos = jstck->v + 32767;
379 dev_stat = read(dev, &js, sizeof(js));
380 if (dev_stat != sizeof(js)) {
381 return JOYERR_UNPLUGGED; /* FIXME: perhaps wrong, but what should I return else ? */
385 if (lpInfo->dwFlags & JOY_RETURNX)
386 lpInfo->dwXpos = js.x; /* FIXME: perhaps multiply it somehow ? */
387 if (lpInfo->dwFlags & JOY_RETURNY)
388 lpInfo->dwYpos = js.y;
389 if (lpInfo->dwFlags & JOY_RETURNBUTTONS)
390 lpInfo->dwButtons = js.buttons;
393 TRACE("x: %ld, y: %ld, z: %ld, r: %ld, u: %ld, v: %ld, buttons: 0x%04x, flags: 0x%04x (fd %d)\n",
394 lpInfo->dwXpos, lpInfo->dwYpos, lpInfo->dwZpos,
395 lpInfo->dwRpos, lpInfo->dwUpos, lpInfo->dwVpos,
396 (unsigned int)lpInfo->dwButtons,
397 (unsigned int)lpInfo->dwFlags,
401 return JOYERR_NOERROR;
404 /**************************************************************************
405 * JSTCK_GetPos [internal]
407 static LONG JSTCK_GetPos(DWORD dwDevID, LPJOYINFO lpInfo)
412 memset(&ji, 0, sizeof(ji));
414 ji.dwSize = sizeof(ji);
415 ji.dwFlags = JOY_RETURNX | JOY_RETURNY | JOY_RETURNZ | JOY_RETURNBUTTONS;
416 ret = JSTCK_GetPosEx(dwDevID, &ji);
417 if (ret == JOYERR_NOERROR) {
418 lpInfo->wXpos = ji.dwXpos;
419 lpInfo->wYpos = ji.dwYpos;
420 lpInfo->wZpos = ji.dwZpos;
421 lpInfo->wButtons = ji.dwButtons;
427 /**************************************************************************
428 * DriverProc (JOYSTICK.@)
430 LONG CALLBACK JSTCK_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
431 DWORD dwParam1, DWORD dwParam2)
433 /* EPP TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n", */
434 /* EPP dwDevID, hDriv, wMsg, dwParam1, dwParam2); */
437 case DRV_LOAD: return 1;
438 case DRV_FREE: return 1;
439 case DRV_OPEN: return JSTCK_drvOpen((LPSTR)dwParam1, dwParam2);
440 case DRV_CLOSE: return JSTCK_drvClose(dwDevID);
441 case DRV_ENABLE: return 1;
442 case DRV_DISABLE: return 1;
443 case DRV_QUERYCONFIGURE: return 1;
444 case DRV_CONFIGURE: MessageBoxA(0, "JoyStick MultiMedia Driver !", "JoyStick Driver", MB_OK); return 1;
445 case DRV_INSTALL: return DRVCNF_RESTART;
446 case DRV_REMOVE: return DRVCNF_RESTART;
448 case JDD_GETNUMDEVS: return 1;
449 case JDD_GETDEVCAPS: return JSTCK_GetDevCaps(dwDevID, (LPJOYCAPSW)dwParam1, dwParam2);
450 case JDD_GETPOS: return JSTCK_GetPos(dwDevID, (LPJOYINFO)dwParam1);
451 case JDD_SETCALIBRATION:
452 case JDD_CONFIGCHANGED: return JOYERR_NOCANDO;
453 case JDD_GETPOSEX: return JSTCK_GetPosEx(dwDevID, (LPJOYINFOEX)dwParam1);
455 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
461 /**************************************************************************
462 * DriverProc (JOYSTICK.@)
464 LONG CALLBACK JSTCK_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
465 DWORD dwParam1, DWORD dwParam2)
467 /* EPP TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n", */
468 /* EPP dwDevID, hDriv, wMsg, dwParam1, dwParam2); */
477 case DRV_QUERYCONFIGURE: return 0;
478 case DRV_CONFIGURE: MessageBoxA(0, "JoyStick MultiMedia Driver !", "JoyStick Driver", MB_OK); return 1;
479 case DRV_INSTALL: return DRVCNF_RESTART;
480 case DRV_REMOVE: return DRVCNF_RESTART;
482 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);