1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
5 * Copyright 1997 Andreas Mohr
6 * Copyright 2000 Wolfgang Schwotzer
7 * Copyright 2002 David Hagood
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 * nearly all joystick functions can be regarded as obsolete,
26 * as Linux (2.1.x) now supports extended joysticks
27 * with a completely new joystick driver interface
28 * new driver's docu says:
29 * "For backward compatibility the old interface is still included,
30 * but will be dropped in the future."
31 * Thus we should implement the new interface and at most keep the old
32 * routines for backward compatibility.
45 #ifdef HAVE_SYS_IOCTL_H
46 #include <sys/ioctl.h>
48 #ifdef HAVE_LINUX_IOCTL_H
49 #include <linux/ioctl.h>
51 #ifdef HAVE_LINUX_JOYSTICK_H
52 #include <linux/joystick.h>
53 #define JOYDEV "/dev/js%d"
55 #ifdef HAVE_SYS_ERRNO_H
56 #include <sys/errno.h>
64 #include "wine/debug.h"
66 WINE_DEFAULT_DEBUG_CHANNEL(joystick);
68 #ifdef HAVE_LINUX_JOYSTICK_H
70 #define MAXJOYSTICK (JOYSTICKID2 + 1)
72 typedef struct tagWINE_JSTCK {
75 /* Some extra info we need to make this acutaly work under the
77 First of all, we cannot keep closing and reopening the device file -
78 that blows away the state of the stick device, and we lose events. So, we
79 need to open the low-level device once, and close it when we are done.
81 Secondly, the event API only gives us what's changed. However, Windows apps
82 want the whole state every time, so we have to cache the data.
85 int dev; /* Linux level device file descriptor */
95 static WINE_JSTCK JSTCK_Data[MAXJOYSTICK];
97 /**************************************************************************
98 * JSTCK_drvGet [internal]
100 static WINE_JSTCK* JSTCK_drvGet(DWORD dwDevID)
104 if ((dwDevID - (DWORD)JSTCK_Data) % sizeof(JSTCK_Data[0]) != 0)
106 p = (dwDevID - (DWORD)JSTCK_Data) / sizeof(JSTCK_Data[0]);
107 if (p < 0 || p >= MAXJOYSTICK || !((WINE_JSTCK*)dwDevID)->in_use)
110 return (WINE_JSTCK*)dwDevID;
113 /**************************************************************************
114 * JSTCK_drvOpen [internal]
116 static DWORD JSTCK_drvOpen(LPSTR str, DWORD dwIntf)
118 if (dwIntf >= MAXJOYSTICK || JSTCK_Data[dwIntf].in_use)
121 JSTCK_Data[dwIntf].joyIntf = dwIntf;
122 JSTCK_Data[dwIntf].in_use = 1;
123 return (DWORD)&JSTCK_Data[dwIntf];
126 /**************************************************************************
127 * JSTCK_drvClose [internal]
129 static DWORD JSTCK_drvClose(DWORD dwDevID)
131 WINE_JSTCK* jstck = JSTCK_drvGet(dwDevID);
151 /**************************************************************************
152 * JSTCK_OpenDevice [internal]
154 static int JSTCK_OpenDevice(WINE_JSTCK* jstick)
162 sprintf(buf, JOYDEV, jstick->joyIntf);
163 #ifdef HAVE_LINUX_22_JOYSTICK_API
164 flags = O_RDONLY | O_NONBLOCK;
168 return (jstick->dev = open(buf, flags));
171 /**************************************************************************
172 * JoyGetDevCaps [MMSYSTEM.102]
174 static LONG JSTCK_GetDevCaps(DWORD dwDevID, LPJOYCAPSA lpCaps, DWORD dwSize)
177 #ifdef HAVE_LINUX_22_JOYSTICK_API
181 char identString[MAXPNAMELEN];
185 if ((jstck = JSTCK_drvGet(dwDevID)) == NULL)
186 return MMSYSERR_NODRIVER;
188 #ifdef HAVE_LINUX_22_JOYSTICK_API
189 if ((dev = JSTCK_OpenDevice(jstck)) < 0) return JOYERR_PARMS;
190 ioctl(dev, JSIOCGAXES, &nrOfAxes);
191 ioctl(dev, JSIOCGBUTTONS, &nrOfButtons);
192 ioctl(dev, JSIOCGVERSION, &driverVersion);
193 ioctl(dev, JSIOCGNAME(sizeof(identString)), &identString);
194 TRACE("Driver: 0x%06x, Name: %s, #Axes: %d, #Buttons: %d\n",
195 driverVersion, identString, nrOfAxes, nrOfButtons);
196 lpCaps->wMid = MM_MICROSOFT;
197 lpCaps->wPid = MM_PC_JOYSTICK;
198 strncpy(lpCaps->szPname, identString, MAXPNAMELEN);
199 lpCaps->szPname[MAXPNAMELEN-1] = '\0';
201 lpCaps->wXmax = 0xFFFF;
203 lpCaps->wYmax = 0xFFFF;
205 lpCaps->wZmax = (nrOfAxes >= 3) ? 0xFFFF : 0;
207 /* HalfLife won't allow you to map an axis event to things like
208 "next weapon" and "use". Linux reports the hat on my stick as
209 axis U and V. So, IFF BODGE_THE_HAT is defined, lie through our
210 teeth and say we have 32 buttons, and we will map the axises to
211 the high buttons. Really, perhaps this should be a registry entry,
212 or even a parameter to the Linux joystick driver (which would completely
213 remove the need for this.)
215 lpCaps->wNumButtons = 32;
217 lpCaps->wNumButtons = nrOfButtons;
219 if (dwSize == sizeof(JOYCAPSA)) {
220 /* since we suppose ntOfAxes <= 6 in the following code, do it explicitly */
221 if (nrOfAxes > 6) nrOfAxes = 6;
222 /* complete 95 structure */
224 lpCaps->wRmax = nrOfAxes >= 4 ? 0xFFFF : 0;
226 lpCaps->wUmax = nrOfAxes >= 5 ? 0xFFFF : 0;
228 lpCaps->wVmax = nrOfAxes >= 6 ? 0xFFFF : 0;
229 lpCaps->wMaxAxes = 6; /* same as MS Joystick Driver */
230 lpCaps->wNumAxes = nrOfAxes; /* nr of axes in use */
231 lpCaps->wMaxButtons = 32; /* same as MS Joystick Driver */
232 strcpy(lpCaps->szRegKey, "");
233 strcpy(lpCaps->szOEMVxD, "");
236 case 6: lpCaps->wCaps |= JOYCAPS_HASV;
237 case 5: lpCaps->wCaps |= JOYCAPS_HASU;
238 case 4: lpCaps->wCaps |= JOYCAPS_HASR;
239 case 3: lpCaps->wCaps |= JOYCAPS_HASZ;
240 /* FIXME: don't know how to detect for
241 JOYCAPS_HASPOV, JOYCAPS_POV4DIR, JOYCAPS_POVCTS */
245 lpCaps->wMid = MM_MICROSOFT;
246 lpCaps->wPid = MM_PC_JOYSTICK;
247 strcpy(lpCaps->szPname, "WineJoy"); /* joystick product name */
249 lpCaps->wXmax = 0xFFFF;
251 lpCaps->wYmax = 0xFFFF;
254 lpCaps->wNumButtons = 2;
255 if (dwSize == sizeof(JOYCAPSA)) {
256 /* complete 95 structure */
264 lpCaps->wMaxAxes = 2;
265 lpCaps->wNumAxes = 2;
266 lpCaps->wMaxButtons = 4;
267 strcpy(lpCaps->szRegKey,"");
268 strcpy(lpCaps->szOEMVxD,"");
272 return JOYERR_NOERROR;
275 /**************************************************************************
276 * JSTCK_GetPos [internal]
278 static LONG JSTCK_GetPosEx(DWORD dwDevID, LPJOYINFOEX lpInfo)
282 #ifdef HAVE_LINUX_22_JOYSTICK_API
289 if ((jstck = JSTCK_drvGet(dwDevID)) == NULL)
290 return MMSYSERR_NODRIVER;
292 if ((dev = JSTCK_OpenDevice(jstck)) < 0) return JOYERR_PARMS;
294 #ifdef HAVE_LINUX_22_JOYSTICK_API
295 while ((read(dev, &ev, sizeof(struct js_event))) > 0) {
296 if (ev.type == (JS_EVENT_AXIS)) {
317 FIXME("Unknown joystick event '%d'\n", ev.number);
319 } else if (ev.type == (JS_EVENT_BUTTON)) {
321 jstck->buttons |= (1 << ev.number);
322 /* FIXME: what to do for this field when
323 * multiple buttons are depressed ?
325 if (lpInfo->dwFlags & JOY_RETURNBUTTONS)
326 lpInfo->dwButtonNumber = ev.number + 1;
329 jstck->buttons &= ~(1 << ev.number);
332 /* EAGAIN is returned when the queue is empty */
333 if (errno != EAGAIN) {
334 /* FIXME: error should not be ignored */
335 ERR("Error while reading joystick state (%s)\n", strerror(errno));
337 /* Now, copy the cached values into Window's structure... */
338 if (lpInfo->dwFlags & JOY_RETURNBUTTONS)
339 lpInfo->dwButtons = jstck->buttons;
340 if (lpInfo->dwFlags & JOY_RETURNX)
341 lpInfo->dwXpos = jstck->x + 32767;
342 if (lpInfo->dwFlags & JOY_RETURNY)
343 lpInfo->dwYpos = jstck->y + 32767;
344 if (lpInfo->dwFlags & JOY_RETURNZ)
345 lpInfo->dwZpos = jstck->z + 32767;
346 if (lpInfo->dwFlags & JOY_RETURNR)
347 lpInfo->dwRpos = jstck->r + 32767;
349 else if (lpInfo->dwFlags & JOY_RETURNBUTTONS)
352 lpInfo->dwButtons |= 1<<7;
353 else if (jstck->r < 0)
354 lpInfo->dwButtons |= 1<<8;
357 if (lpInfo->dwFlags & JOY_RETURNU)
358 lpInfo->dwUpos = jstck->u + 32767;
360 else if (lpInfo->dwFlags & JOY_RETURNBUTTONS)
363 lpInfo->dwButtons |= 1<<9;
364 else if (jstck->u < 0)
365 lpInfo->dwButtons |= 1<<10;
368 if (lpInfo->dwFlags & JOY_RETURNV)
369 lpInfo->dwVpos = jstck->v + 32767;
372 dev_stat = read(dev, &js, sizeof(js));
373 if (dev_stat != sizeof(js)) {
374 return JOYERR_UNPLUGGED; /* FIXME: perhaps wrong, but what should I return else ? */
378 if (lpInfo->dwFlags & JOY_RETURNX)
379 lpInfo->dwXpos = js.x; /* FIXME: perhaps multiply it somehow ? */
380 if (lpInfo->dwFlags & JOY_RETURNY)
381 lpInfo->dwYpos = js.y;
382 if (lpInfo->dwFlags & JOY_RETURNBUTTONS)
383 lpInfo->dwButtons = js.buttons;
386 TRACE("x: %ld, y: %ld, z: %ld, r: %ld, u: %ld, v: %ld, buttons: 0x%04x, flags: 0x%04x (fd %d)\n",
387 lpInfo->dwXpos, lpInfo->dwYpos, lpInfo->dwZpos,
388 lpInfo->dwRpos, lpInfo->dwUpos, lpInfo->dwVpos,
389 (unsigned int)lpInfo->dwButtons,
390 (unsigned int)lpInfo->dwFlags,
394 return JOYERR_NOERROR;
397 /**************************************************************************
398 * JSTCK_GetPos [internal]
400 static LONG JSTCK_GetPos(DWORD dwDevID, LPJOYINFO lpInfo)
405 memset(&ji, 0, sizeof(ji));
407 ji.dwSize = sizeof(ji);
408 ji.dwFlags = JOY_RETURNX | JOY_RETURNY | JOY_RETURNZ | JOY_RETURNBUTTONS;
409 ret = JSTCK_GetPosEx(dwDevID, &ji);
410 if (ret == JOYERR_NOERROR) {
411 lpInfo->wXpos = ji.dwXpos;
412 lpInfo->wYpos = ji.dwYpos;
413 lpInfo->wZpos = ji.dwZpos;
414 lpInfo->wButtons = ji.dwButtons;
420 /**************************************************************************
421 * DriverProc (JOYSTICK.@)
423 LONG CALLBACK JSTCK_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
424 DWORD dwParam1, DWORD dwParam2)
426 /* EPP TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n", */
427 /* EPP dwDevID, hDriv, wMsg, dwParam1, dwParam2); */
430 case DRV_LOAD: return 1;
431 case DRV_FREE: return 1;
432 case DRV_OPEN: return JSTCK_drvOpen((LPSTR)dwParam1, dwParam2);
433 case DRV_CLOSE: return JSTCK_drvClose(dwDevID);
434 case DRV_ENABLE: return 1;
435 case DRV_DISABLE: return 1;
436 case DRV_QUERYCONFIGURE: return 1;
437 case DRV_CONFIGURE: MessageBoxA(0, "JoyStick MultiMedia Driver !", "JoyStick Driver", MB_OK); return 1;
438 case DRV_INSTALL: return DRVCNF_RESTART;
439 case DRV_REMOVE: return DRVCNF_RESTART;
441 case JDD_GETNUMDEVS: return 1;
442 case JDD_GETDEVCAPS: return JSTCK_GetDevCaps(dwDevID, (LPJOYCAPSA)dwParam1, dwParam2);
443 case JDD_GETPOS: return JSTCK_GetPos(dwDevID, (LPJOYINFO)dwParam1);
444 case JDD_SETCALIBRATION:
445 case JDD_CONFIGCHANGED: return JOYERR_NOCANDO;
446 case JDD_GETPOSEX: return JSTCK_GetPosEx(dwDevID, (LPJOYINFOEX)dwParam1);
448 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
454 /**************************************************************************
455 * DriverProc (JOYSTICK.@)
457 LONG CALLBACK JSTCK_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
458 DWORD dwParam1, DWORD dwParam2)
460 /* EPP TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n", */
461 /* EPP dwDevID, hDriv, wMsg, dwParam1, dwParam2); */
470 case DRV_QUERYCONFIGURE: return 0;
471 case DRV_CONFIGURE: MessageBoxA(0, "JoyStick MultiMedia Driver !", "JoyStick Driver", MB_OK); return 1;
472 case DRV_INSTALL: return DRVCNF_RESTART;
473 case DRV_REMOVE: return DRVCNF_RESTART;
475 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);