mfreadwrite/reader: Add missing allocation check (Coverity).
[wine/zf.git] / dlls / winejoystick.drv / joystick_linux.c
blob8d1a7b1a25a028e7e4479b92c0e5c0885dc5661e
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_22_JOYSTICK_API
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 flags = O_RDONLY | O_NONBLOCK;
179 /* The first joystick may not be at /dev/input/js0, find the correct
180 * first or second device. For example the driver for XBOX 360 wireless
181 * receiver creates entries starting at 20.
183 for (found_ix = i = 0; i < MAXJOYSTICK; i++) {
184 sprintf(buf, JOYDEV_NEW, i);
185 if ((fd = open(buf, flags)) < 0) {
186 sprintf(buf, JOYDEV_OLD, i);
187 if ((fd = open(buf, flags)) < 0)
188 continue;
191 if (found_ix++ == jstick->joyIntf)
193 TRACE("Found joystick[%d] at %s\n", jstick->joyIntf, buf);
194 jstick->dev = fd;
195 last_attempt = 0;
196 break;
199 close(fd);
202 if (jstick->dev > 0)
203 ioctl(jstick->dev, JSIOCGAXMAP, jstick->axesMap);
205 return jstick->dev;
209 /**************************************************************************
210 * JoyGetDevCaps [MMSYSTEM.102]
212 LRESULT driver_joyGetDevCaps(DWORD_PTR dwDevID, LPJOYCAPSW lpCaps, DWORD dwSize)
214 WINE_JSTCK* jstck;
215 int dev;
216 char nrOfAxes;
217 char nrOfButtons;
218 char identString[MAXPNAMELEN];
219 int i;
220 int driverVersion;
222 if ((jstck = JSTCK_drvGet(dwDevID)) == NULL)
223 return MMSYSERR_NODRIVER;
225 if ((dev = JSTCK_OpenDevice(jstck)) < 0) return JOYERR_PARMS;
226 ioctl(dev, JSIOCGAXES, &nrOfAxes);
227 ioctl(dev, JSIOCGBUTTONS, &nrOfButtons);
228 ioctl(dev, JSIOCGVERSION, &driverVersion);
229 ioctl(dev, JSIOCGNAME(sizeof(identString)), identString);
230 TRACE("Driver: 0x%06x, Name: %s, #Axes: %d, #Buttons: %d\n",
231 driverVersion, identString, nrOfAxes, nrOfButtons);
232 lpCaps->wMid = MM_MICROSOFT;
233 lpCaps->wPid = MM_PC_JOYSTICK;
234 MultiByteToWideChar(CP_UNIXCP, 0, identString, -1, lpCaps->szPname, MAXPNAMELEN);
235 lpCaps->szPname[MAXPNAMELEN-1] = '\0';
236 lpCaps->wXmin = 0;
237 lpCaps->wXmax = 0xFFFF;
238 lpCaps->wYmin = 0;
239 lpCaps->wYmax = 0xFFFF;
240 lpCaps->wZmin = 0;
241 lpCaps->wZmax = (nrOfAxes >= 3) ? 0xFFFF : 0;
242 #ifdef BODGE_THE_HAT
243 /* Half-Life won't allow you to map an axis event to things like
244 "next weapon" and "use". Linux reports the hat on my stick as
245 axis U and V. So, IFF BODGE_THE_HAT is defined, lie through our
246 teeth and say we have 32 buttons, and we will map the axes to
247 the high buttons. Really, perhaps this should be a registry entry,
248 or even a parameter to the Linux joystick driver (which would completely
249 remove the need for this.)
251 lpCaps->wNumButtons = 32;
252 #else
253 lpCaps->wNumButtons = nrOfButtons;
254 #endif
255 if (dwSize == sizeof(JOYCAPSW)) {
256 /* complete 95 structure */
257 lpCaps->wRmin = 0;
258 lpCaps->wRmax = 0xFFFF;
259 lpCaps->wUmin = 0;
260 lpCaps->wUmax = 0xFFFF;
261 lpCaps->wVmin = 0;
262 lpCaps->wVmax = 0xFFFF;
263 lpCaps->wMaxAxes = 6; /* same as MS Joystick Driver */
264 lpCaps->wNumAxes = 0; /* nr of axes in use */
265 lpCaps->wMaxButtons = 32; /* same as MS Joystick Driver */
266 lpCaps->szRegKey[0] = 0;
267 lpCaps->szOEMVxD[0] = 0;
268 lpCaps->wCaps = 0;
269 for (i = 0; i < nrOfAxes; i++) {
270 switch (jstck->axesMap[i]) {
271 case 0: /* X */
272 case 1: /* Y */
273 case 8: /* Wheel */
274 case 9: /* Gas */
275 lpCaps->wNumAxes++;
276 break;
277 case 2: /* Z */
278 case 6: /* Throttle */
279 case 10: /* Brake */
280 lpCaps->wNumAxes++;
281 lpCaps->wCaps |= JOYCAPS_HASZ;
282 break;
283 case 5: /* Rz */
284 case 7: /* Rudder */
285 lpCaps->wNumAxes++;
286 lpCaps->wCaps |= JOYCAPS_HASR;
287 break;
288 case 3: /* Rx */
289 lpCaps->wNumAxes++;
290 lpCaps->wCaps |= JOYCAPS_HASU;
291 break;
292 case 4: /* Ry */
293 lpCaps->wNumAxes++;
294 lpCaps->wCaps |= JOYCAPS_HASV;
295 break;
296 case 16: /* Hat 0 X */
297 case 17: /* Hat 0 Y */
298 lpCaps->wCaps |= JOYCAPS_HASPOV | JOYCAPS_POV4DIR;
299 /* TODO: JOYCAPS_POVCTS handling */
300 break;
301 default:
302 WARN("Unknown axis %hhu(%u). Skipped.\n", jstck->axesMap[i], i);
307 return JOYERR_NOERROR;
310 /**************************************************************************
311 * driver_joyGetPos
313 LRESULT driver_joyGetPosEx(DWORD_PTR dwDevID, LPJOYINFOEX lpInfo)
315 WINE_JSTCK* jstck;
316 int dev;
317 struct js_event ev;
319 if ((jstck = JSTCK_drvGet(dwDevID)) == NULL)
320 return MMSYSERR_NODRIVER;
322 if ((dev = JSTCK_OpenDevice(jstck)) < 0) return JOYERR_PARMS;
324 while ((read(dev, &ev, sizeof(struct js_event))) > 0) {
325 if (ev.type == (JS_EVENT_AXIS)) {
326 switch (jstck->axesMap[ev.number]) {
327 case 0: /* X */
328 case 8: /* Wheel */
329 jstck->x = ev.value;
330 break;
331 case 1: /* Y */
332 case 9: /* Gas */
333 jstck->y = ev.value;
334 break;
335 case 2: /* Z */
336 case 6: /* Throttle */
337 case 10: /* Brake */
338 jstck->z = ev.value;
339 break;
340 case 5: /* Rz */
341 case 7: /* Rudder */
342 jstck->r = ev.value;
343 break;
344 case 3: /* Rx */
345 jstck->u = ev.value;
346 break;
347 case 4: /* Ry */
348 jstck->v = ev.value;
349 break;
350 case 16: /* Hat 0 X */
351 jstck->pov_x = ev.value;
352 break;
353 case 17: /* Hat 0 Y */
354 jstck->pov_y = ev.value;
355 break;
356 default:
357 FIXME("Unknown joystick event '%d'\n", ev.number);
359 } else if (ev.type == (JS_EVENT_BUTTON)) {
360 if (ev.value) {
361 jstck->buttons |= (1 << ev.number);
362 /* FIXME: what to do for this field when
363 * multiple buttons are depressed ?
365 if (lpInfo->dwFlags & JOY_RETURNBUTTONS)
366 lpInfo->dwButtonNumber = ev.number + 1;
368 else
369 jstck->buttons &= ~(1 << ev.number);
372 /* EAGAIN is returned when the queue is empty */
373 if (errno != EAGAIN) {
374 /* FIXME: error should not be ignored */
375 ERR("Error while reading joystick state (%s)\n", strerror(errno));
377 /* Now, copy the cached values into Window's structure... */
378 if (lpInfo->dwFlags & JOY_RETURNBUTTONS)
379 lpInfo->dwButtons = jstck->buttons;
380 if (lpInfo->dwFlags & JOY_RETURNX)
381 lpInfo->dwXpos = jstck->x + 32767;
382 if (lpInfo->dwFlags & JOY_RETURNY)
383 lpInfo->dwYpos = jstck->y + 32767;
384 if (lpInfo->dwFlags & JOY_RETURNZ)
385 lpInfo->dwZpos = jstck->z + 32767;
386 if (lpInfo->dwFlags & JOY_RETURNR)
387 lpInfo->dwRpos = jstck->r + 32767;
388 # ifdef BODGE_THE_HAT
389 else if (lpInfo->dwFlags & JOY_RETURNBUTTONS)
391 if (jstck->r > 0)
392 lpInfo->dwButtons |= 1<<7;
393 else if (jstck->r < 0)
394 lpInfo->dwButtons |= 1<<8;
396 # endif
397 if (lpInfo->dwFlags & JOY_RETURNU)
398 lpInfo->dwUpos = jstck->u + 32767;
399 # ifdef BODGE_THE_HAT
400 else if (lpInfo->dwFlags & JOY_RETURNBUTTONS)
402 if (jstck->u > 0)
403 lpInfo->dwButtons |= 1<<9;
404 else if (jstck->u < 0)
405 lpInfo->dwButtons |= 1<<10;
407 # endif
408 if (lpInfo->dwFlags & JOY_RETURNV)
409 lpInfo->dwVpos = jstck->v + 32767;
410 if (lpInfo->dwFlags & JOY_RETURNPOV) {
411 if (jstck->pov_y > 0) {
412 if (jstck->pov_x < 0)
413 lpInfo->dwPOV = 22500; /* SW */
414 else if (jstck->pov_x > 0)
415 lpInfo->dwPOV = 13500; /* SE */
416 else
417 lpInfo->dwPOV = 18000; /* S, JOY_POVBACKWARD */
418 } else if (jstck->pov_y < 0) {
419 if (jstck->pov_x < 0)
420 lpInfo->dwPOV = 31500; /* NW */
421 else if (jstck->pov_x > 0)
422 lpInfo->dwPOV = 4500; /* NE */
423 else
424 lpInfo->dwPOV = 0; /* N, JOY_POVFORWARD */
425 } else if (jstck->pov_x < 0)
426 lpInfo->dwPOV = 27000; /* W, JOY_POVLEFT */
427 else if (jstck->pov_x > 0)
428 lpInfo->dwPOV = 9000; /* E, JOY_POVRIGHT */
429 else
430 lpInfo->dwPOV = JOY_POVCENTERED; /* Center */
433 TRACE("x: %d, y: %d, z: %d, r: %d, u: %d, v: %d, buttons: 0x%04x, flags: 0x%04x (fd %d)\n",
434 lpInfo->dwXpos, lpInfo->dwYpos, lpInfo->dwZpos,
435 lpInfo->dwRpos, lpInfo->dwUpos, lpInfo->dwVpos,
436 lpInfo->dwButtons, lpInfo->dwFlags, dev
439 return JOYERR_NOERROR;
442 /**************************************************************************
443 * driver_joyGetPos
445 LRESULT driver_joyGetPos(DWORD_PTR dwDevID, LPJOYINFO lpInfo)
447 JOYINFOEX ji;
448 LONG ret;
450 memset(&ji, 0, sizeof(ji));
452 ji.dwSize = sizeof(ji);
453 ji.dwFlags = JOY_RETURNX | JOY_RETURNY | JOY_RETURNZ | JOY_RETURNBUTTONS;
454 ret = driver_joyGetPosEx(dwDevID, &ji);
455 if (ret == JOYERR_NOERROR) {
456 lpInfo->wXpos = ji.dwXpos;
457 lpInfo->wYpos = ji.dwYpos;
458 lpInfo->wZpos = ji.dwZpos;
459 lpInfo->wButtons = ji.dwButtons;
462 return ret;
465 #endif /* HAVE_LINUX_JOYSTICK_H */