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