Release 20030408.
[wine/gsoc-2012-control.git] / dlls / winmm / joystick / joystick.c
blob286044e955742ca8217e0c278c1fcd2bcaed183a
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2 /*
3 * joystick functions
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
23 * NOTES:
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.
35 #include "config.h"
37 #ifdef HAVE_UNISTD_H
38 # include <unistd.h>
39 #endif
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <fcntl.h>
44 #ifdef HAVE_SYS_IOCTL_H
45 #include <sys/ioctl.h>
46 #endif
47 #ifdef HAVE_LINUX_JOYSTICK_H
48 #include <linux/joystick.h>
49 #define JOYDEV "/dev/js%d"
50 #endif
51 #ifdef HAVE_SYS_ERRNO_H
52 #include <sys/errno.h>
53 #endif
55 #include "windef.h"
56 #include "winbase.h"
57 #include "wingdi.h"
58 #include "winuser.h"
59 #include "mmddk.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 {
69 int joyIntf;
70 int in_use;
71 /* Some extra info we need to make this acutaly work under the
72 Linux 2.2 event api.
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 */
82 int x;
83 int y;
84 int z;
85 int r;
86 int u;
87 int v;
88 int buttons;
89 } WINE_JSTCK;
91 static WINE_JSTCK JSTCK_Data[MAXJOYSTICK];
93 /**************************************************************************
94 * JSTCK_drvGet [internal]
96 static WINE_JSTCK* JSTCK_drvGet(DWORD dwDevID)
98 int p;
100 if ((dwDevID - (DWORD)JSTCK_Data) % sizeof(JSTCK_Data[0]) != 0)
101 return NULL;
102 p = (dwDevID - (DWORD)JSTCK_Data) / sizeof(JSTCK_Data[0]);
103 if (p < 0 || p >= MAXJOYSTICK || !((WINE_JSTCK*)dwDevID)->in_use)
104 return NULL;
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)
115 return 0;
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);
129 if (jstck == NULL)
130 return 0;
131 jstck->in_use = 0;
132 if (jstck->dev > 0)
134 close(jstck->dev);
135 jstck->dev = 0;
137 return 1;
140 struct js_status
142 int buttons;
143 int x;
144 int y;
147 /**************************************************************************
148 * JSTCK_OpenDevice [internal]
150 static int JSTCK_OpenDevice(WINE_JSTCK* jstick)
152 char buf[20];
153 int flags;
155 if (jstick->dev > 0)
156 return jstick->dev;
158 sprintf(buf, JOYDEV, jstick->joyIntf);
159 #ifdef HAVE_LINUX_22_JOYSTICK_API
160 flags = O_RDONLY | O_NONBLOCK;
161 #else
162 flags = O_RDONLY;
163 #endif
164 return (jstick->dev = open(buf, flags));
167 /**************************************************************************
168 * JoyGetDevCaps [MMSYSTEM.102]
170 static LONG JSTCK_GetDevCaps(DWORD dwDevID, LPJOYCAPSA lpCaps, DWORD dwSize)
172 WINE_JSTCK* jstck;
173 #ifdef HAVE_LINUX_22_JOYSTICK_API
174 int dev;
175 char nrOfAxes;
176 char nrOfButtons;
177 char identString[MAXPNAMELEN];
178 int driverVersion;
179 #endif
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';
196 lpCaps->wXmin = 0;
197 lpCaps->wXmax = 0xFFFF;
198 lpCaps->wYmin = 0;
199 lpCaps->wYmax = 0xFFFF;
200 lpCaps->wZmin = 0;
201 lpCaps->wZmax = (nrOfAxes >= 3) ? 0xFFFF : 0;
202 #ifdef BODGE_THE_HAT
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;
212 #else
213 lpCaps->wNumButtons = nrOfButtons;
214 #endif
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 */
219 lpCaps->wRmin = 0;
220 lpCaps->wRmax = nrOfAxes >= 4 ? 0xFFFF : 0;
221 lpCaps->wUmin = 0;
222 lpCaps->wUmax = nrOfAxes >= 5 ? 0xFFFF : 0;
223 lpCaps->wVmin = 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, "");
230 lpCaps->wCaps = 0;
231 switch(nrOfAxes) {
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 */
240 #else
241 lpCaps->wMid = MM_MICROSOFT;
242 lpCaps->wPid = MM_PC_JOYSTICK;
243 strcpy(lpCaps->szPname, "WineJoy"); /* joystick product name */
244 lpCaps->wXmin = 0;
245 lpCaps->wXmax = 0xFFFF;
246 lpCaps->wYmin = 0;
247 lpCaps->wYmax = 0xFFFF;
248 lpCaps->wZmin = 0;
249 lpCaps->wZmax = 0;
250 lpCaps->wNumButtons = 2;
251 if (dwSize == sizeof(JOYCAPSA)) {
252 /* complete 95 structure */
253 lpCaps->wRmin = 0;
254 lpCaps->wRmax = 0;
255 lpCaps->wUmin = 0;
256 lpCaps->wUmax = 0;
257 lpCaps->wVmin = 0;
258 lpCaps->wVmax = 0;
259 lpCaps->wCaps = 0;
260 lpCaps->wMaxAxes = 2;
261 lpCaps->wNumAxes = 2;
262 lpCaps->wMaxButtons = 4;
263 strcpy(lpCaps->szRegKey,"");
264 strcpy(lpCaps->szOEMVxD,"");
266 #endif
268 return JOYERR_NOERROR;
271 /**************************************************************************
272 * JSTCK_GetPos [internal]
274 static LONG JSTCK_GetPosEx(DWORD dwDevID, LPJOYINFOEX lpInfo)
276 WINE_JSTCK* jstck;
277 int dev;
278 #ifdef HAVE_LINUX_22_JOYSTICK_API
279 struct js_event ev;
280 #else
281 struct js_status js;
282 int dev_stat;
283 #endif
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)) {
293 switch (ev.number) {
294 case 0:
295 jstck->x = ev.value;
296 break;
297 case 1:
298 jstck->y = ev.value;
299 break;
300 case 2:
301 jstck->z = ev.value;
302 break;
303 case 3:
304 jstck->r = ev.value;
305 break;
306 case 4:
307 jstck->u = ev.value;
308 break;
309 case 5:
310 jstck->v = ev.value;
311 break;
312 default:
313 FIXME("Unknown joystick event '%d'\n", ev.number);
315 } else if (ev.type == (JS_EVENT_BUTTON)) {
316 if (ev.value) {
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;
324 else
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;
344 #ifdef BODGE_THE_HAT
345 else if (lpInfo->dwFlags & JOY_RETURNBUTTONS)
347 if (jstck->r > 0)
348 lpInfo->dwButtons |= 1<<7;
349 else if (jstck->r < 0)
350 lpInfo->dwButtons |= 1<<8;
352 #endif
353 if (lpInfo->dwFlags & JOY_RETURNU)
354 lpInfo->dwUpos = jstck->u + 32767;
355 #ifdef BODGE_THE_HAT
356 else if (lpInfo->dwFlags & JOY_RETURNBUTTONS)
358 if (jstck->u > 0)
359 lpInfo->dwButtons |= 1<<9;
360 else if (jstck->u < 0)
361 lpInfo->dwButtons |= 1<<10;
363 #endif
364 if (lpInfo->dwFlags & JOY_RETURNV)
365 lpInfo->dwVpos = jstck->v + 32767;
367 #else
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 ? */
372 js.x = js.x<<8;
373 js.y = js.y<<8;
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;
380 #endif
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)
398 JOYINFOEX ji;
399 LONG ret;
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;
413 return ret;
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); */
425 switch(wMsg) {
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);
443 default:
444 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
448 #else
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); */
459 switch(wMsg) {
460 case DRV_LOAD:
461 case DRV_FREE:
462 case DRV_OPEN:
463 case DRV_CLOSE:
464 case DRV_ENABLE:
465 case DRV_DISABLE:
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;
470 default:
471 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
475 #endif