shdocvw: Move IServiceProvider to DocHost object.
[wine] / dlls / dinput / joystick_linuxinput.c
1 /*              DirectInput Joystick device
2  *
3  * Copyright 1998,2000 Marcus Meissner
4  * Copyright 1998,1999 Lionel Ulmer
5  * Copyright 2000-2001 TransGaming Technologies Inc.
6  * Copyright 2005 Daniel Remenak
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #include "config.h"
24 #include "wine/port.h"
25
26 #include <assert.h>
27 #include <stdarg.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <time.h>
31 #ifdef HAVE_UNISTD_H
32 # include <unistd.h>
33 #endif
34 #ifdef HAVE_SYS_TIME_H
35 # include <sys/time.h>
36 #endif
37 #include <sys/fcntl.h>
38 #ifdef HAVE_SYS_IOCTL_H
39 # include <sys/ioctl.h>
40 #endif
41 #include <errno.h>
42 #ifdef HAVE_SYS_ERRNO_H
43 # include <sys/errno.h>
44 #endif
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
49 # endif
50 #endif
51
52 #include "wine/debug.h"
53 #include "wine/unicode.h"
54 #include "windef.h"
55 #include "winbase.h"
56 #include "winerror.h"
57 #include "dinput.h"
58
59 #include "dinput_private.h"
60 #include "device_private.h"
61
62 WINE_DEFAULT_DEBUG_CHANNEL(dinput);
63
64 #ifdef HAVE_CORRECT_LINUXINPUT_H
65
66 #define EVDEVPREFIX     "/dev/input/event"
67
68 /* Wine joystick driver object instances */
69 #define WINE_JOYSTICK_AXIS_BASE   0
70 #define WINE_JOYSTICK_POV_BASE    6
71 #define WINE_JOYSTICK_BUTTON_BASE 8
72
73 typedef struct EffectListItem EffectListItem;
74 struct EffectListItem
75 {
76         LPDIRECTINPUTEFFECT ref;
77         struct EffectListItem* next;
78 };
79
80 /* implemented in effect_linuxinput.c */
81 HRESULT linuxinput_create_effect(int* fd, REFGUID rguid, LPDIRECTINPUTEFFECT* peff);
82 HRESULT linuxinput_get_info_A(int fd, REFGUID rguid, LPDIEFFECTINFOA info);
83 HRESULT linuxinput_get_info_W(int fd, REFGUID rguid, LPDIEFFECTINFOW info);
84
85 typedef struct JoystickImpl JoystickImpl;
86 static const IDirectInputDevice8AVtbl JoystickAvt;
87 static const IDirectInputDevice8WVtbl JoystickWvt;
88 struct JoystickImpl
89 {
90         const void                     *lpVtbl;
91         LONG                            ref;
92         GUID                            guid;
93
94
95         /* The 'parent' DInput */
96         IDirectInputImpl               *dinput;
97
98         /* joystick private */
99         /* what range and deadzone the game wants */
100         LONG                            wantmin[ABS_MAX];
101         LONG                            wantmax[ABS_MAX];
102         LONG                            deadz[ABS_MAX];
103
104         /* autodetecting ranges per axe by following movement */
105         LONG                            havemax[ABS_MAX];
106         LONG                            havemin[ABS_MAX];
107
108         int                             joyfd;
109
110         LPDIDATAFORMAT                  df;
111         HANDLE                          hEvent;
112         LPDIDEVICEOBJECTDATA            data_queue;
113         int                             queue_head, queue_tail, queue_len;
114         BOOL                            overflow;
115         DIJOYSTATE2                     js;
116
117         /* Force feedback variables */
118         BOOL                            has_ff;
119         int                             num_effects;
120         EffectListItem*                 top_effect;
121         int                             ff_state;
122
123         /* data returned by the EVIOCGABS() ioctl */
124         int                             axes[ABS_MAX][5];
125         /* LUT for KEY_ to offset in rgbButtons */
126         BYTE                            buttons[KEY_MAX];
127
128 #define AXE_ABS         0
129 #define AXE_ABSMIN      1
130 #define AXE_ABSMAX      2
131 #define AXE_ABSFUZZ     3
132 #define AXE_ABSFLAT     4
133
134
135         /* data returned by EVIOCGBIT for caps, EV_ABS, EV_KEY, and EV_FF */
136         BYTE                            evbits[(EV_MAX+7)/8];
137         BYTE                            absbits[(ABS_MAX+7)/8];
138         BYTE                            keybits[(KEY_MAX+7)/8];
139         BYTE                            ffbits[(FF_MAX+7)/8];   
140 };
141
142 /* This GUID is slightly different from the linux joystick one. Take note. */
143 static GUID DInput_Wine_Joystick_GUID = { /* 9e573eda-7734-11d2-8d4a-23903fb6bdf7 */
144   0x9e573eda,
145   0x7734,
146   0x11d2,
147   {0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7}
148 };
149
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
154 #define test_bit(arr,bit) (((BYTE*)(arr))[(bit)>>3]&(1<<((bit)&7)))
155
156 static int joydev_have(BOOL require_ff)
157 {
158   int i, fd, flags, num_effects;
159   int havejoy = 0;
160
161   for (i=0;i<64;i++) {
162       char      buf[200];
163       BYTE      absbits[(ABS_MAX+7)/8],keybits[(KEY_MAX+7)/8];
164       BYTE      evbits[(EV_MAX+7)/8],ffbits[(FF_MAX+7)/8];
165
166       sprintf(buf,EVDEVPREFIX"%d",i);
167
168       if (require_ff) 
169           flags = O_RDWR;
170       else
171           flags = O_RDONLY;
172
173       if (-1!=(fd=open(buf,flags))) {
174           if (-1==ioctl(fd,EVIOCGBIT(EV_ABS,sizeof(absbits)),absbits)) {
175               perror("EVIOCGBIT EV_ABS");
176               close(fd);
177               continue;
178           }
179           if (-1==ioctl(fd,EVIOCGBIT(EV_KEY,sizeof(keybits)),keybits)) {
180               perror("EVIOCGBIT EV_KEY");
181               close(fd);
182               continue;
183           }
184
185           /* test for force feedback if it's required */
186           if (require_ff) {
187               if ((-1==ioctl(fd,EVIOCGBIT(0,sizeof(evbits)),evbits))) {
188                   perror("EVIOCGBIT 0");
189                   close(fd);
190                   continue; 
191               }
192               if (   (!test_bit(evbits,EV_FF))
193                   || (-1==ioctl(fd,EVIOCGBIT(EV_FF,sizeof(ffbits)),ffbits)) 
194                   || (-1==ioctl(fd,EVIOCGEFFECTS,&num_effects))
195                   || (num_effects <= 0)) {
196                   close(fd);
197                   continue;
198               }
199           }
200
201           /* A true joystick has at least axis X and Y, and at least 1
202            * button. copied from linux/drivers/input/joydev.c */
203           if (test_bit(absbits,ABS_X) && test_bit(absbits,ABS_Y) &&
204               (   test_bit(keybits,BTN_TRIGGER) ||
205                   test_bit(keybits,BTN_A)       ||
206                   test_bit(keybits,BTN_1)
207               )
208           ) {
209               FIXME("found a joystick at %s!\n",buf);
210               havejoy = 1;
211           }
212           close(fd);
213       }
214       if (havejoy || (errno==ENODEV))
215           break;
216   }
217   return havejoy;
218 }
219
220 static BOOL joydev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
221 {
222   int havejoy = 0;
223
224   if (id != 0)
225       return FALSE;
226
227   if (!((dwDevType == 0) ||
228         ((dwDevType == DIDEVTYPE_JOYSTICK) && (version < 0x0800)) ||
229         (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))))
230     return FALSE;
231
232 #ifndef HAVE_STRUCT_FF_EFFECT_DIRECTION
233   if (dwFlags & DIEDFL_FORCEFEEDBACK)
234     return FALSE;
235 #endif
236
237   havejoy = joydev_have(dwFlags & DIEDFL_FORCEFEEDBACK);
238
239   if (!havejoy)
240       return FALSE;
241
242   TRACE("Enumerating the linuxinput Joystick device\n");
243
244   /* Return joystick */
245   lpddi->guidInstance   = GUID_Joystick;
246   lpddi->guidProduct    = DInput_Wine_Joystick_GUID;
247
248   lpddi->guidFFDriver = GUID_NULL;
249   if (version >= 0x0800)
250     lpddi->dwDevType    = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
251   else
252     lpddi->dwDevType    = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
253
254   strcpy(lpddi->tszInstanceName, "Joystick");
255   /* ioctl JSIOCGNAME(len) */
256   strcpy(lpddi->tszProductName, "Wine Joystick");
257   return TRUE;
258 }
259
260 static BOOL joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
261 {
262   int havejoy = 0;
263
264   if (id != 0)
265       return FALSE;
266
267   if (!((dwDevType == 0) ||
268         ((dwDevType == DIDEVTYPE_JOYSTICK) && (version < 0x0800)) ||
269         (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))))
270     return FALSE;
271
272 #ifndef HAVE_STRUCT_FF_EFFECT_DIRECTION
273   if (dwFlags & DIEDFL_FORCEFEEDBACK)
274     return FALSE;
275 #endif
276
277   havejoy = joydev_have(dwFlags & DIEDFL_FORCEFEEDBACK);
278
279   if (!havejoy)
280       return FALSE;
281
282   TRACE("Enumerating the linuxinput Joystick device\n");
283
284   /* Return joystick */
285   lpddi->guidInstance   = GUID_Joystick;
286   lpddi->guidProduct    = DInput_Wine_Joystick_GUID;
287
288   lpddi->guidFFDriver = GUID_NULL;
289   if (version >= 0x0800)
290     lpddi->dwDevType    = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
291   else
292     lpddi->dwDevType    = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
293
294   MultiByteToWideChar(CP_ACP, 0, "Joystick", -1, lpddi->tszInstanceName, MAX_PATH);
295   /* ioctl JSIOCGNAME(len) */
296   MultiByteToWideChar(CP_ACP, 0, "Wine Joystick", -1, lpddi->tszProductName, MAX_PATH);
297   return TRUE;
298 }
299
300 static JoystickImpl *alloc_device(REFGUID rguid, const void *jvt, IDirectInputImpl *dinput)
301 {
302   JoystickImpl* newDevice;
303   int i;
304
305   newDevice = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(JoystickImpl));
306   newDevice->lpVtbl = jvt;
307   newDevice->ref = 1;
308   newDevice->joyfd = -1;
309   newDevice->dinput = dinput;
310 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
311   newDevice->ff_state = FF_STATUS_STOPPED;
312 #endif
313   memcpy(&(newDevice->guid),rguid,sizeof(*rguid));
314   for (i=0;i<ABS_MAX;i++) {
315     newDevice->wantmin[i] = -32768;
316     newDevice->wantmax[i] =  32767;
317     /* TODO: 
318      * direct input defines a default for the deadzone somewhere; but as long
319      * as in map_axis the code for the dead zone is commented out its no
320      * problem
321      */
322     newDevice->deadz[i]   =  0;
323   }
324   fake_current_js_state(newDevice);
325   return newDevice;
326 }
327
328 static HRESULT joydev_create_deviceA(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPDIRECTINPUTDEVICEA* pdev)
329 {
330   int havejoy = 0;
331
332   havejoy = joydev_have(FALSE);
333
334   if (!havejoy)
335       return DIERR_DEVICENOTREG;
336
337   if ((IsEqualGUID(&GUID_Joystick,rguid)) ||
338       (IsEqualGUID(&DInput_Wine_Joystick_GUID,rguid))) {
339     if ((riid == NULL) ||
340         IsEqualGUID(&IID_IDirectInputDeviceA,riid) ||
341         IsEqualGUID(&IID_IDirectInputDevice2A,riid) ||
342         IsEqualGUID(&IID_IDirectInputDevice7A,riid) ||
343         IsEqualGUID(&IID_IDirectInputDevice8A,riid)) {
344       *pdev = (IDirectInputDeviceA*) alloc_device(rguid, &JoystickAvt, dinput);
345       TRACE("Creating a Joystick device (%p)\n", *pdev);
346       return DI_OK;
347     } else
348       return DIERR_NOINTERFACE;
349   }
350
351   return DIERR_DEVICENOTREG;
352 }
353
354
355 static HRESULT joydev_create_deviceW(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPDIRECTINPUTDEVICEW* pdev)
356 {
357   int havejoy = 0;
358
359   havejoy = joydev_have(FALSE);
360
361   if (!havejoy)
362       return DIERR_DEVICENOTREG;
363
364   if ((IsEqualGUID(&GUID_Joystick,rguid)) ||
365       (IsEqualGUID(&DInput_Wine_Joystick_GUID,rguid))) {
366     if ((riid == NULL) ||
367         IsEqualGUID(&IID_IDirectInputDeviceW,riid) ||
368         IsEqualGUID(&IID_IDirectInputDevice2W,riid) ||
369         IsEqualGUID(&IID_IDirectInputDevice7W,riid) ||
370         IsEqualGUID(&IID_IDirectInputDevice8W,riid)) {
371       *pdev = (IDirectInputDeviceW*) alloc_device(rguid, &JoystickWvt, dinput);
372       TRACE("Creating a Joystick device (%p)\n", *pdev);
373       return DI_OK;
374     } else
375       return DIERR_NOINTERFACE;
376   }
377
378   return DIERR_DEVICENOTREG;
379 }
380
381 const struct dinput_device joystick_linuxinput_device = {
382   "Wine Linux-input joystick driver",
383   joydev_enum_deviceA,
384   joydev_enum_deviceW,
385   joydev_create_deviceA,
386   joydev_create_deviceW
387 };
388
389 /******************************************************************************
390  *      Joystick
391  */
392 static ULONG WINAPI JoystickAImpl_Release(LPDIRECTINPUTDEVICE8A iface)
393 {
394         JoystickImpl *This = (JoystickImpl *)iface;
395         ULONG ref;
396
397         ref = InterlockedDecrement(&(This->ref));
398         if (ref)
399                 return ref;
400
401         /* Reset the FF state, free all effects, etc */
402         IDirectInputDevice8_SendForceFeedbackCommand(iface, DISFFC_RESET);
403
404         /* Free the data queue */
405         HeapFree(GetProcessHeap(),0,This->data_queue);
406
407         /* Free the DataFormat */
408         HeapFree(GetProcessHeap(), 0, This->df);
409
410         HeapFree(GetProcessHeap(),0,This);
411         return 0;
412 }
413
414 /******************************************************************************
415   *   SetDataFormat : the application can choose the format of the data
416   *   the device driver sends back with GetDeviceState.
417   */
418 static HRESULT WINAPI JoystickAImpl_SetDataFormat(
419         LPDIRECTINPUTDEVICE8A iface,LPCDIDATAFORMAT df
420 )
421 {
422   JoystickImpl *This = (JoystickImpl *)iface;
423
424   TRACE("(this=%p,%p)\n",This,df);
425
426   if (df == NULL) {
427     WARN("invalid pointer\n");
428     return E_POINTER;
429   }
430
431   if (df->dwSize != sizeof(*df)) {
432     WARN("invalid argument\n");
433     return DIERR_INVALIDPARAM;
434   }
435
436   _dump_DIDATAFORMAT(df);
437
438   if (This->joyfd!=-1) {
439     WARN("acquired\n");
440     return DIERR_ACQUIRED;
441   }
442
443   /* Store the new data format */
444   This->df = HeapAlloc(GetProcessHeap(),0,df->dwSize);
445   if (This->df==NULL) {
446     return DIERR_OUTOFMEMORY;
447   }
448   memcpy(This->df, df, df->dwSize);
449   This->df->rgodf = HeapAlloc(GetProcessHeap(),0,df->dwNumObjs*df->dwObjSize);
450   if (This->df->rgodf==NULL) {
451     HeapFree(GetProcessHeap(), 0, This->df);
452     return DIERR_OUTOFMEMORY;
453   }
454   memcpy(This->df->rgodf,df->rgodf,df->dwNumObjs*df->dwObjSize);
455
456   return DI_OK;
457 }
458
459 /******************************************************************************
460   *     Acquire : gets exclusive control of the joystick
461   */
462 static HRESULT WINAPI JoystickAImpl_Acquire(LPDIRECTINPUTDEVICE8A iface)
463 {
464     int         i,buttons;
465     JoystickImpl *This = (JoystickImpl *)iface;
466     char        buf[200];
467     BOOL        readonly = TRUE;
468
469     TRACE("(this=%p)\n",This);
470     if (This->joyfd!=-1)
471         return S_FALSE;
472     if (This->df==NULL) {
473       return DIERR_INVALIDPARAM;
474     }
475     for (i=0;i<64;i++) {
476       sprintf(buf,EVDEVPREFIX"%d",i);
477       if (-1==(This->joyfd=open(buf,O_RDWR))) { 
478         if (-1==(This->joyfd=open(buf,O_RDONLY))) {
479           /* Couldn't open the device at all */ 
480           if (errno==ENODEV)
481             return DIERR_NOTFOUND;
482           perror(buf);
483           continue;
484         }
485         else {
486           /* Couldn't open in r/w but opened in read-only. */
487           WARN("Could not open %s in read-write mode.  Force feedback will be disabled.\n",buf);
488         }
489       }
490       else {
491         /* Opened device in read-write */
492         readonly = FALSE;
493       }
494       if ((-1!=ioctl(This->joyfd,EVIOCGBIT(0,sizeof(This->evbits)),This->evbits)) &&
495           (-1!=ioctl(This->joyfd,EVIOCGBIT(EV_ABS,sizeof(This->absbits)),This->absbits)) &&
496           (-1!=ioctl(This->joyfd,EVIOCGBIT(EV_KEY,sizeof(This->keybits)),This->keybits)) &&
497           (test_bit(This->absbits,ABS_X) && test_bit(This->absbits,ABS_Y) &&
498            (test_bit(This->keybits,BTN_TRIGGER)||
499             test_bit(This->keybits,BTN_A)        ||
500             test_bit(This->keybits,BTN_1)
501           )
502          )
503       )
504         break;
505       close(This->joyfd);
506       This->joyfd = -1;
507     }
508     if (This->joyfd==-1)
509         return DIERR_NOTFOUND;
510
511     This->has_ff = FALSE;
512     This->num_effects = 0;
513
514 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
515     if (!readonly && test_bit(This->evbits, EV_FF)) {
516         if (-1!=ioctl(This->joyfd,EVIOCGBIT(EV_FF,sizeof(This->ffbits)),This->ffbits)) {
517             if (-1!=ioctl(This->joyfd,EVIOCGEFFECTS,&This->num_effects) 
518                 && This->num_effects > 0) {
519                 This->has_ff = TRUE;
520                 TRACE("Joystick seems to be capable of force feedback.\n");
521             }
522             else {
523                 TRACE("Joystick does not support any effects, disabling force feedback.\n");
524             }
525         }
526         else {
527             TRACE("Could not get EV_FF bits; disabling force feedback.\n");
528         }
529     }
530     else {
531         TRACE("Force feedback disabled (device is readonly or joystick incapable).\n");
532     }
533 #endif
534
535     for (i=0;i<ABS_MAX;i++) {
536         if (test_bit(This->absbits,i)) {
537           if (-1==ioctl(This->joyfd,EVIOCGABS(i),&(This->axes[i])))
538             continue;
539           TRACE("axe %d: cur=%d, min=%d, max=%d, fuzz=%d, flat=%d\n",
540               i,
541               This->axes[i][AXE_ABS],
542               This->axes[i][AXE_ABSMIN],
543               This->axes[i][AXE_ABSMAX],
544               This->axes[i][AXE_ABSFUZZ],
545               This->axes[i][AXE_ABSFLAT]
546           );
547           This->havemin[i] = This->axes[i][AXE_ABSMIN];
548           This->havemax[i] = This->axes[i][AXE_ABSMAX];
549         }
550     }
551     buttons = 0;
552     for (i=0;i<KEY_MAX;i++) {
553             if (test_bit(This->keybits,i)) {
554                     TRACE("button %d: %d\n", i, buttons);
555                     This->buttons[i] = 0x80 | buttons;
556                     buttons++;
557             }
558     }
559
560         fake_current_js_state(This);
561
562     return 0;
563 }
564
565 /******************************************************************************
566   *     Unacquire : frees the joystick
567   */
568 static HRESULT WINAPI JoystickAImpl_Unacquire(LPDIRECTINPUTDEVICE8A iface)
569 {
570     JoystickImpl *This = (JoystickImpl *)iface;
571
572     TRACE("(this=%p)\n",This);
573     if (This->joyfd!=-1) {
574         close(This->joyfd);
575         This->joyfd = -1;
576         return DI_OK;
577     }
578     else 
579         return DI_NOEFFECT;
580 }
581
582 /*
583  * This maps the read value (from the input event) to a value in the
584  * 'wanted' range. It also autodetects the possible range of the axe and
585  * adapts values accordingly.
586  */
587 static int
588 map_axis(JoystickImpl* This, int axis, int val) {
589     int xmin = This->axes[axis][AXE_ABSMIN];
590     int xmax = This->axes[axis][AXE_ABSMAX];
591     int hmax = This->havemax[axis];
592     int hmin = This->havemin[axis];
593     int wmin = This->wantmin[axis];
594     int wmax = This->wantmax[axis];
595     int ret;
596
597     if (val > hmax) This->havemax[axis] = hmax = val;
598     if (val < hmin) This->havemin[axis] = hmin = val;
599
600     if (xmin == xmax) return val;
601
602     /* map the value from the hmin-hmax range into the wmin-wmax range */
603     ret = ((val-hmin) * (wmax-wmin)) / (hmax-hmin) + wmin;
604
605     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);
606
607 #if 0
608     /* deadzone doesn't work comfortably enough right now. needs more testing*/
609     if ((ret > -deadz/2 ) && (ret < deadz/2)) {
610         FIXME("%d in deadzone, return mid.\n",val);
611         return (wmax-wmin)/2+wmin;
612     }
613 #endif
614     return ret;
615 }
616
617 /* 
618  * set the current state of the js device as it would be with the middle
619  * values on the axes
620  */
621 static void fake_current_js_state(JoystickImpl *ji)
622 {
623         int i;
624         /* center the axes */
625         ji->js.lX  = map_axis(ji, ABS_X,  ji->axes[ABS_X ][AXE_ABS]);
626         ji->js.lY  = map_axis(ji, ABS_Y,  ji->axes[ABS_Y ][AXE_ABS]);
627         ji->js.lZ  = map_axis(ji, ABS_Z,  ji->axes[ABS_Z ][AXE_ABS]);
628         ji->js.lRx = map_axis(ji, ABS_RX, ji->axes[ABS_RX][AXE_ABS]);
629         ji->js.lRy = map_axis(ji, ABS_RY, ji->axes[ABS_RY][AXE_ABS]);
630         ji->js.lRz = map_axis(ji, ABS_RZ, ji->axes[ABS_RZ][AXE_ABS]);
631         ji->js.rglSlider[0] = map_axis(ji, ABS_THROTTLE, ji->axes[ABS_THROTTLE][AXE_ABS]);
632         ji->js.rglSlider[1] = map_axis(ji, ABS_RUDDER,   ji->axes[ABS_RUDDER  ][AXE_ABS]);
633         /* POV center is -1 */
634         for (i=0; i<4; i++) {
635                 ji->js.rgdwPOV[i] = -1;
636         }
637 }
638
639 /*
640  * Maps an event value to a DX "clock" position:
641  *           0
642  * 27000    -1 9000
643  *       18000
644  */
645 static DWORD map_pov(int event_value, int is_x) 
646 {
647         DWORD ret = -1;
648         if (is_x) {
649                 if (event_value<0) {
650                         ret = 27000;
651                 } else if (event_value>0) {
652                         ret = 9000;
653                 }
654         } else {
655                 if (event_value<0) {
656                         ret = 0;
657                 } else if (event_value>0) {
658                         ret = 18000;
659                 }
660         }
661         return ret;
662 }
663
664 static int find_property_offset(JoystickImpl *This, LPCDIPROPHEADER ph)
665 {
666   switch (ph->dwHow) {
667     case DIPH_BYOFFSET: {
668                           int i;
669                           for (i=0; i<This->df->dwNumObjs; i++) {
670                             if (This->df->rgodf[i].dwOfs == ph->dwObj) {
671                               return i;
672                             }
673                           }
674                         }
675                         break;
676     case DIPH_BYID: {
677                       return DIDFT_GETINSTANCE(ph->dwObj)>>WINE_JOYSTICK_AXIS_BASE;
678                     }
679                     break;
680     default:
681                     FIXME("Unhandled ph->dwHow=='%04X'\n", (unsigned int)ph->dwHow);
682   }
683
684   return -1;
685 }
686
687 static void joy_polldev(JoystickImpl *This) {
688     struct timeval tv;
689     fd_set      readfds;
690     struct      input_event ie;
691     int         btn;
692
693     if (This->joyfd==-1)
694         return;
695
696     while (1) {
697         memset(&tv,0,sizeof(tv));
698         FD_ZERO(&readfds);
699         FD_SET(This->joyfd,&readfds);
700
701         if (1>select(This->joyfd+1,&readfds,NULL,NULL,&tv))
702             return;
703
704         /* we have one event, so we can read */
705         if (sizeof(ie)!=read(This->joyfd,&ie,sizeof(ie)))
706             return;
707
708         TRACE("input_event: type %d, code %d, value %d\n",ie.type,ie.code,ie.value);
709         switch (ie.type) {
710         case EV_KEY:    /* button */
711                 btn = This->buttons[ie.code];
712                 TRACE("(%p) %d -> %d\n", This, ie.code, btn);
713                 if (btn&0x80) {
714                         btn &= 0x7F;
715                         This->js.rgbButtons[btn] = ie.value?0x80:0x00;
716                         GEN_EVENT(DIJOFS_BUTTON(btn),ie.value?0x80:0x0,ie.time.tv_usec,(This->dinput->evsequence)++);
717                 }
718                 break;
719         case EV_ABS:
720             switch (ie.code) {
721             case ABS_X:
722                 This->js.lX = map_axis(This,ABS_X,ie.value);
723                 GEN_EVENT(DIJOFS_X,This->js.lX,ie.time.tv_usec,(This->dinput->evsequence)++);
724                 break;
725             case ABS_Y:
726                 This->js.lY = map_axis(This,ABS_Y,ie.value);
727                 GEN_EVENT(DIJOFS_Y,This->js.lY,ie.time.tv_usec,(This->dinput->evsequence)++);
728                 break;
729             case ABS_Z:
730                 This->js.lZ = map_axis(This,ABS_Z,ie.value);
731                 GEN_EVENT(DIJOFS_Z,This->js.lZ,ie.time.tv_usec,(This->dinput->evsequence)++);
732                 break;
733             case ABS_RX:
734                 This->js.lRx = map_axis(This,ABS_RX,ie.value);
735                 GEN_EVENT(DIJOFS_RX,This->js.lRx,ie.time.tv_usec,(This->dinput->evsequence)++);
736                 break;
737             case ABS_RY:
738                 This->js.lRy = map_axis(This,ABS_RY,ie.value);
739                 GEN_EVENT(DIJOFS_RY,This->js.lRy,ie.time.tv_usec,(This->dinput->evsequence)++);
740                 break;
741             case ABS_RZ:
742                 This->js.lRz = map_axis(This,ABS_RZ,ie.value);
743                 GEN_EVENT(DIJOFS_RZ,This->js.lRz,ie.time.tv_usec,(This->dinput->evsequence)++);
744                 break;
745             case ABS_THROTTLE:
746                 This->js.rglSlider[0] = map_axis(This,ABS_THROTTLE,ie.value);
747                 GEN_EVENT(DIJOFS_SLIDER(0),This->js.rglSlider[0],ie.time.tv_usec,(This->dinput->evsequence)++);
748                 break;
749             case ABS_RUDDER:
750                 This->js.rglSlider[1] = map_axis(This,ABS_RUDDER,ie.value);
751                 GEN_EVENT(DIJOFS_SLIDER(1),This->js.rglSlider[1],ie.time.tv_usec,(This->dinput->evsequence)++);
752                 break;
753             case ABS_HAT0X:
754             case ABS_HAT0Y:
755                 This->js.rgdwPOV[0] = map_pov(ie.value,ie.code==ABS_HAT0X);
756                 GEN_EVENT(DIJOFS_POV(0),This->js.rgdwPOV[0],ie.time.tv_usec,(This->dinput->evsequence)++);
757                 break;
758             case ABS_HAT1X:
759             case ABS_HAT1Y:
760                 This->js.rgdwPOV[1] = map_pov(ie.value,ie.code==ABS_HAT1X);
761                 GEN_EVENT(DIJOFS_POV(1),This->js.rgdwPOV[1],ie.time.tv_usec,(This->dinput->evsequence)++);
762                 break;
763             case ABS_HAT2X:
764             case ABS_HAT2Y:
765                 This->js.rgdwPOV[2] = map_pov(ie.value,ie.code==ABS_HAT2X);
766                 GEN_EVENT(DIJOFS_POV(2),This->js.rgdwPOV[2],ie.time.tv_usec,(This->dinput->evsequence)++);
767                 break;
768             case ABS_HAT3X:
769             case ABS_HAT3Y:
770                 This->js.rgdwPOV[3] = map_pov(ie.value,ie.code==ABS_HAT3X);
771                 GEN_EVENT(DIJOFS_POV(3),This->js.rgdwPOV[3],ie.time.tv_usec,(This->dinput->evsequence)++);
772                 break;
773             default:
774                 FIXME("unhandled joystick axe event (code %d, value %d)\n",ie.code,ie.value);
775                 break;
776             }
777             break;
778 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
779         case EV_FF_STATUS:
780             This->ff_state = ie.value;
781             break;
782 #endif
783 #ifdef EV_SYN
784         case EV_SYN:
785             /* there is nothing to do */
786             break;
787 #endif
788         default:
789             FIXME("joystick cannot handle type %d event (code %d)\n",ie.type,ie.code);
790             break;
791         }
792     }
793 }
794
795 /******************************************************************************
796   *     GetDeviceState : returns the "state" of the joystick.
797   *
798   */
799 static HRESULT WINAPI JoystickAImpl_GetDeviceState(
800         LPDIRECTINPUTDEVICE8A iface,DWORD len,LPVOID ptr
801 ) {
802     JoystickImpl *This = (JoystickImpl *)iface;
803
804     joy_polldev(This);
805
806     TRACE("(this=%p,0x%08lx,%p)\n",This,len,ptr);
807     if ((len != sizeof(DIJOYSTATE)) && (len != sizeof(DIJOYSTATE2))) {
808         FIXME("len %ld is not sizeof(DIJOYSTATE) or DIJOYSTATE2, unsupported format.\n",len);
809         return E_FAIL;
810     }
811     memcpy(ptr,&(This->js),len);
812     This->queue_head = 0;
813     This->queue_tail = 0;
814     return 0;
815 }
816
817 /******************************************************************************
818   *     GetDeviceData : gets buffered input data.
819   */
820 static HRESULT WINAPI JoystickAImpl_GetDeviceData(LPDIRECTINPUTDEVICE8A iface,
821                                               DWORD dodsize,
822                                               LPDIDEVICEOBJECTDATA dod,
823                                               LPDWORD entries,
824                                               DWORD flags
825 ) {
826   JoystickImpl *This = (JoystickImpl *)iface;
827   DWORD len;
828   int nqtail;
829   HRESULT hr = DI_OK;
830
831   TRACE("(%p)->(dods=%ld,entries=%ld,fl=0x%08lx)\n",This,dodsize,*entries,flags);
832
833   if (This->joyfd==-!1) {
834     WARN("not acquired\n");
835     return DIERR_NOTACQUIRED;
836   }
837
838   joy_polldev(This);
839   if (flags & DIGDD_PEEK)
840     FIXME("DIGDD_PEEK\n");
841
842   len = ((This->queue_head < This->queue_tail) ? This->queue_len : 0)
843     + (This->queue_head - This->queue_tail);
844   if (len > *entries)
845     len = *entries;
846
847   if (dod == NULL) {
848     if (len)
849       TRACE("Application discarding %ld event(s).\n", len);
850
851     *entries = len;
852     nqtail = This->queue_tail + len;
853     while (nqtail >= This->queue_len)
854       nqtail -= This->queue_len;
855   } else {
856     if (dodsize < sizeof(DIDEVICEOBJECTDATA_DX3)) {
857       ERR("Wrong structure size !\n");
858       return DIERR_INVALIDPARAM;
859   }
860
861     if (len)
862       TRACE("Application retrieving %ld event(s).\n", len);
863
864     *entries = 0;
865     nqtail = This->queue_tail;
866     while (len) {
867       /* Copy the buffered data into the application queue */
868       memcpy((char *)dod + *entries * dodsize, This->data_queue + nqtail, dodsize);
869       /* Advance position */
870       nqtail++;
871       if (nqtail >= This->queue_len)
872         nqtail -= This->queue_len;
873       (*entries)++;
874       len--;
875     }
876   }
877
878   if (This->overflow) {
879     hr = DI_BUFFEROVERFLOW;
880     if (!(flags & DIGDD_PEEK)) {
881       This->overflow = FALSE;
882     }
883   }
884
885   if (!(flags & DIGDD_PEEK))
886     This->queue_tail = nqtail;
887
888   return hr;
889 }
890
891 /******************************************************************************
892   *     SetProperty : change input device properties
893   */
894 static HRESULT WINAPI JoystickAImpl_SetProperty(LPDIRECTINPUTDEVICE8A iface,
895                                             REFGUID rguid,
896                                             LPCDIPROPHEADER ph)
897 {
898   JoystickImpl *This = (JoystickImpl *)iface;
899
900   if (!ph) {
901     WARN("invalid argument\n");
902     return DIERR_INVALIDPARAM;
903   }
904
905   TRACE("(this=%p,%s,%p)\n",This,debugstr_guid(rguid),ph);
906   TRACE("ph.dwSize = %ld, ph.dwHeaderSize =%ld, ph.dwObj = %ld, ph.dwHow= %ld\n",ph->dwSize, ph->dwHeaderSize,ph->dwObj,ph->dwHow);
907
908   if (!HIWORD(rguid)) {
909     switch (LOWORD(rguid)) {
910     case (DWORD) DIPROP_BUFFERSIZE: {
911       LPCDIPROPDWORD    pd = (LPCDIPROPDWORD)ph;
912
913       TRACE("buffersize = %ld\n",pd->dwData);
914       if (This->data_queue) {
915         This->data_queue = HeapReAlloc(GetProcessHeap(),0, This->data_queue, pd->dwData * sizeof(DIDEVICEOBJECTDATA));
916       } else {
917         This->data_queue = HeapAlloc(GetProcessHeap(),0, pd->dwData * sizeof(DIDEVICEOBJECTDATA));
918       }
919       This->queue_head = 0;
920       This->queue_tail = 0;
921       This->queue_len  = pd->dwData;
922       break;
923     }
924     case (DWORD)DIPROP_RANGE: {
925       LPCDIPROPRANGE    pr = (LPCDIPROPRANGE)ph;
926
927       if (ph->dwHow == DIPH_DEVICE) {
928         int i;
929         TRACE("proprange(%ld,%ld) all\n",pr->lMin,pr->lMax);
930         for (i = 0; i < This->df->dwNumObjs; i++) {
931           This->wantmin[i] = pr->lMin;
932           This->wantmax[i] = pr->lMax;
933         }
934       } else {
935         int obj = find_property_offset(This, ph);
936         TRACE("proprange(%ld,%ld) obj=%d\n",pr->lMin,pr->lMax,obj);
937         if (obj >= 0) {
938           This->wantmin[obj] = pr->lMin;
939           This->wantmax[obj] = pr->lMax;
940         }
941       }
942       return DI_OK;
943     }
944     case (DWORD)DIPROP_DEADZONE: {
945       LPCDIPROPDWORD    pd = (LPCDIPROPDWORD)ph;
946       if (ph->dwHow == DIPH_DEVICE) {
947         int i;
948         TRACE("deadzone(%ld) all\n",pd->dwData);
949         for (i = 0; i < This->df->dwNumObjs; i++) {
950           This->deadz[i] = pd->dwData;
951         }
952       } else {
953         int obj = find_property_offset(This, ph);
954         TRACE("deadzone(%ld) obj=%d\n",pd->dwData,obj);
955         if (obj >= 0) {
956           This->deadz[obj] = pd->dwData;
957         }
958       }
959       return DI_OK;
960     }
961     default:
962       FIXME("Unknown type %p (%s)\n",rguid,debugstr_guid(rguid));
963       break;
964     }
965   }
966   fake_current_js_state(This);
967   return 0;
968 }
969
970 /******************************************************************************
971   *     SetEventNotification : specifies event to be sent on state change
972   */
973 static HRESULT WINAPI JoystickAImpl_SetEventNotification(
974         LPDIRECTINPUTDEVICE8A iface, HANDLE hnd
975 ) {
976     JoystickImpl *This = (JoystickImpl *)iface;
977
978     TRACE("(this=%p,%p)\n",This,hnd);
979     This->hEvent = hnd;
980     return DI_OK;
981 }
982
983 static HRESULT WINAPI JoystickAImpl_GetCapabilities(
984         LPDIRECTINPUTDEVICE8A iface,
985         LPDIDEVCAPS lpDIDevCaps)
986 {
987     JoystickImpl *This = (JoystickImpl *)iface;
988     int         xfd = This->joyfd;
989     int         i,axes,buttons;
990
991     TRACE("%p->(%p)\n",iface,lpDIDevCaps);
992
993     if (!lpDIDevCaps) {
994         WARN("invalid pointer\n");
995         return E_POINTER;
996     }
997
998     if (lpDIDevCaps->dwSize != sizeof(DIDEVCAPS)) {
999         WARN("invalid argument\n");
1000         return DIERR_INVALIDPARAM;
1001     }
1002
1003     if (xfd==-1) {
1004         /* yes, games assume we return something, even if unacquired */
1005         JoystickAImpl_Acquire(iface);
1006     }
1007
1008     lpDIDevCaps->dwFlags        = DIDC_ATTACHED;
1009     if (This->dinput->dwVersion >= 0x0800)
1010         lpDIDevCaps->dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
1011     else
1012         lpDIDevCaps->dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
1013
1014     axes=0;
1015     for (i=0;i<ABS_MAX;i++) if (test_bit(This->absbits,i)) axes++;
1016     buttons=0;
1017     for (i=0;i<KEY_MAX;i++) if (test_bit(This->keybits,i)) buttons++;
1018
1019     if (This->has_ff) 
1020          lpDIDevCaps->dwFlags |= DIDC_FORCEFEEDBACK;
1021
1022     lpDIDevCaps->dwAxes = axes;
1023     lpDIDevCaps->dwButtons = buttons;
1024
1025     if (xfd==-1) {
1026       JoystickAImpl_Unacquire(iface);
1027     }
1028
1029     return DI_OK;
1030 }
1031
1032 static HRESULT WINAPI JoystickAImpl_Poll(LPDIRECTINPUTDEVICE8A iface) {
1033     JoystickImpl *This = (JoystickImpl *)iface;
1034     TRACE("(%p)\n",This);
1035
1036     if (This->joyfd==-1) {
1037       return DIERR_NOTACQUIRED;
1038     }
1039
1040     joy_polldev(This);
1041     return DI_OK;
1042 }
1043
1044 /******************************************************************************
1045   *     EnumObjects : enumerate the different buttons and axis...
1046   */
1047 static HRESULT WINAPI JoystickAImpl_EnumObjects(
1048         LPDIRECTINPUTDEVICE8A iface,
1049         LPDIENUMDEVICEOBJECTSCALLBACKA lpCallback,
1050         LPVOID lpvRef,
1051         DWORD dwFlags)
1052 {
1053   JoystickImpl *This = (JoystickImpl *)iface;
1054   DIDEVICEOBJECTINSTANCEA ddoi;
1055   int xfd = This->joyfd;
1056
1057   TRACE("(this=%p,%p,%p,%08lx)\n", This, lpCallback, lpvRef, dwFlags);
1058   if (TRACE_ON(dinput)) {
1059     TRACE("  - flags = ");
1060     _dump_EnumObjects_flags(dwFlags);
1061     TRACE("\n");
1062   }
1063
1064   /* We need to work even if we're not yet acquired */
1065   if (xfd == -1)
1066     IDirectInputDevice8_Acquire(iface);
1067
1068   /* Only the fields till dwFFMaxForce are relevant */
1069   ddoi.dwSize = FIELD_OFFSET(DIDEVICEOBJECTINSTANCEA, dwFFMaxForce);
1070
1071   /* For the joystick, do as is done in the GetCapabilities function */
1072   /* FIXME: needs more items */
1073   if ((dwFlags == DIDFT_ALL) ||
1074       (dwFlags & DIDFT_AXIS)) {
1075     BYTE i;
1076
1077     for (i = 0; i < ABS_MAX; i++) {
1078       if (!test_bit(This->absbits,i)) continue;
1079
1080       switch (i) {
1081       case ABS_X:
1082         ddoi.guidType = GUID_XAxis;
1083         ddoi.dwOfs = DIJOFS_X;
1084         break;
1085       case ABS_Y:
1086         ddoi.guidType = GUID_YAxis;
1087         ddoi.dwOfs = DIJOFS_Y;
1088         break;
1089       case ABS_Z:
1090         ddoi.guidType = GUID_ZAxis;
1091         ddoi.dwOfs = DIJOFS_Z;
1092         break;
1093       case ABS_RX:
1094         ddoi.guidType = GUID_RxAxis;
1095         ddoi.dwOfs = DIJOFS_RX;
1096         break;
1097       case ABS_RY:
1098         ddoi.guidType = GUID_RyAxis;
1099         ddoi.dwOfs = DIJOFS_RY;
1100         break;
1101       case ABS_RZ:
1102         ddoi.guidType = GUID_RzAxis;
1103         ddoi.dwOfs = DIJOFS_RZ;
1104         break;
1105       case ABS_THROTTLE:
1106         ddoi.guidType = GUID_Slider;
1107         ddoi.dwOfs = DIJOFS_SLIDER(0);
1108         break;
1109       case ABS_RUDDER:
1110         ddoi.guidType = GUID_Slider;
1111         ddoi.dwOfs = DIJOFS_SLIDER(1);
1112         break;
1113       default:
1114         FIXME("unhandled abs axis %d, ignoring!\n",i);
1115       }
1116       ddoi.dwType = DIDFT_MAKEINSTANCE(i << WINE_JOYSTICK_AXIS_BASE) | DIDFT_ABSAXIS;
1117       /* Linux event force feedback supports only (and always) x and y axes */
1118       if (i == ABS_X || i == ABS_Y) {
1119         if (This->has_ff)
1120           ddoi.dwFlags |= DIDOI_FFACTUATOR;
1121       }
1122       sprintf(ddoi.tszName, "%d-Axis", i);
1123       _dump_OBJECTINSTANCEA(&ddoi);
1124       if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE) {
1125         /* return to unaquired state if that's where we were */
1126         if (xfd == -1)
1127           IDirectInputDevice8_Unacquire(iface);
1128         return DI_OK;
1129       }
1130     }
1131   }
1132
1133   if ((dwFlags == DIDFT_ALL) ||
1134       (dwFlags & DIDFT_POV)) {
1135     int i;
1136     ddoi.guidType = GUID_POV;
1137     for (i=0; i<4; i++) {
1138       if (test_bit(This->absbits,ABS_HAT0X+(i<<1)) && test_bit(This->absbits,ABS_HAT0Y+(i<<1))) {
1139         ddoi.dwOfs = DIJOFS_POV(i);
1140         ddoi.dwType = DIDFT_MAKEINSTANCE(i << WINE_JOYSTICK_POV_BASE) | DIDFT_POV;
1141         sprintf(ddoi.tszName, "%d-POV", i);
1142         _dump_OBJECTINSTANCEA(&ddoi);
1143         if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE) {
1144           /* return to unaquired state if that's where we were */
1145           if (xfd == -1)
1146             IDirectInputDevice8_Unacquire(iface);
1147           return DI_OK;
1148         }
1149       }
1150     }
1151   }
1152
1153   if ((dwFlags == DIDFT_ALL) ||
1154       (dwFlags & DIDFT_BUTTON)) {
1155     int i, btncount=0;
1156
1157     /*The DInput SDK says that GUID_Button is only for mouse buttons but well*/
1158
1159     ddoi.guidType = GUID_Button;
1160
1161     for (i = 0; i < KEY_MAX; i++) {
1162       if (!test_bit(This->keybits,i)) continue;
1163       ddoi.dwOfs = DIJOFS_BUTTON(btncount);
1164       ddoi.dwType = DIDFT_MAKEINSTANCE(btncount << WINE_JOYSTICK_BUTTON_BASE) | DIDFT_PSHBUTTON;
1165       sprintf(ddoi.tszName, "%d-Button", btncount);
1166       btncount++;
1167       _dump_OBJECTINSTANCEA(&ddoi);
1168       if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE) {
1169         /* return to unaquired state if that's where we were */
1170         if (xfd == -1)
1171           IDirectInputDevice8_Unacquire(iface);
1172         return DI_OK;
1173       }
1174     }
1175   }
1176
1177   /* return to unaquired state if that's where we were */
1178   if (xfd == -1)
1179     IDirectInputDevice8_Unacquire(iface);
1180
1181   return DI_OK;
1182 }
1183
1184 static HRESULT WINAPI JoystickWImpl_EnumObjects(LPDIRECTINPUTDEVICE8W iface,
1185                                                 LPDIENUMDEVICEOBJECTSCALLBACKW lpCallback,
1186                                                 LPVOID lpvRef,
1187                                                 DWORD dwFlags)
1188 {
1189   JoystickImpl *This = (JoystickImpl *)iface;
1190
1191   device_enumobjects_AtoWcb_data data;
1192
1193   data.lpCallBack = lpCallback;
1194   data.lpvRef = lpvRef;
1195
1196   return JoystickAImpl_EnumObjects((LPDIRECTINPUTDEVICE8A) This, (LPDIENUMDEVICEOBJECTSCALLBACKA) DIEnumDevicesCallbackAtoW, (LPVOID) &data, dwFlags);
1197 }
1198
1199 /******************************************************************************
1200   *     GetProperty : get input device properties
1201   */
1202 static HRESULT WINAPI JoystickAImpl_GetProperty(LPDIRECTINPUTDEVICE8A iface,
1203                                                 REFGUID rguid,
1204                                                 LPDIPROPHEADER pdiph)
1205 {
1206   JoystickImpl *This = (JoystickImpl *)iface;
1207
1208   TRACE("(this=%p,%s,%p)\n",
1209         iface, debugstr_guid(rguid), pdiph);
1210
1211   if (TRACE_ON(dinput))
1212     _dump_DIPROPHEADER(pdiph);
1213
1214   if (!HIWORD(rguid)) {
1215     switch (LOWORD(rguid)) {
1216     case (DWORD) DIPROP_BUFFERSIZE: {
1217       LPDIPROPDWORD     pd = (LPDIPROPDWORD)pdiph;
1218
1219       TRACE(" return buffersize = %d\n",This->queue_len);
1220       pd->dwData = This->queue_len;
1221       break;
1222     }
1223
1224     case (DWORD) DIPROP_RANGE: {
1225       /* LPDIPROPRANGE pr = (LPDIPROPRANGE) pdiph; */
1226       if ((pdiph->dwHow == DIPH_BYID) &&
1227           (pdiph->dwObj & DIDFT_ABSAXIS)) {
1228         /* The app is querying the current range of the axis : return the lMin and lMax values */
1229         FIXME("unimplemented axis range query.\n");
1230       }
1231
1232       break;
1233     }
1234
1235     default:
1236       FIXME("Unknown type %p (%s)\n",rguid,debugstr_guid(rguid));
1237       break;
1238     }
1239   }
1240
1241
1242   return DI_OK;
1243 }
1244
1245 /****************************************************************************** 
1246   *     CreateEffect - Create a new FF effect with the specified params
1247   */
1248 static HRESULT WINAPI JoystickAImpl_CreateEffect(LPDIRECTINPUTDEVICE8A iface,
1249                                                  REFGUID rguid,
1250                                                  LPCDIEFFECT lpeff,
1251                                                  LPDIRECTINPUTEFFECT *ppdef,
1252                                                  LPUNKNOWN pUnkOuter)
1253 {
1254 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1255     EffectListItem* new = NULL;
1256     HRESULT retval = DI_OK;
1257 #endif
1258
1259     JoystickImpl* This = (JoystickImpl*)iface;
1260     TRACE("(this=%p,%p,%p,%p,%p)\n", This, rguid, lpeff, ppdef, pUnkOuter);
1261
1262 #ifndef HAVE_STRUCT_FF_EFFECT_DIRECTION
1263     TRACE("not available (compiled w/o ff support)\n");
1264     *ppdef = NULL;
1265     return DI_OK; 
1266 #else
1267
1268     new = malloc(sizeof(EffectListItem));
1269     new->next = This->top_effect;
1270     This->top_effect = new;
1271
1272     retval = linuxinput_create_effect(&(This->joyfd), rguid, &(new->ref));
1273     if (retval != DI_OK)
1274         return retval;
1275  
1276     if (lpeff != NULL)
1277         retval = IDirectInputEffect_SetParameters(new->ref, lpeff, 0);
1278     if (retval != DI_OK && retval != DI_DOWNLOADSKIPPED)
1279         return retval;
1280
1281     *ppdef = new->ref;
1282
1283     if (pUnkOuter != NULL)
1284         FIXME("Interface aggregation not implemented.\n");
1285
1286     return DI_OK;
1287
1288 #endif /* HAVE_STRUCT_FF_EFFECT_DIRECTION */
1289
1290
1291 /*******************************************************************************
1292  *      EnumEffects - Enumerate available FF effects
1293  */
1294 static HRESULT WINAPI JoystickAImpl_EnumEffects(LPDIRECTINPUTDEVICE8A iface,
1295                                                 LPDIENUMEFFECTSCALLBACKA lpCallback,
1296                                                 LPVOID pvRef,
1297                                                 DWORD dwEffType)
1298 {
1299 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1300     DIEFFECTINFOA dei; /* feif */
1301     DWORD type = DIEFT_GETTYPE(dwEffType);
1302     JoystickImpl* This = (JoystickImpl*)iface;
1303     int xfd = This->joyfd;
1304
1305     TRACE("(this=%p,%p,%ld) type=%ld fd=%d\n", This, pvRef, dwEffType, type, xfd);
1306
1307     dei.dwSize = sizeof(DIEFFECTINFOA);          
1308
1309     /* We need to return something even if we're not yet acquired */
1310     if (xfd == -1)
1311         IDirectInputDevice8_Acquire(iface);
1312
1313     if ((type == DIEFT_ALL || type == DIEFT_CONSTANTFORCE)
1314         && test_bit(This->ffbits, FF_CONSTANT)) {
1315         IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_ConstantForce);
1316         (*lpCallback)(&dei, pvRef);
1317     }
1318
1319     if ((type == DIEFT_ALL || type == DIEFT_PERIODIC)
1320         && test_bit(This->ffbits, FF_PERIODIC)) {
1321         if (test_bit(This->ffbits, FF_SQUARE)) {
1322             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Square);
1323             (*lpCallback)(&dei, pvRef);
1324         }
1325         if (test_bit(This->ffbits, FF_SINE)) {
1326             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Sine);
1327             (*lpCallback)(&dei, pvRef);
1328         }
1329         if (test_bit(This->ffbits, FF_TRIANGLE)) {
1330             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Triangle);
1331             (*lpCallback)(&dei, pvRef);
1332         }
1333         if (test_bit(This->ffbits, FF_SAW_UP)) {
1334             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_SawtoothUp);
1335             (*lpCallback)(&dei, pvRef);
1336         }
1337         if (test_bit(This->ffbits, FF_SAW_DOWN)) {
1338             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_SawtoothDown);
1339             (*lpCallback)(&dei, pvRef);
1340         }
1341     } 
1342
1343     if ((type == DIEFT_ALL || type == DIEFT_RAMPFORCE)
1344         && test_bit(This->ffbits, FF_RAMP)) {
1345         IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_RampForce);
1346         (*lpCallback)(&dei, pvRef);
1347     }
1348
1349     if (type == DIEFT_ALL || type == DIEFT_CONDITION) {
1350         if (test_bit(This->ffbits, FF_SPRING)) {
1351             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Spring);
1352             (*lpCallback)(&dei, pvRef);
1353         }
1354         if (test_bit(This->ffbits, FF_DAMPER)) {
1355             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Damper);
1356             (*lpCallback)(&dei, pvRef);
1357         }
1358         if (test_bit(This->ffbits, FF_INERTIA)) {
1359             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Inertia);
1360             (*lpCallback)(&dei, pvRef);
1361         }
1362         if (test_bit(This->ffbits, FF_FRICTION)) {
1363             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Friction);
1364             (*lpCallback)(&dei, pvRef);
1365         }
1366     }
1367
1368     /* return to unaquired state if that's where it was */
1369     if (xfd == -1)
1370         IDirectInputDevice8_Unacquire(iface);
1371 #endif
1372
1373     return DI_OK;
1374 }
1375
1376 static HRESULT WINAPI JoystickWImpl_EnumEffects(LPDIRECTINPUTDEVICE8W iface,
1377                                                 LPDIENUMEFFECTSCALLBACKW lpCallback,
1378                                                 LPVOID pvRef,
1379                                                 DWORD dwEffType)
1380 {
1381 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1382     /* seems silly to duplicate all this code but all the structures and functions
1383      * are actually different (A/W) */
1384     DIEFFECTINFOW dei; /* feif */
1385     DWORD type = DIEFT_GETTYPE(dwEffType);
1386     JoystickImpl* This = (JoystickImpl*)iface;
1387     int xfd = This->joyfd;
1388
1389     TRACE("(this=%p,%p,%ld) type=%ld fd=%d\n", This, pvRef, dwEffType, type, xfd);
1390
1391     dei.dwSize = sizeof(DIEFFECTINFOW);          
1392
1393     /* We need to return something even if we're not yet acquired */
1394     if (xfd == -1)
1395         IDirectInputDevice8_Acquire(iface);
1396
1397     if ((type == DIEFT_ALL || type == DIEFT_CONSTANTFORCE)
1398         && test_bit(This->ffbits, FF_CONSTANT)) {
1399         IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_ConstantForce);
1400         (*lpCallback)(&dei, pvRef);
1401     }
1402
1403     if ((type == DIEFT_ALL || type == DIEFT_PERIODIC)
1404         && test_bit(This->ffbits, FF_PERIODIC)) {
1405         if (test_bit(This->ffbits, FF_SQUARE)) {
1406             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Square);
1407             (*lpCallback)(&dei, pvRef);
1408         }
1409         if (test_bit(This->ffbits, FF_SINE)) {
1410             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Sine);
1411             (*lpCallback)(&dei, pvRef);
1412         }
1413         if (test_bit(This->ffbits, FF_TRIANGLE)) {
1414             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Triangle);
1415             (*lpCallback)(&dei, pvRef);
1416         }
1417         if (test_bit(This->ffbits, FF_SAW_UP)) {
1418             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_SawtoothUp);
1419             (*lpCallback)(&dei, pvRef);
1420         }
1421         if (test_bit(This->ffbits, FF_SAW_DOWN)) {
1422             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_SawtoothDown);
1423             (*lpCallback)(&dei, pvRef);
1424         }
1425     } 
1426
1427     if ((type == DIEFT_ALL || type == DIEFT_RAMPFORCE)
1428         && test_bit(This->ffbits, FF_RAMP)) {
1429         IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_RampForce);
1430         (*lpCallback)(&dei, pvRef);
1431     }
1432
1433     if (type == DIEFT_ALL || type == DIEFT_CONDITION) {
1434         if (test_bit(This->ffbits, FF_SPRING)) {
1435             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Spring);
1436             (*lpCallback)(&dei, pvRef);
1437         }
1438         if (test_bit(This->ffbits, FF_DAMPER)) {
1439             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Damper);
1440             (*lpCallback)(&dei, pvRef);
1441         }
1442         if (test_bit(This->ffbits, FF_INERTIA)) {
1443             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Inertia);
1444             (*lpCallback)(&dei, pvRef);
1445         }
1446         if (test_bit(This->ffbits, FF_FRICTION)) {
1447             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Friction);
1448             (*lpCallback)(&dei, pvRef);
1449         }
1450     }
1451
1452     /* return to unaquired state if that's where it was */
1453     if (xfd == -1)
1454         IDirectInputDevice8_Unacquire(iface);
1455 #endif
1456
1457     return DI_OK;
1458 }
1459
1460 /*******************************************************************************
1461  *      GetEffectInfo - Get information about a particular effect 
1462  */
1463 static HRESULT WINAPI JoystickAImpl_GetEffectInfo(LPDIRECTINPUTDEVICE8A iface,
1464                                                   LPDIEFFECTINFOA pdei,
1465                                                   REFGUID guid)
1466 {
1467     JoystickImpl* This = (JoystickImpl*)iface;
1468
1469     TRACE("(this=%p,%p,%s)\n", This, pdei, _dump_dinput_GUID(guid));
1470
1471 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1472     return linuxinput_get_info_A(This->joyfd, guid, pdei); 
1473 #else
1474     return DI_OK;
1475 #endif
1476 }
1477
1478 static HRESULT WINAPI JoystickWImpl_GetEffectInfo(LPDIRECTINPUTDEVICE8W iface,
1479                                                   LPDIEFFECTINFOW pdei,
1480                                                   REFGUID guid)
1481 {
1482     JoystickImpl* This = (JoystickImpl*)iface;
1483             
1484     TRACE("(this=%p,%p,%s)\n", This, pdei, _dump_dinput_GUID(guid));
1485         
1486 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1487     return linuxinput_get_info_W(This->joyfd, guid, pdei);
1488 #else
1489     return DI_OK;
1490 #endif
1491 }
1492
1493 /*******************************************************************************
1494  *      GetForceFeedbackState - Get information about the device's FF state 
1495  */
1496 static HRESULT WINAPI JoystickAImpl_GetForceFeedbackState(
1497         LPDIRECTINPUTDEVICE8A iface,
1498         LPDWORD pdwOut)
1499 {
1500     JoystickImpl* This = (JoystickImpl*)iface;
1501
1502     TRACE("(this=%p,%p)\n", This, pdwOut);
1503
1504     (*pdwOut) = 0;
1505
1506 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1507     /* DIGFFS_STOPPED is the only mandatory flag to report */
1508     if (This->ff_state == FF_STATUS_STOPPED)
1509         (*pdwOut) |= DIGFFS_STOPPED;
1510 #endif
1511
1512     return DI_OK;
1513 }
1514
1515 /*******************************************************************************
1516  *      SendForceFeedbackCommand - Send a command to the device's FF system
1517  */
1518 static HRESULT WINAPI JoystickAImpl_SendForceFeedbackCommand(
1519         LPDIRECTINPUTDEVICE8A iface,
1520         DWORD dwFlags)
1521 {
1522     JoystickImpl* This = (JoystickImpl*)iface;
1523     TRACE("(this=%p,%ld)\n", This, dwFlags);
1524
1525 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1526     if (dwFlags == DISFFC_STOPALL) {
1527         /* Stop all effects */
1528         EffectListItem* itr = This->top_effect;
1529         while (itr) {
1530             IDirectInputEffect_Stop(itr->ref);
1531             itr = itr->next;
1532         }
1533     } else if (dwFlags == DISFFC_RESET) {
1534         /* Stop, unload, release and free all effects */
1535         /* This returns the device to its "bare" state */
1536         while (This->top_effect) {
1537             EffectListItem* temp = This->top_effect; 
1538             IDirectInputEffect_Stop(temp->ref);
1539             IDirectInputEffect_Unload(temp->ref);
1540             IDirectInputEffect_Release(temp->ref);
1541             This->top_effect = temp->next; 
1542             free(temp);
1543         }
1544     } else if (dwFlags == DISFFC_PAUSE || dwFlags == DISFFC_CONTINUE) {
1545         FIXME("No support for Pause or Continue in linux\n");
1546     } else if (dwFlags == DISFFC_SETACTUATORSOFF 
1547                 || dwFlags == DISFFC_SETACTUATORSON) {
1548         FIXME("No direct actuator control in linux\n");
1549     } else {
1550         FIXME("Unknown Force Feedback Command!\n");
1551         return DIERR_INVALIDPARAM;
1552     }
1553     return DI_OK;
1554 #else
1555     return DIERR_UNSUPPORTED;
1556 #endif
1557 }
1558
1559 /*******************************************************************************
1560  *      EnumCreatedEffectObjects - Enumerate all the effects that have been
1561  *              created for this device.
1562  */
1563 static HRESULT WINAPI JoystickAImpl_EnumCreatedEffectObjects(
1564         LPDIRECTINPUTDEVICE8A iface,
1565         LPDIENUMCREATEDEFFECTOBJECTSCALLBACK lpCallback,
1566         LPVOID pvRef,
1567         DWORD dwFlags)
1568 {
1569     /* this function is safe to call on non-ff-enabled builds */
1570
1571     JoystickImpl* This = (JoystickImpl*)iface;
1572     EffectListItem* itr = This->top_effect;
1573     TRACE("(this=%p,%p,%p,%ld)\n", This, lpCallback, pvRef, dwFlags);
1574
1575     if (!lpCallback)
1576         return DIERR_INVALIDPARAM;
1577
1578     if (dwFlags != 0)
1579         FIXME("Flags specified, but no flags exist yet (DX9)!");
1580
1581     while (itr) {
1582         (*lpCallback)(itr->ref, pvRef);
1583         itr = itr->next;
1584     }
1585
1586     return DI_OK;
1587 }
1588
1589 static const IDirectInputDevice8AVtbl JoystickAvt =
1590 {
1591         IDirectInputDevice2AImpl_QueryInterface,
1592         IDirectInputDevice2AImpl_AddRef,
1593         JoystickAImpl_Release,
1594         JoystickAImpl_GetCapabilities,
1595         JoystickAImpl_EnumObjects,
1596         JoystickAImpl_GetProperty,
1597         JoystickAImpl_SetProperty,
1598         JoystickAImpl_Acquire,
1599         JoystickAImpl_Unacquire,
1600         JoystickAImpl_GetDeviceState,
1601         JoystickAImpl_GetDeviceData,
1602         JoystickAImpl_SetDataFormat,
1603         JoystickAImpl_SetEventNotification,
1604         IDirectInputDevice2AImpl_SetCooperativeLevel,
1605         IDirectInputDevice2AImpl_GetObjectInfo,
1606         IDirectInputDevice2AImpl_GetDeviceInfo,
1607         IDirectInputDevice2AImpl_RunControlPanel,
1608         IDirectInputDevice2AImpl_Initialize,
1609         JoystickAImpl_CreateEffect,
1610         JoystickAImpl_EnumEffects,
1611         JoystickAImpl_GetEffectInfo,
1612         JoystickAImpl_GetForceFeedbackState,
1613         JoystickAImpl_SendForceFeedbackCommand,
1614         JoystickAImpl_EnumCreatedEffectObjects,
1615         IDirectInputDevice2AImpl_Escape,
1616         JoystickAImpl_Poll,
1617         IDirectInputDevice2AImpl_SendDeviceData,
1618         IDirectInputDevice7AImpl_EnumEffectsInFile,
1619         IDirectInputDevice7AImpl_WriteEffectToFile,
1620         IDirectInputDevice8AImpl_BuildActionMap,
1621         IDirectInputDevice8AImpl_SetActionMap,
1622         IDirectInputDevice8AImpl_GetImageInfo
1623 };
1624
1625 #if !defined(__STRICT_ANSI__) && defined(__GNUC__)
1626 # define XCAST(fun)     (typeof(JoystickWvt.fun))
1627 #else
1628 # define XCAST(fun)     (void*)
1629 #endif
1630
1631 static const IDirectInputDevice8WVtbl JoystickWvt =
1632 {
1633         IDirectInputDevice2WImpl_QueryInterface,
1634         XCAST(AddRef)IDirectInputDevice2AImpl_AddRef,
1635         XCAST(Release)JoystickAImpl_Release,
1636         XCAST(GetCapabilities)JoystickAImpl_GetCapabilities,
1637         JoystickWImpl_EnumObjects,
1638         XCAST(GetProperty)JoystickAImpl_GetProperty,
1639         XCAST(SetProperty)JoystickAImpl_SetProperty,
1640         XCAST(Acquire)JoystickAImpl_Acquire,
1641         XCAST(Unacquire)JoystickAImpl_Unacquire,
1642         XCAST(GetDeviceState)JoystickAImpl_GetDeviceState,
1643         XCAST(GetDeviceData)JoystickAImpl_GetDeviceData,
1644         XCAST(SetDataFormat)JoystickAImpl_SetDataFormat,
1645         XCAST(SetEventNotification)JoystickAImpl_SetEventNotification,
1646         XCAST(SetCooperativeLevel)IDirectInputDevice2AImpl_SetCooperativeLevel,
1647         IDirectInputDevice2WImpl_GetObjectInfo,
1648         IDirectInputDevice2WImpl_GetDeviceInfo,
1649         XCAST(RunControlPanel)IDirectInputDevice2AImpl_RunControlPanel,
1650         XCAST(Initialize)IDirectInputDevice2AImpl_Initialize,
1651         XCAST(CreateEffect)JoystickAImpl_CreateEffect,
1652         JoystickWImpl_EnumEffects,
1653         JoystickWImpl_GetEffectInfo,
1654         XCAST(GetForceFeedbackState)JoystickAImpl_GetForceFeedbackState,
1655         XCAST(SendForceFeedbackCommand)JoystickAImpl_SendForceFeedbackCommand,
1656         XCAST(EnumCreatedEffectObjects)JoystickAImpl_EnumCreatedEffectObjects,
1657         XCAST(Escape)IDirectInputDevice2AImpl_Escape,
1658         XCAST(Poll)JoystickAImpl_Poll,
1659         XCAST(SendDeviceData)IDirectInputDevice2AImpl_SendDeviceData,
1660         IDirectInputDevice7WImpl_EnumEffectsInFile,
1661         IDirectInputDevice7WImpl_WriteEffectToFile,
1662         IDirectInputDevice8WImpl_BuildActionMap,
1663         IDirectInputDevice8WImpl_SetActionMap,
1664         IDirectInputDevice8WImpl_GetImageInfo
1665 };
1666 #undef XCAST
1667
1668 #else  /* HAVE_CORRECT_LINUXINPUT_H */
1669
1670 const struct dinput_device joystick_linuxinput_device = {
1671   "Wine Linux-input joystick driver",
1672   NULL,
1673   NULL,
1674   NULL,
1675   NULL
1676 };
1677
1678 #endif  /* HAVE_CORRECT_LINUXINPUT_H */