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