1 /* DirectInput Joystick device
3 * Copyright 1998,2000 Marcus Meissner
4 * Copyright 1998,1999 Lionel Ulmer
5 * Copyright 2000-2001 TransGaming Technologies Inc.
6 * Copyright 2005 Daniel Remenak
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
24 #include "wine/port.h"
34 #ifdef HAVE_SYS_TIME_H
35 # include <sys/time.h>
37 #include <sys/fcntl.h>
38 #ifdef HAVE_SYS_IOCTL_H
39 # include <sys/ioctl.h>
42 #ifdef HAVE_SYS_ERRNO_H
43 # include <sys/errno.h>
45 #ifdef HAVE_LINUX_INPUT_H
46 # include <linux/input.h>
47 # if defined(EVIOCGBIT) && defined(EV_ABS) && defined(BTN_PINKIE)
48 # define HAVE_CORRECT_LINUXINPUT_H
51 #ifdef HAVE_SYS_POLL_H
52 # include <sys/poll.h>
55 #include "wine/debug.h"
56 #include "wine/unicode.h"
62 #include "dinput_private.h"
63 #include "device_private.h"
65 WINE_DEFAULT_DEBUG_CHANNEL(dinput);
67 #ifdef HAVE_CORRECT_LINUXINPUT_H
69 #define EVDEVPREFIX "/dev/input/event"
71 /* Wine joystick driver object instances */
72 #define WINE_JOYSTICK_AXIS_BASE 0
73 #define WINE_JOYSTICK_POV_BASE 6
74 #define WINE_JOYSTICK_BUTTON_BASE 8
76 typedef struct EffectListItem EffectListItem;
79 LPDIRECTINPUTEFFECT ref;
80 struct EffectListItem* next;
83 /* implemented in effect_linuxinput.c */
84 HRESULT linuxinput_create_effect(int* fd, REFGUID rguid, LPDIRECTINPUTEFFECT* peff);
85 HRESULT linuxinput_get_info_A(int fd, REFGUID rguid, LPDIEFFECTINFOA info);
86 HRESULT linuxinput_get_info_W(int fd, REFGUID rguid, LPDIEFFECTINFOW info);
88 typedef struct JoystickImpl JoystickImpl;
89 static const IDirectInputDevice8AVtbl JoystickAvt;
90 static const IDirectInputDevice8WVtbl JoystickWvt;
100 /* data returned by EVIOCGBIT for caps, EV_ABS, EV_KEY, and EV_FF */
101 BYTE evbits[(EV_MAX+7)/8];
102 BYTE absbits[(ABS_MAX+7)/8];
103 BYTE keybits[(KEY_MAX+7)/8];
104 BYTE ffbits[(FF_MAX+7)/8];
109 #define AXE_ABSFUZZ 3
110 #define AXE_ABSFLAT 4
112 /* data returned by the EVIOCGABS() ioctl */
113 int axes[ABS_MAX][5];
114 /* LUT for KEY_ to offset in rgbButtons */
115 BYTE buttons[KEY_MAX];
117 /* autodetecting ranges per axe by following movement */
118 LONG havemax[ABS_MAX];
119 LONG havemin[ABS_MAX];
124 struct IDirectInputDevice2AImpl base;
126 struct JoyDev *joydev;
128 /* The 'parent' DInput */
129 IDirectInputImpl *dinput;
131 /* joystick private */
132 /* what range and deadzone the game wants */
133 LONG wantmin[ABS_MAX];
134 LONG wantmax[ABS_MAX];
139 LPDIDATAFORMAT internal_df;
141 DataFormat *transform; /* wine to user format converter */
142 int *offsets; /* object offsets */
145 /* Force feedback variables */
146 EffectListItem* top_effect;
150 static void fake_current_js_state(JoystickImpl *ji);
151 static int find_property_offset(JoystickImpl *This, LPCDIPROPHEADER ph);
152 static DWORD map_pov(int event_value, int is_x);
153 static void find_joydevs(void);
154 static int lxinput_to_user_offset(JoystickImpl *This, int ie_type, int ie_code);
155 static int offset_to_object(JoystickImpl *This, int offset);
156 static void calculate_ids(LPDIDATAFORMAT df);
158 /* This GUID is slightly different from the linux joystick one. Take note. */
159 static const GUID DInput_Wine_Joystick_Base_GUID = { /* 9e573eda-7734-11d2-8d4a-23903fb6bdf7 */
163 {0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7}
166 #define test_bit(arr,bit) (((BYTE*)(arr))[(bit)>>3]&(1<<((bit)&7)))
168 #define MAX_JOYDEV 64
170 static int have_joydevs = -1;
171 static struct JoyDev *joydevs = NULL;
173 static void find_joydevs(void)
177 if (have_joydevs!=-1) {
183 for (i=0;i<MAX_JOYDEV;i++) {
185 struct JoyDev joydev = {0};
190 snprintf(buf,MAX_PATH,EVDEVPREFIX"%d",i);
193 if ((fd=open(buf, O_RDWR))==-1) {
194 fd = open(buf, O_RDONLY);
199 if ((-1==ioctl(fd,EVIOCGBIT(0,sizeof(joydev.evbits)),joydev.evbits))) {
200 perror("EVIOCGBIT 0");
204 if (-1==ioctl(fd,EVIOCGBIT(EV_ABS,sizeof(joydev.absbits)),joydev.absbits)) {
205 perror("EVIOCGBIT EV_ABS");
209 if (-1==ioctl(fd,EVIOCGBIT(EV_KEY,sizeof(joydev.keybits)),joydev.keybits)) {
210 perror("EVIOCGBIT EV_KEY");
214 /* A true joystick has at least axis X and Y, and at least 1
215 * button. copied from linux/drivers/input/joydev.c */
216 if (test_bit(joydev.absbits,ABS_X) && test_bit(joydev.absbits,ABS_Y) &&
217 ( test_bit(joydev.keybits,BTN_TRIGGER) ||
218 test_bit(joydev.keybits,BTN_A) ||
219 test_bit(joydev.keybits,BTN_1)
223 joydev.device = strdup(buf);
225 if (-1!=ioctl(fd, EVIOCGNAME(MAX_PATH-1), buf)) {
227 joydev.name = strdup(buf);
229 joydev.name = joydev.device;
232 joydev.guid = DInput_Wine_Joystick_Base_GUID;
233 joydev.guid.Data3 += have_joydevs;
235 TRACE("Found a joystick on %s: %s (%s)\n",
236 joydev.device, joydev.name,
237 debugstr_guid(&joydev.guid)
240 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
242 if ((!test_bit(joydev.evbits,EV_FF))
243 || (-1==ioctl(fd,EVIOCGBIT(EV_FF,sizeof(joydev.ffbits)),joydev.ffbits))
244 || (-1==ioctl(fd,EVIOCGEFFECTS,&joydev.num_effects))
245 || (joydev.num_effects <= 0)) {
248 TRACE(" ... with force feedback\n");
254 for (j=0;j<ABS_MAX;j++) {
255 if (test_bit(joydev.absbits,j)) {
256 if (-1!=ioctl(fd,EVIOCGABS(j),&(joydev.axes[j]))) {
257 TRACE(" ... with axe %d: cur=%d, min=%d, max=%d, fuzz=%d, flat=%d\n",
259 joydev.axes[j][AXE_ABS],
260 joydev.axes[j][AXE_ABSMIN],
261 joydev.axes[j][AXE_ABSMAX],
262 joydev.axes[j][AXE_ABSFUZZ],
263 joydev.axes[j][AXE_ABSFLAT]
265 joydev.havemin[j] = joydev.axes[j][AXE_ABSMIN];
266 joydev.havemax[j] = joydev.axes[j][AXE_ABSMAX];
272 for (j=0;j<KEY_MAX;j++) {
273 if (test_bit(joydev.keybits,j)) {
274 TRACE(" ... with button %d: %d\n", j, buttons);
275 joydev.buttons[j] = 0x80 | buttons;
280 if (have_joydevs==0) {
281 joydevs = HeapAlloc(GetProcessHeap(), 0, sizeof(struct JoyDev));
283 HeapReAlloc(GetProcessHeap(), 0, joydevs, (1+have_joydevs) * sizeof(struct JoyDev));
285 memcpy(joydevs+have_joydevs, &joydev, sizeof(struct JoyDev));
294 static BOOL joydev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
298 if (id >= have_joydevs) {
302 if (!((dwDevType == 0) ||
303 ((dwDevType == DIDEVTYPE_JOYSTICK) && (version < 0x0800)) ||
304 (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))))
307 #ifndef HAVE_STRUCT_FF_EFFECT_DIRECTION
308 if (dwFlags & DIEDFL_FORCEFEEDBACK)
312 if (!(dwFlags & DIEDFL_FORCEFEEDBACK) || joydevs[id].has_ff) {
313 lpddi->guidInstance = joydevs[id].guid;
314 lpddi->guidProduct = DInput_Wine_Joystick_Base_GUID;
316 lpddi->guidFFDriver = GUID_NULL;
317 if (version >= 0x0800)
318 lpddi->dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
320 lpddi->dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
322 strcpy(lpddi->tszInstanceName, joydevs[id].name);
323 strcpy(lpddi->tszProductName, joydevs[id].device);
329 static BOOL joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
333 if (id >= have_joydevs) {
337 if (!((dwDevType == 0) ||
338 ((dwDevType == DIDEVTYPE_JOYSTICK) && (version < 0x0800)) ||
339 (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))))
342 #ifndef HAVE_STRUCT_FF_EFFECT_DIRECTION
343 if (dwFlags & DIEDFL_FORCEFEEDBACK)
347 if (!(dwFlags & DIEDFL_FORCEFEEDBACK) || joydevs[id].has_ff) {
348 lpddi->guidInstance = joydevs[id].guid;
349 lpddi->guidProduct = DInput_Wine_Joystick_Base_GUID;
351 lpddi->guidFFDriver = GUID_NULL;
352 if (version >= 0x0800)
353 lpddi->dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
355 lpddi->dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
357 MultiByteToWideChar(CP_ACP, 0, joydevs[id].name, -1, lpddi->tszInstanceName, MAX_PATH);
358 MultiByteToWideChar(CP_ACP, 0, joydevs[id].device, -1, lpddi->tszProductName, MAX_PATH);
364 static JoystickImpl *alloc_device(REFGUID rguid, const void *jvt, IDirectInputImpl *dinput, struct JoyDev *joydev)
366 JoystickImpl* newDevice;
369 newDevice = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(JoystickImpl));
370 if (newDevice==NULL) {
374 newDevice->base.lpVtbl = jvt;
375 newDevice->base.ref = 1;
376 memcpy(&newDevice->base.guid, rguid, sizeof(*rguid));
377 InitializeCriticalSection(&newDevice->base.crit);
378 newDevice->joyfd = -1;
379 newDevice->dinput = dinput;
380 newDevice->joydev = joydev;
381 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
382 newDevice->ff_state = FF_STATUS_STOPPED;
384 for (i=0;i<ABS_MAX;i++) {
385 /* apps expect the range to be the same they would get from the
386 * GetProperty/range method */
387 newDevice->wantmin[i] = newDevice->joydev->havemin[i];
388 newDevice->wantmax[i] = newDevice->joydev->havemax[i];
390 * direct input defines a default for the deadzone somewhere; but as long
391 * as in map_axis the code for the dead zone is commented out its no
394 newDevice->deadz[i] = 0;
396 fake_current_js_state(newDevice);
398 /* wine uses DIJOYSTATE2 as it's internal format so copy
399 * the already defined format c_dfDIJoystick2 */
400 newDevice->df = HeapAlloc(GetProcessHeap(),0,c_dfDIJoystick2.dwSize);
401 if (newDevice->df == 0)
403 CopyMemory(newDevice->df, &c_dfDIJoystick2, c_dfDIJoystick2.dwSize);
405 /* copy default objects */
406 newDevice->df->rgodf = HeapAlloc(GetProcessHeap(),0,c_dfDIJoystick2.dwNumObjs*c_dfDIJoystick2.dwObjSize);
407 if (newDevice->df->rgodf == 0)
409 CopyMemory(newDevice->df->rgodf,c_dfDIJoystick2.rgodf,c_dfDIJoystick2.dwNumObjs*c_dfDIJoystick2.dwObjSize);
411 /* no do the same for the internal df */
412 newDevice->internal_df = HeapAlloc(GetProcessHeap(),0,c_dfDIJoystick2.dwSize);
413 if (newDevice->internal_df == 0)
415 CopyMemory(newDevice->internal_df, &c_dfDIJoystick2, c_dfDIJoystick2.dwSize);
417 /* copy default objects */
418 newDevice->internal_df->rgodf = HeapAlloc(GetProcessHeap(),0,c_dfDIJoystick2.dwNumObjs*c_dfDIJoystick2.dwObjSize);
419 if (newDevice->internal_df->rgodf == 0)
421 CopyMemory(newDevice->internal_df->rgodf,c_dfDIJoystick2.rgodf,c_dfDIJoystick2.dwNumObjs*c_dfDIJoystick2.dwObjSize);
423 /* create an offsets array */
424 newDevice->offsets = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,c_dfDIJoystick2.dwNumObjs*sizeof(int));
425 if (newDevice->offsets == 0)
428 calculate_ids(newDevice->internal_df);
430 /* create the default transform filter */
431 newDevice->transform = create_DataFormat(newDevice->internal_df, newDevice->df, newDevice->offsets);
432 calculate_ids(newDevice->df);
437 HeapFree(GetProcessHeap(),0,newDevice->df->rgodf);
438 HeapFree(GetProcessHeap(),0,newDevice->df);
439 HeapFree(GetProcessHeap(),0,newDevice->internal_df->rgodf);
440 HeapFree(GetProcessHeap(),0,newDevice->internal_df);
441 HeapFree(GetProcessHeap(),0,newDevice);
445 static HRESULT joydev_create_deviceA(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPDIRECTINPUTDEVICEA* pdev)
451 for (i=0; i<have_joydevs; i++) {
452 if (IsEqualGUID(&GUID_Joystick,rguid) ||
453 IsEqualGUID(&joydevs[i].guid,rguid)
455 if ((riid == NULL) ||
456 IsEqualGUID(&IID_IDirectInputDeviceA,riid) ||
457 IsEqualGUID(&IID_IDirectInputDevice2A,riid) ||
458 IsEqualGUID(&IID_IDirectInputDevice7A,riid) ||
459 IsEqualGUID(&IID_IDirectInputDevice8A,riid)) {
460 *pdev = (IDirectInputDeviceA*) alloc_device(rguid, &JoystickAvt, dinput, &joydevs[i]);
461 TRACE("Creating a Joystick device (%p)\n", *pdev);
463 ERR("out of memory\n");
464 return DIERR_OUTOFMEMORY;
468 return DIERR_NOINTERFACE;
473 return DIERR_DEVICENOTREG;
477 static HRESULT joydev_create_deviceW(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPDIRECTINPUTDEVICEW* pdev)
483 for (i=0; i<have_joydevs; i++) {
484 if (IsEqualGUID(&GUID_Joystick,rguid) ||
485 IsEqualGUID(&joydevs[i].guid,rguid)
487 if ((riid == NULL) ||
488 IsEqualGUID(&IID_IDirectInputDeviceW,riid) ||
489 IsEqualGUID(&IID_IDirectInputDevice2W,riid) ||
490 IsEqualGUID(&IID_IDirectInputDevice7W,riid) ||
491 IsEqualGUID(&IID_IDirectInputDevice8W,riid)) {
492 *pdev = (IDirectInputDeviceW*) alloc_device(rguid, &JoystickWvt, dinput, &joydevs[i]);
493 TRACE("Creating a Joystick device (%p)\n", *pdev);
495 ERR("out of memory\n");
496 return DIERR_OUTOFMEMORY;
500 return DIERR_NOINTERFACE;
505 return DIERR_DEVICENOTREG;
508 const struct dinput_device joystick_linuxinput_device = {
509 "Wine Linux-input joystick driver",
512 joydev_create_deviceA,
513 joydev_create_deviceW
516 /******************************************************************************
519 static ULONG WINAPI JoystickAImpl_Release(LPDIRECTINPUTDEVICE8A iface)
521 JoystickImpl *This = (JoystickImpl *)iface;
524 ref = InterlockedDecrement(&This->base.ref);
528 /* Reset the FF state, free all effects, etc */
529 IDirectInputDevice8_SendForceFeedbackCommand(iface, DISFFC_RESET);
531 /* Free the data queue */
532 HeapFree(GetProcessHeap(), 0, This->base.data_queue);
534 /* Free the DataFormat */
535 HeapFree(GetProcessHeap(), 0, This->df->rgodf);
536 HeapFree(GetProcessHeap(), 0, This->df);
538 /* Free the offsets array */
539 HeapFree(GetProcessHeap(),0,This->offsets);
541 /* release the data transform filter */
542 release_DataFormat(This->transform);
544 DeleteCriticalSection(&This->base.crit);
546 HeapFree(GetProcessHeap(),0,This);
550 /******************************************************************************
551 * SetDataFormat : the application can choose the format of the data
552 * the device driver sends back with GetDeviceState.
554 static HRESULT WINAPI JoystickAImpl_SetDataFormat(
555 LPDIRECTINPUTDEVICE8A iface,LPCDIDATAFORMAT df
558 JoystickImpl *This = (JoystickImpl *)iface;
560 TRACE("(this=%p,%p)\n",This,df);
563 WARN("invalid pointer\n");
567 if (df->dwSize != sizeof(*df)) {
568 WARN("invalid argument\n");
569 return DIERR_INVALIDPARAM;
572 _dump_DIDATAFORMAT(df);
574 if (This->joyfd!=-1) {
576 return DIERR_ACQUIRED;
579 HeapFree(GetProcessHeap(),0,This->df->rgodf);
580 HeapFree(GetProcessHeap(),0,This->df);
581 release_DataFormat(This->transform);
583 /* Store the new data format */
584 This->df = HeapAlloc(GetProcessHeap(),0,df->dwSize);
585 if (This->df==NULL) {
586 return DIERR_OUTOFMEMORY;
588 memcpy(This->df, df, df->dwSize);
589 This->df->rgodf = HeapAlloc(GetProcessHeap(),0,df->dwNumObjs*df->dwObjSize);
590 if (This->df->rgodf==NULL) {
591 HeapFree(GetProcessHeap(), 0, This->df);
592 return DIERR_OUTOFMEMORY;
594 memcpy(This->df->rgodf,df->rgodf,df->dwNumObjs*df->dwObjSize);
596 This->transform = create_DataFormat(This->internal_df, This->df, This->offsets);
597 calculate_ids(This->df);
602 /******************************************************************************
603 * Acquire : gets exclusive control of the joystick
605 static HRESULT WINAPI JoystickAImpl_Acquire(LPDIRECTINPUTDEVICE8A iface)
607 JoystickImpl *This = (JoystickImpl *)iface;
609 TRACE("(this=%p)\n",This);
612 if (This->df==NULL) {
613 return DIERR_INVALIDPARAM;
616 if (-1==(This->joyfd=open(This->joydev->device,O_RDWR))) {
617 if (-1==(This->joyfd=open(This->joydev->device,O_RDONLY))) {
618 /* Couldn't open the device at all */
619 perror(This->joydev->device);
620 return DIERR_NOTFOUND;
622 /* Couldn't open in r/w but opened in read-only. */
623 WARN("Could not open %s in read-write mode. Force feedback will be disabled.\n", This->joydev->device);
630 /******************************************************************************
631 * Unacquire : frees the joystick
633 static HRESULT WINAPI JoystickAImpl_Unacquire(LPDIRECTINPUTDEVICE8A iface)
635 JoystickImpl *This = (JoystickImpl *)iface;
637 TRACE("(this=%p)\n",This);
638 if (This->joyfd!=-1) {
648 * This maps the read value (from the input event) to a value in the
649 * 'wanted' range. It also autodetects the possible range of the axe and
650 * adapts values accordingly.
653 map_axis(JoystickImpl* This, int axis, int val) {
654 int xmin = This->joydev->axes[axis][AXE_ABSMIN];
655 int xmax = This->joydev->axes[axis][AXE_ABSMAX];
656 int hmax = This->joydev->havemax[axis];
657 int hmin = This->joydev->havemin[axis];
658 int wmin = This->wantmin[axis];
659 int wmax = This->wantmax[axis];
662 if (val > hmax) This->joydev->havemax[axis] = hmax = val;
663 if (val < hmin) This->joydev->havemin[axis] = hmin = val;
665 if (xmin == xmax) return val;
667 /* map the value from the hmin-hmax range into the wmin-wmax range */
668 ret = MulDiv( val - hmin, wmax - wmin, hmax - hmin ) + wmin;
670 TRACE("xmin=%d xmax=%d hmin=%d hmax=%d wmin=%d wmax=%d val=%d ret=%d\n", xmin, xmax, hmin, hmax, wmin, wmax, val, ret);
673 /* deadzone doesn't work comfortably enough right now. needs more testing*/
674 if ((ret > -deadz/2 ) && (ret < deadz/2)) {
675 FIXME("%d in deadzone, return mid.\n",val);
676 return (wmax-wmin)/2+wmin;
683 * set the current state of the js device as it would be with the middle
686 static void fake_current_js_state(JoystickImpl *ji)
689 /* center the axes */
690 ji->js.lX = test_bit(ji->joydev->absbits, ABS_X) ? map_axis(ji, ABS_X, ji->joydev->axes[ABS_X ][AXE_ABS]) : 0;
691 ji->js.lY = test_bit(ji->joydev->absbits, ABS_Y) ? map_axis(ji, ABS_Y, ji->joydev->axes[ABS_Y ][AXE_ABS]) : 0;
692 ji->js.lZ = test_bit(ji->joydev->absbits, ABS_Z) ? map_axis(ji, ABS_Z, ji->joydev->axes[ABS_Z ][AXE_ABS]) : 0;
693 ji->js.lRx = test_bit(ji->joydev->absbits, ABS_RX) ? map_axis(ji, ABS_RX, ji->joydev->axes[ABS_RX ][AXE_ABS]) : 0;
694 ji->js.lRy = test_bit(ji->joydev->absbits, ABS_RY) ? map_axis(ji, ABS_RY, ji->joydev->axes[ABS_RY ][AXE_ABS]) : 0;
695 ji->js.lRz = test_bit(ji->joydev->absbits, ABS_RZ) ? map_axis(ji, ABS_RZ, ji->joydev->axes[ABS_RZ ][AXE_ABS]) : 0;
696 ji->js.rglSlider[0] = test_bit(ji->joydev->absbits, ABS_THROTTLE) ? map_axis(ji, ABS_THROTTLE, ji->joydev->axes[ABS_THROTTLE][AXE_ABS]) : 0;
697 ji->js.rglSlider[1] = test_bit(ji->joydev->absbits, ABS_RUDDER) ? map_axis(ji, ABS_RUDDER, ji->joydev->axes[ABS_RUDDER ][AXE_ABS]) : 0;
698 /* POV center is -1 */
699 for (i=0; i<4; i++) {
700 ji->js.rgdwPOV[i] = -1;
705 * Maps an event value to a DX "clock" position:
710 static DWORD map_pov(int event_value, int is_x)
716 } else if (event_value>0) {
722 } else if (event_value>0) {
729 static int find_property_offset(JoystickImpl *This, LPCDIPROPHEADER ph)
734 for (i=0; i<This->df->dwNumObjs; i++) {
735 if (This->offsets[i]==ph->dwObj) {
741 for (i=0; i<This->df->dwNumObjs; i++) {
742 if ((This->df->rgodf[i].dwType & 0x00ffffff) == (ph->dwObj & 0x00ffffff)) {
743 ofs = This->df->rgodf[i].dwOfs;
748 for (i=0; i<This->df->dwNumObjs; i++) {
749 if (This->offsets[i]==ofs) {
756 FIXME("Unhandled ph->dwHow=='%04X'\n", (unsigned int)ph->dwHow);
762 /* defines how the linux input system offset mappings into c_dfDIJoystick2 */
764 lxinput_to_user_offset(JoystickImpl *This, int ie_type, int ie_code )
770 case ABS_X: offset = 0; break;
771 case ABS_Y: offset = 1; break;
772 case ABS_Z: offset = 2; break;
773 case ABS_RX: offset = 3; break;
774 case ABS_RY: offset = 4; break;
775 case ABS_RZ: offset = 5; break;
776 case ABS_THROTTLE: offset = 6; break;
777 case ABS_RUDDER: offset = 7; break;
778 case ABS_HAT0X: case ABS_HAT0Y: offset = 8; break;
779 case ABS_HAT1X: case ABS_HAT1Y: offset = 9; break;
780 case ABS_HAT2X: case ABS_HAT2Y: offset = 10; break;
781 case ABS_HAT3X: case ABS_HAT3Y: offset = 11; break;
782 /* XXX when adding new axes here also fix the offset for the buttons bellow */
784 FIXME("Unhandled EV_ABS(0x%02X)\n", ie_code);
789 if (ie_code >= 128) {
790 WARN("DX8 does not support more than 128 buttons\n");
793 offset = 12 + ie_code; /* XXX */
796 FIXME("Unhandled type(0x%02X)\n", ie_type);
799 return This->offsets[offset];
802 /* convert wine format offset to user format object index */
803 static int offset_to_object(JoystickImpl *This, int offset)
807 for (i = 0; i < This->df->dwNumObjs; i++) {
808 if (This->df->rgodf[i].dwOfs == offset)
815 static void calculate_ids(LPDIDATAFORMAT df)
825 /* Make two passes over the format. The first counts the number
826 * for each type and the second sets the id */
827 for (i = 0; i < df->dwNumObjs; i++) {
828 if (DIDFT_GETTYPE(df->rgodf[i].dwType) & DIDFT_AXIS)
830 else if (DIDFT_GETTYPE(df->rgodf[i].dwType) & DIDFT_POV)
832 else if (DIDFT_GETTYPE(df->rgodf[i].dwType) & DIDFT_BUTTON)
838 button_base = axis + pov;
844 for (i = 0; i < df->dwNumObjs; i++) {
846 if (DIDFT_GETTYPE(df->rgodf[i].dwType) & DIDFT_AXIS) {
848 type = DIDFT_GETTYPE(df->rgodf[i].dwType) |
849 DIDFT_MAKEINSTANCE(axis + axis_base);
850 TRACE("axis type = 0x%08x\n", type);
851 } else if (DIDFT_GETTYPE(df->rgodf[i].dwType) & DIDFT_POV) {
853 type = DIDFT_GETTYPE(df->rgodf[i].dwType) |
854 DIDFT_MAKEINSTANCE(pov + pov_base);
855 TRACE("POV type = 0x%08x\n", type);
856 } else if (DIDFT_GETTYPE(df->rgodf[i].dwType) & DIDFT_BUTTON) {
858 type = DIDFT_GETTYPE(df->rgodf[i].dwType) |
859 DIDFT_MAKEINSTANCE(button + button_base);
860 TRACE("button type = 0x%08x\n", type);
862 df->rgodf[i].dwType = type;
866 static void joy_polldev(JoystickImpl *This) {
868 struct input_event ie;
875 plfd.fd = This->joyfd;
876 plfd.events = POLLIN;
878 if (poll(&plfd,1,0) != 1)
881 /* we have one event, so we can read */
882 if (sizeof(ie)!=read(This->joyfd,&ie,sizeof(ie)))
885 TRACE("input_event: type %d, code %d, value %d\n",ie.type,ie.code,ie.value);
887 case EV_KEY: /* button */
888 btn = This->joydev->buttons[ie.code];
889 TRACE("(%p) %d -> %d\n", This, ie.code, btn);
892 if ((offset = lxinput_to_user_offset(This, ie.type, btn)) == -1) {
895 This->js.rgbButtons[btn] = ie.value?0x80:0x00;
896 queue_event((LPDIRECTINPUTDEVICE8A)This, offset, This->js.rgbButtons[btn],
897 ie.time.tv_usec, This->dinput->evsequence++);
901 if ((offset = lxinput_to_user_offset(This, ie.type, ie.code)) == -1) {
906 This->js.lX = map_axis(This,ABS_X,ie.value);
907 queue_event((LPDIRECTINPUTDEVICE8A)This, offset, This->js.lX,
908 ie.time.tv_usec, This->dinput->evsequence++);
911 This->js.lY = map_axis(This,ABS_Y,ie.value);
912 queue_event((LPDIRECTINPUTDEVICE8A)This, offset, This->js.lY,
913 ie.time.tv_usec, This->dinput->evsequence++);
916 This->js.lZ = map_axis(This,ABS_Z,ie.value);
917 queue_event((LPDIRECTINPUTDEVICE8A)This, offset, This->js.lZ,
918 ie.time.tv_usec, This->dinput->evsequence++);
921 This->js.lRx = map_axis(This,ABS_RX,ie.value);
922 queue_event((LPDIRECTINPUTDEVICE8A)This, offset, This->js.lRx,
923 ie.time.tv_usec, This->dinput->evsequence++);
926 This->js.lRy = map_axis(This,ABS_RY,ie.value);
927 queue_event((LPDIRECTINPUTDEVICE8A)This, offset, This->js.lRy,
928 ie.time.tv_usec, This->dinput->evsequence++);
931 This->js.lRz = map_axis(This,ABS_RZ,ie.value);
932 queue_event((LPDIRECTINPUTDEVICE8A)This, offset, This->js.lRz,
933 ie.time.tv_usec, This->dinput->evsequence++);
936 This->js.rglSlider[0] = map_axis(This,ABS_THROTTLE,ie.value);
937 queue_event((LPDIRECTINPUTDEVICE8A)This, offset, This->js.rglSlider[0],
938 ie.time.tv_usec, This->dinput->evsequence++);
941 This->js.rglSlider[1] = map_axis(This,ABS_RUDDER,ie.value);
942 queue_event((LPDIRECTINPUTDEVICE8A)This, offset, This->js.rglSlider[1],
943 ie.time.tv_usec, This->dinput->evsequence++);
947 This->js.rgdwPOV[0] = map_pov(ie.value,ie.code==ABS_HAT0X);
948 queue_event((LPDIRECTINPUTDEVICE8A)This, offset, This->js.rgdwPOV[0],
949 ie.time.tv_usec, This->dinput->evsequence++);
953 This->js.rgdwPOV[1] = map_pov(ie.value,ie.code==ABS_HAT1X);
954 queue_event((LPDIRECTINPUTDEVICE8A)This, offset, This->js.rgdwPOV[1],
955 ie.time.tv_usec, This->dinput->evsequence++);
959 This->js.rgdwPOV[2] = map_pov(ie.value,ie.code==ABS_HAT2X);
960 queue_event((LPDIRECTINPUTDEVICE8A)This, offset, This->js.rgdwPOV[2],
961 ie.time.tv_usec, This->dinput->evsequence++);
965 This->js.rgdwPOV[3] = map_pov(ie.value,ie.code==ABS_HAT3X);
966 queue_event((LPDIRECTINPUTDEVICE8A)This, offset, This->js.rgdwPOV[3],
967 ie.time.tv_usec, This->dinput->evsequence++);
970 FIXME("unhandled joystick axe event (code %d, value %d)\n",ie.code,ie.value);
974 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
976 This->ff_state = ie.value;
981 /* there is nothing to do */
985 FIXME("joystick cannot handle type %d event (code %d)\n",ie.type,ie.code);
991 /******************************************************************************
992 * GetDeviceState : returns the "state" of the joystick.
995 static HRESULT WINAPI JoystickAImpl_GetDeviceState(
996 LPDIRECTINPUTDEVICE8A iface,DWORD len,LPVOID ptr
998 JoystickImpl *This = (JoystickImpl *)iface;
1000 TRACE("(this=%p,0x%08x,%p)\n", This, len, ptr);
1002 if (This->joyfd==-1) {
1003 WARN("not acquired\n");
1004 return DIERR_NOTACQUIRED;
1009 /* convert and copy data to user supplied buffer */
1010 fill_DataFormat(ptr, &This->js, This->transform);
1015 /******************************************************************************
1016 * SetProperty : change input device properties
1018 static HRESULT WINAPI JoystickAImpl_SetProperty(LPDIRECTINPUTDEVICE8A iface,
1022 JoystickImpl *This = (JoystickImpl *)iface;
1025 WARN("invalid argument\n");
1026 return DIERR_INVALIDPARAM;
1029 TRACE("(this=%p,%s,%p)\n",This,debugstr_guid(rguid),ph);
1030 TRACE("ph.dwSize = %d, ph.dwHeaderSize =%d, ph.dwObj = %d, ph.dwHow= %d\n",
1031 ph->dwSize, ph->dwHeaderSize, ph->dwObj, ph->dwHow);
1033 if (!HIWORD(rguid)) {
1034 switch (LOWORD(rguid)) {
1035 case (DWORD)DIPROP_RANGE: {
1036 LPCDIPROPRANGE pr = (LPCDIPROPRANGE)ph;
1038 if (ph->dwHow == DIPH_DEVICE) {
1040 TRACE("proprange(%d,%d) all\n", pr->lMin, pr->lMax);
1041 for (i = 0; i < This->df->dwNumObjs; i++) {
1042 This->wantmin[i] = pr->lMin;
1043 This->wantmax[i] = pr->lMax;
1046 int obj = find_property_offset(This, ph);
1047 TRACE("proprange(%d,%d) obj=%d\n", pr->lMin, pr->lMax, obj);
1049 This->wantmin[obj] = pr->lMin;
1050 This->wantmax[obj] = pr->lMax;
1053 fake_current_js_state(This);
1056 case (DWORD)DIPROP_DEADZONE: {
1057 LPCDIPROPDWORD pd = (LPCDIPROPDWORD)ph;
1058 if (ph->dwHow == DIPH_DEVICE) {
1060 TRACE("deadzone(%d) all\n", pd->dwData);
1061 for (i = 0; i < This->df->dwNumObjs; i++) {
1062 This->deadz[i] = pd->dwData;
1065 int obj = find_property_offset(This, ph);
1066 TRACE("deadzone(%d) obj=%d\n", pd->dwData, obj);
1068 This->deadz[obj] = pd->dwData;
1071 fake_current_js_state(This);
1074 case (DWORD)DIPROP_CALIBRATIONMODE: {
1075 LPCDIPROPDWORD pd = (LPCDIPROPDWORD)ph;
1076 FIXME("DIPROP_CALIBRATIONMODE(%d)\n", pd->dwData);
1080 return IDirectInputDevice2AImpl_SetProperty(iface, rguid, ph);
1086 static HRESULT WINAPI JoystickAImpl_GetCapabilities(
1087 LPDIRECTINPUTDEVICE8A iface,
1088 LPDIDEVCAPS lpDIDevCaps)
1090 JoystickImpl *This = (JoystickImpl *)iface;
1091 int i,axes,buttons,povs;
1093 TRACE("%p->(%p)\n",iface,lpDIDevCaps);
1096 WARN("invalid pointer\n");
1100 if (lpDIDevCaps->dwSize != sizeof(DIDEVCAPS)) {
1101 WARN("invalid argument\n");
1102 return DIERR_INVALIDPARAM;
1105 lpDIDevCaps->dwFlags = DIDC_ATTACHED;
1106 if (This->dinput->dwVersion >= 0x0800)
1107 lpDIDevCaps->dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
1109 lpDIDevCaps->dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
1112 for (i=0;i<ABS_MAX;i++) {
1113 if (!test_bit(This->joydev->absbits,i)) continue;
1115 case ABS_HAT0X: case ABS_HAT0Y:
1116 case ABS_HAT1X: case ABS_HAT1Y:
1117 case ABS_HAT2X: case ABS_HAT2Y:
1118 case ABS_HAT3X: case ABS_HAT3Y:
1119 /* will be handled as POV - see below */
1126 for (i=0;i<KEY_MAX;i++) if (test_bit(This->joydev->keybits,i)) buttons++;
1128 for (i=0; i<4; i++) {
1129 if (test_bit(This->joydev->absbits,ABS_HAT0X+(i<<1)) && test_bit(This->joydev->absbits,ABS_HAT0Y+(i<<1))) {
1134 if (This->joydev->has_ff)
1135 lpDIDevCaps->dwFlags |= DIDC_FORCEFEEDBACK;
1137 lpDIDevCaps->dwAxes = axes;
1138 lpDIDevCaps->dwButtons = buttons;
1139 lpDIDevCaps->dwPOVs = povs;
1144 static HRESULT WINAPI JoystickAImpl_Poll(LPDIRECTINPUTDEVICE8A iface) {
1145 JoystickImpl *This = (JoystickImpl *)iface;
1146 TRACE("(%p)\n",This);
1148 if (This->joyfd==-1) {
1149 return DIERR_NOTACQUIRED;
1156 /******************************************************************************
1157 * EnumObjects : enumerate the different buttons and axis...
1159 static HRESULT WINAPI JoystickAImpl_EnumObjects(
1160 LPDIRECTINPUTDEVICE8A iface,
1161 LPDIENUMDEVICEOBJECTSCALLBACKA lpCallback,
1165 JoystickImpl *This = (JoystickImpl *)iface;
1166 DIDEVICEOBJECTINSTANCEA ddoi;
1167 int user_offset, user_object;
1169 TRACE("(this=%p,%p,%p,%08x)\n", This, lpCallback, lpvRef, dwFlags);
1170 if (TRACE_ON(dinput)) {
1171 TRACE(" - flags = ");
1172 _dump_EnumObjects_flags(dwFlags);
1176 /* Only the fields till dwFFMaxForce are relevant */
1177 ddoi.dwSize = FIELD_OFFSET(DIDEVICEOBJECTINSTANCEA, dwFFMaxForce);
1179 /* For the joystick, do as is done in the GetCapabilities function */
1180 /* FIXME: needs more items */
1181 if ((dwFlags == DIDFT_ALL) ||
1182 (dwFlags & DIDFT_AXIS)) {
1185 for (i = 0; i < ABS_MAX; i++) {
1186 if (!test_bit(This->joydev->absbits,i)) continue;
1190 ddoi.guidType = GUID_XAxis;
1193 ddoi.guidType = GUID_YAxis;
1196 ddoi.guidType = GUID_ZAxis;
1199 ddoi.guidType = GUID_RxAxis;
1202 ddoi.guidType = GUID_RyAxis;
1205 ddoi.guidType = GUID_RzAxis;
1209 ddoi.guidType = GUID_Slider;
1211 case ABS_HAT0X: case ABS_HAT0Y:
1212 case ABS_HAT1X: case ABS_HAT1Y:
1213 case ABS_HAT2X: case ABS_HAT2Y:
1214 case ABS_HAT3X: case ABS_HAT3Y:
1215 /* will be handled as POV - see below */
1218 FIXME("unhandled abs axis 0x%02x, ignoring!\n",i);
1221 if ((user_offset = lxinput_to_user_offset(This, EV_ABS, i)) == -1) {
1224 user_object = offset_to_object(This, user_offset);
1225 ddoi.dwType = This->df->rgodf[user_object].dwType & 0x00ffffff;
1226 ddoi.dwOfs = This->df->rgodf[user_object].dwOfs;
1227 /* Linux event force feedback supports only (and always) x and y axes */
1228 if (i == ABS_X || i == ABS_Y) {
1229 if (This->joydev->has_ff)
1230 ddoi.dwFlags |= DIDOI_FFACTUATOR;
1232 sprintf(ddoi.tszName, "%d-Axis", i);
1233 _dump_OBJECTINSTANCEA(&ddoi);
1234 if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE) {
1240 if ((dwFlags == DIDFT_ALL) ||
1241 (dwFlags & DIDFT_POV)) {
1243 ddoi.guidType = GUID_POV;
1244 for (i=0; i<4; i++) {
1245 if (test_bit(This->joydev->absbits,ABS_HAT0X+(i<<1)) && test_bit(This->joydev->absbits,ABS_HAT0Y+(i<<1))) {
1246 if ((user_offset = lxinput_to_user_offset(This, EV_ABS, ABS_HAT0X+i))== -1) {
1249 user_object = offset_to_object(This, user_offset);
1250 ddoi.dwType = This->df->rgodf[user_object].dwType & 0x00ffffff;
1251 ddoi.dwOfs = This->df->rgodf[user_object].dwOfs;
1252 sprintf(ddoi.tszName, "%d-POV", i);
1253 _dump_OBJECTINSTANCEA(&ddoi);
1254 if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE) {
1261 if ((dwFlags == DIDFT_ALL) ||
1262 (dwFlags & DIDFT_BUTTON)) {
1265 /*The DInput SDK says that GUID_Button is only for mouse buttons but well*/
1267 ddoi.guidType = GUID_Button;
1269 for (i = 0; i < KEY_MAX; i++) {
1270 if (!test_bit(This->joydev->keybits,i)) continue;
1271 if ((user_offset = lxinput_to_user_offset(This, EV_KEY, btncount)) == -1) {
1274 user_object = offset_to_object(This, user_offset);
1275 ddoi.dwType = This->df->rgodf[user_object].dwType & 0x00ffffff;
1276 ddoi.dwOfs = This->df->rgodf[user_object].dwOfs;
1277 sprintf(ddoi.tszName, "%d-Button", btncount);
1279 _dump_OBJECTINSTANCEA(&ddoi);
1280 if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE) {
1289 static HRESULT WINAPI JoystickWImpl_EnumObjects(LPDIRECTINPUTDEVICE8W iface,
1290 LPDIENUMDEVICEOBJECTSCALLBACKW lpCallback,
1294 JoystickImpl *This = (JoystickImpl *)iface;
1296 device_enumobjects_AtoWcb_data data;
1298 data.lpCallBack = lpCallback;
1299 data.lpvRef = lpvRef;
1301 return JoystickAImpl_EnumObjects((LPDIRECTINPUTDEVICE8A) This, (LPDIENUMDEVICEOBJECTSCALLBACKA) DIEnumDevicesCallbackAtoW, (LPVOID) &data, dwFlags);
1304 /******************************************************************************
1305 * GetProperty : get input device properties
1307 static HRESULT WINAPI JoystickAImpl_GetProperty(LPDIRECTINPUTDEVICE8A iface,
1309 LPDIPROPHEADER pdiph)
1311 JoystickImpl *This = (JoystickImpl *)iface;
1313 TRACE("(this=%p,%s,%p)\n",
1314 iface, debugstr_guid(rguid), pdiph);
1316 if (TRACE_ON(dinput))
1317 _dump_DIPROPHEADER(pdiph);
1319 if (!HIWORD(rguid)) {
1320 switch (LOWORD(rguid)) {
1321 case (DWORD) DIPROP_RANGE: {
1322 LPDIPROPRANGE pr = (LPDIPROPRANGE) pdiph;
1323 int obj = find_property_offset(This, pdiph);
1325 pr->lMin = This->joydev->havemin[obj];
1326 pr->lMax = This->joydev->havemax[obj];
1327 TRACE("range(%d, %d) obj=%d\n", pr->lMin, pr->lMax, obj);
1333 return IDirectInputDevice2AImpl_GetProperty(iface, rguid, pdiph);
1341 /******************************************************************************
1342 * CreateEffect - Create a new FF effect with the specified params
1344 static HRESULT WINAPI JoystickAImpl_CreateEffect(LPDIRECTINPUTDEVICE8A iface,
1347 LPDIRECTINPUTEFFECT *ppdef,
1348 LPUNKNOWN pUnkOuter)
1350 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1351 EffectListItem* new = NULL;
1352 HRESULT retval = DI_OK;
1355 JoystickImpl* This = (JoystickImpl*)iface;
1356 TRACE("(this=%p,%p,%p,%p,%p)\n", This, rguid, lpeff, ppdef, pUnkOuter);
1358 #ifndef HAVE_STRUCT_FF_EFFECT_DIRECTION
1359 TRACE("not available (compiled w/o ff support)\n");
1364 new = HeapAlloc(GetProcessHeap(), 0, sizeof(EffectListItem));
1365 new->next = This->top_effect;
1366 This->top_effect = new;
1368 retval = linuxinput_create_effect(&(This->joyfd), rguid, &(new->ref));
1369 if (retval != DI_OK)
1373 retval = IDirectInputEffect_SetParameters(new->ref, lpeff, 0);
1374 if (retval != DI_OK && retval != DI_DOWNLOADSKIPPED)
1379 if (pUnkOuter != NULL)
1380 FIXME("Interface aggregation not implemented.\n");
1384 #endif /* HAVE_STRUCT_FF_EFFECT_DIRECTION */
1387 /*******************************************************************************
1388 * EnumEffects - Enumerate available FF effects
1390 static HRESULT WINAPI JoystickAImpl_EnumEffects(LPDIRECTINPUTDEVICE8A iface,
1391 LPDIENUMEFFECTSCALLBACKA lpCallback,
1395 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1396 DIEFFECTINFOA dei; /* feif */
1397 DWORD type = DIEFT_GETTYPE(dwEffType);
1398 JoystickImpl* This = (JoystickImpl*)iface;
1400 TRACE("(this=%p,%p,%d) type=%d\n", This, pvRef, dwEffType, type);
1402 dei.dwSize = sizeof(DIEFFECTINFOA);
1404 if ((type == DIEFT_ALL || type == DIEFT_CONSTANTFORCE)
1405 && test_bit(This->joydev->ffbits, FF_CONSTANT)) {
1406 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_ConstantForce);
1407 (*lpCallback)(&dei, pvRef);
1410 if ((type == DIEFT_ALL || type == DIEFT_PERIODIC)
1411 && test_bit(This->joydev->ffbits, FF_PERIODIC)) {
1412 if (test_bit(This->joydev->ffbits, FF_SQUARE)) {
1413 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Square);
1414 (*lpCallback)(&dei, pvRef);
1416 if (test_bit(This->joydev->ffbits, FF_SINE)) {
1417 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Sine);
1418 (*lpCallback)(&dei, pvRef);
1420 if (test_bit(This->joydev->ffbits, FF_TRIANGLE)) {
1421 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Triangle);
1422 (*lpCallback)(&dei, pvRef);
1424 if (test_bit(This->joydev->ffbits, FF_SAW_UP)) {
1425 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_SawtoothUp);
1426 (*lpCallback)(&dei, pvRef);
1428 if (test_bit(This->joydev->ffbits, FF_SAW_DOWN)) {
1429 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_SawtoothDown);
1430 (*lpCallback)(&dei, pvRef);
1434 if ((type == DIEFT_ALL || type == DIEFT_RAMPFORCE)
1435 && test_bit(This->joydev->ffbits, FF_RAMP)) {
1436 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_RampForce);
1437 (*lpCallback)(&dei, pvRef);
1440 if (type == DIEFT_ALL || type == DIEFT_CONDITION) {
1441 if (test_bit(This->joydev->ffbits, FF_SPRING)) {
1442 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Spring);
1443 (*lpCallback)(&dei, pvRef);
1445 if (test_bit(This->joydev->ffbits, FF_DAMPER)) {
1446 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Damper);
1447 (*lpCallback)(&dei, pvRef);
1449 if (test_bit(This->joydev->ffbits, FF_INERTIA)) {
1450 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Inertia);
1451 (*lpCallback)(&dei, pvRef);
1453 if (test_bit(This->joydev->ffbits, FF_FRICTION)) {
1454 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Friction);
1455 (*lpCallback)(&dei, pvRef);
1464 static HRESULT WINAPI JoystickWImpl_EnumEffects(LPDIRECTINPUTDEVICE8W iface,
1465 LPDIENUMEFFECTSCALLBACKW lpCallback,
1469 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1470 /* seems silly to duplicate all this code but all the structures and functions
1471 * are actually different (A/W) */
1472 DIEFFECTINFOW dei; /* feif */
1473 DWORD type = DIEFT_GETTYPE(dwEffType);
1474 JoystickImpl* This = (JoystickImpl*)iface;
1475 int xfd = This->joyfd;
1477 TRACE("(this=%p,%p,%d) type=%d fd=%d\n", This, pvRef, dwEffType, type, xfd);
1479 dei.dwSize = sizeof(DIEFFECTINFOW);
1481 if ((type == DIEFT_ALL || type == DIEFT_CONSTANTFORCE)
1482 && test_bit(This->joydev->ffbits, FF_CONSTANT)) {
1483 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_ConstantForce);
1484 (*lpCallback)(&dei, pvRef);
1487 if ((type == DIEFT_ALL || type == DIEFT_PERIODIC)
1488 && test_bit(This->joydev->ffbits, FF_PERIODIC)) {
1489 if (test_bit(This->joydev->ffbits, FF_SQUARE)) {
1490 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Square);
1491 (*lpCallback)(&dei, pvRef);
1493 if (test_bit(This->joydev->ffbits, FF_SINE)) {
1494 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Sine);
1495 (*lpCallback)(&dei, pvRef);
1497 if (test_bit(This->joydev->ffbits, FF_TRIANGLE)) {
1498 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Triangle);
1499 (*lpCallback)(&dei, pvRef);
1501 if (test_bit(This->joydev->ffbits, FF_SAW_UP)) {
1502 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_SawtoothUp);
1503 (*lpCallback)(&dei, pvRef);
1505 if (test_bit(This->joydev->ffbits, FF_SAW_DOWN)) {
1506 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_SawtoothDown);
1507 (*lpCallback)(&dei, pvRef);
1511 if ((type == DIEFT_ALL || type == DIEFT_RAMPFORCE)
1512 && test_bit(This->joydev->ffbits, FF_RAMP)) {
1513 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_RampForce);
1514 (*lpCallback)(&dei, pvRef);
1517 if (type == DIEFT_ALL || type == DIEFT_CONDITION) {
1518 if (test_bit(This->joydev->ffbits, FF_SPRING)) {
1519 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Spring);
1520 (*lpCallback)(&dei, pvRef);
1522 if (test_bit(This->joydev->ffbits, FF_DAMPER)) {
1523 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Damper);
1524 (*lpCallback)(&dei, pvRef);
1526 if (test_bit(This->joydev->ffbits, FF_INERTIA)) {
1527 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Inertia);
1528 (*lpCallback)(&dei, pvRef);
1530 if (test_bit(This->joydev->ffbits, FF_FRICTION)) {
1531 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Friction);
1532 (*lpCallback)(&dei, pvRef);
1536 /* return to unacquired state if that's where it was */
1538 IDirectInputDevice8_Unacquire(iface);
1544 /*******************************************************************************
1545 * GetEffectInfo - Get information about a particular effect
1547 static HRESULT WINAPI JoystickAImpl_GetEffectInfo(LPDIRECTINPUTDEVICE8A iface,
1548 LPDIEFFECTINFOA pdei,
1551 JoystickImpl* This = (JoystickImpl*)iface;
1553 TRACE("(this=%p,%p,%s)\n", This, pdei, _dump_dinput_GUID(guid));
1555 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1556 return linuxinput_get_info_A(This->joyfd, guid, pdei);
1562 static HRESULT WINAPI JoystickWImpl_GetEffectInfo(LPDIRECTINPUTDEVICE8W iface,
1563 LPDIEFFECTINFOW pdei,
1566 JoystickImpl* This = (JoystickImpl*)iface;
1568 TRACE("(this=%p,%p,%s)\n", This, pdei, _dump_dinput_GUID(guid));
1570 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1571 return linuxinput_get_info_W(This->joyfd, guid, pdei);
1577 /*******************************************************************************
1578 * GetForceFeedbackState - Get information about the device's FF state
1580 static HRESULT WINAPI JoystickAImpl_GetForceFeedbackState(
1581 LPDIRECTINPUTDEVICE8A iface,
1584 JoystickImpl* This = (JoystickImpl*)iface;
1586 TRACE("(this=%p,%p)\n", This, pdwOut);
1590 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1591 /* DIGFFS_STOPPED is the only mandatory flag to report */
1592 if (This->ff_state == FF_STATUS_STOPPED)
1593 (*pdwOut) |= DIGFFS_STOPPED;
1599 /*******************************************************************************
1600 * SendForceFeedbackCommand - Send a command to the device's FF system
1602 static HRESULT WINAPI JoystickAImpl_SendForceFeedbackCommand(
1603 LPDIRECTINPUTDEVICE8A iface,
1606 JoystickImpl* This = (JoystickImpl*)iface;
1607 TRACE("(this=%p,%d)\n", This, dwFlags);
1609 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1610 if (dwFlags == DISFFC_STOPALL) {
1611 /* Stop all effects */
1612 EffectListItem* itr = This->top_effect;
1614 IDirectInputEffect_Stop(itr->ref);
1617 } else if (dwFlags == DISFFC_RESET) {
1618 /* Stop, unload, release and free all effects */
1619 /* This returns the device to its "bare" state */
1620 while (This->top_effect) {
1621 EffectListItem* temp = This->top_effect;
1622 IDirectInputEffect_Stop(temp->ref);
1623 IDirectInputEffect_Unload(temp->ref);
1624 IDirectInputEffect_Release(temp->ref);
1625 This->top_effect = temp->next;
1626 HeapFree(GetProcessHeap(), 0, temp);
1628 } else if (dwFlags == DISFFC_PAUSE || dwFlags == DISFFC_CONTINUE) {
1629 FIXME("No support for Pause or Continue in linux\n");
1630 } else if (dwFlags == DISFFC_SETACTUATORSOFF
1631 || dwFlags == DISFFC_SETACTUATORSON) {
1632 FIXME("No direct actuator control in linux\n");
1634 FIXME("Unknown Force Feedback Command!\n");
1635 return DIERR_INVALIDPARAM;
1639 return DIERR_UNSUPPORTED;
1643 /*******************************************************************************
1644 * EnumCreatedEffectObjects - Enumerate all the effects that have been
1645 * created for this device.
1647 static HRESULT WINAPI JoystickAImpl_EnumCreatedEffectObjects(
1648 LPDIRECTINPUTDEVICE8A iface,
1649 LPDIENUMCREATEDEFFECTOBJECTSCALLBACK lpCallback,
1653 /* this function is safe to call on non-ff-enabled builds */
1655 JoystickImpl* This = (JoystickImpl*)iface;
1656 EffectListItem* itr = This->top_effect;
1657 TRACE("(this=%p,%p,%p,%d)\n", This, lpCallback, pvRef, dwFlags);
1660 return DIERR_INVALIDPARAM;
1663 FIXME("Flags specified, but no flags exist yet (DX9)!\n");
1666 (*lpCallback)(itr->ref, pvRef);
1673 static const IDirectInputDevice8AVtbl JoystickAvt =
1675 IDirectInputDevice2AImpl_QueryInterface,
1676 IDirectInputDevice2AImpl_AddRef,
1677 JoystickAImpl_Release,
1678 JoystickAImpl_GetCapabilities,
1679 JoystickAImpl_EnumObjects,
1680 JoystickAImpl_GetProperty,
1681 JoystickAImpl_SetProperty,
1682 JoystickAImpl_Acquire,
1683 JoystickAImpl_Unacquire,
1684 JoystickAImpl_GetDeviceState,
1685 IDirectInputDevice2AImpl_GetDeviceData,
1686 JoystickAImpl_SetDataFormat,
1687 IDirectInputDevice2AImpl_SetEventNotification,
1688 IDirectInputDevice2AImpl_SetCooperativeLevel,
1689 IDirectInputDevice2AImpl_GetObjectInfo,
1690 IDirectInputDevice2AImpl_GetDeviceInfo,
1691 IDirectInputDevice2AImpl_RunControlPanel,
1692 IDirectInputDevice2AImpl_Initialize,
1693 JoystickAImpl_CreateEffect,
1694 JoystickAImpl_EnumEffects,
1695 JoystickAImpl_GetEffectInfo,
1696 JoystickAImpl_GetForceFeedbackState,
1697 JoystickAImpl_SendForceFeedbackCommand,
1698 JoystickAImpl_EnumCreatedEffectObjects,
1699 IDirectInputDevice2AImpl_Escape,
1701 IDirectInputDevice2AImpl_SendDeviceData,
1702 IDirectInputDevice7AImpl_EnumEffectsInFile,
1703 IDirectInputDevice7AImpl_WriteEffectToFile,
1704 IDirectInputDevice8AImpl_BuildActionMap,
1705 IDirectInputDevice8AImpl_SetActionMap,
1706 IDirectInputDevice8AImpl_GetImageInfo
1709 #if !defined(__STRICT_ANSI__) && defined(__GNUC__)
1710 # define XCAST(fun) (typeof(JoystickWvt.fun))
1712 # define XCAST(fun) (void*)
1715 static const IDirectInputDevice8WVtbl JoystickWvt =
1717 IDirectInputDevice2WImpl_QueryInterface,
1718 XCAST(AddRef)IDirectInputDevice2AImpl_AddRef,
1719 XCAST(Release)JoystickAImpl_Release,
1720 XCAST(GetCapabilities)JoystickAImpl_GetCapabilities,
1721 JoystickWImpl_EnumObjects,
1722 XCAST(GetProperty)JoystickAImpl_GetProperty,
1723 XCAST(SetProperty)JoystickAImpl_SetProperty,
1724 XCAST(Acquire)JoystickAImpl_Acquire,
1725 XCAST(Unacquire)JoystickAImpl_Unacquire,
1726 XCAST(GetDeviceState)JoystickAImpl_GetDeviceState,
1727 XCAST(GetDeviceData)IDirectInputDevice2AImpl_GetDeviceData,
1728 XCAST(SetDataFormat)JoystickAImpl_SetDataFormat,
1729 XCAST(SetEventNotification)IDirectInputDevice2AImpl_SetEventNotification,
1730 XCAST(SetCooperativeLevel)IDirectInputDevice2AImpl_SetCooperativeLevel,
1731 IDirectInputDevice2WImpl_GetObjectInfo,
1732 IDirectInputDevice2WImpl_GetDeviceInfo,
1733 XCAST(RunControlPanel)IDirectInputDevice2AImpl_RunControlPanel,
1734 XCAST(Initialize)IDirectInputDevice2AImpl_Initialize,
1735 XCAST(CreateEffect)JoystickAImpl_CreateEffect,
1736 JoystickWImpl_EnumEffects,
1737 JoystickWImpl_GetEffectInfo,
1738 XCAST(GetForceFeedbackState)JoystickAImpl_GetForceFeedbackState,
1739 XCAST(SendForceFeedbackCommand)JoystickAImpl_SendForceFeedbackCommand,
1740 XCAST(EnumCreatedEffectObjects)JoystickAImpl_EnumCreatedEffectObjects,
1741 XCAST(Escape)IDirectInputDevice2AImpl_Escape,
1742 XCAST(Poll)JoystickAImpl_Poll,
1743 XCAST(SendDeviceData)IDirectInputDevice2AImpl_SendDeviceData,
1744 IDirectInputDevice7WImpl_EnumEffectsInFile,
1745 IDirectInputDevice7WImpl_WriteEffectToFile,
1746 IDirectInputDevice8WImpl_BuildActionMap,
1747 IDirectInputDevice8WImpl_SetActionMap,
1748 IDirectInputDevice8WImpl_GetImageInfo
1752 #else /* HAVE_CORRECT_LINUXINPUT_H */
1754 const struct dinput_device joystick_linuxinput_device = {
1755 "Wine Linux-input joystick driver",
1762 #endif /* HAVE_CORRECT_LINUXINPUT_H */