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.
44 #ifdef HAVE_SYS_IOCTL_H
45 #include <sys/ioctl.h>
47 #ifdef HAVE_LINUX_JOYSTICK_H
48 #include <linux/joystick.h>
49 #define JOYDEV "/dev/js%d"
51 #ifdef HAVE_SYS_ERRNO_H
52 #include <sys/errno.h>
60 #include "wine/debug.h"
62 WINE_DEFAULT_DEBUG_CHANNEL(joystick
);
64 #ifdef HAVE_LINUX_JOYSTICK_H
66 #define MAXJOYSTICK (JOYSTICKID2 + 1)
68 typedef struct tagWINE_JSTCK
{
71 /* Some extra info we need to make this acutaly work under the
73 First of all, we cannot keep closing and reopening the device file -
74 that blows away the state of the stick device, and we lose events. So, we
75 need to open the low-level device once, and close it when we are done.
77 Secondly, the event API only gives us what's changed. However, Windows apps
78 want the whole state every time, so we have to cache the data.
81 int dev
; /* Linux level device file descriptor */
91 static WINE_JSTCK JSTCK_Data
[MAXJOYSTICK
];
93 /**************************************************************************
94 * JSTCK_drvGet [internal]
96 static WINE_JSTCK
* JSTCK_drvGet(DWORD dwDevID
)
100 if ((dwDevID
- (DWORD
)JSTCK_Data
) % sizeof(JSTCK_Data
[0]) != 0)
102 p
= (dwDevID
- (DWORD
)JSTCK_Data
) / sizeof(JSTCK_Data
[0]);
103 if (p
< 0 || p
>= MAXJOYSTICK
|| !((WINE_JSTCK
*)dwDevID
)->in_use
)
106 return (WINE_JSTCK
*)dwDevID
;
109 /**************************************************************************
110 * JSTCK_drvOpen [internal]
112 static DWORD
JSTCK_drvOpen(LPSTR str
, DWORD dwIntf
)
114 if (dwIntf
>= MAXJOYSTICK
|| JSTCK_Data
[dwIntf
].in_use
)
117 JSTCK_Data
[dwIntf
].joyIntf
= dwIntf
;
118 JSTCK_Data
[dwIntf
].in_use
= 1;
119 return (DWORD
)&JSTCK_Data
[dwIntf
];
122 /**************************************************************************
123 * JSTCK_drvClose [internal]
125 static DWORD
JSTCK_drvClose(DWORD dwDevID
)
127 WINE_JSTCK
* jstck
= JSTCK_drvGet(dwDevID
);
147 /**************************************************************************
148 * JSTCK_OpenDevice [internal]
150 static int JSTCK_OpenDevice(WINE_JSTCK
* jstick
)
158 sprintf(buf
, JOYDEV
, jstick
->joyIntf
);
159 #ifdef HAVE_LINUX_22_JOYSTICK_API
160 flags
= O_RDONLY
| O_NONBLOCK
;
164 return (jstick
->dev
= open(buf
, flags
));
167 /**************************************************************************
168 * JoyGetDevCaps [MMSYSTEM.102]
170 static LONG
JSTCK_GetDevCaps(DWORD dwDevID
, LPJOYCAPSA lpCaps
, DWORD dwSize
)
173 #ifdef HAVE_LINUX_22_JOYSTICK_API
177 char identString
[MAXPNAMELEN
];
181 if ((jstck
= JSTCK_drvGet(dwDevID
)) == NULL
)
182 return MMSYSERR_NODRIVER
;
184 #ifdef HAVE_LINUX_22_JOYSTICK_API
185 if ((dev
= JSTCK_OpenDevice(jstck
)) < 0) return JOYERR_PARMS
;
186 ioctl(dev
, JSIOCGAXES
, &nrOfAxes
);
187 ioctl(dev
, JSIOCGBUTTONS
, &nrOfButtons
);
188 ioctl(dev
, JSIOCGVERSION
, &driverVersion
);
189 ioctl(dev
, JSIOCGNAME(sizeof(identString
)), &identString
);
190 TRACE("Driver: 0x%06x, Name: %s, #Axes: %d, #Buttons: %d\n",
191 driverVersion
, identString
, nrOfAxes
, nrOfButtons
);
192 lpCaps
->wMid
= MM_MICROSOFT
;
193 lpCaps
->wPid
= MM_PC_JOYSTICK
;
194 strncpy(lpCaps
->szPname
, identString
, MAXPNAMELEN
);
195 lpCaps
->szPname
[MAXPNAMELEN
-1] = '\0';
197 lpCaps
->wXmax
= 0xFFFF;
199 lpCaps
->wYmax
= 0xFFFF;
201 lpCaps
->wZmax
= (nrOfAxes
>= 3) ? 0xFFFF : 0;
203 /* HalfLife won't allow you to map an axis event to things like
204 "next weapon" and "use". Linux reports the hat on my stick as
205 axis U and V. So, IFF BODGE_THE_HAT is defined, lie through our
206 teeth and say we have 32 buttons, and we will map the axises to
207 the high buttons. Really, perhaps this should be a registry entry,
208 or even a parameter to the Linux joystick driver (which would completely
209 remove the need for this.)
211 lpCaps
->wNumButtons
= 32;
213 lpCaps
->wNumButtons
= nrOfButtons
;
215 if (dwSize
== sizeof(JOYCAPSA
)) {
216 /* since we suppose ntOfAxes <= 6 in the following code, do it explicitly */
217 if (nrOfAxes
> 6) nrOfAxes
= 6;
218 /* complete 95 structure */
220 lpCaps
->wRmax
= nrOfAxes
>= 4 ? 0xFFFF : 0;
222 lpCaps
->wUmax
= nrOfAxes
>= 5 ? 0xFFFF : 0;
224 lpCaps
->wVmax
= nrOfAxes
>= 6 ? 0xFFFF : 0;
225 lpCaps
->wMaxAxes
= 6; /* same as MS Joystick Driver */
226 lpCaps
->wNumAxes
= nrOfAxes
; /* nr of axes in use */
227 lpCaps
->wMaxButtons
= 32; /* same as MS Joystick Driver */
228 strcpy(lpCaps
->szRegKey
, "");
229 strcpy(lpCaps
->szOEMVxD
, "");
232 case 6: lpCaps
->wCaps
|= JOYCAPS_HASV
;
233 case 5: lpCaps
->wCaps
|= JOYCAPS_HASU
;
234 case 4: lpCaps
->wCaps
|= JOYCAPS_HASR
;
235 case 3: lpCaps
->wCaps
|= JOYCAPS_HASZ
;
236 /* FIXME: don't know how to detect for
237 JOYCAPS_HASPOV, JOYCAPS_POV4DIR, JOYCAPS_POVCTS */
241 lpCaps
->wMid
= MM_MICROSOFT
;
242 lpCaps
->wPid
= MM_PC_JOYSTICK
;
243 strcpy(lpCaps
->szPname
, "WineJoy"); /* joystick product name */
245 lpCaps
->wXmax
= 0xFFFF;
247 lpCaps
->wYmax
= 0xFFFF;
250 lpCaps
->wNumButtons
= 2;
251 if (dwSize
== sizeof(JOYCAPSA
)) {
252 /* complete 95 structure */
260 lpCaps
->wMaxAxes
= 2;
261 lpCaps
->wNumAxes
= 2;
262 lpCaps
->wMaxButtons
= 4;
263 strcpy(lpCaps
->szRegKey
,"");
264 strcpy(lpCaps
->szOEMVxD
,"");
268 return JOYERR_NOERROR
;
271 /**************************************************************************
272 * JSTCK_GetPos [internal]
274 static LONG
JSTCK_GetPosEx(DWORD dwDevID
, LPJOYINFOEX lpInfo
)
278 #ifdef HAVE_LINUX_22_JOYSTICK_API
285 if ((jstck
= JSTCK_drvGet(dwDevID
)) == NULL
)
286 return MMSYSERR_NODRIVER
;
288 if ((dev
= JSTCK_OpenDevice(jstck
)) < 0) return JOYERR_PARMS
;
290 #ifdef HAVE_LINUX_22_JOYSTICK_API
291 while ((read(dev
, &ev
, sizeof(struct js_event
))) > 0) {
292 if (ev
.type
== (JS_EVENT_AXIS
)) {
313 FIXME("Unknown joystick event '%d'\n", ev
.number
);
315 } else if (ev
.type
== (JS_EVENT_BUTTON
)) {
317 jstck
->buttons
|= (1 << ev
.number
);
318 /* FIXME: what to do for this field when
319 * multiple buttons are depressed ?
321 if (lpInfo
->dwFlags
& JOY_RETURNBUTTONS
)
322 lpInfo
->dwButtonNumber
= ev
.number
+ 1;
325 jstck
->buttons
&= ~(1 << ev
.number
);
328 /* EAGAIN is returned when the queue is empty */
329 if (errno
!= EAGAIN
) {
330 /* FIXME: error should not be ignored */
331 ERR("Error while reading joystick state (%s)\n", strerror(errno
));
333 /* Now, copy the cached values into Window's structure... */
334 if (lpInfo
->dwFlags
& JOY_RETURNBUTTONS
)
335 lpInfo
->dwButtons
= jstck
->buttons
;
336 if (lpInfo
->dwFlags
& JOY_RETURNX
)
337 lpInfo
->dwXpos
= jstck
->x
+ 32767;
338 if (lpInfo
->dwFlags
& JOY_RETURNY
)
339 lpInfo
->dwYpos
= jstck
->y
+ 32767;
340 if (lpInfo
->dwFlags
& JOY_RETURNZ
)
341 lpInfo
->dwZpos
= jstck
->z
+ 32767;
342 if (lpInfo
->dwFlags
& JOY_RETURNR
)
343 lpInfo
->dwRpos
= jstck
->r
+ 32767;
345 else if (lpInfo
->dwFlags
& JOY_RETURNBUTTONS
)
348 lpInfo
->dwButtons
|= 1<<7;
349 else if (jstck
->r
< 0)
350 lpInfo
->dwButtons
|= 1<<8;
353 if (lpInfo
->dwFlags
& JOY_RETURNU
)
354 lpInfo
->dwUpos
= jstck
->u
+ 32767;
356 else if (lpInfo
->dwFlags
& JOY_RETURNBUTTONS
)
359 lpInfo
->dwButtons
|= 1<<9;
360 else if (jstck
->u
< 0)
361 lpInfo
->dwButtons
|= 1<<10;
364 if (lpInfo
->dwFlags
& JOY_RETURNV
)
365 lpInfo
->dwVpos
= jstck
->v
+ 32767;
368 dev_stat
= read(dev
, &js
, sizeof(js
));
369 if (dev_stat
!= sizeof(js
)) {
370 return JOYERR_UNPLUGGED
; /* FIXME: perhaps wrong, but what should I return else ? */
374 if (lpInfo
->dwFlags
& JOY_RETURNX
)
375 lpInfo
->dwXpos
= js
.x
; /* FIXME: perhaps multiply it somehow ? */
376 if (lpInfo
->dwFlags
& JOY_RETURNY
)
377 lpInfo
->dwYpos
= js
.y
;
378 if (lpInfo
->dwFlags
& JOY_RETURNBUTTONS
)
379 lpInfo
->dwButtons
= js
.buttons
;
382 TRACE("x: %ld, y: %ld, z: %ld, r: %ld, u: %ld, v: %ld, buttons: 0x%04x, flags: 0x%04x (fd %d)\n",
383 lpInfo
->dwXpos
, lpInfo
->dwYpos
, lpInfo
->dwZpos
,
384 lpInfo
->dwRpos
, lpInfo
->dwUpos
, lpInfo
->dwVpos
,
385 (unsigned int)lpInfo
->dwButtons
,
386 (unsigned int)lpInfo
->dwFlags
,
390 return JOYERR_NOERROR
;
393 /**************************************************************************
394 * JSTCK_GetPos [internal]
396 static LONG
JSTCK_GetPos(DWORD dwDevID
, LPJOYINFO lpInfo
)
401 memset(&ji
, 0, sizeof(ji
));
403 ji
.dwSize
= sizeof(ji
);
404 ji
.dwFlags
= JOY_RETURNX
| JOY_RETURNY
| JOY_RETURNZ
| JOY_RETURNBUTTONS
;
405 ret
= JSTCK_GetPosEx(dwDevID
, &ji
);
406 if (ret
== JOYERR_NOERROR
) {
407 lpInfo
->wXpos
= ji
.dwXpos
;
408 lpInfo
->wYpos
= ji
.dwYpos
;
409 lpInfo
->wZpos
= ji
.dwZpos
;
410 lpInfo
->wButtons
= ji
.dwButtons
;
416 /**************************************************************************
417 * DriverProc (JOYSTICK.@)
419 LONG CALLBACK
JSTCK_DriverProc(DWORD dwDevID
, HDRVR hDriv
, DWORD wMsg
,
420 DWORD dwParam1
, DWORD dwParam2
)
422 /* EPP TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n", */
423 /* EPP dwDevID, hDriv, wMsg, dwParam1, dwParam2); */
426 case DRV_LOAD
: return 1;
427 case DRV_FREE
: return 1;
428 case DRV_OPEN
: return JSTCK_drvOpen((LPSTR
)dwParam1
, dwParam2
);
429 case DRV_CLOSE
: return JSTCK_drvClose(dwDevID
);
430 case DRV_ENABLE
: return 1;
431 case DRV_DISABLE
: return 1;
432 case DRV_QUERYCONFIGURE
: return 1;
433 case DRV_CONFIGURE
: MessageBoxA(0, "JoyStick MultiMedia Driver !", "JoyStick Driver", MB_OK
); return 1;
434 case DRV_INSTALL
: return DRVCNF_RESTART
;
435 case DRV_REMOVE
: return DRVCNF_RESTART
;
437 case JDD_GETNUMDEVS
: return 1;
438 case JDD_GETDEVCAPS
: return JSTCK_GetDevCaps(dwDevID
, (LPJOYCAPSA
)dwParam1
, dwParam2
);
439 case JDD_GETPOS
: return JSTCK_GetPos(dwDevID
, (LPJOYINFO
)dwParam1
);
440 case JDD_SETCALIBRATION
:
441 case JDD_CONFIGCHANGED
: return JOYERR_NOCANDO
;
442 case JDD_GETPOSEX
: return JSTCK_GetPosEx(dwDevID
, (LPJOYINFOEX
)dwParam1
);
444 return DefDriverProc(dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
450 /**************************************************************************
451 * DriverProc (JOYSTICK.@)
453 LONG CALLBACK
JSTCK_DriverProc(DWORD dwDevID
, HDRVR hDriv
, DWORD wMsg
,
454 DWORD dwParam1
, DWORD dwParam2
)
456 /* EPP TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n", */
457 /* EPP dwDevID, hDriv, wMsg, dwParam1, dwParam2); */
466 case DRV_QUERYCONFIGURE
: return 0;
467 case DRV_CONFIGURE
: MessageBoxA(0, "JoyStick MultiMedia Driver !", "JoyStick Driver", MB_OK
); return 1;
468 case DRV_INSTALL
: return DRVCNF_RESTART
;
469 case DRV_REMOVE
: return DRVCNF_RESTART
;
471 return DefDriverProc(dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);