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