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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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.
39 #include "wine/port.h"
49 #ifdef HAVE_SYS_IOCTL_H
50 #include <sys/ioctl.h>
52 #ifdef HAVE_LINUX_IOCTL_H
53 #include <linux/ioctl.h>
55 #ifdef HAVE_LINUX_JOYSTICK_H
56 #include <linux/joystick.h>
57 #define JOYDEV_NEW "/dev/input/js%d"
58 #define JOYDEV_OLD "/dev/js%d"
60 #ifdef HAVE_SYS_ERRNO_H
61 #include <sys/errno.h>
70 #include "wine/debug.h"
72 #include "wine/unicode.h"
74 WINE_DEFAULT_DEBUG_CHANNEL(joystick);
76 #ifdef HAVE_LINUX_JOYSTICK_H
78 #define MAXJOYSTICK (JOYSTICKID2 + 1)
80 typedef struct tagWINE_JSTCK {
83 /* Some extra info we need to make this acutaly work under the
85 First of all, we cannot keep closing and reopening the device file -
86 that blows away the state of the stick device, and we lose events. So, we
87 need to open the low-level device once, and close it when we are done.
89 Secondly, the event API only gives us what's changed. However, Windows apps
90 want the whole state every time, so we have to cache the data.
93 int dev; /* Linux level device file descriptor */
103 static WINE_JSTCK JSTCK_Data[MAXJOYSTICK];
105 /**************************************************************************
106 * JSTCK_drvGet [internal]
108 static WINE_JSTCK *JSTCK_drvGet(DWORD_PTR dwDevID)
112 if ((dwDevID - (DWORD_PTR)JSTCK_Data) % sizeof(JSTCK_Data[0]) != 0)
114 p = (dwDevID - (DWORD_PTR)JSTCK_Data) / sizeof(JSTCK_Data[0]);
115 if (p < 0 || p >= MAXJOYSTICK || !((WINE_JSTCK*)dwDevID)->in_use)
118 return (WINE_JSTCK*)dwDevID;
121 /**************************************************************************
122 * JSTCK_drvOpen [internal]
124 static LRESULT JSTCK_drvOpen(LPSTR str, DWORD dwIntf)
126 if (dwIntf >= MAXJOYSTICK || JSTCK_Data[dwIntf].in_use)
129 JSTCK_Data[dwIntf].joyIntf = dwIntf;
130 JSTCK_Data[dwIntf].in_use = 1;
131 return (LRESULT)&JSTCK_Data[dwIntf];
134 /**************************************************************************
135 * JSTCK_drvClose [internal]
137 static LRESULT JSTCK_drvClose(DWORD_PTR dwDevID)
139 WINE_JSTCK* jstck = JSTCK_drvGet(dwDevID);
159 /**************************************************************************
160 * JSTCK_OpenDevice [internal]
162 static int JSTCK_OpenDevice(WINE_JSTCK* jstick)
170 sprintf(buf, JOYDEV_NEW, jstick->joyIntf);
171 #ifdef HAVE_LINUX_22_JOYSTICK_API
172 flags = O_RDONLY | O_NONBLOCK;
176 if ((jstick->dev = open(buf, flags)) < 0) {
177 sprintf(buf, JOYDEV_OLD, jstick->joyIntf);
179 return (jstick->dev = open(buf, flags));
183 /**************************************************************************
184 * JoyGetDevCaps [MMSYSTEM.102]
186 static LRESULT JSTCK_GetDevCaps(DWORD_PTR dwDevID, LPJOYCAPSW lpCaps, DWORD dwSize)
189 #ifdef HAVE_LINUX_22_JOYSTICK_API
193 char identString[MAXPNAMELEN];
196 static const WCHAR ini[] = {'W','i','n','e',' ','J','o','y','s','t','i','c','k',' ','D','r','i','v','e','r',0};
199 if ((jstck = JSTCK_drvGet(dwDevID)) == NULL)
200 return MMSYSERR_NODRIVER;
202 #ifdef HAVE_LINUX_22_JOYSTICK_API
203 if ((dev = JSTCK_OpenDevice(jstck)) < 0) return JOYERR_PARMS;
204 ioctl(dev, JSIOCGAXES, &nrOfAxes);
205 ioctl(dev, JSIOCGBUTTONS, &nrOfButtons);
206 ioctl(dev, JSIOCGVERSION, &driverVersion);
207 ioctl(dev, JSIOCGNAME(sizeof(identString)), &identString);
208 TRACE("Driver: 0x%06x, Name: %s, #Axes: %d, #Buttons: %d\n",
209 driverVersion, identString, nrOfAxes, nrOfButtons);
210 lpCaps->wMid = MM_MICROSOFT;
211 lpCaps->wPid = MM_PC_JOYSTICK;
212 MultiByteToWideChar(CP_ACP, 0, identString, -1, lpCaps->szPname, MAXPNAMELEN);
213 lpCaps->szPname[MAXPNAMELEN-1] = '\0';
215 lpCaps->wXmax = 0xFFFF;
217 lpCaps->wYmax = 0xFFFF;
219 lpCaps->wZmax = (nrOfAxes >= 3) ? 0xFFFF : 0;
221 /* HalfLife won't allow you to map an axis event to things like
222 "next weapon" and "use". Linux reports the hat on my stick as
223 axis U and V. So, IFF BODGE_THE_HAT is defined, lie through our
224 teeth and say we have 32 buttons, and we will map the axises to
225 the high buttons. Really, perhaps this should be a registry entry,
226 or even a parameter to the Linux joystick driver (which would completely
227 remove the need for this.)
229 lpCaps->wNumButtons = 32;
231 lpCaps->wNumButtons = nrOfButtons;
233 if (dwSize == sizeof(JOYCAPSW)) {
234 /* since we suppose ntOfAxes <= 6 in the following code, do it explicitly */
235 if (nrOfAxes > 6) nrOfAxes = 6;
236 /* complete 95 structure */
238 lpCaps->wRmax = nrOfAxes >= 4 ? 0xFFFF : 0;
240 lpCaps->wUmax = nrOfAxes >= 5 ? 0xFFFF : 0;
242 lpCaps->wVmax = nrOfAxes >= 6 ? 0xFFFF : 0;
243 lpCaps->wMaxAxes = 6; /* same as MS Joystick Driver */
244 lpCaps->wNumAxes = nrOfAxes; /* nr of axes in use */
245 lpCaps->wMaxButtons = 32; /* same as MS Joystick Driver */
246 lpCaps->szRegKey[0] = 0;
247 lpCaps->szOEMVxD[0] = 0;
250 case 6: lpCaps->wCaps |= JOYCAPS_HASV;
251 case 5: lpCaps->wCaps |= JOYCAPS_HASU;
252 case 4: lpCaps->wCaps |= JOYCAPS_HASR;
253 case 3: lpCaps->wCaps |= JOYCAPS_HASZ;
254 /* FIXME: don't know how to detect for
255 JOYCAPS_HASPOV, JOYCAPS_POV4DIR, JOYCAPS_POVCTS */
259 lpCaps->wMid = MM_MICROSOFT;
260 lpCaps->wPid = MM_PC_JOYSTICK;
261 strcpyW(lpCaps->szPname, ini); /* joystick product name */
263 lpCaps->wXmax = 0xFFFF;
265 lpCaps->wYmax = 0xFFFF;
268 lpCaps->wNumButtons = 2;
269 if (dwSize == sizeof(JOYCAPSW)) {
270 /* complete 95 structure */
278 lpCaps->wMaxAxes = 2;
279 lpCaps->wNumAxes = 2;
280 lpCaps->wMaxButtons = 4;
281 lpCaps->szRegKey[0] = 0;
282 lpCaps->szOEMVxD[0] = 0;
286 return JOYERR_NOERROR;
289 /**************************************************************************
290 * JSTCK_GetPos [internal]
292 static LRESULT JSTCK_GetPosEx(DWORD_PTR dwDevID, LPJOYINFOEX lpInfo)
296 #ifdef HAVE_LINUX_22_JOYSTICK_API
303 if ((jstck = JSTCK_drvGet(dwDevID)) == NULL)
304 return MMSYSERR_NODRIVER;
306 if ((dev = JSTCK_OpenDevice(jstck)) < 0) return JOYERR_PARMS;
308 #ifdef HAVE_LINUX_22_JOYSTICK_API
309 while ((read(dev, &ev, sizeof(struct js_event))) > 0) {
310 if (ev.type == (JS_EVENT_AXIS)) {
331 FIXME("Unknown joystick event '%d'\n", ev.number);
333 } else if (ev.type == (JS_EVENT_BUTTON)) {
335 jstck->buttons |= (1 << ev.number);
336 /* FIXME: what to do for this field when
337 * multiple buttons are depressed ?
339 if (lpInfo->dwFlags & JOY_RETURNBUTTONS)
340 lpInfo->dwButtonNumber = ev.number + 1;
343 jstck->buttons &= ~(1 << ev.number);
346 /* EAGAIN is returned when the queue is empty */
347 if (errno != EAGAIN) {
348 /* FIXME: error should not be ignored */
349 ERR("Error while reading joystick state (%s)\n", strerror(errno));
351 /* Now, copy the cached values into Window's structure... */
352 if (lpInfo->dwFlags & JOY_RETURNBUTTONS)
353 lpInfo->dwButtons = jstck->buttons;
354 if (lpInfo->dwFlags & JOY_RETURNX)
355 lpInfo->dwXpos = jstck->x + 32767;
356 if (lpInfo->dwFlags & JOY_RETURNY)
357 lpInfo->dwYpos = jstck->y + 32767;
358 if (lpInfo->dwFlags & JOY_RETURNZ)
359 lpInfo->dwZpos = jstck->z + 32767;
360 if (lpInfo->dwFlags & JOY_RETURNR)
361 lpInfo->dwRpos = jstck->r + 32767;
362 # ifdef BODGE_THE_HAT
363 else if (lpInfo->dwFlags & JOY_RETURNBUTTONS)
366 lpInfo->dwButtons |= 1<<7;
367 else if (jstck->r < 0)
368 lpInfo->dwButtons |= 1<<8;
371 if (lpInfo->dwFlags & JOY_RETURNU)
372 lpInfo->dwUpos = jstck->u + 32767;
373 # ifdef BODGE_THE_HAT
374 else if (lpInfo->dwFlags & JOY_RETURNBUTTONS)
377 lpInfo->dwButtons |= 1<<9;
378 else if (jstck->u < 0)
379 lpInfo->dwButtons |= 1<<10;
382 if (lpInfo->dwFlags & JOY_RETURNV)
383 lpInfo->dwVpos = jstck->v + 32767;
386 dev_stat = read(dev, &js, sizeof(js));
387 if (dev_stat != sizeof(js)) {
388 return JOYERR_UNPLUGGED; /* FIXME: perhaps wrong, but what should I return else ? */
392 if (lpInfo->dwFlags & JOY_RETURNX)
393 lpInfo->dwXpos = js.x; /* FIXME: perhaps multiply it somehow ? */
394 if (lpInfo->dwFlags & JOY_RETURNY)
395 lpInfo->dwYpos = js.y;
396 if (lpInfo->dwFlags & JOY_RETURNBUTTONS)
397 lpInfo->dwButtons = js.buttons;
400 TRACE("x: %ld, y: %ld, z: %ld, r: %ld, u: %ld, v: %ld, buttons: 0x%04x, flags: 0x%04x (fd %d)\n",
401 lpInfo->dwXpos, lpInfo->dwYpos, lpInfo->dwZpos,
402 lpInfo->dwRpos, lpInfo->dwUpos, lpInfo->dwVpos,
403 (unsigned int)lpInfo->dwButtons,
404 (unsigned int)lpInfo->dwFlags,
408 return JOYERR_NOERROR;
411 /**************************************************************************
412 * JSTCK_GetPos [internal]
414 static LRESULT JSTCK_GetPos(DWORD_PTR dwDevID, LPJOYINFO lpInfo)
419 memset(&ji, 0, sizeof(ji));
421 ji.dwSize = sizeof(ji);
422 ji.dwFlags = JOY_RETURNX | JOY_RETURNY | JOY_RETURNZ | JOY_RETURNBUTTONS;
423 ret = JSTCK_GetPosEx(dwDevID, &ji);
424 if (ret == JOYERR_NOERROR) {
425 lpInfo->wXpos = ji.dwXpos;
426 lpInfo->wYpos = ji.dwYpos;
427 lpInfo->wZpos = ji.dwZpos;
428 lpInfo->wButtons = ji.dwButtons;
434 /**************************************************************************
435 * DriverProc (JOYSTICK.@)
437 LRESULT CALLBACK JSTCK_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
438 LPARAM dwParam1, LPARAM dwParam2)
440 /* EPP TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n", */
441 /* EPP dwDevID, hDriv, wMsg, dwParam1, dwParam2); */
444 case DRV_LOAD: return 1;
445 case DRV_FREE: return 1;
446 case DRV_OPEN: return JSTCK_drvOpen((LPSTR)dwParam1, dwParam2);
447 case DRV_CLOSE: return JSTCK_drvClose(dwDevID);
448 case DRV_ENABLE: return 1;
449 case DRV_DISABLE: return 1;
450 case DRV_QUERYCONFIGURE: return 1;
451 case DRV_CONFIGURE: MessageBoxA(0, "JoyStick MultiMedia Driver !", "JoyStick Driver", MB_OK); return 1;
452 case DRV_INSTALL: return DRVCNF_RESTART;
453 case DRV_REMOVE: return DRVCNF_RESTART;
455 case JDD_GETNUMDEVS: return 1;
456 case JDD_GETDEVCAPS: return JSTCK_GetDevCaps(dwDevID, (LPJOYCAPSW)dwParam1, dwParam2);
457 case JDD_GETPOS: return JSTCK_GetPos(dwDevID, (LPJOYINFO)dwParam1);
458 case JDD_SETCALIBRATION:
459 case JDD_CONFIGCHANGED: return JOYERR_NOCANDO;
460 case JDD_GETPOSEX: return JSTCK_GetPosEx(dwDevID, (LPJOYINFOEX)dwParam1);
462 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
468 /**************************************************************************
469 * DriverProc (JOYSTICK.@)
471 LRESULT CALLBACK JSTCK_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
472 LPARAM dwParam1, LPARAM dwParam2)
474 /* EPP TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n", */
475 /* EPP dwDevID, hDriv, wMsg, dwParam1, dwParam2); */
484 case DRV_QUERYCONFIGURE: return 0;
485 case DRV_CONFIGURE: MessageBoxA(0, "JoyStick MultiMedia Driver !", "JoyStick Driver", MB_OK); return 1;
486 case DRV_INSTALL: return DRVCNF_RESTART;
487 case DRV_REMOVE: return DRVCNF_RESTART;
489 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);