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