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