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