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