Reverse the order for deleting the items in resetcontent to correctly
[wine] / dlls / winmm / joystick / joystick.c
1 /*
2  * joystick functions
3  *
4  * Copyright 1997 Andreas Mohr
5  * Copyright 2000 Wolfgang Schwotzer
6  * Copyright 2002 David Hagood
7  *
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.
12  *
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.
17  *
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
21  *
22  * NOTES:
23  *
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.
36  */
37
38 #include "config.h"
39
40 #ifdef HAVE_UNISTD_H
41 # include <unistd.h>
42 #endif
43 #include <stdarg.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <fcntl.h>
48 #ifdef HAVE_SYS_IOCTL_H
49 #include <sys/ioctl.h>
50 #endif
51 #ifdef HAVE_LINUX_IOCTL_H
52 #include <linux/ioctl.h>
53 #endif
54 #ifdef HAVE_LINUX_JOYSTICK_H
55 #include <linux/joystick.h>
56 #define JOYDEV "/dev/js%d"
57 #endif
58 #ifdef HAVE_SYS_ERRNO_H
59 #include <sys/errno.h>
60 #endif
61
62 #include "windef.h"
63 #include "winbase.h"
64 #include "wingdi.h"
65 #include "winuser.h"
66 #include "winnls.h"
67 #include "mmddk.h"
68 #include "wine/debug.h"
69
70 WINE_DEFAULT_DEBUG_CHANNEL(joystick);
71
72 #ifdef HAVE_LINUX_JOYSTICK_H
73
74 #define MAXJOYSTICK     (JOYSTICKID2 + 1)
75
76 typedef struct tagWINE_JSTCK {
77     int         joyIntf;
78     int         in_use;
79     /* Some extra info we need to make this acutaly work under the
80        Linux 2.2 event api.
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.
84
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.
87     */
88
89     int         dev; /* Linux level device file descriptor */
90     int         x;
91     int         y;
92     int         z;
93     int         r;
94     int         u;
95     int         v;
96     int         buttons;
97 } WINE_JSTCK;
98
99 static  WINE_JSTCK      JSTCK_Data[MAXJOYSTICK];
100
101 /**************************************************************************
102  *                              JSTCK_drvGet                    [internal]
103  */
104 static  WINE_JSTCK*     JSTCK_drvGet(DWORD dwDevID)
105 {
106     int p;
107
108     if ((dwDevID - (DWORD)JSTCK_Data) % sizeof(JSTCK_Data[0]) != 0)
109         return NULL;
110     p = (dwDevID - (DWORD)JSTCK_Data) / sizeof(JSTCK_Data[0]);
111     if (p < 0 || p >= MAXJOYSTICK || !((WINE_JSTCK*)dwDevID)->in_use)
112         return NULL;
113
114     return (WINE_JSTCK*)dwDevID;
115 }
116
117 /**************************************************************************
118  *                              JSTCK_drvOpen                   [internal]
119  */
120 static  DWORD   JSTCK_drvOpen(LPSTR str, DWORD dwIntf)
121 {
122     if (dwIntf >= MAXJOYSTICK || JSTCK_Data[dwIntf].in_use)
123         return 0;
124
125     JSTCK_Data[dwIntf].joyIntf = dwIntf;
126     JSTCK_Data[dwIntf].in_use = 1;
127     return (DWORD)&JSTCK_Data[dwIntf];
128 }
129
130 /**************************************************************************
131  *                              JSTCK_drvClose                  [internal]
132  */
133 static  DWORD   JSTCK_drvClose(DWORD dwDevID)
134 {
135     WINE_JSTCK* jstck = JSTCK_drvGet(dwDevID);
136
137     if (jstck == NULL)
138         return 0;
139     jstck->in_use = 0;
140     if (jstck->dev > 0)
141     {
142        close(jstck->dev);
143        jstck->dev = 0;
144     }
145     return 1;
146 }
147
148 struct js_status
149 {
150     int buttons;
151     int x;
152     int y;
153 };
154
155 /**************************************************************************
156  *                              JSTCK_OpenDevice           [internal]
157  */
158 static  int     JSTCK_OpenDevice(WINE_JSTCK* jstick)
159 {
160     char        buf[20];
161     int         flags;
162
163     if (jstick->dev > 0)
164       return jstick->dev;
165
166     sprintf(buf, JOYDEV, jstick->joyIntf);
167 #ifdef HAVE_LINUX_22_JOYSTICK_API
168     flags = O_RDONLY | O_NONBLOCK;
169 #else
170     flags = O_RDONLY;
171 #endif
172     return (jstick->dev = open(buf, flags));
173 }
174
175
176 /**************************************************************************
177  *                              JoyGetDevCaps           [MMSYSTEM.102]
178  */
179 static  LONG    JSTCK_GetDevCaps(DWORD dwDevID, LPJOYCAPSW lpCaps, DWORD dwSize)
180 {
181     WINE_JSTCK* jstck;
182 #ifdef HAVE_LINUX_22_JOYSTICK_API
183     int         dev;
184     char        nrOfAxes;
185     char        nrOfButtons;
186     char        identString[MAXPNAMELEN];
187     int         driverVersion;
188 #else
189 static const WCHAR ini[] = {'W','i','n','e',' ','J','o','y','s','t','i','c','k',' ','D','r','i','v','e','r',0};
190 #endif
191
192     if ((jstck = JSTCK_drvGet(dwDevID)) == NULL)
193         return MMSYSERR_NODRIVER;
194
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';
207     lpCaps->wXmin = 0;
208     lpCaps->wXmax = 0xFFFF;
209     lpCaps->wYmin = 0;
210     lpCaps->wYmax = 0xFFFF;
211     lpCaps->wZmin = 0;
212     lpCaps->wZmax = (nrOfAxes >= 3) ? 0xFFFF : 0;
213 #ifdef BODGE_THE_HAT
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.)
221     */
222     lpCaps->wNumButtons = 32;
223 #else
224     lpCaps->wNumButtons = nrOfButtons;
225 #endif
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 */
230         lpCaps->wRmin = 0;
231         lpCaps->wRmax = nrOfAxes >= 4 ? 0xFFFF : 0;
232         lpCaps->wUmin = 0;
233         lpCaps->wUmax = nrOfAxes >= 5 ? 0xFFFF : 0;
234         lpCaps->wVmin = 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;
241         lpCaps->wCaps = 0;
242         switch(nrOfAxes) {
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 */
249         }
250     }
251 #else
252     lpCaps->wMid = MM_MICROSOFT;
253     lpCaps->wPid = MM_PC_JOYSTICK;
254     strcpyW(lpCaps->szPname, ini); /* joystick product name */
255     lpCaps->wXmin = 0;
256     lpCaps->wXmax = 0xFFFF;
257     lpCaps->wYmin = 0;
258     lpCaps->wYmax = 0xFFFF;
259     lpCaps->wZmin = 0;
260     lpCaps->wZmax = 0;
261     lpCaps->wNumButtons = 2;
262     if (dwSize == sizeof(JOYCAPSW)) {
263         /* complete 95 structure */
264         lpCaps->wRmin = 0;
265         lpCaps->wRmax = 0;
266         lpCaps->wUmin = 0;
267         lpCaps->wUmax = 0;
268         lpCaps->wVmin = 0;
269         lpCaps->wVmax = 0;
270         lpCaps->wCaps = 0;
271         lpCaps->wMaxAxes = 2;
272         lpCaps->wNumAxes = 2;
273         lpCaps->wMaxButtons = 4;
274         lpCaps->szRegKey[0] = 0;
275         lpCaps->szOEMVxD[0] = 0;
276     }
277 #endif
278
279     return JOYERR_NOERROR;
280 }
281
282 /**************************************************************************
283  *                              JSTCK_GetPos                    [internal]
284  */
285 static LONG     JSTCK_GetPosEx(DWORD dwDevID, LPJOYINFOEX lpInfo)
286 {
287     WINE_JSTCK*         jstck;
288     int                 dev;
289 #ifdef HAVE_LINUX_22_JOYSTICK_API
290     struct js_event     ev;
291 #else
292     struct js_status    js;
293     int                 dev_stat;
294 #endif
295
296     if ((jstck = JSTCK_drvGet(dwDevID)) == NULL)
297         return MMSYSERR_NODRIVER;
298
299     if ((dev = JSTCK_OpenDevice(jstck)) < 0) return JOYERR_PARMS;
300
301 #ifdef HAVE_LINUX_22_JOYSTICK_API
302     while ((read(dev, &ev, sizeof(struct js_event))) > 0) {
303         if (ev.type == (JS_EVENT_AXIS)) {
304             switch (ev.number) {
305             case 0:
306                 jstck->x = ev.value;
307                 break;
308             case 1:
309                 jstck->y = ev.value;
310                 break;
311             case 2:
312                 jstck->z = ev.value;
313                 break;
314             case 3:
315                 jstck->r = ev.value;
316                 break;
317             case 4:
318                 jstck->u = ev.value;
319                 break;
320             case 5:
321                 jstck->v = ev.value;
322                 break;
323             default:
324                 FIXME("Unknown joystick event '%d'\n", ev.number);
325             }
326         } else if (ev.type == (JS_EVENT_BUTTON)) {
327             if (ev.value) {
328                     jstck->buttons |= (1 << ev.number);
329                     /* FIXME: what to do for this field when
330                      * multiple buttons are depressed ?
331                      */
332                     if (lpInfo->dwFlags & JOY_RETURNBUTTONS)
333                        lpInfo->dwButtonNumber = ev.number + 1;
334                 }
335              else
336                jstck->buttons  &= ~(1 << ev.number);
337         }
338     }
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));
343     }
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;
355     #ifdef BODGE_THE_HAT
356     else if (lpInfo->dwFlags & JOY_RETURNBUTTONS)
357     {
358          if (jstck->r > 0)
359             lpInfo->dwButtons |= 1<<7;
360          else if (jstck->r < 0)
361             lpInfo->dwButtons |= 1<<8;
362     }
363     #endif
364     if (lpInfo->dwFlags & JOY_RETURNU)
365         lpInfo->dwUpos   = jstck->u + 32767;
366     #ifdef BODGE_THE_HAT
367     else if (lpInfo->dwFlags & JOY_RETURNBUTTONS)
368     {
369        if (jstck->u > 0)
370           lpInfo->dwButtons |= 1<<9;
371         else if (jstck->u < 0)
372           lpInfo->dwButtons |= 1<<10;
373     }
374     #endif
375     if (lpInfo->dwFlags & JOY_RETURNV)
376        lpInfo->dwVpos   = jstck->v + 32767;
377
378 #else
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 ? */
382     }
383     js.x = js.x<<8;
384     js.y = js.y<<8;
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;
391 #endif
392
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,
398           dev
399          );
400
401     return JOYERR_NOERROR;
402 }
403
404 /**************************************************************************
405  *                              JSTCK_GetPos                    [internal]
406  */
407 static LONG     JSTCK_GetPos(DWORD dwDevID, LPJOYINFO lpInfo)
408 {
409     JOYINFOEX   ji;
410     LONG        ret;
411
412     memset(&ji, 0, sizeof(ji));
413
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;
422     }
423
424     return ret;
425 }
426
427 /**************************************************************************
428  *                              DriverProc (JOYSTICK.@)
429  */
430 LONG CALLBACK   JSTCK_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
431                                  DWORD dwParam1, DWORD dwParam2)
432 {
433     /* EPP     TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n",  */
434     /* EPP        dwDevID, hDriv, wMsg, dwParam1, dwParam2); */
435
436     switch(wMsg) {
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;
447
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);
454     default:
455         return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
456     }
457 }
458
459 #else
460
461 /**************************************************************************
462  *                              DriverProc (JOYSTICK.@)
463  */
464 LONG CALLBACK   JSTCK_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
465                                  DWORD dwParam1, DWORD dwParam2)
466 {
467     /* EPP     TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n",  */
468     /* EPP        dwDevID, hDriv, wMsg, dwParam1, dwParam2); */
469
470     switch(wMsg) {
471     case DRV_LOAD:
472     case DRV_FREE:
473     case DRV_OPEN:
474     case DRV_CLOSE:
475     case DRV_ENABLE:
476     case DRV_DISABLE:
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;
481     default:
482         return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
483     }
484 }
485
486 #endif