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