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