imagehlp: Remove unused variable.
[wine] / dlls / dinput / effect_linuxinput.c
1 /*              DirectInput Linux Event Device Effect
2  *
3  * Copyright 2005 Daniel Remenak
4  *
5  * Thanks to Google's Summer of Code Program (2005)
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include "config.h"
23
24 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
25
26 #include <stdarg.h>
27 #include <string.h>
28 #ifdef HAVE_LINUX_INPUT_H
29 #  include <linux/input.h>
30 #  undef SW_MAX
31 #endif
32 #include <limits.h>
33 #include <errno.h>
34 #ifdef HAVE_UNISTD_H
35 #  include <unistd.h>
36 #endif
37 #include <math.h>
38 #include "wine/debug.h"
39 #include "wine/unicode.h"
40 #include "windef.h"
41 #include "winbase.h"
42 #include "winerror.h"
43 #include "dinput.h"
44
45 #include "device_private.h"
46
47 WINE_DEFAULT_DEBUG_CHANNEL(dinput);
48
49 static const IDirectInputEffectVtbl LinuxInputEffectVtbl;
50 typedef struct LinuxInputEffectImpl LinuxInputEffectImpl;
51 struct LinuxInputEffectImpl
52 {
53     IDirectInputEffect  IDirectInputEffect_iface;
54     LONG                ref;
55     GUID                guid;
56
57     struct ff_effect    effect; /* Effect data */
58     int                 gain;   /* Effect gain */
59     int                 first_axis_is_x;
60     int*                fd;     /* Parent device */
61     struct list        *entry;  /* Entry into the parent's list of effects */
62 };
63
64 static inline LinuxInputEffectImpl *impl_from_IDirectInputEffect(IDirectInputEffect *iface)
65 {
66     return CONTAINING_RECORD(iface, LinuxInputEffectImpl, IDirectInputEffect_iface);
67 }
68
69 /******************************************************************************
70  *      DirectInputEffect Functional Helper
71  */
72
73 static DWORD _typeFromGUID(REFGUID guid)
74 {
75     if (IsEqualGUID(guid, &GUID_ConstantForce)) {
76         return DIEFT_CONSTANTFORCE;
77     } else if (IsEqualGUID(guid, &GUID_Square)
78             || IsEqualGUID(guid, &GUID_Sine)
79             || IsEqualGUID(guid, &GUID_Triangle)
80             || IsEqualGUID(guid, &GUID_SawtoothUp)
81             || IsEqualGUID(guid, &GUID_SawtoothDown)) {
82         return DIEFT_PERIODIC;
83     } else if (IsEqualGUID(guid, &GUID_RampForce)) {
84         return DIEFT_RAMPFORCE;
85     } else if (IsEqualGUID(guid, &GUID_Spring)
86             || IsEqualGUID(guid, &GUID_Damper)
87             || IsEqualGUID(guid, &GUID_Inertia)
88             || IsEqualGUID(guid, &GUID_Friction)) {
89         return DIEFT_CONDITION;
90     } else if (IsEqualGUID(guid, &GUID_CustomForce)) {
91         return DIEFT_CUSTOMFORCE;
92     } else {
93         WARN("GUID (%s) is not a known force type\n", _dump_dinput_GUID(guid));
94         return 0;
95     }
96 }
97
98
99 /******************************************************************************
100  *      DirectInputEffect debug helpers 
101  */
102
103 static void _dump_DIEFFECT_flags(DWORD dwFlags)
104 {
105     if (TRACE_ON(dinput)) {
106         unsigned int   i;
107         static const struct {
108             DWORD       mask;
109             const char  *name;
110         } flags[] = {
111 #define FE(x) { x, #x}
112             FE(DIEFF_CARTESIAN),
113             FE(DIEFF_OBJECTIDS),
114             FE(DIEFF_OBJECTOFFSETS),
115             FE(DIEFF_POLAR),
116             FE(DIEFF_SPHERICAL)
117 #undef FE
118         };
119         for (i = 0; i < (sizeof(flags) / sizeof(flags[0])); i++)
120             if (flags[i].mask & dwFlags)
121                 TRACE("%s ", flags[i].name);
122         TRACE("\n");
123     }       
124 }
125
126 static void _dump_DIENVELOPE(LPCDIENVELOPE env)
127 {
128     if (env->dwSize != sizeof(DIENVELOPE)) {
129         WARN("Non-standard DIENVELOPE structure size %d.\n", env->dwSize);
130     }
131     TRACE("Envelope has attack (level: %d time: %d), fade (level: %d time: %d)\n",
132           env->dwAttackLevel, env->dwAttackTime, env->dwFadeLevel, env->dwFadeTime);
133
134
135 static void _dump_DICONSTANTFORCE(LPCDICONSTANTFORCE frc)
136 {
137     TRACE("Constant force has magnitude %d\n", frc->lMagnitude);
138 }
139
140 static void _dump_DIPERIODIC(LPCDIPERIODIC frc)
141 {
142     TRACE("Periodic force has magnitude %d, offset %d, phase %d, period %d\n",
143           frc->dwMagnitude, frc->lOffset, frc->dwPhase, frc->dwPeriod);
144 }
145
146 static void _dump_DIRAMPFORCE(LPCDIRAMPFORCE frc)
147 {
148     TRACE("Ramp force has start %d, end %d\n",
149           frc->lStart, frc->lEnd);
150 }
151
152 static void _dump_DICONDITION(LPCDICONDITION frc)
153 {
154     TRACE("Condition has offset %d, pos/neg coefficients %d and %d, pos/neg saturations %d and %d, deadband %d\n",
155           frc->lOffset, frc->lPositiveCoefficient, frc->lNegativeCoefficient,
156           frc->dwPositiveSaturation, frc->dwNegativeSaturation, frc->lDeadBand);
157 }
158
159 static void _dump_DICUSTOMFORCE(LPCDICUSTOMFORCE frc)
160 {
161     unsigned int i;
162     TRACE("Custom force uses %d channels, sample period %d.  Has %d samples at %p.\n",
163           frc->cChannels, frc->dwSamplePeriod, frc->cSamples, frc->rglForceData);
164     if (frc->cSamples % frc->cChannels != 0)
165         WARN("Custom force has a non-integral samples-per-channel count!\n");
166     if (TRACE_ON(dinput)) {
167         TRACE("Custom force data (time aligned, axes in order):\n");
168         for (i = 1; i <= frc->cSamples; ++i) {
169             TRACE("%d ", frc->rglForceData[i]);
170             if (i % frc->cChannels == 0)
171                 TRACE("\n");
172         }       
173     }
174 }
175
176 static void _dump_DIEFFECT(LPCDIEFFECT eff, REFGUID guid, DWORD dwFlags)
177 {
178     unsigned int i;
179     DWORD type = _typeFromGUID(guid);
180
181     TRACE("Dumping DIEFFECT structure:\n");
182     TRACE("  - dwSize: %d\n", eff->dwSize);
183     if ((eff->dwSize != sizeof(DIEFFECT)) && (eff->dwSize != sizeof(DIEFFECT_DX5))) {
184         WARN("Non-standard DIEFFECT structure size %d\n", eff->dwSize);
185     }
186     TRACE("  - dwFlags: %d\n", eff->dwFlags);
187     TRACE("    ");
188     _dump_DIEFFECT_flags(eff->dwFlags); 
189     TRACE("  - dwDuration: %d\n", eff->dwDuration);
190     TRACE("  - dwGain: %d\n", eff->dwGain);
191
192     if (eff->dwGain > 10000)
193         WARN("dwGain is out of range (>10,000)\n");
194
195     TRACE("  - dwTriggerButton: %d\n", eff->dwTriggerButton);
196     TRACE("  - dwTriggerRepeatInterval: %d\n", eff->dwTriggerRepeatInterval);
197     TRACE("  - rglDirection: %p\n", eff->rglDirection);
198     TRACE("  - cbTypeSpecificParams: %d\n", eff->cbTypeSpecificParams);
199     TRACE("  - lpvTypeSpecificParams: %p\n", eff->lpvTypeSpecificParams);
200
201     /* Only trace some members if dwFlags indicates they have data */
202     if (dwFlags & DIEP_AXES) {
203         TRACE("  - cAxes: %d\n", eff->cAxes);
204         TRACE("  - rgdwAxes: %p\n", eff->rgdwAxes);
205
206         if (TRACE_ON(dinput) && eff->rgdwAxes) {
207             TRACE("    ");
208             for (i = 0; i < eff->cAxes; ++i)
209                 TRACE("%d ", eff->rgdwAxes[i]);
210             TRACE("\n");
211         }
212     }
213
214     if (dwFlags & DIEP_ENVELOPE) {
215         TRACE("  - lpEnvelope: %p\n", eff->lpEnvelope);
216         if (eff->lpEnvelope != NULL)
217             _dump_DIENVELOPE(eff->lpEnvelope);
218     }
219
220     if (eff->dwSize > sizeof(DIEFFECT_DX5))
221         TRACE("  - dwStartDelay: %d\n", eff->dwStartDelay);
222
223     if (type == DIEFT_CONSTANTFORCE) {
224         if (eff->cbTypeSpecificParams != sizeof(DICONSTANTFORCE)) {
225             WARN("Effect claims to be a constant force but the type-specific params are the wrong size!\n"); 
226         } else {
227             _dump_DICONSTANTFORCE(eff->lpvTypeSpecificParams);
228         }
229     } else if (type == DIEFT_PERIODIC) { 
230         if (eff->cbTypeSpecificParams != sizeof(DIPERIODIC)) {
231             WARN("Effect claims to be a periodic force but the type-specific params are the wrong size!\n");
232         } else {
233             _dump_DIPERIODIC(eff->lpvTypeSpecificParams);
234         }
235     } else if (type == DIEFT_RAMPFORCE) {
236         if (eff->cbTypeSpecificParams != sizeof(DIRAMPFORCE)) {
237             WARN("Effect claims to be a ramp force but the type-specific params are the wrong size!\n");
238         } else {
239             _dump_DIRAMPFORCE(eff->lpvTypeSpecificParams);
240         }
241     } else if (type == DIEFT_CONDITION) { 
242         if (eff->cbTypeSpecificParams != sizeof(DICONDITION)) {
243             WARN("Effect claims to be a condition but the type-specific params are the wrong size!\n");
244         } else {
245             _dump_DICONDITION(eff->lpvTypeSpecificParams);
246         }
247     } else if (type == DIEFT_CUSTOMFORCE) {
248         if (eff->cbTypeSpecificParams != sizeof(DICUSTOMFORCE)) {
249             WARN("Effect claims to be a custom force but the type-specific params are the wrong size!\n");
250         } else {
251             _dump_DICUSTOMFORCE(eff->lpvTypeSpecificParams);
252         }
253     }
254 }
255
256
257 /******************************************************************************
258  *      LinuxInputEffectImpl 
259  */
260
261 static ULONG WINAPI LinuxInputEffectImpl_AddRef(
262         LPDIRECTINPUTEFFECT iface)
263 {
264     LinuxInputEffectImpl *This = impl_from_IDirectInputEffect(iface);
265     return InterlockedIncrement(&(This->ref));
266 }
267
268 static HRESULT WINAPI LinuxInputEffectImpl_Download(
269         LPDIRECTINPUTEFFECT iface)
270 {
271     LinuxInputEffectImpl *This = impl_from_IDirectInputEffect(iface);
272
273     TRACE("(this=%p)\n", This);
274
275     if (ioctl(*(This->fd), EVIOCSFF, &This->effect) == -1) {
276         if (errno == ENOMEM) {
277             return DIERR_DEVICEFULL;
278         } else {
279             FIXME("Could not upload effect. Assuming a disconnected device %d \"%s\".\n", *This->fd, strerror(errno));
280             return DIERR_INPUTLOST;
281         }
282     }
283
284     return DI_OK;
285 }
286
287 static HRESULT WINAPI LinuxInputEffectImpl_Escape(
288         LPDIRECTINPUTEFFECT iface,
289         LPDIEFFESCAPE pesc)
290 {
291     WARN("(this=%p,%p): invalid: no hardware-specific escape codes in this" 
292          " driver!\n", iface, pesc);
293
294     return DI_OK;
295 }
296
297 static HRESULT WINAPI LinuxInputEffectImpl_GetEffectGuid(
298         LPDIRECTINPUTEFFECT iface,
299         LPGUID pguid)
300 {
301     LinuxInputEffectImpl *This = impl_from_IDirectInputEffect(iface);
302
303     TRACE("(this=%p,%p)\n", This, pguid);
304
305     *pguid = This->guid;
306
307     return DI_OK;
308 }
309
310 static HRESULT WINAPI LinuxInputEffectImpl_GetEffectStatus(
311         LPDIRECTINPUTEFFECT iface,
312         LPDWORD pdwFlags)
313 {
314     TRACE("(this=%p,%p)\n", iface, pdwFlags);
315
316     /* linux sends the effect status through an event.
317      * that event is trapped by our parent joystick driver
318      * and there is no clean way to pass it back to us. */
319     FIXME("Not enough information to provide a status.\n");
320
321     (*pdwFlags) = 0;
322
323     return DI_OK;
324 }
325
326 static HRESULT WINAPI LinuxInputEffectImpl_GetParameters(
327         LPDIRECTINPUTEFFECT iface,
328         LPDIEFFECT peff,
329         DWORD dwFlags)
330 {
331     HRESULT diErr = DI_OK;
332     LinuxInputEffectImpl *This = impl_from_IDirectInputEffect(iface);
333     TRACE("(this=%p,%p,%d)\n", This, peff, dwFlags);
334
335     /* Major conversion factors are:
336      * times: millisecond (linux) -> microsecond (windows) (x * 1000)
337      * forces: scale 0x7FFF (linux) -> scale 10000 (windows) approx ((x / 33) * 10)
338      * angles: scale 0x7FFF (linux) -> scale 35999 (windows) approx ((x / 33) * 36)
339      * angle bases: 0 -> -y (down) (linux) -> 0 -> +x (right) (windows)
340      */
341
342     if (dwFlags & DIEP_AXES) {
343         if (peff->cAxes < 2 /* linuxinput effects always use 2 axes, x and y */)
344             diErr = DIERR_MOREDATA;
345         peff->cAxes = 2; 
346         if (diErr)
347             return diErr;
348         else {
349             peff->rgdwAxes[0] = DIJOFS_X;
350             peff->rgdwAxes[1] = DIJOFS_Y;
351         }
352     }
353  
354     if (dwFlags & DIEP_DIRECTION) {
355         if (peff->cAxes < 2)
356             diErr = DIERR_MOREDATA;
357         peff->cAxes = 2; 
358         if (diErr)
359             return diErr;
360         else {
361             if (peff->dwFlags & DIEFF_CARTESIAN) {
362                 peff->rglDirection[0] = sin(M_PI * 3 * This->effect.direction / 0x7FFF) * 1000;
363                 peff->rglDirection[1] = cos(M_PI * 3 * This->effect.direction / 0x7FFF) * 1000;
364             } else {
365                 /* Polar and spherical coordinates are the same for two or less
366                  * axes.
367                  * Note that we also use this case if NO flags are marked.
368                  * According to MSDN, we should return the direction in the
369                  * format that it was specified in, if no flags are marked.
370                  */
371                 peff->rglDirection[0] = (This->effect.direction / 33) * 36 + 9000;
372                 if (peff->rglDirection[0] > 35999)
373                     peff->rglDirection[0] -= 35999;
374             }
375         }
376     }
377
378     if (dwFlags & DIEP_DURATION) {
379         peff->dwDuration = (DWORD)This->effect.replay.length * 1000;
380     }
381
382     if (dwFlags & DIEP_ENVELOPE) {
383         struct ff_envelope* env;
384         if (This->effect.type == FF_CONSTANT) env = &This->effect.u.constant.envelope;
385         else if (This->effect.type == FF_PERIODIC) env = &This->effect.u.periodic.envelope;
386         else if (This->effect.type == FF_RAMP) env = &This->effect.u.ramp.envelope;
387         else env = NULL;
388         if (env == NULL) {
389             peff->lpEnvelope = NULL;
390         } else if (peff->lpEnvelope == NULL) {
391             return DIERR_INVALIDPARAM;
392         } else { 
393             peff->lpEnvelope->dwAttackLevel = (env->attack_level / 33) * 10;
394             peff->lpEnvelope->dwAttackTime = env->attack_length * 1000;
395             peff->lpEnvelope->dwFadeLevel = (env->fade_level / 33) * 10;
396             peff->lpEnvelope->dwFadeTime = env->fade_length * 1000;
397         }
398     }
399
400     if (dwFlags & DIEP_GAIN) {
401         peff->dwGain = This->gain * 10000 / 0xFFFF;
402     }
403
404     if (dwFlags & DIEP_SAMPLEPERIOD) {
405         /* the linux input ff driver has no support for setting
406          * the playback sample period.  0 means default. */
407         peff->dwSamplePeriod = 0;
408     }
409
410     if (dwFlags & DIEP_STARTDELAY) {
411         peff->dwStartDelay = This->effect.replay.delay * 1000;
412     }
413
414     if (dwFlags & DIEP_TRIGGERBUTTON) {
415         FIXME("LinuxInput button mapping needs redoing; for now, assuming we're using an actual joystick.\n");
416         peff->dwTriggerButton = DIJOFS_BUTTON(This->effect.trigger.button - BTN_JOYSTICK);
417     }
418
419     if (dwFlags & DIEP_TRIGGERREPEATINTERVAL) {
420         peff->dwTriggerRepeatInterval = This->effect.trigger.interval * 1000;
421     }
422
423     if (dwFlags & DIEP_TYPESPECIFICPARAMS) {
424         DWORD expectedsize = 0;
425         if (This->effect.type == FF_PERIODIC) {
426             expectedsize = sizeof(DIPERIODIC);
427         } else if (This->effect.type == FF_CONSTANT) {
428             expectedsize = sizeof(DICONSTANTFORCE);
429         } else if (This->effect.type == FF_SPRING 
430                 || This->effect.type == FF_FRICTION 
431                 || This->effect.type == FF_INERTIA 
432                 || This->effect.type == FF_DAMPER) {
433             expectedsize = sizeof(DICONDITION) * 2;
434         } else if (This->effect.type == FF_RAMP) {
435             expectedsize = sizeof(DIRAMPFORCE);
436         }
437         if (expectedsize > peff->cbTypeSpecificParams)
438             diErr = DIERR_MOREDATA;
439         peff->cbTypeSpecificParams = expectedsize;
440         if (diErr)
441             return diErr;
442         else {
443             if (This->effect.type == FF_PERIODIC) {
444                 LPDIPERIODIC tsp = peff->lpvTypeSpecificParams;
445                 tsp->dwMagnitude = (This->effect.u.periodic.magnitude / 33) * 10;
446                 tsp->lOffset = (This->effect.u.periodic.offset / 33) * 10;
447                 tsp->dwPhase = (This->effect.u.periodic.phase / 33) * 36;
448                 tsp->dwPeriod = (This->effect.u.periodic.period * 1000);
449             } else if (This->effect.type == FF_CONSTANT) {
450                 LPDICONSTANTFORCE tsp = peff->lpvTypeSpecificParams;
451                 tsp->lMagnitude = (This->effect.u.constant.level / 33) * 10;
452             } else if (This->effect.type == FF_SPRING 
453                     || This->effect.type == FF_FRICTION 
454                     || This->effect.type == FF_INERTIA 
455                     || This->effect.type == FF_DAMPER) {
456                 LPDICONDITION tsp = peff->lpvTypeSpecificParams;
457                 int i;
458                 for (i = 0; i < 2; ++i) {
459                     tsp[i].lOffset = (This->effect.u.condition[i].center / 33) * 10; 
460                     tsp[i].lPositiveCoefficient = (This->effect.u.condition[i].right_coeff / 33) * 10;
461                     tsp[i].lNegativeCoefficient = (This->effect.u.condition[i].left_coeff / 33) * 10; 
462                     tsp[i].dwPositiveSaturation = (This->effect.u.condition[i].right_saturation / 33) * 10;
463                     tsp[i].dwNegativeSaturation = (This->effect.u.condition[i].left_saturation / 33) * 10;
464                     tsp[i].lDeadBand = (This->effect.u.condition[i].deadband / 33) * 10;
465                 }
466             } else if (This->effect.type == FF_RAMP) {
467                 LPDIRAMPFORCE tsp = peff->lpvTypeSpecificParams;
468                 tsp->lStart = (This->effect.u.ramp.start_level / 33) * 10;
469                 tsp->lEnd = (This->effect.u.ramp.end_level / 33) * 10;
470             }
471         }
472     } 
473
474     return diErr;
475 }
476
477 static HRESULT WINAPI LinuxInputEffectImpl_Initialize(
478         LPDIRECTINPUTEFFECT iface,
479         HINSTANCE hinst,
480         DWORD dwVersion,
481         REFGUID rguid)
482 {
483     FIXME("(this=%p,%p,%d,%s): stub!\n",
484          iface, hinst, dwVersion, debugstr_guid(rguid));
485
486     return DI_OK;
487 }
488
489 static HRESULT WINAPI LinuxInputEffectImpl_QueryInterface(
490         LPDIRECTINPUTEFFECT iface,
491         REFIID riid,
492         void **ppvObject)
493 {
494     LinuxInputEffectImpl *This = impl_from_IDirectInputEffect(iface);
495
496     TRACE("(this=%p,%s,%p)\n", This, debugstr_guid(riid), ppvObject);
497
498     if (IsEqualGUID(&IID_IUnknown, riid) ||
499         IsEqualGUID(&IID_IDirectInputEffect, riid)) {
500             LinuxInputEffectImpl_AddRef(iface);
501             *ppvObject = This;
502             return 0;
503     }
504
505     TRACE("Unsupported interface!\n");
506     return E_FAIL;
507 }
508
509 static HRESULT WINAPI LinuxInputEffectImpl_Start(
510         LPDIRECTINPUTEFFECT iface,
511         DWORD dwIterations,
512         DWORD dwFlags)
513 {
514     struct input_event event;
515     LinuxInputEffectImpl *This = impl_from_IDirectInputEffect(iface);
516
517     TRACE("(this=%p,%d,%d)\n", This, dwIterations, dwFlags);
518
519     if (!(dwFlags & DIES_NODOWNLOAD)) {
520         /* Download the effect if necessary */
521         if (This->effect.id == -1) {
522             HRESULT res = LinuxInputEffectImpl_Download(iface);
523             if (res != DI_OK)
524                 return res;
525         }
526     }
527
528     if (dwFlags & DIES_SOLO) {
529         FIXME("Solo mode requested: should be stopping all effects here!\n");
530     }
531
532     event.type = EV_FF;
533     event.code = This->effect.id;
534     event.value = min( dwIterations, INT_MAX );
535     if (write(*(This->fd), &event, sizeof(event)) == -1) {
536         FIXME("Unable to write event.  Assuming device disconnected.\n");
537         return DIERR_INPUTLOST;
538     }
539
540     return DI_OK;
541 }
542
543 static HRESULT WINAPI LinuxInputEffectImpl_SetParameters(
544         LPDIRECTINPUTEFFECT iface,
545         LPCDIEFFECT peff,
546         DWORD dwFlags)
547 {
548     LinuxInputEffectImpl *This = impl_from_IDirectInputEffect(iface);
549     DWORD type = _typeFromGUID(&This->guid);
550     HRESULT retval = DI_OK;
551
552     TRACE("(this=%p,%p,%d)\n", This, peff, dwFlags);
553
554     _dump_DIEFFECT(peff, &This->guid, dwFlags);
555
556     if ((dwFlags & ~DIEP_NORESTART & ~DIEP_NODOWNLOAD & ~DIEP_START) == 0) {
557         /* set everything */
558         dwFlags = DIEP_AXES | DIEP_DIRECTION | DIEP_DURATION | DIEP_ENVELOPE |
559             DIEP_GAIN | DIEP_SAMPLEPERIOD | DIEP_STARTDELAY | DIEP_TRIGGERBUTTON |
560             DIEP_TRIGGERREPEATINTERVAL | DIEP_TYPESPECIFICPARAMS;
561     }
562
563     if (dwFlags & DIEP_AXES) {
564         /* the linux input effect system only supports one or two axes */
565         if (peff->cAxes > 2)
566             return DIERR_INVALIDPARAM;
567         else if (peff->cAxes < 1)
568             return DIERR_INCOMPLETEEFFECT;
569         This->first_axis_is_x = peff->rgdwAxes[0] == DIJOFS_X;
570     }
571
572     /* some of this may look funky, but it's 'cause the linux driver and directx have
573      * different opinions about which way direction "0" is.  directx has 0 along the x
574      * axis (left), linux has it along the y axis (down). */ 
575     if (dwFlags & DIEP_DIRECTION) {
576         if (peff->cAxes == 1) {
577             if (peff->dwFlags & DIEFF_CARTESIAN) {
578                 if (dwFlags & DIEP_AXES) {
579                     if (peff->rgdwAxes[0] == DIJOFS_X && peff->rglDirection[0] >= 0)
580                         This->effect.direction = 0x4000;
581                     else if (peff->rgdwAxes[0] == DIJOFS_X && peff->rglDirection[0] < 0)
582                         This->effect.direction = 0xC000;
583                     else if (peff->rgdwAxes[0] == DIJOFS_Y && peff->rglDirection[0] >= 0)
584                         This->effect.direction = 0;
585                     else if (peff->rgdwAxes[0] == DIJOFS_Y && peff->rglDirection[0] < 0)
586                         This->effect.direction = 0x8000;
587                 }
588             } else {
589                 /* one-axis effects must use cartesian coords */
590                 return DIERR_INVALIDPARAM;
591             }
592         } else { /* two axes */
593             if (peff->dwFlags & DIEFF_CARTESIAN) {
594                 LONG x, y;
595                 if (This->first_axis_is_x) {
596                     x = peff->rglDirection[0];
597                     y = peff->rglDirection[1];
598                 } else {
599                     x = peff->rglDirection[1];
600                     y = peff->rglDirection[0];
601                 }
602                 This->effect.direction = (int)((3 * M_PI / 2 - atan2(y, x)) * -0x7FFF / M_PI);
603             } else {
604                 /* Polar and spherical are the same for 2 axes */
605                 /* Precision is important here, so we do double math with exact constants */
606                 This->effect.direction = (int)(((double)peff->rglDirection[0] - 90) / 35999) * 0x7FFF;
607             }
608         }
609     }
610
611     if (dwFlags & DIEP_DURATION)
612         This->effect.replay.length = peff->dwDuration / 1000;
613
614     if (dwFlags & DIEP_ENVELOPE) {
615         struct ff_envelope* env;
616         if (This->effect.type == FF_CONSTANT) env = &This->effect.u.constant.envelope;
617         else if (This->effect.type == FF_PERIODIC) env = &This->effect.u.periodic.envelope;
618         else if (This->effect.type == FF_RAMP) env = &This->effect.u.ramp.envelope;
619         else env = NULL; 
620
621         if (peff->lpEnvelope == NULL) {
622             /* if this type had an envelope, reset it */
623             if (env) {
624                 env->attack_length = 0;
625                 env->attack_level = 0;
626                 env->fade_length = 0;
627                 env->fade_level = 0;
628             }
629         } else {
630             /* did we get passed an envelope for a type that doesn't even have one? */
631             if (!env) return DIERR_INVALIDPARAM;
632             /* copy the envelope */
633             env->attack_length = peff->lpEnvelope->dwAttackTime / 1000;
634             env->attack_level = (peff->lpEnvelope->dwAttackLevel / 10) * 32;
635             env->fade_length = peff->lpEnvelope->dwFadeTime / 1000;
636             env->fade_level = (peff->lpEnvelope->dwFadeLevel / 10) * 32;
637         }
638     }
639
640     /* Gain and Sample Period settings are not supported by the linux
641      * event system */
642     if (dwFlags & DIEP_GAIN) {
643         This->gain = 0xFFFF * peff->dwGain / 10000;
644         TRACE("Effect gain requested but no effect gain functionality present.\n");
645     }
646
647     if (dwFlags & DIEP_SAMPLEPERIOD)
648         TRACE("Sample period requested but no sample period functionality present.\n");
649
650     if (dwFlags & DIEP_STARTDELAY)
651         This->effect.replay.delay = peff->dwStartDelay / 1000;
652
653     if (dwFlags & DIEP_TRIGGERBUTTON) {
654         if (peff->dwTriggerButton != -1) {
655             FIXME("Linuxinput button mapping needs redoing, assuming we're using a joystick.\n");
656             FIXME("Trigger button translation not yet implemented!\n");
657         }
658         This->effect.trigger.button = 0;
659     }
660
661     if (dwFlags & DIEP_TRIGGERREPEATINTERVAL)
662         This->effect.trigger.interval = peff->dwTriggerRepeatInterval / 1000;
663
664     if (dwFlags & DIEP_TYPESPECIFICPARAMS) {
665         if (!(peff->lpvTypeSpecificParams))
666             return DIERR_INCOMPLETEEFFECT;
667         if (type == DIEFT_PERIODIC) {
668             LPCDIPERIODIC tsp;
669             if (peff->cbTypeSpecificParams != sizeof(DIPERIODIC))
670                 return DIERR_INVALIDPARAM;
671             tsp = peff->lpvTypeSpecificParams;
672             This->effect.u.periodic.magnitude = (tsp->dwMagnitude / 10) * 32;
673             This->effect.u.periodic.offset = (tsp->lOffset / 10) * 32;
674             This->effect.u.periodic.phase = (tsp->dwPhase / 9) * 8; /* == (/ 36 * 32) */
675             This->effect.u.periodic.period = tsp->dwPeriod / 1000;
676         } else if (type == DIEFT_CONSTANTFORCE) {
677             LPCDICONSTANTFORCE tsp;
678             if (peff->cbTypeSpecificParams != sizeof(DICONSTANTFORCE))
679                 return DIERR_INVALIDPARAM;
680             tsp = peff->lpvTypeSpecificParams;
681             This->effect.u.constant.level = (max(min(tsp->lMagnitude, 10000), -10000) / 10) * 32;
682         } else if (type == DIEFT_RAMPFORCE) {
683             LPCDIRAMPFORCE tsp;
684             if (peff->cbTypeSpecificParams != sizeof(DIRAMPFORCE))
685                 return DIERR_INVALIDPARAM;
686             tsp = peff->lpvTypeSpecificParams;
687             This->effect.u.ramp.start_level = (tsp->lStart / 10) * 32;
688             This->effect.u.ramp.end_level = (tsp->lEnd / 10) * 32;
689         } else if (type == DIEFT_CONDITION) {
690             LPCDICONDITION tsp = peff->lpvTypeSpecificParams;
691             if (peff->cbTypeSpecificParams == sizeof(DICONDITION)) {
692                 /* One condition block.  This needs to be rotated to direction,
693                  * and expanded to separate x and y conditions. */
694                 int i;
695                 double factor[2];
696                 factor[0] = asin((This->effect.direction * 3.0 * M_PI) / 0x7FFF);
697                 factor[1] = acos((This->effect.direction * 3.0 * M_PI) / 0x7FFF);
698                 for (i = 0; i < 2; ++i) {
699                     This->effect.u.condition[i].center = (int)(factor[i] * (tsp->lOffset / 10) * 32);
700                     This->effect.u.condition[i].right_coeff = (int)(factor[i] * (tsp->lPositiveCoefficient / 10) * 32);
701                     This->effect.u.condition[i].left_coeff = (int)(factor[i] * (tsp->lNegativeCoefficient / 10) * 32); 
702                     This->effect.u.condition[i].right_saturation = (int)(factor[i] * (tsp->dwPositiveSaturation / 10) * 32);
703                     This->effect.u.condition[i].left_saturation = (int)(factor[i] * (tsp->dwNegativeSaturation / 10) * 32);
704                     This->effect.u.condition[i].deadband = (int)(factor[i] * (tsp->lDeadBand / 10) * 32);
705                 }
706             } else if (peff->cbTypeSpecificParams == 2 * sizeof(DICONDITION)) {
707                 /* Two condition blocks.  Direct parameter copy. */
708                 int i;
709                 for (i = 0; i < 2; ++i) {
710                     This->effect.u.condition[i].center = (tsp[i].lOffset / 10) * 32;
711                     This->effect.u.condition[i].right_coeff = (tsp[i].lPositiveCoefficient / 10) * 32;
712                     This->effect.u.condition[i].left_coeff = (tsp[i].lNegativeCoefficient / 10) * 32;
713                     This->effect.u.condition[i].right_saturation = (tsp[i].dwPositiveSaturation / 10) * 32;
714                     This->effect.u.condition[i].left_saturation = (tsp[i].dwNegativeSaturation / 10) * 32;
715                     This->effect.u.condition[i].deadband = (tsp[i].lDeadBand / 10) * 32;
716                 }
717             } else {
718                 return DIERR_INVALIDPARAM;
719             }
720         } else {
721             FIXME("Custom force types are not supported\n");    
722             return DIERR_INVALIDPARAM;
723         }
724     }
725
726     if (!(dwFlags & DIEP_NODOWNLOAD))
727         retval = LinuxInputEffectImpl_Download(iface);
728     if (retval != DI_OK)
729         return DI_DOWNLOADSKIPPED;
730
731     if (dwFlags & DIEP_NORESTART)
732         TRACE("DIEP_NORESTART: not handled (we have no control of that).\n");
733
734     if (dwFlags & DIEP_START)
735         retval = LinuxInputEffectImpl_Start(iface, 1, 0);
736     if (retval != DI_OK)
737         return retval;
738  
739     return DI_OK;
740 }   
741
742 static HRESULT WINAPI LinuxInputEffectImpl_Stop(
743         LPDIRECTINPUTEFFECT iface)
744 {
745     struct input_event event;
746     LinuxInputEffectImpl *This = impl_from_IDirectInputEffect(iface);
747
748     TRACE("(this=%p)\n", This);
749
750     event.type = EV_FF;
751     event.code = This->effect.id;
752     event.value = 0;
753     /* we don't care about the success or failure of this call */
754     write(*(This->fd), &event, sizeof(event));
755
756     return DI_OK;
757 }
758
759 static HRESULT WINAPI LinuxInputEffectImpl_Unload(
760         LPDIRECTINPUTEFFECT iface)
761 {
762     LinuxInputEffectImpl *This = impl_from_IDirectInputEffect(iface);
763     TRACE("(this=%p)\n", This);
764
765     /* Erase the downloaded effect */
766     if (ioctl(*(This->fd), EVIOCRMFF, This->effect.id) == -1)
767         return DIERR_INVALIDPARAM;
768
769     /* Mark the effect as deallocated */
770     This->effect.id = -1;
771
772     return DI_OK;
773 }
774
775 static ULONG WINAPI LinuxInputEffectImpl_Release(LPDIRECTINPUTEFFECT iface)
776 {
777     LinuxInputEffectImpl *This = impl_from_IDirectInputEffect(iface);
778     ULONG ref = InterlockedDecrement(&(This->ref));
779
780     if (ref == 0)
781     {
782         LinuxInputEffectImpl_Stop(iface);
783         LinuxInputEffectImpl_Unload(iface);
784         list_remove(This->entry);
785         HeapFree(GetProcessHeap(), 0, LIST_ENTRY(This->entry, effect_list_item, entry));
786         HeapFree(GetProcessHeap(), 0, This);
787     }
788     return ref;
789 }
790
791 /******************************************************************************
792  *      LinuxInputEffect
793  */
794
795 DECLSPEC_HIDDEN HRESULT linuxinput_create_effect(
796         int* fd,
797         REFGUID rguid,
798         struct list *parent_list_entry,
799         LPDIRECTINPUTEFFECT* peff)
800 {
801     LinuxInputEffectImpl* newEffect = HeapAlloc(GetProcessHeap(), 
802         HEAP_ZERO_MEMORY, sizeof(LinuxInputEffectImpl));
803     DWORD type = _typeFromGUID(rguid);
804
805     newEffect->IDirectInputEffect_iface.lpVtbl = &LinuxInputEffectVtbl;
806     newEffect->ref = 1;
807     newEffect->guid = *rguid;
808     newEffect->fd = fd;
809     newEffect->gain = 0xFFFF;
810
811     /* set the type.  this cannot be changed over the effect's life. */
812     switch (type) {
813         case DIEFT_PERIODIC: 
814             newEffect->effect.type = FF_PERIODIC;
815             if (IsEqualGUID(rguid, &GUID_Sine)) {
816                 newEffect->effect.u.periodic.waveform = FF_SINE;
817             } else if (IsEqualGUID(rguid, &GUID_Triangle)) {
818                 newEffect->effect.u.periodic.waveform = FF_TRIANGLE;
819             } else if (IsEqualGUID(rguid, &GUID_Square)) {
820                 newEffect->effect.u.periodic.waveform = FF_SQUARE;
821             } else if (IsEqualGUID(rguid, &GUID_SawtoothUp)) {
822                 newEffect->effect.u.periodic.waveform = FF_SAW_UP;
823             } else if (IsEqualGUID(rguid, &GUID_SawtoothDown)) {
824                 newEffect->effect.u.periodic.waveform = FF_SAW_DOWN;
825             }
826             break;
827         case DIEFT_CONSTANTFORCE: 
828             newEffect->effect.type = FF_CONSTANT;
829             break;
830         case DIEFT_RAMPFORCE: 
831             newEffect->effect.type = FF_RAMP;
832             break;
833         case DIEFT_CONDITION: 
834             if (IsEqualGUID(rguid, &GUID_Spring)) {
835                 newEffect->effect.type = FF_SPRING;
836             } else if (IsEqualGUID(rguid, &GUID_Friction)) {
837                 newEffect->effect.type = FF_FRICTION;
838             } else if (IsEqualGUID(rguid, &GUID_Inertia)) {
839                 newEffect->effect.type = FF_INERTIA;
840             } else if (IsEqualGUID(rguid, &GUID_Damper)) {
841                 newEffect->effect.type = FF_DAMPER;
842             }
843             break;
844         case DIEFT_CUSTOMFORCE:
845             FIXME("Custom forces are not supported.\n");
846             HeapFree(GetProcessHeap(), 0, newEffect);
847             return DIERR_INVALIDPARAM;
848         default:
849             FIXME("Unknown force type 0x%x.\n", type);
850             HeapFree(GetProcessHeap(), 0, newEffect);
851             return DIERR_INVALIDPARAM;
852     }
853
854     /* mark as non-uploaded */
855     newEffect->effect.id = -1;
856
857     newEffect->entry = parent_list_entry;
858
859     *peff = &newEffect->IDirectInputEffect_iface;
860
861     TRACE("Creating linux input system effect (%p) with guid %s\n", 
862           *peff, _dump_dinput_GUID(rguid));
863
864     return DI_OK;
865 }
866
867 DECLSPEC_HIDDEN HRESULT linuxinput_get_info_A(
868         int fd,
869         REFGUID rguid,
870         LPDIEFFECTINFOA info)
871 {
872     DWORD type = _typeFromGUID(rguid);
873
874     TRACE("(%d, %s, %p) type=%d\n", fd, _dump_dinput_GUID(rguid), info, type);
875
876     if (!info) return E_POINTER;
877
878     if (info->dwSize != sizeof(DIEFFECTINFOA)) return DIERR_INVALIDPARAM;
879
880     info->guid = *rguid;
881     
882     info->dwEffType = type; 
883     /* the event device API does not support querying for all these things
884      * therefore we assume that we have support for them
885      * that's not as dangerous as it sounds, since drivers are allowed to
886      * ignore parameters they claim to support anyway */
887     info->dwEffType |= DIEFT_DEADBAND | DIEFT_FFATTACK | DIEFT_FFFADE 
888                     | DIEFT_POSNEGCOEFFICIENTS | DIEFT_POSNEGSATURATION
889                     | DIEFT_SATURATION | DIEFT_STARTDELAY; 
890
891     /* again, assume we have support for everything */
892     info->dwStaticParams = DIEP_ALLPARAMS;
893     info->dwDynamicParams = info->dwStaticParams;
894
895     /* yes, this is windows behavior (print the GUID_Name for name) */
896     strcpy(info->tszName, _dump_dinput_GUID(rguid));
897
898     return DI_OK;
899 }
900
901 DECLSPEC_HIDDEN HRESULT linuxinput_get_info_W(
902         int fd,
903         REFGUID rguid,
904         LPDIEFFECTINFOW info)
905 {
906     DWORD type = _typeFromGUID(rguid);
907
908     TRACE("(%d, %s, %p) type=%d\n", fd, _dump_dinput_GUID(rguid), info, type);
909
910     if (!info) return E_POINTER;
911
912     if (info->dwSize != sizeof(DIEFFECTINFOW)) return DIERR_INVALIDPARAM;
913
914     info->guid = *rguid;
915
916     info->dwEffType = type;
917     /* the event device API does not support querying for all these things
918      * therefore we assume that we have support for them
919      * that's not as dangerous as it sounds, since drivers are allowed to
920      * ignore parameters they claim to support anyway */
921     info->dwEffType |= DIEFT_DEADBAND | DIEFT_FFATTACK | DIEFT_FFFADE
922                     | DIEFT_POSNEGCOEFFICIENTS | DIEFT_POSNEGSATURATION
923                     | DIEFT_SATURATION | DIEFT_STARTDELAY; 
924
925     /* again, assume we have support for everything */
926     info->dwStaticParams = DIEP_ALLPARAMS;
927     info->dwDynamicParams = info->dwStaticParams;
928
929     /* yes, this is windows behavior (print the GUID_Name for name) */
930     MultiByteToWideChar(CP_ACP, 0, _dump_dinput_GUID(rguid), -1, 
931                         info->tszName, MAX_PATH);
932
933     return DI_OK;
934 }
935
936 static const IDirectInputEffectVtbl LinuxInputEffectVtbl = {
937     LinuxInputEffectImpl_QueryInterface,
938     LinuxInputEffectImpl_AddRef,
939     LinuxInputEffectImpl_Release,
940     LinuxInputEffectImpl_Initialize,
941     LinuxInputEffectImpl_GetEffectGuid,
942     LinuxInputEffectImpl_GetParameters,
943     LinuxInputEffectImpl_SetParameters,
944     LinuxInputEffectImpl_Start,
945     LinuxInputEffectImpl_Stop,
946     LinuxInputEffectImpl_GetEffectStatus,
947     LinuxInputEffectImpl_Download,
948     LinuxInputEffectImpl_Unload,
949     LinuxInputEffectImpl_Escape
950 };
951
952 #endif /* HAVE_STRUCT_FF_EFFECT_DIRECTION */