joystick_linux.c 25.7 KB
Newer Older
Lionel Ulmer's avatar
Lionel Ulmer committed
1 2 3 4
/*		DirectInput Joystick device
 *
 * Copyright 1998 Marcus Meissner
 * Copyright 1998,1999 Lionel Ulmer
5
 * Copyright 2000-2001 TransGaming Technologies Inc.
Lionel Ulmer's avatar
Lionel Ulmer committed
6
 *
7 8 9 10 11 12 13 14 15 16 17 18
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
Lionel Ulmer's avatar
Lionel Ulmer committed
20 21
 */

22 23 24
/*
 * To Do:
 *	dead zone
25
 *	force feedback
26 27
 */

Lionel Ulmer's avatar
Lionel Ulmer committed
28
#include "config.h"
29
#include "wine/port.h"
Lionel Ulmer's avatar
Lionel Ulmer committed
30

31
#include <stdarg.h>
Lionel Ulmer's avatar
Lionel Ulmer committed
32 33 34
#include <stdio.h>
#include <string.h>
#include <time.h>
35 36 37 38 39 40
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
41
#include <fcntl.h>
42 43 44
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
Lionel Ulmer's avatar
Lionel Ulmer committed
45
#include <errno.h>
46 47 48
#ifdef HAVE_LINUX_IOCTL_H
# include <linux/ioctl.h>
#endif
49 50
#ifdef HAVE_LINUX_JOYSTICK_H
# include <linux/joystick.h>
51
# undef SW_MAX
52
#endif
53 54 55
#ifdef HAVE_SYS_POLL_H
# include <sys/poll.h>
#endif
Lionel Ulmer's avatar
Lionel Ulmer committed
56

57
#include "wine/debug.h"
58
#include "wine/unicode.h"
59
#include "windef.h"
Lionel Ulmer's avatar
Lionel Ulmer committed
60 61 62 63 64 65
#include "winbase.h"
#include "winerror.h"
#include "dinput.h"

#include "dinput_private.h"
#include "device_private.h"
66
#include "joystick_private.h"
Lionel Ulmer's avatar
Lionel Ulmer committed
67

68
WINE_DEFAULT_DEBUG_CHANNEL(dinput);
Lionel Ulmer's avatar
Lionel Ulmer committed
69

70 71
#ifdef HAVE_LINUX_22_JOYSTICK_API

72 73
#define JOYDEV_NEW "/dev/input/js"
#define JOYDEV_OLD "/dev/js"
74
#define JOYDEVDRIVER " (js)"
75

76 77 78 79
struct JoyDev
{
    char device[MAX_PATH];
    char name[MAX_PATH];
80

81 82
    BYTE axis_count;
    BYTE button_count;
83
    int  *dev_axes_map;
84 85
};

86
typedef struct JoystickImpl JoystickImpl;
87 88
static const IDirectInputDevice8AVtbl JoystickAvt;
static const IDirectInputDevice8WVtbl JoystickWvt;
89
struct JoystickImpl
Lionel Ulmer's avatar
Lionel Ulmer committed
90
{
91
        struct JoystickGenericImpl generic;
92

93
        struct JoyDev                  *joydev;
Lionel Ulmer's avatar
Lionel Ulmer committed
94 95 96

	/* joystick private */
	int				joyfd;
97
        POINTL                          povs[4];
Lionel Ulmer's avatar
Lionel Ulmer committed
98 99
};

100 101
static inline JoystickImpl *impl_from_IDirectInputDevice8A(IDirectInputDevice8A *iface)
{
102 103
    return CONTAINING_RECORD(CONTAINING_RECORD(CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8A_iface),
           JoystickGenericImpl, base), JoystickImpl, generic);
104 105 106
}
static inline JoystickImpl *impl_from_IDirectInputDevice8W(IDirectInputDevice8W *iface)
{
107 108
    return CONTAINING_RECORD(CONTAINING_RECORD(CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8W_iface),
           JoystickGenericImpl, base), JoystickImpl, generic);
109
}
110 111
static inline IDirectInputDevice8A *IDirectInputDevice8A_from_impl(JoystickImpl *This)
{
112
    return &This->generic.base.IDirectInputDevice8A_iface;
113 114 115
}
static inline IDirectInputDevice8W *IDirectInputDevice8W_from_impl(JoystickImpl *This)
{
116
    return &This->generic.base.IDirectInputDevice8W_iface;
117
}
118

119
static const GUID DInput_Wine_Joystick_GUID = { /* 9e573ed9-7734-11d2-8d4a-23903fb6bdf7 */
Lionel Ulmer's avatar
Lionel Ulmer committed
120 121 122 123 124 125
  0x9e573ed9,
  0x7734,
  0x11d2,
  {0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7}
};

126 127
#define MAX_JOYSTICKS 64
static INT joystick_devices_count = -1;
128
static struct JoyDev *joystick_devices;
129

130
static void joy_polldev(LPDIRECTINPUTDEVICE8A iface);
131

132
static INT find_joystick_devices(void)
133
{
134 135 136 137
    INT i;

    if (joystick_devices_count != -1) return joystick_devices_count;

138
    joystick_devices_count = 0;
139 140 141
    for (i = 0; i < MAX_JOYSTICKS; i++)
    {
        int fd;
142
        struct JoyDev joydev, *new_joydevs;
143
        BYTE axes_map[ABS_MAX + 1];
144

145 146
        snprintf(joydev.device, sizeof(joydev.device), "%s%d", JOYDEV_NEW, i);
        if ((fd = open(joydev.device, O_RDONLY)) < 0)
147
        {
148 149
            snprintf(joydev.device, sizeof(joydev.device), "%s%d", JOYDEV_OLD, i);
            if ((fd = open(joydev.device, O_RDONLY)) < 0) continue;
150
        }
151

152 153
        strcpy(joydev.name, "Wine Joystick");
#if defined(JSIOCGNAME)
154
        if (ioctl(fd, JSIOCGNAME(sizeof(joydev.name) - sizeof(JOYDEVDRIVER)), joydev.name) < 0)
155 156
            WARN("ioctl(%s,JSIOCGNAME) failed: %s\n", joydev.device, strerror(errno));
#endif
157 158 159 160

        /* Append driver name */
        strcat(joydev.name, JOYDEVDRIVER);

161 162 163 164
        if (device_disabled_registry(joydev.name)) {
            close(fd);
            continue;
        }
165

166 167 168 169 170 171 172 173 174 175 176 177 178 179
#ifdef JSIOCGAXES
        if (ioctl(fd, JSIOCGAXES, &joydev.axis_count) < 0)
        {
            WARN("ioctl(%s,JSIOCGAXES) failed: %s, defauting to 2\n", joydev.device, strerror(errno));
            joydev.axis_count = 2;
        }
#endif
#ifdef JSIOCGBUTTONS
        if (ioctl(fd, JSIOCGBUTTONS, &joydev.button_count) < 0)
        {
            WARN("ioctl(%s,JSIOCGBUTTONS) failed: %s, defauting to 2\n", joydev.device, strerror(errno));
            joydev.button_count = 2;
        }
#endif
180

181
        if (ioctl(fd, JSIOCGAXMAP, axes_map) < 0)
182
        {
183
            WARN("ioctl(%s,JSIOCGAXMAP) failed: %s\n", joydev.device, strerror(errno));
184
            joydev.dev_axes_map = NULL;
185 186
        }
        else
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
            if ((joydev.dev_axes_map = HeapAlloc(GetProcessHeap(), 0, joydev.axis_count * sizeof(int))))
            {
                INT j;

                /* Remap to DI numbers */
                for (j = 0; j < joydev.axis_count; j++)
                    if (axes_map[j] < 8)
                        /* Axis match 1-to-1 */
                        joydev.dev_axes_map[j] = j;
                    else if (axes_map[j] == 16 ||
                             axes_map[j] == 17)
                        /* POV axis */
                        joydev.dev_axes_map[j] = 8;
                    else
                        joydev.dev_axes_map[j] = -1;
            }
203

Andrew Talbot's avatar
Andrew Talbot committed
204 205
        close(fd);

206 207 208 209 210 211
        if (!joystick_devices_count)
            new_joydevs = HeapAlloc(GetProcessHeap(), 0, sizeof(struct JoyDev));
        else
            new_joydevs = HeapReAlloc(GetProcessHeap(), 0, joystick_devices,
                                      (joystick_devices_count + 1) * sizeof(struct JoyDev));
        if (!new_joydevs) continue;
212

213 214 215
        TRACE("Found a joystick on %s: %s\n  with %d axes and %d buttons\n", joydev.device,
              joydev.name, joydev.axis_count, joydev.button_count);

216 217
        joystick_devices = new_joydevs;
        joystick_devices[joystick_devices_count++] = joydev;
218
    }
219 220

    return joystick_devices_count;
221 222
}

223
static HRESULT joydev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
Lionel Ulmer's avatar
Lionel Ulmer committed
224
{
225
    int fd = -1;
226

227
    if (id >= find_joystick_devices()) return E_FAIL;
228

229 230
    if (dwFlags & DIEDFL_FORCEFEEDBACK) {
        WARN("force feedback not supported\n");
231
        return S_FALSE;
232 233
    }

234
    if ((dwDevType == 0) ||
235 236
	((dwDevType == DIDEVTYPE_JOYSTICK) && (version > 0x0300 && version < 0x0800)) ||
	(((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))) {
237
        /* check whether we have a joystick */
238
        if ((fd = open(joystick_devices[id].device, O_RDONLY)) < 0)
239
        {
240
            WARN("open(%s, O_RDONLY) failed: %s\n", joystick_devices[id].name, strerror(errno));
241
            return S_FALSE;
242
        }
243

244
        /* Return joystick */
245 246
        lpddi->guidInstance = DInput_Wine_Joystick_GUID;
        lpddi->guidInstance.Data3 = id;
247 248
        lpddi->guidProduct = DInput_Wine_Joystick_GUID;
        /* we only support traditional joysticks for now */
249
        if (version >= 0x0800)
250 251 252
            lpddi->dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
        else
            lpddi->dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
253 254 255

        strcpy(lpddi->tszInstanceName, joystick_devices[id].name);
        strcpy(lpddi->tszProductName,  joystick_devices[id].name);
256 257 258

        lpddi->guidFFDriver = GUID_NULL;
        close(fd);
259
        TRACE("Enumerating the linux Joystick device: %s (%s)\n", joystick_devices[id].device, lpddi->tszProductName);
260
        return S_OK;
Lionel Ulmer's avatar
Lionel Ulmer committed
261 262
    }

263
    return S_FALSE;
Lionel Ulmer's avatar
Lionel Ulmer committed
264 265
}

266
static HRESULT joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
Lionel Ulmer's avatar
Lionel Ulmer committed
267
{
268
    int fd = -1;
269

270
    if (id >= find_joystick_devices()) return E_FAIL;
271

272 273
    if (dwFlags & DIEDFL_FORCEFEEDBACK) {
        WARN("force feedback not supported\n");
274
        return S_FALSE;
275
    }
276

277
    if ((dwDevType == 0) ||
278 279
	((dwDevType == DIDEVTYPE_JOYSTICK) && (version > 0x0300 && version < 0x0800)) ||
	(((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))) {
280
        /* check whether we have a joystick */
281
        if ((fd = open(joystick_devices[id].device, O_RDONLY)) < 0)
282
        {
283
            WARN("open(%s,O_RDONLY) failed: %s\n", joystick_devices[id].device, strerror(errno));
284
            return S_FALSE;
285 286 287
        }

        /* Return joystick */
288 289
        lpddi->guidInstance = DInput_Wine_Joystick_GUID;
        lpddi->guidInstance.Data3 = id;
290 291
        lpddi->guidProduct = DInput_Wine_Joystick_GUID;
        /* we only support traditional joysticks for now */
292
        if (version >= 0x0800)
293 294 295
            lpddi->dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
        else
            lpddi->dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
296 297 298

        MultiByteToWideChar(CP_ACP, 0, joystick_devices[id].name, -1, lpddi->tszInstanceName, MAX_PATH);
        MultiByteToWideChar(CP_ACP, 0, joystick_devices[id].name, -1, lpddi->tszProductName, MAX_PATH);
299 300
        lpddi->guidFFDriver = GUID_NULL;
        close(fd);
301
        TRACE("Enumerating the linux Joystick device: %s (%s)\n", joystick_devices[id].device, joystick_devices[id].name);
302
        return S_OK;
303 304
    }

305
    return S_FALSE;
306 307
}

308 309
static HRESULT alloc_device(REFGUID rguid, IDirectInputImpl *dinput,
                            JoystickImpl **pdev, unsigned short index)
310
{
311 312
    DWORD i;
    JoystickImpl* newDevice;
313
    HRESULT hr;
314 315
    LPDIDATAFORMAT df = NULL;
    int idx = 0;
316

317
    TRACE("%s %p %p %hu\n", debugstr_guid(rguid), dinput, pdev, index);
318

319
    newDevice = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(JoystickImpl));
320 321 322 323 324 325
    if (newDevice == 0) {
        WARN("out of memory\n");
        *pdev = 0;
        return DIERR_OUTOFMEMORY;
    }

326
    newDevice->joydev = &joystick_devices[index];
327
    newDevice->joyfd = -1;
328 329
    newDevice->generic.guidInstance = DInput_Wine_Joystick_GUID;
    newDevice->generic.guidInstance.Data3 = index;
330
    newDevice->generic.guidProduct = DInput_Wine_Joystick_GUID;
331
    newDevice->generic.joy_polldev = joy_polldev;
332
    newDevice->generic.name        = newDevice->joydev->name;
333 334
    newDevice->generic.device_axis_count = newDevice->joydev->axis_count;
    newDevice->generic.devcaps.dwButtons = newDevice->joydev->button_count;
335

336
    if (newDevice->generic.devcaps.dwButtons > 128)
337
    {
338 339
        WARN("Can't support %d buttons. Clamping down to 128\n", newDevice->generic.devcaps.dwButtons);
        newDevice->generic.devcaps.dwButtons = 128;
340 341
    }

342 343
    newDevice->generic.base.IDirectInputDevice8A_iface.lpVtbl = &JoystickAvt;
    newDevice->generic.base.IDirectInputDevice8W_iface.lpVtbl = &JoystickWvt;
344 345 346 347 348
    newDevice->generic.base.ref = 1;
    newDevice->generic.base.dinput = dinput;
    newDevice->generic.base.guid = *rguid;
    InitializeCriticalSection(&newDevice->generic.base.crit);
    newDevice->generic.base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": JoystickImpl*->generic.base.crit");
349

350
    /* setup_dinput_options may change these */
351
    newDevice->generic.deadzone = 0;
352 353

    /* do any user specified configuration */
354
    hr = setup_dinput_options(&newDevice->generic, newDevice->joydev->dev_axes_map);
355 356 357
    if (hr != DI_OK)
        goto FAILED1;

358 359 360 361
    /* Create copy of default data format */
    if (!(df = HeapAlloc(GetProcessHeap(), 0, c_dfDIJoystick2.dwSize))) goto FAILED;
    memcpy(df, &c_dfDIJoystick2, c_dfDIJoystick2.dwSize);

362
    df->dwNumObjs = newDevice->generic.devcaps.dwAxes + newDevice->generic.devcaps.dwPOVs + newDevice->generic.devcaps.dwButtons;
363 364
    if (!(df->rgodf = HeapAlloc(GetProcessHeap(), 0, df->dwNumObjs * df->dwObjSize))) goto FAILED;

365
    for (i = 0; i < newDevice->generic.device_axis_count; i++)
366
    {
367
        int wine_obj = newDevice->generic.axis_map[i];
368

369 370
        if (wine_obj < 0) continue;

371 372 373 374
        memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[wine_obj], df->dwObjSize);
        if (wine_obj < 8)
            df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(wine_obj) | DIDFT_ABSAXIS;
        else
375
        {
376
            df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(wine_obj - 8) | DIDFT_POV;
377 378
            i++; /* POV takes 2 axes */
        }
379
    }
380
    for (i = 0; i < newDevice->generic.devcaps.dwButtons; i++)
381 382
    {
        memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[i + 12], df->dwObjSize);
383
        df->rgodf[idx  ].pguid = &GUID_Button;
384 385
        df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(i) | DIDFT_PSHBUTTON;
    }
386
    newDevice->generic.base.data_format.wine_df = df;
387

388 389
    /* initialize default properties */
    for (i = 0; i < c_dfDIJoystick2.dwNumObjs; i++) {
390 391 392 393
        newDevice->generic.props[i].lDevMin = -32767;
        newDevice->generic.props[i].lDevMax = +32767;
        newDevice->generic.props[i].lMin = 0;
        newDevice->generic.props[i].lMax = 0xffff;
394
        newDevice->generic.props[i].lDeadZone = newDevice->generic.deadzone; /* % * 1000 */
395
        newDevice->generic.props[i].lSaturation = 0;
396 397
    }

398
    IDirectInput_AddRef(&newDevice->generic.base.dinput->IDirectInput7A_iface);
399

400 401 402 403
    EnterCriticalSection(&dinput->crit);
    list_add_tail(&dinput->devices_list, &newDevice->generic.base.entry);
    LeaveCriticalSection(&dinput->crit);

404 405 406 407
    newDevice->generic.devcaps.dwSize = sizeof(newDevice->generic.devcaps);
    newDevice->generic.devcaps.dwFlags = DIDC_ATTACHED;
    if (newDevice->generic.base.dinput->dwVersion >= 0x0800)
        newDevice->generic.devcaps.dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
408
    else
409 410 411 412 413 414
        newDevice->generic.devcaps.dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
    newDevice->generic.devcaps.dwFFSamplePeriod = 0;
    newDevice->generic.devcaps.dwFFMinTimeResolution = 0;
    newDevice->generic.devcaps.dwFirmwareRevision = 0;
    newDevice->generic.devcaps.dwHardwareRevision = 0;
    newDevice->generic.devcaps.dwFFDriverVersion = 0;
415 416

    if (TRACE_ON(dinput)) {
417
        _dump_DIDATAFORMAT(newDevice->generic.base.data_format.wine_df);
418
       for (i = 0; i < (newDevice->generic.device_axis_count); i++)
419
           TRACE("axis_map[%d] = %d\n", i, newDevice->generic.axis_map[i]);
420
        _dump_DIDEVCAPS(&newDevice->generic.devcaps);
421 422
    }

423
    *pdev = newDevice;
424

425
    return DI_OK;
426 427

FAILED:
428 429
    hr = DIERR_OUTOFMEMORY;
FAILED1:
430 431
    if (df) HeapFree(GetProcessHeap(), 0, df->rgodf);
    HeapFree(GetProcessHeap(), 0, df);
432
    release_DataFormat(&newDevice->generic.base.data_format);
433
    HeapFree(GetProcessHeap(),0,newDevice->generic.axis_map);
434
    HeapFree(GetProcessHeap(),0,newDevice);
435 436 437
    *pdev = 0;

    return hr;
Lionel Ulmer's avatar
Lionel Ulmer committed
438 439
}

440 441 442 443
/******************************************************************************
  *     get_joystick_index : Get the joystick index from a given GUID
  */
static unsigned short get_joystick_index(REFGUID guid)
444 445 446 447 448 449 450
{
    GUID wine_joystick = DInput_Wine_Joystick_GUID;
    GUID dev_guid = *guid;

    wine_joystick.Data3 = 0;
    dev_guid.Data3 = 0;

451 452 453 454 455 456 457
    /* for the standard joystick GUID use index 0 */
    if(IsEqualGUID(&GUID_Joystick,guid)) return 0;

    /* for the wine joystick GUIDs use the index stored in Data3 */
    if(IsEqualGUID(&wine_joystick, &dev_guid)) return guid->Data3;

    return MAX_JOYSTICKS;
458 459
}

460
static HRESULT joydev_create_device(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPVOID *pdev, int unicode)
Lionel Ulmer's avatar
Lionel Ulmer committed
461
{
462 463
    unsigned short index;

464
    TRACE("%p %s %s %p %i\n", dinput, debugstr_guid(rguid), debugstr_guid(riid), pdev, unicode);
465 466 467 468 469 470
    find_joystick_devices();
    *pdev = NULL;

    if ((index = get_joystick_index(rguid)) < MAX_JOYSTICKS &&
        joystick_devices_count && index < joystick_devices_count)
    {
471 472 473 474 475 476 477 478 479
        JoystickImpl *This;
        HRESULT hr;

        if (riid == NULL)
            ;/* nothing */
        else if (IsEqualGUID(&IID_IDirectInputDeviceA,  riid) ||
                 IsEqualGUID(&IID_IDirectInputDevice2A, riid) ||
                 IsEqualGUID(&IID_IDirectInputDevice7A, riid) ||
                 IsEqualGUID(&IID_IDirectInputDevice8A, riid))
480
        {
481 482 483 484 485 486 487 488 489 490 491 492 493
            unicode = 0;
        }
        else if (IsEqualGUID(&IID_IDirectInputDeviceW,  riid) ||
                 IsEqualGUID(&IID_IDirectInputDevice2W, riid) ||
                 IsEqualGUID(&IID_IDirectInputDevice7W, riid) ||
                 IsEqualGUID(&IID_IDirectInputDevice8W, riid))
        {
            unicode = 1;
        }
        else
        {
            WARN("no interface\n");
            return DIERR_NOINTERFACE;
494 495
        }

496 497
        hr = alloc_device(rguid, dinput, &This, index);
        if (!This) return hr;
498

499 500 501 502
        if (unicode)
            *pdev = &This->generic.base.IDirectInputDevice8W_iface;
        else
            *pdev = &This->generic.base.IDirectInputDevice8A_iface;
503

504
        return hr;
505
    }
506

507
    return DIERR_DEVICENOTREG;
508 509
}

510 511
#undef MAX_JOYSTICKS

512
const struct dinput_device joystick_linux_device = {
513
  "Wine Linux joystick driver",
514 515
  joydev_enum_deviceA,
  joydev_enum_deviceW,
516
  joydev_create_device
Lionel Ulmer's avatar
Lionel Ulmer committed
517 518 519 520 521
};

/******************************************************************************
  *     Acquire : gets exclusive control of the joystick
  */
522
static HRESULT WINAPI JoystickLinuxWImpl_Acquire(LPDIRECTINPUTDEVICE8W iface)
Lionel Ulmer's avatar
Lionel Ulmer committed
523
{
524
    JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
525
    HRESULT res;
526

527 528
    TRACE("(%p)\n",This);

529
    res = IDirectInputDevice2WImpl_Acquire(iface);
530 531
    if (res != DI_OK)
        return res;
532 533 534

    /* open the joystick device */
    if (This->joyfd==-1) {
535
        TRACE("opening joystick device %s\n", This->joydev->device);
536

537
        This->joyfd = open(This->joydev->device, O_RDONLY);
538
        if (This->joyfd==-1) {
539
            ERR("open(%s) failed: %s\n", This->joydev->device, strerror(errno));
540
            IDirectInputDevice2WImpl_Unacquire(iface);
541 542 543 544
            return DIERR_NOTFOUND;
        }
    }

545
    return DI_OK;
Lionel Ulmer's avatar
Lionel Ulmer committed
546 547
}

548 549 550 551 552 553
static HRESULT WINAPI JoystickLinuxAImpl_Acquire(LPDIRECTINPUTDEVICE8A iface)
{
    JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
    return JoystickLinuxWImpl_Acquire(IDirectInputDevice8W_from_impl(This));
}

554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
/******************************************************************************
  *     GetProperty : get input device properties
  */
static HRESULT WINAPI JoystickLinuxWImpl_GetProperty(LPDIRECTINPUTDEVICE8W iface, REFGUID rguid, LPDIPROPHEADER pdiph)
{
    JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);

    TRACE("(this=%p,%s,%p)\n", iface, debugstr_guid(rguid), pdiph);
    _dump_DIPROPHEADER(pdiph);

    if (!IS_DIPROP(rguid)) return DI_OK;

    switch (LOWORD(rguid)) {

        case (DWORD_PTR) DIPROP_JOYSTICKID:
        {
            LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph;

            pd->dwData = get_joystick_index(&This->generic.base.guid);
            TRACE("DIPROP_JOYSTICKID(%d)\n", pd->dwData);
            break;
        }

    default:
        return JoystickWGenericImpl_GetProperty(iface, rguid, pdiph);
    }

    return DI_OK;
}

static HRESULT WINAPI JoystickLinuxAImpl_GetProperty(LPDIRECTINPUTDEVICE8A iface, REFGUID rguid, LPDIPROPHEADER pdiph)
{
    JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
    return JoystickLinuxWImpl_GetProperty(IDirectInputDevice8W_from_impl(This), rguid, pdiph);
}

Lionel Ulmer's avatar
Lionel Ulmer committed
590 591 592
/******************************************************************************
  *     Unacquire : frees the joystick
  */
593
static HRESULT WINAPI JoystickLinuxWImpl_Unacquire(LPDIRECTINPUTDEVICE8W iface)
Lionel Ulmer's avatar
Lionel Ulmer committed
594
{
595
    JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
596
    HRESULT res;
Lionel Ulmer's avatar
Lionel Ulmer committed
597

598 599
    TRACE("(%p)\n",This);

600
    res = IDirectInputDevice2WImpl_Unacquire(iface);
601 602 603

    if (res != DI_OK)
        return res;
604

Lionel Ulmer's avatar
Lionel Ulmer committed
605
    if (This->joyfd!=-1) {
606 607 608 609
        TRACE("closing joystick device\n");
        close(This->joyfd);
        This->joyfd = -1;
        return DI_OK;
Lionel Ulmer's avatar
Lionel Ulmer committed
610
    }
611 612

    return DI_NOEFFECT;
Lionel Ulmer's avatar
Lionel Ulmer committed
613 614
}

615 616 617 618 619 620
static HRESULT WINAPI JoystickLinuxAImpl_Unacquire(LPDIRECTINPUTDEVICE8A iface)
{
    JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
    return JoystickLinuxWImpl_Unacquire(IDirectInputDevice8W_from_impl(This));
}

621 622
static void joy_polldev(LPDIRECTINPUTDEVICE8A iface)
{
623
    struct pollfd plfd;
624
    struct js_event jse;
625
    JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
626

627
    TRACE("(%p)\n", This);
Lionel Ulmer's avatar
Lionel Ulmer committed
628

629
    if (This->joyfd==-1) {
630 631
        WARN("no device\n");
        return;
632
    }
633 634 635 636 637
    while (1)
    {
        LONG value;
        int inst_id = -1;

638 639 640
	plfd.fd = This->joyfd;
	plfd.events = POLLIN;
	if (poll(&plfd,1,0) != 1)
Lionel Ulmer's avatar
Lionel Ulmer committed
641 642 643 644 645
	    return;
	/* we have one event, so we can read */
	if (sizeof(jse)!=read(This->joyfd,&jse,sizeof(jse))) {
	    return;
	}
646
        TRACE("js_event: type 0x%x, number %d, value %d\n",
647
              jse.type,jse.number,jse.value);
648 649
        if (jse.type & JS_EVENT_BUTTON)
        {
650
            if (jse.number >= This->generic.devcaps.dwButtons) return;
651

652
            inst_id = DIDFT_MAKEINSTANCE(jse.number) | DIDFT_PSHBUTTON;
653
            This->generic.js.rgbButtons[jse.number] = value = jse.value ? 0x80 : 0x00;
654 655 656
        }
        else if (jse.type & JS_EVENT_AXIS)
        {
657
            int number = This->generic.axis_map[jse.number];	/* wine format object index */
658

659
            if (number < 0) return;
660 661
            inst_id = number < 8 ?  DIDFT_MAKEINSTANCE(number) | DIDFT_ABSAXIS :
                                    DIDFT_MAKEINSTANCE(number - 8) | DIDFT_POV;
662
            value = joystick_map_axis(&This->generic.props[id_to_object(This->generic.base.data_format.wine_df, inst_id)], jse.value);
663 664 665

            TRACE("changing axis %d => %d\n", jse.number, number);
            switch (number)
666
            {
667 668 669 670 671 672 673 674
                case 0: This->generic.js.lX  = value; break;
                case 1: This->generic.js.lY  = value; break;
                case 2: This->generic.js.lZ  = value; break;
                case 3: This->generic.js.lRx = value; break;
                case 4: This->generic.js.lRy = value; break;
                case 5: This->generic.js.lRz = value; break;
                case 6: This->generic.js.rglSlider[0] = value; break;
                case 7: This->generic.js.rglSlider[1] = value; break;
675 676 677 678 679 680 681 682 683
                case 8: case 9: case 10: case 11:
                {
                    int idx = number - 8;

                    if (jse.number % 2)
                        This->povs[idx].y = jse.value;
                    else
                        This->povs[idx].x = jse.value;

684
                    This->generic.js.rgdwPOV[idx] = value = joystick_map_pov(&This->povs[idx]);
685
                    break;
686
                }
687 688 689
                default:
                    WARN("axis %d not supported\n", number);
            }
690
        }
691
        if (inst_id >= 0)
692
            queue_event(iface, inst_id, value, GetCurrentTime(), This->generic.base.dinput->evsequence++);
Lionel Ulmer's avatar
Lionel Ulmer committed
693 694 695
    }
}

696
static const IDirectInputDevice8AVtbl JoystickAvt =
Lionel Ulmer's avatar
Lionel Ulmer committed
697 698 699
{
	IDirectInputDevice2AImpl_QueryInterface,
	IDirectInputDevice2AImpl_AddRef,
700
        IDirectInputDevice2AImpl_Release,
701
	JoystickAGenericImpl_GetCapabilities,
702
        IDirectInputDevice2AImpl_EnumObjects,
703
	JoystickLinuxAImpl_GetProperty,
704
	JoystickAGenericImpl_SetProperty,
705 706
	JoystickLinuxAImpl_Acquire,
	JoystickLinuxAImpl_Unacquire,
707
	JoystickAGenericImpl_GetDeviceState,
708
	IDirectInputDevice2AImpl_GetDeviceData,
709
	IDirectInputDevice2AImpl_SetDataFormat,
710
	IDirectInputDevice2AImpl_SetEventNotification,
Lionel Ulmer's avatar
Lionel Ulmer committed
711
	IDirectInputDevice2AImpl_SetCooperativeLevel,
712
	JoystickAGenericImpl_GetObjectInfo,
713
	JoystickAGenericImpl_GetDeviceInfo,
Lionel Ulmer's avatar
Lionel Ulmer committed
714 715 716 717 718 719 720 721 722
	IDirectInputDevice2AImpl_RunControlPanel,
	IDirectInputDevice2AImpl_Initialize,
	IDirectInputDevice2AImpl_CreateEffect,
	IDirectInputDevice2AImpl_EnumEffects,
	IDirectInputDevice2AImpl_GetEffectInfo,
	IDirectInputDevice2AImpl_GetForceFeedbackState,
	IDirectInputDevice2AImpl_SendForceFeedbackCommand,
	IDirectInputDevice2AImpl_EnumCreatedEffectObjects,
	IDirectInputDevice2AImpl_Escape,
723
	JoystickAGenericImpl_Poll,
Lionel Ulmer's avatar
Lionel Ulmer committed
724 725
	IDirectInputDevice2AImpl_SendDeviceData,
	IDirectInputDevice7AImpl_EnumEffectsInFile,
726
	IDirectInputDevice7AImpl_WriteEffectToFile,
727 728
	JoystickAGenericImpl_BuildActionMap,
	JoystickAGenericImpl_SetActionMap,
729
	IDirectInputDevice8AImpl_GetImageInfo
Lionel Ulmer's avatar
Lionel Ulmer committed
730 731
};

Vitaliy Margolen's avatar
Vitaliy Margolen committed
732
static const IDirectInputDevice8WVtbl JoystickWvt =
733
{
734 735 736
    IDirectInputDevice2WImpl_QueryInterface,
    IDirectInputDevice2WImpl_AddRef,
    IDirectInputDevice2WImpl_Release,
737
    JoystickWGenericImpl_GetCapabilities,
738
    IDirectInputDevice2WImpl_EnumObjects,
739
    JoystickLinuxWImpl_GetProperty,
740
    JoystickWGenericImpl_SetProperty,
741 742
    JoystickLinuxWImpl_Acquire,
    JoystickLinuxWImpl_Unacquire,
743
    JoystickWGenericImpl_GetDeviceState,
744 745 746 747 748 749 750 751 752 753 754 755 756 757 758
    IDirectInputDevice2WImpl_GetDeviceData,
    IDirectInputDevice2WImpl_SetDataFormat,
    IDirectInputDevice2WImpl_SetEventNotification,
    IDirectInputDevice2WImpl_SetCooperativeLevel,
    JoystickWGenericImpl_GetObjectInfo,
    JoystickWGenericImpl_GetDeviceInfo,
    IDirectInputDevice2WImpl_RunControlPanel,
    IDirectInputDevice2WImpl_Initialize,
    IDirectInputDevice2WImpl_CreateEffect,
    IDirectInputDevice2WImpl_EnumEffects,
    IDirectInputDevice2WImpl_GetEffectInfo,
    IDirectInputDevice2WImpl_GetForceFeedbackState,
    IDirectInputDevice2WImpl_SendForceFeedbackCommand,
    IDirectInputDevice2WImpl_EnumCreatedEffectObjects,
    IDirectInputDevice2WImpl_Escape,
759
    JoystickWGenericImpl_Poll,
760 761 762
    IDirectInputDevice2WImpl_SendDeviceData,
    IDirectInputDevice7WImpl_EnumEffectsInFile,
    IDirectInputDevice7WImpl_WriteEffectToFile,
763 764
    JoystickWGenericImpl_BuildActionMap,
    JoystickWGenericImpl_SetActionMap,
765
    IDirectInputDevice8WImpl_GetImageInfo
766 767
};

768 769 770 771 772 773 774 775 776
#else  /* HAVE_LINUX_22_JOYSTICK_API */

const struct dinput_device joystick_linux_device = {
  "Wine Linux joystick driver",
  NULL,
  NULL,
  NULL
};

Lionel Ulmer's avatar
Lionel Ulmer committed
777
#endif  /* HAVE_LINUX_22_JOYSTICK_API */