devenum: Implement IMoniker::GetClassID().
[wine.git] / dlls / winejoystick.drv / joystick_linux.c
blob287f4be60d795f77509d0a3480c14c2d13b326b5
1 /*
2 * joystick functions
4 * Copyright 1997 Andreas Mohr
5 * Copyright 2000 Wolfgang Schwotzer
6 * Copyright 2002 David Hagood
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.
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.
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
22 * NOTES:
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.
38 #include "config.h"
39 #include "wine/port.h"
41 #ifdef HAVE_LINUX_JOYSTICK_H
43 #ifdef HAVE_UNISTD_H
44 # include <unistd.h>
45 #endif
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <fcntl.h>
50 #ifdef HAVE_SYS_IOCTL_H
51 #include <sys/ioctl.h>
52 #endif
53 #ifdef HAVE_LINUX_IOCTL_H
54 #include <linux/ioctl.h>
55 #endif
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 #include <errno.h>
64 #include "joystick.h"
66 #include "wingdi.h"
67 #include "winnls.h"
68 #include "wine/debug.h"
70 #include "wine/unicode.h"
72 WINE_DEFAULT_DEBUG_CHANNEL(joystick);
74 #define MAXJOYSTICK (JOYSTICKID2 + 30)
76 typedef struct tagWINE_JSTCK {
77 int joyIntf;
78 BOOL in_use;
79 /* Some extra info we need to make this actually work under the
80 Linux 2.2 event api.
81 First of all, we cannot keep closing and reopening the device file -
82 that blows away the state of the stick device, and we lose events. So, we
83 need to open the low-level device once, and close it when we are done.
85 Secondly, the event API only gives us what's changed. However, Windows apps
86 want the whole state every time, so we have to cache the data.
89 int dev; /* Linux level device file descriptor */
90 int x;
91 int y;
92 int z;
93 int r;
94 int u;
95 int v;
96 int pov_x;
97 int pov_y;
98 int buttons;
99 char axesMap[ABS_MAX + 1];
100 } WINE_JSTCK;
102 static WINE_JSTCK JSTCK_Data[MAXJOYSTICK];
104 /**************************************************************************
105 * JSTCK_drvGet [internal]
107 static WINE_JSTCK *JSTCK_drvGet(DWORD_PTR dwDevID)
109 int p;
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;
117 return (WINE_JSTCK*)dwDevID;
120 /**************************************************************************
121 * driver_open
123 LRESULT driver_open(LPSTR str, DWORD dwIntf)
125 if (dwIntf >= MAXJOYSTICK || JSTCK_Data[dwIntf].in_use)
126 return 0;
128 JSTCK_Data[dwIntf].joyIntf = dwIntf;
129 JSTCK_Data[dwIntf].dev = -1;
130 JSTCK_Data[dwIntf].in_use = TRUE;
131 return (LRESULT)&JSTCK_Data[dwIntf];
134 /**************************************************************************
135 * driver_close
137 LRESULT driver_close(DWORD_PTR dwDevID)
139 WINE_JSTCK* jstck = JSTCK_drvGet(dwDevID);
141 if (jstck == NULL)
142 return 0;
143 jstck->in_use = FALSE;
144 if (jstck->dev > 0)
146 close(jstck->dev);
147 jstck->dev = 0;
149 return 1;
152 struct js_status
154 int buttons;
155 int x;
156 int y;
159 /**************************************************************************
160 * JSTCK_OpenDevice [internal]
162 static int JSTCK_OpenDevice(WINE_JSTCK* jstick)
164 char buf[20];
165 int flags, fd, found_ix, i;
166 static DWORD last_attempt;
167 DWORD now;
169 if (jstick->dev > 0)
170 return jstick->dev;
172 now = GetTickCount();
173 if (now - last_attempt < 2000)
174 return -1;
175 last_attempt = now;
177 #ifdef HAVE_LINUX_22_JOYSTICK_API
178 flags = O_RDONLY | O_NONBLOCK;
179 #else
180 flags = O_RDONLY;
181 #endif
183 /* The first joystick may not be at /dev/input/js0, find the correct
184 * first or second device. For example the driver for XBOX 360 wireless
185 * receiver creates entries starting at 20.
187 for (found_ix = i = 0; i < MAXJOYSTICK; i++) {
188 sprintf(buf, JOYDEV_NEW, i);
189 if ((fd = open(buf, flags)) < 0) {
190 sprintf(buf, JOYDEV_OLD, i);
191 if ((fd = open(buf, flags)) < 0)
192 continue;
195 if (found_ix++ == jstick->joyIntf)
197 TRACE("Found joystick[%d] at %s\n", jstick->joyIntf, buf);
198 jstick->dev = fd;
199 last_attempt = 0;
200 break;
203 close(fd);
206 #ifdef HAVE_LINUX_22_JOYSTICK_API
207 if (jstick->dev > 0)
208 ioctl(jstick->dev, JSIOCGAXMAP, jstick->axesMap);
209 #endif
210 return jstick->dev;
214 /**************************************************************************
215 * JoyGetDevCaps [MMSYSTEM.102]
217 LRESULT driver_joyGetDevCaps(DWORD_PTR dwDevID, LPJOYCAPSW lpCaps, DWORD dwSize)
219 WINE_JSTCK* jstck;
220 #ifdef HAVE_LINUX_22_JOYSTICK_API
221 int dev;
222 char nrOfAxes;
223 char nrOfButtons;
224 char identString[MAXPNAMELEN];
225 int i;
226 int driverVersion;
227 #else
228 static const WCHAR ini[] = {'W','i','n','e',' ','J','o','y','s','t','i','c','k',' ','D','r','i','v','e','r',0};
229 #endif
231 if ((jstck = JSTCK_drvGet(dwDevID)) == NULL)
232 return MMSYSERR_NODRIVER;
234 #ifdef HAVE_LINUX_22_JOYSTICK_API
235 if ((dev = JSTCK_OpenDevice(jstck)) < 0) return JOYERR_PARMS;
236 ioctl(dev, JSIOCGAXES, &nrOfAxes);
237 ioctl(dev, JSIOCGBUTTONS, &nrOfButtons);
238 ioctl(dev, JSIOCGVERSION, &driverVersion);
239 ioctl(dev, JSIOCGNAME(sizeof(identString)), identString);
240 TRACE("Driver: 0x%06x, Name: %s, #Axes: %d, #Buttons: %d\n",
241 driverVersion, identString, nrOfAxes, nrOfButtons);
242 lpCaps->wMid = MM_MICROSOFT;
243 lpCaps->wPid = MM_PC_JOYSTICK;
244 MultiByteToWideChar(CP_UNIXCP, 0, identString, -1, lpCaps->szPname, MAXPNAMELEN);
245 lpCaps->szPname[MAXPNAMELEN-1] = '\0';
246 lpCaps->wXmin = 0;
247 lpCaps->wXmax = 0xFFFF;
248 lpCaps->wYmin = 0;
249 lpCaps->wYmax = 0xFFFF;
250 lpCaps->wZmin = 0;
251 lpCaps->wZmax = (nrOfAxes >= 3) ? 0xFFFF : 0;
252 #ifdef BODGE_THE_HAT
253 /* Half-Life won't allow you to map an axis event to things like
254 "next weapon" and "use". Linux reports the hat on my stick as
255 axis U and V. So, IFF BODGE_THE_HAT is defined, lie through our
256 teeth and say we have 32 buttons, and we will map the axes to
257 the high buttons. Really, perhaps this should be a registry entry,
258 or even a parameter to the Linux joystick driver (which would completely
259 remove the need for this.)
261 lpCaps->wNumButtons = 32;
262 #else
263 lpCaps->wNumButtons = nrOfButtons;
264 #endif
265 if (dwSize == sizeof(JOYCAPSW)) {
266 /* complete 95 structure */
267 lpCaps->wRmin = 0;
268 lpCaps->wRmax = 0xFFFF;
269 lpCaps->wUmin = 0;
270 lpCaps->wUmax = 0xFFFF;
271 lpCaps->wVmin = 0;
272 lpCaps->wVmax = 0xFFFF;
273 lpCaps->wMaxAxes = 6; /* same as MS Joystick Driver */
274 lpCaps->wNumAxes = 0; /* nr of axes in use */
275 lpCaps->wMaxButtons = 32; /* same as MS Joystick Driver */
276 lpCaps->szRegKey[0] = 0;
277 lpCaps->szOEMVxD[0] = 0;
278 lpCaps->wCaps = 0;
279 for (i = 0; i < nrOfAxes; i++) {
280 switch (jstck->axesMap[i]) {
281 case 0: /* X */
282 case 1: /* Y */
283 lpCaps->wNumAxes++;
284 break;
285 case 2: /* Z */
286 case 6: /* Throttle */
287 lpCaps->wNumAxes++;
288 lpCaps->wCaps |= JOYCAPS_HASZ;
289 break;
290 case 5: /* Rz */
291 case 7: /* Rudder */
292 lpCaps->wNumAxes++;
293 lpCaps->wCaps |= JOYCAPS_HASR;
294 break;
295 case 3: /* Rx */
296 lpCaps->wNumAxes++;
297 lpCaps->wCaps |= JOYCAPS_HASU;
298 break;
299 case 4: /* Ry */
300 lpCaps->wNumAxes++;
301 lpCaps->wCaps |= JOYCAPS_HASV;
302 break;
303 case 16: /* Hat 0 X */
304 case 17: /* Hat 0 Y */
305 lpCaps->wCaps |= JOYCAPS_HASPOV | JOYCAPS_POV4DIR;
306 /* TODO: JOYCAPS_POVCTS handling */
307 break;
308 default:
309 WARN("Unknown axis %hhu(%u). Skipped.\n", jstck->axesMap[i], i);
313 #else
314 lpCaps->wMid = MM_MICROSOFT;
315 lpCaps->wPid = MM_PC_JOYSTICK;
316 strcpyW(lpCaps->szPname, ini); /* joystick product name */
317 lpCaps->wXmin = 0;
318 lpCaps->wXmax = 0xFFFF;
319 lpCaps->wYmin = 0;
320 lpCaps->wYmax = 0xFFFF;
321 lpCaps->wZmin = 0;
322 lpCaps->wZmax = 0;
323 lpCaps->wNumButtons = 2;
324 if (dwSize == sizeof(JOYCAPSW)) {
325 /* complete 95 structure */
326 lpCaps->wRmin = 0;
327 lpCaps->wRmax = 0;
328 lpCaps->wUmin = 0;
329 lpCaps->wUmax = 0;
330 lpCaps->wVmin = 0;
331 lpCaps->wVmax = 0;
332 lpCaps->wCaps = 0;
333 lpCaps->wMaxAxes = 2;
334 lpCaps->wNumAxes = 2;
335 lpCaps->wMaxButtons = 4;
336 lpCaps->szRegKey[0] = 0;
337 lpCaps->szOEMVxD[0] = 0;
339 #endif
341 return JOYERR_NOERROR;
344 /**************************************************************************
345 * driver_joyGetPos
347 LRESULT driver_joyGetPosEx(DWORD_PTR dwDevID, LPJOYINFOEX lpInfo)
349 WINE_JSTCK* jstck;
350 int dev;
351 #ifdef HAVE_LINUX_22_JOYSTICK_API
352 struct js_event ev;
353 #else
354 struct js_status js;
355 int dev_stat;
356 #endif
358 if ((jstck = JSTCK_drvGet(dwDevID)) == NULL)
359 return MMSYSERR_NODRIVER;
361 if ((dev = JSTCK_OpenDevice(jstck)) < 0) return JOYERR_PARMS;
363 #ifdef HAVE_LINUX_22_JOYSTICK_API
364 while ((read(dev, &ev, sizeof(struct js_event))) > 0) {
365 if (ev.type == (JS_EVENT_AXIS)) {
366 switch (jstck->axesMap[ev.number]) {
367 case 0: /* X */
368 jstck->x = ev.value;
369 break;
370 case 1: /* Y */
371 jstck->y = ev.value;
372 break;
373 case 2: /* Z */
374 case 6: /* Throttle */
375 jstck->z = ev.value;
376 break;
377 case 5: /* Rz */
378 case 7: /* Rudder */
379 jstck->r = ev.value;
380 break;
381 case 3: /* Rx */
382 jstck->u = ev.value;
383 break;
384 case 4: /* Ry */
385 jstck->v = ev.value;
386 break;
387 case 16: /* Hat 0 X */
388 jstck->pov_x = ev.value;
389 break;
390 case 17: /* Hat 0 Y */
391 jstck->pov_y = ev.value;
392 break;
393 default:
394 FIXME("Unknown joystick event '%d'\n", ev.number);
396 } else if (ev.type == (JS_EVENT_BUTTON)) {
397 if (ev.value) {
398 jstck->buttons |= (1 << ev.number);
399 /* FIXME: what to do for this field when
400 * multiple buttons are depressed ?
402 if (lpInfo->dwFlags & JOY_RETURNBUTTONS)
403 lpInfo->dwButtonNumber = ev.number + 1;
405 else
406 jstck->buttons &= ~(1 << ev.number);
409 /* EAGAIN is returned when the queue is empty */
410 if (errno != EAGAIN) {
411 /* FIXME: error should not be ignored */
412 ERR("Error while reading joystick state (%s)\n", strerror(errno));
414 /* Now, copy the cached values into Window's structure... */
415 if (lpInfo->dwFlags & JOY_RETURNBUTTONS)
416 lpInfo->dwButtons = jstck->buttons;
417 if (lpInfo->dwFlags & JOY_RETURNX)
418 lpInfo->dwXpos = jstck->x + 32767;
419 if (lpInfo->dwFlags & JOY_RETURNY)
420 lpInfo->dwYpos = jstck->y + 32767;
421 if (lpInfo->dwFlags & JOY_RETURNZ)
422 lpInfo->dwZpos = jstck->z + 32767;
423 if (lpInfo->dwFlags & JOY_RETURNR)
424 lpInfo->dwRpos = jstck->r + 32767;
425 # ifdef BODGE_THE_HAT
426 else if (lpInfo->dwFlags & JOY_RETURNBUTTONS)
428 if (jstck->r > 0)
429 lpInfo->dwButtons |= 1<<7;
430 else if (jstck->r < 0)
431 lpInfo->dwButtons |= 1<<8;
433 # endif
434 if (lpInfo->dwFlags & JOY_RETURNU)
435 lpInfo->dwUpos = jstck->u + 32767;
436 # ifdef BODGE_THE_HAT
437 else if (lpInfo->dwFlags & JOY_RETURNBUTTONS)
439 if (jstck->u > 0)
440 lpInfo->dwButtons |= 1<<9;
441 else if (jstck->u < 0)
442 lpInfo->dwButtons |= 1<<10;
444 # endif
445 if (lpInfo->dwFlags & JOY_RETURNV)
446 lpInfo->dwVpos = jstck->v + 32767;
447 if (lpInfo->dwFlags & JOY_RETURNPOV) {
448 if (jstck->pov_y > 0) {
449 if (jstck->pov_x < 0)
450 lpInfo->dwPOV = 22500; /* SW */
451 else if (jstck->pov_x > 0)
452 lpInfo->dwPOV = 13500; /* SE */
453 else
454 lpInfo->dwPOV = 18000; /* S, JOY_POVBACKWARD */
455 } else if (jstck->pov_y < 0) {
456 if (jstck->pov_x < 0)
457 lpInfo->dwPOV = 31500; /* NW */
458 else if (jstck->pov_x > 0)
459 lpInfo->dwPOV = 4500; /* NE */
460 else
461 lpInfo->dwPOV = 0; /* N, JOY_POVFORWARD */
462 } else if (jstck->pov_x < 0)
463 lpInfo->dwPOV = 27000; /* W, JOY_POVLEFT */
464 else if (jstck->pov_x > 0)
465 lpInfo->dwPOV = 9000; /* E, JOY_POVRIGHT */
466 else
467 lpInfo->dwPOV = JOY_POVCENTERED; /* Center */
470 #else
471 dev_stat = read(dev, &js, sizeof(js));
472 if (dev_stat != sizeof(js)) {
473 return JOYERR_UNPLUGGED; /* FIXME: perhaps wrong, but what should I return else ? */
475 js.x = js.x<<8;
476 js.y = js.y<<8;
477 if (lpInfo->dwFlags & JOY_RETURNX)
478 lpInfo->dwXpos = js.x; /* FIXME: perhaps multiply it somehow ? */
479 if (lpInfo->dwFlags & JOY_RETURNY)
480 lpInfo->dwYpos = js.y;
481 if (lpInfo->dwFlags & JOY_RETURNBUTTONS)
482 lpInfo->dwButtons = js.buttons;
483 #endif
485 TRACE("x: %d, y: %d, z: %d, r: %d, u: %d, v: %d, buttons: 0x%04x, flags: 0x%04x (fd %d)\n",
486 lpInfo->dwXpos, lpInfo->dwYpos, lpInfo->dwZpos,
487 lpInfo->dwRpos, lpInfo->dwUpos, lpInfo->dwVpos,
488 lpInfo->dwButtons, lpInfo->dwFlags, dev
491 return JOYERR_NOERROR;
494 /**************************************************************************
495 * driver_joyGetPos
497 LRESULT driver_joyGetPos(DWORD_PTR dwDevID, LPJOYINFO lpInfo)
499 JOYINFOEX ji;
500 LONG ret;
502 memset(&ji, 0, sizeof(ji));
504 ji.dwSize = sizeof(ji);
505 ji.dwFlags = JOY_RETURNX | JOY_RETURNY | JOY_RETURNZ | JOY_RETURNBUTTONS;
506 ret = driver_joyGetPosEx(dwDevID, &ji);
507 if (ret == JOYERR_NOERROR) {
508 lpInfo->wXpos = ji.dwXpos;
509 lpInfo->wYpos = ji.dwYpos;
510 lpInfo->wZpos = ji.dwZpos;
511 lpInfo->wButtons = ji.dwButtons;
514 return ret;
517 #endif /* HAVE_LINUX_JOYSTICK_H */