- MCI_SAVE should overwrite its destination file.
[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  *
7  * nearly all joystick functions can be regarded as obsolete,
8  * as Linux (2.1.x) now supports extended joysticks
9  * with a completely new joystick driver interface
10  * new driver's docu says:
11  * "For backward compatibility the old interface is still included,
12  * but will be dropped in the future."
13  * Thus we should implement the new interface and at most keep the old
14  * routines for backward compatibility.
15  */
16
17 /*
18  * Wolfgang Schwotzer
19  *
20  *    01/2000    added support for new joystick driver
21  *
22  */
23
24 #include "config.h"
25
26 #include <unistd.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <fcntl.h>
31 #include <sys/ioctl.h>
32 #ifdef HAVE_LINUX_JOYSTICK_H
33 #include <linux/joystick.h>
34 #define JOYDEV "/dev/js%d"
35 #endif
36 #ifdef HAVE_SYS_ERRNO_H
37 #include <sys/errno.h>
38 #endif
39
40 #include "windef.h"
41 #include "winbase.h"
42 #include "wingdi.h"
43 #include "winuser.h"
44 #include "mmddk.h"
45 #include "debugtools.h"
46
47 DEFAULT_DEBUG_CHANNEL(joystick);
48
49 #ifdef HAVE_LINUX_JOYSTICK_H
50
51 #define MAXJOYSTICK     (JOYSTICKID2 + 1)
52
53 typedef struct tagWINE_JSTCK {
54     int         joyIntf;
55     int         in_use;
56 } WINE_JSTCK;
57
58 static  WINE_JSTCK      JSTCK_Data[MAXJOYSTICK];
59
60 /**************************************************************************
61  *                              JSTCK_drvGet                    [internal]      
62  */
63 static  WINE_JSTCK*     JSTCK_drvGet(DWORD dwDevID)
64 {
65     int p;
66
67     if ((dwDevID - (DWORD)JSTCK_Data) % sizeof(JSTCK_Data[0]) != 0)
68         return NULL;
69     p = (dwDevID - (DWORD)JSTCK_Data) / sizeof(JSTCK_Data[0]);
70     if (p < 0 || p >= MAXJOYSTICK || !((WINE_JSTCK*)dwDevID)->in_use)
71         return NULL;
72
73     return (WINE_JSTCK*)dwDevID;
74 }
75
76 /**************************************************************************
77  *                              JSTCK_drvOpen                   [internal]      
78  */
79 static  DWORD   JSTCK_drvOpen(LPSTR str, DWORD dwIntf)
80 {
81     if (dwIntf >= MAXJOYSTICK || JSTCK_Data[dwIntf].in_use)
82         return 0;
83
84     JSTCK_Data[dwIntf].joyIntf = dwIntf;
85     JSTCK_Data[dwIntf].in_use = 1;
86     return (DWORD)&JSTCK_Data[dwIntf];
87 }
88
89 /**************************************************************************
90  *                              JSTCK_drvClose                  [internal]      
91  */
92 static  DWORD   JSTCK_drvClose(DWORD dwDevID)
93 {
94     WINE_JSTCK* jstck = JSTCK_drvGet(dwDevID);
95
96     if (jstck == NULL)
97         return 0;
98     jstck->in_use = 0;
99     return 1;
100 }
101
102 struct js_status
103 {
104     int buttons;
105     int x;
106     int y;
107 };
108
109 /**************************************************************************
110  *                              JSTCK_OpenDevice           [internal]
111  */
112 static  int     JSTCK_OpenDevice(WINE_JSTCK* jstick)
113 {
114     char        buf[20];
115     int         flags;
116
117     sprintf(buf, JOYDEV, jstick->joyIntf);
118 #ifdef HAVE_LINUX_22_JOYSTICK_API
119     flags = O_RDONLY | O_NONBLOCK;
120 #else
121     flags = O_RDONLY;
122 #endif
123     return open(buf, flags);
124 }
125
126 /**************************************************************************
127  *                              JoyGetDevCaps           [MMSYSTEM.102]
128  */
129 static  LONG    JSTCK_GetDevCaps(DWORD dwDevID, LPJOYCAPSA lpCaps, DWORD dwSize)
130 {
131     WINE_JSTCK* jstck;
132 #ifdef HAVE_LINUX_22_JOYSTICK_API
133     int         dev;
134     char        nrOfAxes;
135     char        nrOfButtons;
136     char        identString[MAXPNAMELEN];
137     int         driverVersion;
138 #endif
139
140     if ((jstck = JSTCK_drvGet(dwDevID)) == NULL)
141         return MMSYSERR_NODRIVER;
142
143 #ifdef HAVE_LINUX_22_JOYSTICK_API
144     
145     if ((dev = JSTCK_OpenDevice(jstck)) < 0) return JOYERR_PARMS;
146     ioctl(dev, JSIOCGAXES, &nrOfAxes);
147     ioctl(dev, JSIOCGBUTTONS, &nrOfButtons);
148     ioctl(dev, JSIOCGVERSION, &driverVersion);
149     ioctl(dev, JSIOCGNAME(sizeof(identString)), &identString);
150     TRACE("Driver: 0x%06x, Name: %s, #Axes: %d, #Buttons: %d\n",
151           driverVersion, identString, nrOfAxes, nrOfButtons);
152     lpCaps->wMid = MM_MICROSOFT;
153     lpCaps->wPid = MM_PC_JOYSTICK;
154     strncpy(lpCaps->szPname, identString, MAXPNAMELEN);
155     lpCaps->szPname[MAXPNAMELEN-1] = '\0';
156     lpCaps->wXmin = 0;
157     lpCaps->wXmax = 0xFFFF;
158     lpCaps->wYmin = 0;
159     lpCaps->wYmax = 0xFFFF;
160     lpCaps->wZmin = 0;
161     lpCaps->wZmax = (nrOfAxes >= 3) ? 0xFFFF : 0;
162     lpCaps->wNumButtons = nrOfButtons;
163     if (dwSize == sizeof(JOYCAPSA)) {
164         /* since we suppose ntOfAxes <= 6 in the following code, do it explicitly */
165         if (nrOfAxes > 6) nrOfAxes = 6;
166         /* complete 95 structure */
167         lpCaps->wRmin = 0;
168         lpCaps->wRmax = nrOfAxes >= 4 ? 0xFFFF : 0;
169         lpCaps->wUmin = 0;
170         lpCaps->wUmax = nrOfAxes >= 5 ? 0xFFFF : 0;
171         lpCaps->wVmin = 0;
172         lpCaps->wVmax = nrOfAxes >= 6 ? 0xFFFF : 0;
173         lpCaps->wMaxAxes = 6; /* same as MS Joystick Driver */
174         lpCaps->wNumAxes = nrOfAxes; /* nr of axes in use */
175         lpCaps->wMaxButtons = 32; /* same as MS Joystick Driver */
176         strcpy(lpCaps->szRegKey, "");
177         strcpy(lpCaps->szOEMVxD, "");
178         lpCaps->wCaps = 0;
179         switch(nrOfAxes) {
180         case 6: lpCaps->wCaps |= JOYCAPS_HASV;
181         case 5: lpCaps->wCaps |= JOYCAPS_HASU;
182         case 4: lpCaps->wCaps |= JOYCAPS_HASR;
183         case 3: lpCaps->wCaps |= JOYCAPS_HASZ;
184             /* FIXME: don't know how to detect for
185                JOYCAPS_HASPOV, JOYCAPS_POV4DIR, JOYCAPS_POVCTS */
186         }
187     }
188     close(dev);
189
190 #else
191     lpCaps->wMid = MM_MICROSOFT;
192     lpCaps->wPid = MM_PC_JOYSTICK;
193     strcpy(lpCaps->szPname, "WineJoy"); /* joystick product name */
194     lpCaps->wXmin = 0;
195     lpCaps->wXmax = 0xFFFF;
196     lpCaps->wYmin = 0;
197     lpCaps->wYmax = 0xFFFF;
198     lpCaps->wZmin = 0;
199     lpCaps->wZmax = 0;
200     lpCaps->wNumButtons = 2;
201     if (dwSize == sizeof(JOYCAPSA)) {
202         /* complete 95 structure */
203         lpCaps->wRmin = 0;
204         lpCaps->wRmax = 0;
205         lpCaps->wUmin = 0;
206         lpCaps->wUmax = 0;
207         lpCaps->wVmin = 0;
208         lpCaps->wVmax = 0;
209         lpCaps->wCaps = 0;
210         lpCaps->wMaxAxes = 2;
211         lpCaps->wNumAxes = 2;
212         lpCaps->wMaxButtons = 4;
213         strcpy(lpCaps->szRegKey,"");
214         strcpy(lpCaps->szOEMVxD,"");
215     }
216 #endif
217
218     return JOYERR_NOERROR;
219 }
220
221 /**************************************************************************
222  *                              JSTCK_GetPos                    [internal]
223  */
224 static LONG     JSTCK_GetPosEx(DWORD dwDevID, LPJOYINFOEX lpInfo)
225 {
226     WINE_JSTCK*         jstck;
227     int                 dev;
228 #ifdef HAVE_LINUX_22_JOYSTICK_API
229     struct js_event     ev;
230 #else
231     struct js_status    js;
232     int                 dev_stat;
233 #endif
234     
235     if ((jstck = JSTCK_drvGet(dwDevID)) == NULL)
236         return MMSYSERR_NODRIVER;
237
238     if ((dev = JSTCK_OpenDevice(jstck)) < 0) return JOYERR_PARMS;
239
240 #ifdef HAVE_LINUX_22_JOYSTICK_API
241     /* After opening the device it's state can be
242        read with JS_EVENT_INIT flag */
243     while ((read(dev, &ev, sizeof(struct js_event))) > 0) {
244         if (ev.type == (JS_EVENT_AXIS | JS_EVENT_INIT)) {
245             switch (ev.number) {
246             case 0:
247                 if (lpInfo->dwFlags & JOY_RETURNX)
248                     lpInfo->dwXpos   = ev.value + 32767;
249                 break;
250             case 1: 
251                 if (lpInfo->dwFlags & JOY_RETURNY)
252                     lpInfo->dwYpos   = ev.value + 32767;
253                 break;
254             case 2:
255                 if (lpInfo->dwFlags & JOY_RETURNZ)
256                     lpInfo->dwZpos   = ev.value + 32767;
257                 break;
258             case 3: 
259                 if (lpInfo->dwFlags & JOY_RETURNR)
260                     lpInfo->dwRpos   = ev.value + 32767;
261             case 4: 
262                 if (lpInfo->dwFlags & JOY_RETURNU)
263                     lpInfo->dwUpos   = ev.value + 32767;
264             case 5: 
265                 if (lpInfo->dwFlags & JOY_RETURNV)
266                     lpInfo->dwVpos   = ev.value + 32767;
267                 break;
268             default: 
269                 FIXME("Unknown joystick event '%d'\n", ev.number);
270             }
271         } else if (ev.type == (JS_EVENT_BUTTON | JS_EVENT_INIT)) {
272             if (lpInfo->dwFlags & JOY_RETURNBUTTONS) {
273                 if (ev.value) {
274                     lpInfo->dwButtons |= (1 << ev.number);
275                     /* FIXME: what to do for this field when 
276                      * multiple buttons are depressed ?
277                      */
278                     lpInfo->dwButtonNumber = ev.number + 1;
279                 }
280             }
281         }
282     }
283     /* EAGAIN is returned when the queue is empty */
284     if (errno != EAGAIN) {
285         /* FIXME: error should not be ignored */
286         ERR("Error while reading joystick state (%s)\n", strerror(errno));
287     }
288 #else
289     dev_stat = read(dev, &js, sizeof(js));
290     if (dev_stat != sizeof(js)) {
291         close(dev);
292         return JOYERR_UNPLUGGED; /* FIXME: perhaps wrong, but what should I return else ? */
293     }
294     js.x = js.x<<8;
295     js.y = js.y<<8;
296     if (lpInfo->dwFlags & JOY_RETURNX)
297         lpInfo->dwXpos = js.x;   /* FIXME: perhaps multiply it somehow ? */
298     if (lpInfo->dwFlags & JOY_RETURNY)
299         lpInfo->dwYpos = js.y;
300     if (lpInfo->dwFlags & JOY_RETURNBUTTONS)
301         lpInfo->dwButtons = js.buttons;
302 #endif
303
304     close(dev);
305
306     TRACE("x: %ld, y: %ld, z: %ld, r: %ld, u: %ld, v: %ld, buttons: 0x%04x, flags: 0x%04x\n", 
307           lpInfo->dwXpos, lpInfo->dwYpos, lpInfo->dwZpos,
308           lpInfo->dwRpos, lpInfo->dwUpos, lpInfo->dwVpos,
309           (unsigned int)lpInfo->dwButtons,
310           (unsigned int)lpInfo->dwFlags);
311
312     return JOYERR_NOERROR;
313 }
314
315 /**************************************************************************
316  *                              JSTCK_GetPos                    [internal]
317  */
318 static LONG     JSTCK_GetPos(DWORD dwDevID, LPJOYINFO lpInfo)
319 {
320     JOYINFOEX   ji;
321     LONG        ret;
322
323     memset(&ji, 0, sizeof(ji));
324
325     ji.dwSize = sizeof(ji);
326     ji.dwFlags = JOY_RETURNX | JOY_RETURNY | JOY_RETURNZ | JOY_RETURNBUTTONS;
327     ret = JSTCK_GetPosEx(dwDevID, &ji);
328     if (ret == JOYERR_NOERROR)  {
329         lpInfo->wXpos    = ji.dwXpos;
330         lpInfo->wYpos    = ji.dwYpos;
331         lpInfo->wZpos    = ji.dwZpos;
332         lpInfo->wButtons = ji.dwButtons;
333     }
334
335     return ret;
336 }
337
338 /**************************************************************************
339  *                              JSTCK_DriverProc                [internal]
340  */
341 LONG CALLBACK   JSTCK_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg, 
342                                  DWORD dwParam1, DWORD dwParam2)
343 {
344     /* EPP     TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n",  */
345     /* EPP        dwDevID, hDriv, wMsg, dwParam1, dwParam2); */
346     
347     switch(wMsg) {
348     case DRV_LOAD:              return 1;
349     case DRV_FREE:              return 1;
350     case DRV_OPEN:              return JSTCK_drvOpen((LPSTR)dwParam1, dwParam2);
351     case DRV_CLOSE:             return JSTCK_drvClose(dwDevID);
352     case DRV_ENABLE:            return 1;
353     case DRV_DISABLE:           return 1;
354     case DRV_QUERYCONFIGURE:    return 1;
355     case DRV_CONFIGURE:         MessageBoxA(0, "JoyStick MultiMedia Driver !", "JoyStick Driver", MB_OK);       return 1;
356     case DRV_INSTALL:           return DRVCNF_RESTART;
357     case DRV_REMOVE:            return DRVCNF_RESTART;
358
359     case JDD_GETNUMDEVS:        return 1;
360     case JDD_GETDEVCAPS:        return JSTCK_GetDevCaps(dwDevID, (LPJOYCAPSA)dwParam1, dwParam2);
361     case JDD_GETPOS:            return JSTCK_GetPos(dwDevID, (LPJOYINFO)dwParam1);
362     case JDD_SETCALIBRATION:    
363     case JDD_CONFIGCHANGED:     return JOYERR_NOCANDO;
364     case JDD_GETPOSEX:          return JSTCK_GetPosEx(dwDevID, (LPJOYINFOEX)dwParam1);
365     default:
366         return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
367     }
368 }
369
370 #else
371
372 /**************************************************************************
373  *                              JSTCK_DriverProc                [internal]
374  */
375 LONG CALLBACK   JSTCK_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg, 
376                                  DWORD dwParam1, DWORD dwParam2)
377 {
378     /* EPP     TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n",  */
379     /* EPP        dwDevID, hDriv, wMsg, dwParam1, dwParam2); */
380     
381     switch(wMsg) {
382     case DRV_LOAD:              
383     case DRV_FREE:              
384     case DRV_OPEN:              
385     case DRV_CLOSE:             
386     case DRV_ENABLE:            
387     case DRV_DISABLE:           
388     case DRV_QUERYCONFIGURE:    return 0;
389     case DRV_CONFIGURE:         MessageBoxA(0, "JoyStick MultiMedia Driver !", "JoyStick Driver", MB_OK);       return 1;
390     case DRV_INSTALL:           return DRVCNF_RESTART;
391     case DRV_REMOVE:            return DRVCNF_RESTART;
392     default:
393         return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
394     }
395 }
396
397 #endif