Spelling fixes.
[wine] / dlls / winejoystick.drv / 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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 documentation 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 #include "wine/port.h"
40
41 #ifdef HAVE_UNISTD_H
42 # include <unistd.h>
43 #endif
44 #include <stdarg.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <fcntl.h>
49 #ifdef HAVE_SYS_IOCTL_H
50 #include <sys/ioctl.h>
51 #endif
52 #ifdef HAVE_LINUX_IOCTL_H
53 #include <linux/ioctl.h>
54 #endif
55 #ifdef HAVE_LINUX_JOYSTICK_H
56 #include <linux/joystick.h>
57 #ifdef SW_MAX
58 #undef SW_MAX
59 #endif
60 #define JOYDEV_NEW "/dev/input/js%d"
61 #define JOYDEV_OLD "/dev/js%d"
62 #endif
63 #ifdef HAVE_SYS_ERRNO_H
64 #include <sys/errno.h>
65 #endif
66
67 #include "windef.h"
68 #include "winbase.h"
69 #include "wingdi.h"
70 #include "winuser.h"
71 #include "winnls.h"
72 #include "mmddk.h"
73 #include "wine/debug.h"
74
75 #include "wine/unicode.h"
76
77 WINE_DEFAULT_DEBUG_CHANNEL(joystick);
78
79 #ifdef HAVE_LINUX_JOYSTICK_H
80
81 #define MAXJOYSTICK     (JOYSTICKID2 + 1)
82
83 typedef struct tagWINE_JSTCK {
84     int         joyIntf;
85     int         in_use;
86     /* Some extra info we need to make this actually work under the
87        Linux 2.2 event api.
88        First of all, we cannot keep closing and reopening the device file -
89        that blows away the state of the stick device, and we lose events. So, we
90        need to open the low-level device once, and close it when we are done.
91
92        Secondly, the event API only gives us what's changed. However, Windows apps
93        want the whole state every time, so we have to cache the data.
94     */
95
96     int         dev; /* Linux level device file descriptor */
97     int         x;
98     int         y;
99     int         z;
100     int         r;
101     int         u;
102     int         v;
103     int         buttons;
104 } WINE_JSTCK;
105
106 static  WINE_JSTCK      JSTCK_Data[MAXJOYSTICK];
107
108 /**************************************************************************
109  *                              JSTCK_drvGet                    [internal]
110  */
111 static WINE_JSTCK *JSTCK_drvGet(DWORD_PTR dwDevID)
112 {
113     int p;
114
115     if ((dwDevID - (DWORD_PTR)JSTCK_Data) % sizeof(JSTCK_Data[0]) != 0)
116         return NULL;
117     p = (dwDevID - (DWORD_PTR)JSTCK_Data) / sizeof(JSTCK_Data[0]);
118     if (p < 0 || p >= MAXJOYSTICK || !((WINE_JSTCK*)dwDevID)->in_use)
119         return NULL;
120
121     return (WINE_JSTCK*)dwDevID;
122 }
123
124 /**************************************************************************
125  *                              JSTCK_drvOpen                   [internal]
126  */
127 static LRESULT JSTCK_drvOpen(LPSTR str, DWORD dwIntf)
128 {
129     if (dwIntf >= MAXJOYSTICK || JSTCK_Data[dwIntf].in_use)
130         return 0;
131
132     JSTCK_Data[dwIntf].joyIntf = dwIntf;
133     JSTCK_Data[dwIntf].in_use = 1;
134     return (LRESULT)&JSTCK_Data[dwIntf];
135 }
136
137 /**************************************************************************
138  *                              JSTCK_drvClose                  [internal]
139  */
140 static LRESULT JSTCK_drvClose(DWORD_PTR dwDevID)
141 {
142     WINE_JSTCK* jstck = JSTCK_drvGet(dwDevID);
143
144     if (jstck == NULL)
145         return 0;
146     jstck->in_use = 0;
147     if (jstck->dev > 0)
148     {
149        close(jstck->dev);
150        jstck->dev = 0;
151     }
152     return 1;
153 }
154
155 struct js_status
156 {
157     int buttons;
158     int x;
159     int y;
160 };
161
162 /**************************************************************************
163  *                              JSTCK_OpenDevice           [internal]
164  */
165 static  int     JSTCK_OpenDevice(WINE_JSTCK* jstick)
166 {
167     char        buf[20];
168     int         flags;
169
170     if (jstick->dev > 0)
171       return jstick->dev;
172
173     sprintf(buf, JOYDEV_NEW, jstick->joyIntf);
174 #ifdef HAVE_LINUX_22_JOYSTICK_API
175     flags = O_RDONLY | O_NONBLOCK;
176 #else
177     flags = O_RDONLY;
178 #endif
179     if ((jstick->dev = open(buf, flags)) < 0) {
180         sprintf(buf, JOYDEV_OLD, jstick->joyIntf);
181     }
182     return (jstick->dev = open(buf, flags));
183 }
184
185
186 /**************************************************************************
187  *                              JoyGetDevCaps           [MMSYSTEM.102]
188  */
189 static LRESULT JSTCK_GetDevCaps(DWORD_PTR dwDevID, LPJOYCAPSW lpCaps, DWORD dwSize)
190 {
191     WINE_JSTCK* jstck;
192 #ifdef HAVE_LINUX_22_JOYSTICK_API
193     int         dev;
194     char        nrOfAxes;
195     char        nrOfButtons;
196     char        identString[MAXPNAMELEN];
197     int         driverVersion;
198 #else
199 static const WCHAR ini[] = {'W','i','n','e',' ','J','o','y','s','t','i','c','k',' ','D','r','i','v','e','r',0};
200 #endif
201
202     if ((jstck = JSTCK_drvGet(dwDevID)) == NULL)
203         return MMSYSERR_NODRIVER;
204
205 #ifdef HAVE_LINUX_22_JOYSTICK_API
206     if ((dev = JSTCK_OpenDevice(jstck)) < 0) return JOYERR_PARMS;
207     ioctl(dev, JSIOCGAXES, &nrOfAxes);
208     ioctl(dev, JSIOCGBUTTONS, &nrOfButtons);
209     ioctl(dev, JSIOCGVERSION, &driverVersion);
210     ioctl(dev, JSIOCGNAME(sizeof(identString)), &identString);
211     TRACE("Driver: 0x%06x, Name: %s, #Axes: %d, #Buttons: %d\n",
212           driverVersion, identString, nrOfAxes, nrOfButtons);
213     lpCaps->wMid = MM_MICROSOFT;
214     lpCaps->wPid = MM_PC_JOYSTICK;
215     MultiByteToWideChar(CP_ACP, 0, identString, -1, lpCaps->szPname, MAXPNAMELEN);
216     lpCaps->szPname[MAXPNAMELEN-1] = '\0';
217     lpCaps->wXmin = 0;
218     lpCaps->wXmax = 0xFFFF;
219     lpCaps->wYmin = 0;
220     lpCaps->wYmax = 0xFFFF;
221     lpCaps->wZmin = 0;
222     lpCaps->wZmax = (nrOfAxes >= 3) ? 0xFFFF : 0;
223 #ifdef BODGE_THE_HAT
224     /* HalfLife won't allow you to map an axis event to things like
225        "next weapon" and "use". Linux reports the hat on my stick as
226        axis U and V. So, IFF BODGE_THE_HAT is defined, lie through our
227        teeth and say we have 32 buttons, and we will map the axises to
228        the high buttons. Really, perhaps this should be a registry entry,
229        or even a parameter to the Linux joystick driver (which would completely
230        remove the need for this.)
231     */
232     lpCaps->wNumButtons = 32;
233 #else
234     lpCaps->wNumButtons = nrOfButtons;
235 #endif
236     if (dwSize == sizeof(JOYCAPSW)) {
237         /* since we suppose ntOfAxes <= 6 in the following code, do it explicitly */
238         if (nrOfAxes > 6) nrOfAxes = 6;
239         /* complete 95 structure */
240         lpCaps->wRmin = 0;
241         lpCaps->wRmax = nrOfAxes >= 4 ? 0xFFFF : 0;
242         lpCaps->wUmin = 0;
243         lpCaps->wUmax = nrOfAxes >= 5 ? 0xFFFF : 0;
244         lpCaps->wVmin = 0;
245         lpCaps->wVmax = nrOfAxes >= 6 ? 0xFFFF : 0;
246         lpCaps->wMaxAxes = 6; /* same as MS Joystick Driver */
247         lpCaps->wNumAxes = nrOfAxes; /* nr of axes in use */
248         lpCaps->wMaxButtons = 32; /* same as MS Joystick Driver */
249         lpCaps->szRegKey[0] = 0;
250         lpCaps->szOEMVxD[0] = 0;
251         lpCaps->wCaps = 0;
252         switch(nrOfAxes) {
253         case 6: lpCaps->wCaps |= JOYCAPS_HASV;
254         case 5: lpCaps->wCaps |= JOYCAPS_HASU;
255         case 4: lpCaps->wCaps |= JOYCAPS_HASR;
256         case 3: lpCaps->wCaps |= JOYCAPS_HASZ;
257             /* FIXME: don't know how to detect for
258                JOYCAPS_HASPOV, JOYCAPS_POV4DIR, JOYCAPS_POVCTS */
259         }
260     }
261 #else
262     lpCaps->wMid = MM_MICROSOFT;
263     lpCaps->wPid = MM_PC_JOYSTICK;
264     strcpyW(lpCaps->szPname, ini); /* joystick product name */
265     lpCaps->wXmin = 0;
266     lpCaps->wXmax = 0xFFFF;
267     lpCaps->wYmin = 0;
268     lpCaps->wYmax = 0xFFFF;
269     lpCaps->wZmin = 0;
270     lpCaps->wZmax = 0;
271     lpCaps->wNumButtons = 2;
272     if (dwSize == sizeof(JOYCAPSW)) {
273         /* complete 95 structure */
274         lpCaps->wRmin = 0;
275         lpCaps->wRmax = 0;
276         lpCaps->wUmin = 0;
277         lpCaps->wUmax = 0;
278         lpCaps->wVmin = 0;
279         lpCaps->wVmax = 0;
280         lpCaps->wCaps = 0;
281         lpCaps->wMaxAxes = 2;
282         lpCaps->wNumAxes = 2;
283         lpCaps->wMaxButtons = 4;
284         lpCaps->szRegKey[0] = 0;
285         lpCaps->szOEMVxD[0] = 0;
286     }
287 #endif
288
289     return JOYERR_NOERROR;
290 }
291
292 /**************************************************************************
293  *                              JSTCK_GetPos                    [internal]
294  */
295 static LRESULT JSTCK_GetPosEx(DWORD_PTR dwDevID, LPJOYINFOEX lpInfo)
296 {
297     WINE_JSTCK*         jstck;
298     int                 dev;
299 #ifdef HAVE_LINUX_22_JOYSTICK_API
300     struct js_event     ev;
301 #else
302     struct js_status    js;
303     int                 dev_stat;
304 #endif
305
306     if ((jstck = JSTCK_drvGet(dwDevID)) == NULL)
307         return MMSYSERR_NODRIVER;
308
309     if ((dev = JSTCK_OpenDevice(jstck)) < 0) return JOYERR_PARMS;
310
311 #ifdef HAVE_LINUX_22_JOYSTICK_API
312     while ((read(dev, &ev, sizeof(struct js_event))) > 0) {
313         if (ev.type == (JS_EVENT_AXIS)) {
314             switch (ev.number) {
315             case 0:
316                 jstck->x = ev.value;
317                 break;
318             case 1:
319                 jstck->y = ev.value;
320                 break;
321             case 2:
322                 jstck->z = ev.value;
323                 break;
324             case 3:
325                 jstck->r = ev.value;
326                 break;
327             case 4:
328                 jstck->u = ev.value;
329                 break;
330             case 5:
331                 jstck->v = ev.value;
332                 break;
333             default:
334                 FIXME("Unknown joystick event '%d'\n", ev.number);
335             }
336         } else if (ev.type == (JS_EVENT_BUTTON)) {
337             if (ev.value) {
338                     jstck->buttons |= (1 << ev.number);
339                     /* FIXME: what to do for this field when
340                      * multiple buttons are depressed ?
341                      */
342                     if (lpInfo->dwFlags & JOY_RETURNBUTTONS)
343                        lpInfo->dwButtonNumber = ev.number + 1;
344                 }
345              else
346                jstck->buttons  &= ~(1 << ev.number);
347         }
348     }
349     /* EAGAIN is returned when the queue is empty */
350     if (errno != EAGAIN) {
351         /* FIXME: error should not be ignored */
352         ERR("Error while reading joystick state (%s)\n", strerror(errno));
353     }
354     /* Now, copy the cached values into Window's structure... */
355     if (lpInfo->dwFlags & JOY_RETURNBUTTONS)
356        lpInfo->dwButtons = jstck->buttons;
357     if (lpInfo->dwFlags & JOY_RETURNX)
358        lpInfo->dwXpos   = jstck->x + 32767;
359     if (lpInfo->dwFlags & JOY_RETURNY)
360        lpInfo->dwYpos   = jstck->y + 32767;
361     if (lpInfo->dwFlags & JOY_RETURNZ)
362        lpInfo->dwZpos   = jstck->z + 32767;
363     if (lpInfo->dwFlags & JOY_RETURNR)
364        lpInfo->dwRpos   = jstck->r + 32767;
365 # ifdef BODGE_THE_HAT
366     else if (lpInfo->dwFlags & JOY_RETURNBUTTONS)
367     {
368          if (jstck->r > 0)
369             lpInfo->dwButtons |= 1<<7;
370          else if (jstck->r < 0)
371             lpInfo->dwButtons |= 1<<8;
372     }
373 # endif
374     if (lpInfo->dwFlags & JOY_RETURNU)
375         lpInfo->dwUpos   = jstck->u + 32767;
376 # ifdef BODGE_THE_HAT
377     else if (lpInfo->dwFlags & JOY_RETURNBUTTONS)
378     {
379        if (jstck->u > 0)
380           lpInfo->dwButtons |= 1<<9;
381         else if (jstck->u < 0)
382           lpInfo->dwButtons |= 1<<10;
383     }
384 # endif
385     if (lpInfo->dwFlags & JOY_RETURNV)
386        lpInfo->dwVpos   = jstck->v + 32767;
387
388 #else
389     dev_stat = read(dev, &js, sizeof(js));
390     if (dev_stat != sizeof(js)) {
391         return JOYERR_UNPLUGGED; /* FIXME: perhaps wrong, but what should I return else ? */
392     }
393     js.x = js.x<<8;
394     js.y = js.y<<8;
395     if (lpInfo->dwFlags & JOY_RETURNX)
396         lpInfo->dwXpos = js.x;   /* FIXME: perhaps multiply it somehow ? */
397     if (lpInfo->dwFlags & JOY_RETURNY)
398         lpInfo->dwYpos = js.y;
399     if (lpInfo->dwFlags & JOY_RETURNBUTTONS)
400         lpInfo->dwButtons = js.buttons;
401 #endif
402
403     TRACE("x: %d, y: %d, z: %d, r: %d, u: %d, v: %d, buttons: 0x%04x, flags: 0x%04x (fd %d)\n",
404           lpInfo->dwXpos, lpInfo->dwYpos, lpInfo->dwZpos,
405           lpInfo->dwRpos, lpInfo->dwUpos, lpInfo->dwVpos,
406           (unsigned int)lpInfo->dwButtons,
407           (unsigned int)lpInfo->dwFlags,
408           dev
409          );
410
411     return JOYERR_NOERROR;
412 }
413
414 /**************************************************************************
415  *                              JSTCK_GetPos                    [internal]
416  */
417 static LRESULT JSTCK_GetPos(DWORD_PTR dwDevID, LPJOYINFO lpInfo)
418 {
419     JOYINFOEX   ji;
420     LONG        ret;
421
422     memset(&ji, 0, sizeof(ji));
423
424     ji.dwSize = sizeof(ji);
425     ji.dwFlags = JOY_RETURNX | JOY_RETURNY | JOY_RETURNZ | JOY_RETURNBUTTONS;
426     ret = JSTCK_GetPosEx(dwDevID, &ji);
427     if (ret == JOYERR_NOERROR)  {
428         lpInfo->wXpos    = ji.dwXpos;
429         lpInfo->wYpos    = ji.dwYpos;
430         lpInfo->wZpos    = ji.dwZpos;
431         lpInfo->wButtons = ji.dwButtons;
432     }
433
434     return ret;
435 }
436
437 /**************************************************************************
438  *                              DriverProc (JOYSTICK.@)
439  */
440 LRESULT CALLBACK JSTCK_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
441                                   LPARAM dwParam1, LPARAM dwParam2)
442 {
443     /* EPP     TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n",  */
444     /* EPP        dwDevID, hDriv, wMsg, dwParam1, dwParam2); */
445
446     switch(wMsg) {
447     case DRV_LOAD:              return 1;
448     case DRV_FREE:              return 1;
449     case DRV_OPEN:              return JSTCK_drvOpen((LPSTR)dwParam1, dwParam2);
450     case DRV_CLOSE:             return JSTCK_drvClose(dwDevID);
451     case DRV_ENABLE:            return 1;
452     case DRV_DISABLE:           return 1;
453     case DRV_QUERYCONFIGURE:    return 1;
454     case DRV_CONFIGURE:         MessageBoxA(0, "JoyStick MultiMedia Driver !", "JoyStick Driver", MB_OK);       return 1;
455     case DRV_INSTALL:           return DRVCNF_RESTART;
456     case DRV_REMOVE:            return DRVCNF_RESTART;
457
458     case JDD_GETNUMDEVS:        return 1;
459     case JDD_GETDEVCAPS:        return JSTCK_GetDevCaps(dwDevID, (LPJOYCAPSW)dwParam1, dwParam2);
460     case JDD_GETPOS:            return JSTCK_GetPos(dwDevID, (LPJOYINFO)dwParam1);
461     case JDD_SETCALIBRATION:
462     case JDD_CONFIGCHANGED:     return JOYERR_NOCANDO;
463     case JDD_GETPOSEX:          return JSTCK_GetPosEx(dwDevID, (LPJOYINFOEX)dwParam1);
464     default:
465         return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
466     }
467 }
468
469 #else
470
471 /**************************************************************************
472  *                              DriverProc (JOYSTICK.@)
473  */
474 LRESULT CALLBACK JSTCK_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
475                                   LPARAM dwParam1, LPARAM dwParam2)
476 {
477     /* EPP     TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n",  */
478     /* EPP        dwDevID, hDriv, wMsg, dwParam1, dwParam2); */
479
480     switch(wMsg) {
481     case DRV_LOAD:
482     case DRV_FREE:
483     case DRV_OPEN:
484     case DRV_CLOSE:
485     case DRV_ENABLE:
486     case DRV_DISABLE:
487     case DRV_QUERYCONFIGURE:    return 0;
488     case DRV_CONFIGURE:         MessageBoxA(0, "JoyStick MultiMedia Driver !", "JoyStick Driver", MB_OK);       return 1;
489     case DRV_INSTALL:           return DRVCNF_RESTART;
490     case DRV_REMOVE:            return DRVCNF_RESTART;
491     default:
492         return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
493     }
494 }
495
496 #endif